// Perform automated code reviews checking for security vulnerabilities, performance issues, and code quality. Use before creating PRs or when reviewing complex changes.
| name | code-review |
| description | Perform automated code reviews checking for security vulnerabilities, performance issues, and code quality. Use before creating PRs or when reviewing complex changes. |
| allowed-tools | Read, Grep, Glob, Bash |
This skill helps you perform thorough code reviews for quality, security, and performance.
any (or justified with comment)โ Magic Numbers
// Bad
if (user.age > 18) {
allowAccess();
}
// Good
const LEGAL_AGE = 18;
if (user.age >= LEGAL_AGE) {
allowAccess();
}
โ Deep Nesting
// Bad
if (user) {
if (user.isActive) {
if (user.hasPermission) {
if (resource.isAvailable) {
// do something
}
}
}
}
// Good
if (!user || !user.isActive || !user.hasPermission || !resource.isAvailable) {
return;
}
// do something
โ Large Functions
// Bad
function processUser() {
// 200 lines of code
}
// Good
function processUser() {
validateUser();
enrichUserData();
saveUser();
notifyUser();
}
โ Using any
// Bad
function processData(data: any) {
return data.value;
}
// Good
interface Data {
value: string;
}
function processData(data: Data) {
return data.value;
}
โ SQL Injection
// Bad
const query = `SELECT * FROM users WHERE id = ${userId}`;
// Good
const query = db.query.users.findFirst({
where: eq(users.id, userId),
});
โ XSS Vulnerability
// Bad
element.innerHTML = userInput;
// Good
element.textContent = userInput;
// Or use framework's safe rendering
<div>{userInput}</div>
โ Exposed Secrets
// Bad
const apiKey = "sk_live_12345...";
// Good
const apiKey = process.env.API_KEY;
โ Missing Input Validation
// Bad
export async function updateUser(data: any) {
await db.update(users).set(data);
}
// Good
const updateUserSchema = z.object({
name: z.string().min(1).max(100),
email: z.string().email(),
});
export async function updateUser(data: unknown) {
const validated = updateUserSchema.parse(data);
await db.update(users).set(validated);
}
โ N+1 Queries
// Bad
const posts = await db.query.posts.findMany();
for (const post of posts) {
const author = await db.query.users.findFirst({
where: eq(users.id, post.authorId),
});
post.author = author;
}
// Good
const posts = await db.query.posts.findMany({
with: {
author: true,
},
});
โ Missing Memoization
// Bad
function Component({ data }) {
const processedData = expensiveOperation(data);
return <div>{processedData}</div>;
}
// Good
function Component({ data }) {
const processedData = useMemo(() => expensiveOperation(data), [data]);
return <div>{processedData}</div>;
}
โ Unnecessary Re-renders
// Bad
function Parent() {
return <Child onClick={() => handleClick()} />;
}
// Good
function Parent() {
const handleClick = useCallback(() => {
// handle click
}, []);
return <Child onClick={handleClick} />;
}
# View the diff
git diff main...feature-branch
# See changed files
git diff --name-only main...feature-branch
# See commit history
git log main..feature-branch --oneline
# Run linter
pnpm biome check .
# Run type checker
pnpm tsc --noEmit
# Run tests
pnpm test
# Search for 'any' usage
grep -r "any" apps/ packages/ --include="*.ts" --include="*.tsx"
# Search for console.log
grep -r "console.log" apps/ packages/ --include="*.ts" --include="*.tsx"
# Search for TODO comments
grep -r "TODO" apps/ packages/ --include="*.ts" --include="*.tsx"
# Search for hardcoded credentials
grep -r "password\|secret\|key" apps/ packages/ --include="*.ts"
# Check migration files
ls -la packages/database/migrations/
# Review migration SQL
cat packages/database/migrations/latest.sql
# Check for new dependencies
git diff package.json
# Check for security vulnerabilities
pnpm audit
# Check for outdated packages
pnpm outdated
# Check types across workspace
pnpm tsc --noEmit
# Check specific package
pnpm -F @sgcarstrends/web tsc --noEmit
# Lint all files
pnpm biome check .
# Lint with details
pnpm biome check . --verbose
# Check specific files
pnpm biome check apps/web/src/**/*.ts
# Run all tests
pnpm test
# Run with coverage
pnpm test:coverage
# Check coverage threshold
pnpm test -- --coverage --coverageThreshold='{"global":{"branches":80,"functions":80,"lines":80}}'
โ Poor Comment
This is wrong.
โ Good Comment
Consider using `useMemo` here to avoid recalculating on every render.
This could impact performance with large datasets.
โ Poor Comment
Don't use any.
โ Good Comment
The `any` type bypasses type safety. Consider defining a proper interface:
interface UserData {
id: string;
name: string;
email: string;
}
๐ด Must Fix - Critical issues that block merge
๐ด This creates an SQL injection vulnerability. Use parameterized queries.
๐ก Should Fix - Important but not blocking
๐ก This function is doing too much. Consider breaking it into smaller functions.
๐ข Suggestion - Nice to have improvements
๐ข You could simplify this with optional chaining: `user?.profile?.name`
๐ก Learning - Educational comments
๐ก FYI: Next.js automatically optimizes images when using the Image component.
โ Question - Asking for clarification
โ Is there a reason we're not using the existing `formatDate` utility?
Check for:
any)// Look for improvements
const value = data && data.user && data.user.name; // โ
const value = data?.user?.name; // โ
const value = data || 'default'; // โ (0, '', false are falsy)
const value = data ?? 'default'; // โ
(only null/undefined)
Check for:
// Check useEffect dependencies
useEffect(() => {
fetchData(userId); // โ Missing dependency
}, []);
useEffect(() => {
fetchData(userId); // โ
Correct dependencies
}, [userId]);
Check for:
// โ Missing 'use client'
import { useState } from 'react';
export default function Component() {
const [state, setState] = useState();
// ...
}
// โ
Correct
'use client';
import { useState } from 'react';
export default function Component() {
const [state, setState] = useState();
// ...
}
Check for:
// โ N+1 query
const users = await db.query.users.findMany();
for (const user of users) {
user.posts = await db.query.posts.findMany({
where: eq(posts.userId, user.id),
});
}
// โ
Single query with join
const users = await db.query.users.findMany({
with: {
posts: true,
},
});
Level 1: Quick Review (< 100 lines)
Level 2: Standard Review (100-500 lines)
Level 3: Thorough Review (> 500 lines)
Before requesting review:
# 1. Check your changes
git diff main...HEAD
# 2. Run quality checks
pnpm biome check --write .
pnpm tsc --noEmit
pnpm test
# 3. Check for common issues
grep -r "console.log" . --include="*.ts" --include="*.tsx"
grep -r "TODO" . --include="*.ts" --include="*.tsx"
# 4. Review your commits
git log main..HEAD --oneline
# 5. Check PR size
git diff --stat main...HEAD
Track these metrics:
biome.json - Linting rules