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
-
any types 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/should prefix → 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
- [Security] Hardcoded API key on line 15
- Fix: Move to
.envasAPI_KEY
- Fix: Move to
🟡 Warnings
-
[TypeScript]
anytype on line 42- Fix: Define interface for
FormData
- Fix: Define interface for
-
[Performance] Missing memoization for expensive sort
- Fix: Wrap in
useMemo
- Fix: Wrap in
🟢 Suggestions
- [Style] Consider extracting validation to separate function
- [Naming]
datais too generic, considerformSubmissions
✅ Good Practices Observed
- Proper error handling in API routes
- Consistent use of UI components
Quick Checklist
Before approving code:
-
No TypeScript errors
-
No any types (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