🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
14 KiB
Code Reviewer Skill
This skill ensures all frontend and backend code follows proper coding standards, best practices, and maintains high quality.
Purpose
Automatically review code for:
- Coding Standards: Naming conventions, formatting, structure
- Best Practices: Design patterns, error handling, security
- Code Quality: Readability, maintainability, performance
- Common Issues: Anti-patterns, code smells, potential bugs
When to Use
This skill is automatically applied when:
- Backend agent generates code
- Frontend agent generates code
- Any code modifications are proposed
- Code refactoring is performed
Review Checklist
Backend Code (TypeScript/NestJS)
1. Naming Conventions
// ✅ CORRECT
export class UserService {
async getUserById(userId: string): Promise<User> { }
}
const MAX_RETRY_ATTEMPTS = 3;
// ❌ INCORRECT
export class userservice {
async getuser(id) { }
}
const max_retry = 3;
Rules:
- Classes:
PascalCase - Functions/variables:
camelCase - Constants:
UPPER_SNAKE_CASE - Files:
kebab-case.ts - Interfaces:
IPascalCaseorPascalCase
2. TypeScript Best Practices
// ✅ CORRECT: Strong typing
interface CreateUserDto {
email: string;
name: string;
age?: number;
}
async function createUser(dto: CreateUserDto): Promise<User> {
// Implementation
}
// ❌ INCORRECT: Using 'any'
async function createUser(dto: any): Promise<any> {
// Don't use 'any'
}
Rules:
- ❌ Never use
anytype - ✅ Use proper interfaces/types
- ✅ Use
readonlywhere appropriate - ✅ Use generics for reusable code
3. Error Handling
// ✅ CORRECT: Proper error handling
export class IssueService {
async getIssueById(id: string): Promise<Issue> {
try {
const issue = await this.issueRepository.findOne({ where: { id } });
if (!issue) {
throw new NotFoundException(`Issue not found: ${id}`);
}
return issue;
} catch (error) {
this.logger.error(`Failed to get issue ${id}`, error);
throw error;
}
}
}
// ❌ INCORRECT: Silent failures
async getIssueById(id: string) {
const issue = await this.issueRepository.findOne({ where: { id } });
return issue; // Returns null/undefined without error
}
Rules:
- ✅ Use custom error classes
- ✅ Log errors with context
- ✅ Throw descriptive errors
- ❌ Don't swallow errors silently
- ✅ Use try-catch for async operations
4. Dependency Injection (NestJS)
// ✅ CORRECT: Constructor injection
@Injectable()
export class IssueService {
constructor(
@InjectRepository(Issue)
private readonly issueRepository: Repository<Issue>,
private readonly auditService: AuditService,
private readonly logger: Logger,
) {}
}
// ❌ INCORRECT: Direct instantiation
export class IssueService {
private issueRepository = new IssueRepository();
private auditService = new AuditService();
}
Rules:
- ✅ Use constructor injection
- ✅ Mark dependencies as
private readonly - ✅ Use
@Injectable()decorator - ❌ Don't create instances manually
5. Database Operations
// ✅ CORRECT: Parameterized queries, proper error handling
async findByEmail(email: string): Promise<User | null> {
return this.userRepository.findOne({
where: { email },
select: ['id', 'email', 'name'] // Only select needed fields
});
}
// ❌ INCORRECT: SQL injection risk, selecting all fields
async findByEmail(email: string) {
return this.connection.query(`SELECT * FROM users WHERE email = '${email}'`);
}
Rules:
- ✅ Use ORM (TypeORM/Prisma)
- ✅ Parameterized queries only
- ✅ Select only needed fields
- ✅ Use transactions for multi-step operations
- ❌ Never concatenate SQL strings
6. Service Layer Structure
// ✅ CORRECT: Clean service structure
@Injectable()
export class IssueService {
constructor(
private readonly issueRepository: IssueRepository,
private readonly auditService: AuditService,
) {}
// Public API
async create(dto: CreateIssueDto, userId: string): Promise<Issue> {
const validated = this.validateDto(dto);
const issue = await this.createIssue(validated, userId);
await this.logAudit(issue, userId);
return issue;
}
// Private helper methods
private validateDto(dto: CreateIssueDto): CreateIssueDto {
// Validation logic
return dto;
}
private async createIssue(dto: CreateIssueDto, userId: string): Promise<Issue> {
// Creation logic
}
private async logAudit(issue: Issue, userId: string): Promise<void> {
// Audit logging
}
}
Rules:
- ✅ Single Responsibility Principle
- ✅ Public methods for API, private for helpers
- ✅ Keep methods small and focused
- ✅ Extract complex logic to helper methods
Frontend Code (React/TypeScript)
1. Component Structure
// ✅ CORRECT: Functional component with TypeScript
import { FC, useState, useEffect } from 'react';
import styles from './IssueCard.module.css';
interface IssueCardProps {
issueId: string;
onUpdate?: (issue: Issue) => void;
}
export const IssueCard: FC<IssueCardProps> = ({ issueId, onUpdate }) => {
const [issue, setIssue] = useState<Issue | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
fetchIssue();
}, [issueId]);
const fetchIssue = async () => {
// Implementation
};
if (loading) return <LoadingSpinner />;
if (error) return <ErrorMessage message={error} />;
if (!issue) return null;
return (
<div className={styles.card}>
<h3>{issue.title}</h3>
</div>
);
};
// ❌ INCORRECT: Class component, no types
export default function issuecard(props) {
const [data, setdata] = useState();
useEffect(() => {
fetch('/api/issues/' + props.id)
.then(r => r.json())
.then(d => setdata(d));
});
return <div>{data?.title}</div>;
}
Rules:
- ✅ Use functional components with hooks
- ✅ Define prop interfaces
- ✅ Use
FC<Props>type - ✅ Handle loading/error states
- ✅ Use CSS modules or styled-components
- ❌ Don't use default exports
- ❌ Don't use inline styles (except dynamic)
2. State Management
// ✅ CORRECT: Zustand store with TypeScript
import { create } from 'zustand';
interface ProjectStore {
projects: Project[];
loading: boolean;
error: string | null;
// Actions
fetchProjects: () => Promise<void>;
addProject: (project: Project) => void;
}
export const useProjectStore = create<ProjectStore>((set, get) => ({
projects: [],
loading: false,
error: null,
fetchProjects: async () => {
set({ loading: true, error: null });
try {
const projects = await ProjectService.getAll();
set({ projects, loading: false });
} catch (error) {
set({
error: error instanceof Error ? error.message : 'Unknown error',
loading: false
});
}
},
addProject: (project) => {
set(state => ({ projects: [...state.projects, project] }));
},
}));
// ❌ INCORRECT: No types, mutating state
const useProjectStore = create((set) => ({
projects: [],
addProject: (project) => {
set(state => {
state.projects.push(project); // Mutation!
return state;
});
},
}));
Rules:
- ✅ Define store interface
- ✅ Immutable updates
- ✅ Handle loading/error states
- ✅ Use TypeScript
- ❌ Don't mutate state directly
3. Custom Hooks
// ✅ CORRECT: Proper custom hook
import { useState, useEffect } from 'react';
interface UseIssueResult {
issue: Issue | null;
loading: boolean;
error: string | null;
refetch: () => Promise<void>;
}
export const useIssue = (issueId: string): UseIssueResult => {
const [issue, setIssue] = useState<Issue | null>(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const fetchIssue = async () => {
try {
setLoading(true);
setError(null);
const data = await IssueService.getById(issueId);
setIssue(data);
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to fetch');
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchIssue();
}, [issueId]);
return { issue, loading, error, refetch: fetchIssue };
};
// Usage
const { issue, loading, error, refetch } = useIssue('123');
// ❌ INCORRECT: No error handling, no types
function useIssue(id) {
const [data, setData] = useState();
useEffect(() => {
fetch(`/api/issues/${id}`)
.then(r => r.json())
.then(setData);
}, [id]);
return data;
}
Rules:
- ✅ Name starts with "use"
- ✅ Return object with named properties
- ✅ Include loading/error states
- ✅ Provide refetch capability
- ✅ Define return type interface
4. Event Handlers
// ✅ CORRECT: Typed event handlers
import { ChangeEvent, FormEvent } from 'react';
export const IssueForm: FC = () => {
const [title, setTitle] = useState('');
const handleTitleChange = (e: ChangeEvent<HTMLInputElement>) => {
setTitle(e.target.value);
};
const handleSubmit = async (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
await createIssue({ title });
};
return (
<form onSubmit={handleSubmit}>
<input
value={title}
onChange={handleTitleChange}
/>
<button type="submit">Create</button>
</form>
);
};
// ❌ INCORRECT: No types, inline functions
export const IssueForm = () => {
const [title, setTitle] = useState('');
return (
<form onSubmit={(e) => {
e.preventDefault();
createIssue({ title });
}}>
<input
value={title}
onChange={(e) => setTitle(e.target.value)}
/>
</form>
);
};
Rules:
- ✅ Type event parameters
- ✅ Extract handlers to named functions
- ✅ Use
preventDefault()for forms - ❌ Avoid inline arrow functions in JSX (performance)
5. Performance Optimization
// ✅ CORRECT: Memoization
import { memo, useMemo, useCallback } from 'react';
export const IssueCard = memo<IssueCardProps>(({ issue, onUpdate }) => {
const formattedDate = useMemo(
() => new Date(issue.createdAt).toLocaleDateString(),
[issue.createdAt]
);
const handleClick = useCallback(() => {
onUpdate?.(issue);
}, [issue, onUpdate]);
return (
<div onClick={handleClick}>
<h3>{issue.title}</h3>
<span>{formattedDate}</span>
</div>
);
});
// ❌ INCORRECT: Re-computing on every render
export const IssueCard = ({ issue, onUpdate }) => {
const formattedDate = new Date(issue.createdAt).toLocaleDateString();
return (
<div onClick={() => onUpdate(issue)}>
<h3>{issue.title}</h3>
<span>{formattedDate}</span>
</div>
);
};
Rules:
- ✅ Use
memofor expensive components - ✅ Use
useMemofor expensive computations - ✅ Use
useCallbackfor callback props - ❌ Don't over-optimize (measure first)
Common Anti-Patterns to Avoid
Backend
❌ God Classes: Classes with too many responsibilities ❌ Magic Numbers: Use named constants instead ❌ Callback Hell: Use async/await ❌ N+1 Queries: Use eager loading or joins ❌ Ignoring Errors: Always handle errors ❌ Hardcoded Values: Use config/environment variables
Frontend
❌ Prop Drilling: Use Context or state management ❌ Inline Styles: Use CSS modules or styled-components ❌ Large Components: Break into smaller components ❌ Missing Keys: Always provide keys in lists ❌ Premature Optimization: Measure before optimizing ❌ Missing Error Boundaries: Wrap components with error boundaries
Security Checklist
Backend
- Validate all input
- Sanitize user data
- Use parameterized queries
- Implement authentication/authorization
- Hash passwords (bcrypt)
- Use HTTPS
- Set security headers
- Rate limiting on APIs
- Log security events
Frontend
- Sanitize user input (XSS prevention)
- Validate before sending to backend
- Secure token storage (httpOnly cookies)
- CSRF protection
- Content Security Policy
- No sensitive data in localStorage
- Validate API responses
Code Review Process
When reviewing code:
-
First Pass: Architecture & Design
- Does it follow SOLID principles?
- Is the structure logical?
- Are there clear separation of concerns?
-
Second Pass: Implementation
- Correct naming conventions?
- Proper error handling?
- Type safety?
- Performance considerations?
-
Third Pass: Testing & Edge Cases
- Are there unit tests?
- Edge cases handled?
- Error scenarios covered?
-
Fourth Pass: Security & Best Practices
- Any security vulnerabilities?
- Follows best practices?
- Documentation adequate?
Automated Checks
Recommended tools:
- Backend: ESLint, Prettier, TypeScript compiler
- Frontend: ESLint, Prettier, TypeScript compiler
- Both: Husky (pre-commit hooks), SonarQube
Review Feedback Format
## Code Review: [File Name]
### ✅ Good Practices
- [What was done well]
### ⚠️ Issues Found
#### Critical (Must Fix)
- [ ] Issue 1: [Description]
- Location: `file.ts:123`
- Fix: [Suggested fix]
#### Moderate (Should Fix)
- [ ] Issue 2: [Description]
#### Minor (Consider)
- [ ] Issue 3: [Description]
### 💡 Suggestions
- [Improvement suggestions]
### Overall Rating: [Approved / Needs Changes / Rejected]
Usage
This skill is automatically invoked by the main coordinator whenever backend or frontend agents generate code. The coordinator will:
- Receive code from agent
- Apply code-reviewer skill
- Report any issues found
- Request fixes if needed
- Approve once standards are met
Remember: The goal is not perfection, but maintainability, reliability, and consistency.