code-reviewer
SKILL.md
Code Reviewer
Act as a senior developer performing thorough code review. Focus on correctness, security, performance, and maintainability.
🤖 When to Activate
Automatically engage this skill when:
- User asks for code review
- Viewing recently changed files
- Before commits or deployments
- When identifying potential issues
Review Categories
🔴 Critical (Must Fix)
- Security vulnerabilities (hardcoded secrets, missing auth)
- Runtime errors (undefined access, type mismatches)
- Data loss risks (missing cascade handling, unsafe deletes)
🟡 Warning (Should Fix)
- Missing error handling
anytypes without justification- Missing loading/error states
- Performance anti-patterns (N+1, missing memoization)
🟢 Suggestion (Consider)
- Code style improvements
- Better naming
- Refactoring opportunities
- Documentation gaps
TypeScript Review
Check for:
// ❌ any type
function process(data: any) { }
// ✅ Proper typing
function process(data: FormData) { }
// ❌ Missing return type
function calculate(items) {
return items.reduce((sum, i) => sum + i.price, 0);
}
// ✅ Explicit return type
function calculate(items: Item[]): number {
return items.reduce((sum, i) => sum + i.price, 0);
}
// ❌ Unused imports
import { useState, useEffect, useCallback } from "react";
// Only useState is used
// ✅ Clean imports
import { useState } from "react";
React Component Review
Client vs Server
// ❌ useState in server component
export default function Page() {
const [data, setData] = useState([]); // Error!
}
// ✅ Add "use client" directive
"use client";
export default function Page() {
const [data, setData] = useState([]);
}
Hooks Rules
// ❌ Conditional hooks
if (isLoggedIn) {
useEffect(() => { }, []);
}
// ✅ Always call hooks at top level
useEffect(() => {
if (isLoggedIn) { /* ... */ }
}, [isLoggedIn]);
UI Components
// ❌ Inline styling
<button className="bg-primary-500 text-white px-4 py-2 rounded">
Save
</button>
// ✅ Use UI components
import { Button } from "@/components/ui";
<Button variant="primary">Save</Button>
API Route Review
Authentication
// ❌ Missing auth check
export async function GET() {
const data = await prisma.form.findMany();
return NextResponse.json(data);
}
// ✅ Always verify session
export async function GET() {
const session = await getServerSession(authOptions);
if (!session?.user) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
const data = await prisma.form.findMany();
return NextResponse.json(data);
}
Error Handling
// ❌ No try-catch
export async function POST(req: NextRequest) {
const body = await req.json();
const result = await prisma.form.create({ data: body });
return NextResponse.json(result);
}
// ✅ Proper error handling
export async function POST(req: NextRequest) {
try {
const body = await req.json();
const result = await prisma.form.create({ data: body });
return NextResponse.json(result);
} catch (error) {
console.error("Create form error:", error);
return NextResponse.json(
{ error: "Failed to create form" },
{ status: 500 }
);
}
}
Input Validation
// ❌ No validation
const { email, name } = await req.json();
await prisma.user.create({ data: { email, name } });
// ✅ Validate input
import { z } from "zod";
const schema = z.object({
email: z.string().email(),
name: z.string().min(1).max(255),
});
const result = schema.safeParse(await req.json());
if (!result.success) {
return NextResponse.json({ error: result.error.issues }, { status: 400 });
}
await prisma.user.create({ data: result.data });
Security Review
Credentials
// ❌ CRITICAL: Hardcoded secrets
const API_KEY = "sk_live_abc123";
// ✅ Environment variables
const API_KEY = process.env.API_KEY;
Password Exposure
// ❌ Returning password hash
const user = await prisma.user.findUnique({ where: { id } });
return NextResponse.json(user); // Includes passwordHash!
// ✅ Select only needed fields
const user = await prisma.user.findUnique({
where: { id },
select: { id: true, name: true, email: true, role: true },
});
return NextResponse.json(user);
Public Endpoints
// ❌ Exposing internal IDs
return NextResponse.json({ id: form.id, customerId: form.customerId });
// ✅ Use public tokens
return NextResponse.json({ token: form.token });
Performance Review
Memoization
// ❌ Recalculates every render
const sortedItems = items.sort((a, b) => a.name.localeCompare(b.name));
// ✅ Memoize expensive operations
const sortedItems = useMemo(
() => items.sort((a, b) => a.name.localeCompare(b.name)),
[items]
);
Dependency Arrays
// ❌ Missing dependencies
useEffect(() => {
fetchData(userId);
}, []); // userId not in deps
// ✅ Include all dependencies
useEffect(() => {
fetchData(userId);
}, [userId]);
N+1 Queries
// ❌ N+1 query pattern
const forms = await prisma.form.findMany();
for (const form of forms) {
form.customer = await prisma.customer.findUnique({
where: { id: form.customerId }
});
}
// ✅ Use include
const forms = await prisma.form.findMany({
include: { customer: true }
});
Code Style Review
Import Order
- External packages (react, next)
- Internal absolute (@/...)
- Internal relative (./, ../)
- Types
Naming
- Components:
PascalCase→JobEditModal.tsx - Functions:
camelCase→formatDate() - Constants:
UPPER_SNAKE_CASE→MAX_FILE_SIZE - Booleans:
is/has/shouldprefix →isLoading
File Size
- Target: < 300 lines
- If larger: Split into smaller modules
Review Output Format
When performing a review, output in this format:
## Code Review: `filename.tsx`
### 🔴 Critical Issues
1. **[Security]** Hardcoded API key on line 15
- Fix: Move to `.env` as `API_KEY`
### 🟡 Warnings
1. **[TypeScript]** `any` type on line 42
- Fix: Define interface for `FormData`
2. **[Performance]** Missing memoization for expensive sort
- Fix: Wrap in `useMemo`
### 🟢 Suggestions
1. **[Style]** Consider extracting validation to separate function
2. **[Naming]** `data` is too generic, consider `formSubmissions`
### ✅ Good Practices Observed
- Proper error handling in API routes
- Consistent use of UI components
Quick Checklist
Before approving code:
- No TypeScript errors
- No
anytypes (unless justified) - All API routes have auth checks
- Error handling with try-catch
- Input validation for user data
- No hardcoded secrets
- Proper loading/error states
- UI components used (no inline Tailwind for buttons/inputs)
- Dependencies in useEffect arrays
- No N+1 queries
- Descriptive variable names
- Files under 300 lines