Code Review Guidelines
This document serves as a comprehensive checklist for reviewing code in the TurboStack project. It is designed to ensure code quality, consistency, security, and architectural integrity across the monorepo. Use this checklist before submitting a Pull Request (PR) or during the code review process.These guidelines are enforced to maintain the high standards of the TurboStack architecture. Adhering to them ensures a smooth development process and a robust production environment.
🧠 General (Stack Agnostic)
Core Principles
- Single Responsibility Principle (SRP): Does each file, function, and service perform a single, well-defined task?
- Naming Conventions: Is the naming clear and domain-specific? (e.g.,
userSettings,billingCycle,subscriptionPlaninstead of generic names likedata,config). - Dead Code: Are there any unused exports, variables, or imports that should be removed?
- Side Effects: Are side effects (especially in server actions and hooks) explicit and managed correctly?
- Environment Variables: Are env variables handled securely? (e.g.,
process.envshould not be leaked to the client directly; use validated config via@t3-oss/env-nextjs). - Magic Numbers/Strings: Are magic strings and numbers replaced with enums or constants?
- Error Handling: Is there robust error handling, or does the code only cover the “happy path”?
- Logging: Is the log level appropriate? (No
console.login production; use structured logging likepino).
⚛️ Frontend – Next.js (App Router)
1️⃣ Architecture & Folder Structure
- Routing vs. Logic: Does
app/strictly handle routing and composition? - Business Logic Placement:
- ❌ Business logic in
page.tsx - ✅ Logic moved to
features/,services/, orhooks/
- ❌ Business logic in
- Component Classification:
components/ui: Dumb/presentational components (shadcn/ui)components/features: Domain-aware components
- Responsibility: Page components should orchestrate; UI components should render.
2️⃣ Server / Client Boundary
- “use client” Usage: Is
"use client"minimized and used only when necessary? - Server Components: Ensure no client-side specific APIs (e.g.,
useEffect,window,localStorage) are used in Server Components. - Client Components: Avoid heavy data fetching directly in Client Components unless necessary.
- Data Fetching Strategy: Fetch data on the server, manage state on the client where appropriate.
3️⃣ Data Fetching
- Caching Strategy: Are fetch cache settings (
no-store,revalidate) explicitly defined? - Service Layer:
- ❌ Fetching the same endpoint in multiple places
- ✅ Using a unified service layer (
apps/frontend/services) for API calls
- Parallel Fetching: Are independent data fetches executed in parallel (e.g., using
Promise.all)?
4️⃣ Performance
- Re-renders: Are unnecessary re-renders avoided?
- Memoization: Are
useMemoanduseCallbackused effectively (not just reflexively)? - Large Lists: Is pagination or virtualization used for large datasets?
- Image Optimization: Is
next/imageused for images? - Bundle Size: Are there any heavy libraries bloating the bundle?
5️⃣ Form & Validation
- Validation Layer:
- ❌ Validation logic only in the frontend
- ✅ Shared Zod schemas (
@repo/validations) used for both frontend and backend
- User Feedback: Are validation errors and success states clearly communicated to the user (e.g., via
sonnertoast)? - Optimistic UI: If used, is there a rollback mechanism for failed operations?
6️⃣ DX & Maintainability
- Reusable Hooks: Are custom hooks truly reusable and logic-focused?
- ❌ Hook rendering UI
- ✅ Hook managing logic only
- Type Safety:
- ❌ Avoid
anyandas unknown as - ✅ Ensure API responses are fully type-safe via shared types or Eden Treaty
- ❌ Avoid
🦊 Backend – Elysia.js
1️⃣ Route Structure
- Logic Separation:
- ❌ Routes containing business logic
- ✅ Routes strictly handling HTTP request/response orchestration, delegating logic to
services/
- Layered Architecture: Is there a clear separation between Route Handlers and Services?
2️⃣ Elysia Best Practices
- Schema Validation: Are
t.Object,t.Enum, and other TypeBox schemas used for validation in routes? - Schema Definition: Are request/response schemas explicitly defined for Swagger documentation?
- Shared Schemas: Are schemas shared with the frontend for consistency?
- Schema Role: Treat schemas as validation, documentation, and type safety mechanisms.
3️⃣ Auth & Security
- Auth Implementation:
- ❌ Scattered auth checks in routes
- ✅ Centralized middleware or
deriveusage for session injection
- Role Checking: Is
requireAdminor similar helpers used for role-based access control? - Error Security:
- ❌ Leaking database errors (e.g., “User not found in DB query”)
- ✅ Returning standardized errors (e.g., “Unauthorized”, “Not Found”)
4️⃣ Error Handling
- Global Handling: Is the global
.onErrorhandler used for standardizing responses? - Custom Errors: Are custom error types (
AppError) used? - Status Codes: Are HTTP status codes used consistently and correctly (e.g., 400 for validation, 401 for auth, 403 for permission)?
5️⃣ Database / ORM
- N+1 Queries: Are potential N+1 query issues identified and resolved (e.g., using
includecorrectly)? - Transactions: Are database transactions used where atomicity is required?
- Pagination:
- ❌ Fetching all records (
findMany()without limits) - ✅ Using
take,skipbased on pagination params
- ❌ Fetching all records (
- Indexing: Are database indexes applied to frequently queried fields (in
schema.prisma)?
6️⃣ Performance
- Heavy Operations: Are heavy computations (e.g., image processing) handled efficiently?
- Response Size: Is the response payload optimized (no unnecessary data)?
- Await Chains: Are unnecessary sequential
awaitcalls avoided wherePromise.allcould be used?
🔗 Frontend ↔ Backend Consistency
- Response Shape:
- ❌ Inconsistent response formats
- ✅ Standardized response structure (
ApiResponse<T>):
- Shared Types: Is there a shared
@repo/typesor validation package used by both ends? - Breaking Changes: Does the type system catch breaking changes across the boundary?
🚩 Red Flags (Immediate Changes Required)
- Effect Fetching:
useEffectcontaining directfetchcalls without a robust strategy (use Service layer). - Direct DB Access: API routes accessing the database directly instead of through a service.
- Logic in Pages: Next.js pages containing complex business logic.
- Massive Components: Components exceeding 500 lines of code.
- “Refactor Later”: Comments promising future refactoring (“TODO: fix later”) without immediate plans.