Code Review Standards
by Convext
Pull request and code review guidelines
Rules (214)
Angular
Observables for async. Use async pipe in templates. Unsubscribe or use takeUntilDestroyed. Signals for simple state.
Standalone components are the default (Angular 17+). NgModules are legacy. Use Signals for reactive state. Deferrable views for lazy loading.
`ng generate` for scaffolding. `ng build --configuration production`. Schematics for custom generators.
Injectable services for business logic. `providedIn: 'root'` for singletons. Inject with constructor or inject().
Api
GET/POST/PUT/DELETE on resources. Nested routes for relationships. Proper HTTP status codes (200/201/400/401/404/500).
Consistent error format: code, message, field-level details. Same structure across all endpoints.
Version in URL (`/api/v1/`) or headers. Enables breaking changes without affecting clients.
Ci Cd
Jobs for lint, test, build. Matrix for versions. Cache dependencies. Secrets for credentials. Reusable workflows.
.travis.yml for config. Matrix for versions. Cache dependencies. Deploy on tag. Free for open source.
Jenkinsfile (declarative pipeline). Stages for phases. Shared libraries for reuse. Blue Ocean for UI.
config.yml with jobs and workflows. Orbs for common tasks. Cache for speed. Context for secrets.
actionlint for workflow validation. Catches syntax errors, expression issues. Run locally before push.
.gitlab-ci.yml with stages. Include templates. Cache artifacts. Variables for config. Runners for execution.
Config
Config in env vars, not code. Never commit .env. Commit .env.example. Never hardcode secrets.
Csharp
xUnit preferred (modern, parallel). NUnit also good. Use `[Fact]`/`[Theory]` or `[Test]`/`[TestCase]`.
Primary constructors, collection expressions, default lambda params. Required members. Raw string literals. .NET 8+ for best support.
Async all the way down. Return `Task<T>`, never block with `.Result` or `.Wait()`. ConfigureAwait unnecessary in ASP.NET Core.
.NET SDK for builds and tooling. `dotnet new` for scaffolding. `dotnet run` for dev. .NET 8+ LTS.
ASP.NET Core for web APIs and apps. Minimal APIs for simple endpoints. MVC for complex. DI built-in.
`dotnet` CLI for builds/runs. NuGet for packages. Lock file with `packages.lock.json`. .NET 8+.
Enable `<Nullable>enable</Nullable>`. Mark nullable with `?`. Compiler catches null issues.
Minimal APIs for simple microservices and endpoints. MapGet/MapPost with lambdas. MVC for complex scenarios with views.
PascalCase for public members, camelCase for private. Use dotnet-format or StyleCop. Async suffix for async methods.
Database
Passwords: bcrypt. API keys: encrypt. PII: field-level encryption. Never log sensitive data.
Define schemas even though schemaless. Use Mongoose (Node) or ODMs. Index query fields. Embed vs reference thoughtfully.
Redis for cache, sessions, queues. Set TTL on keys. Use Redis Cluster for scale. Avoid large keys (>1MB).
Index: foreign keys, WHERE columns, ORDER BY columns, JOINs. Use EXPLAIN ANALYZE to verify.
Wrap multi-step DB operations in transactions. All-or-nothing, prevents partial updates.
Elasticsearch for full-text search, logs, analytics. Define mappings. Use bulk API for indexing. Replicas for availability.
SQLite for dev/test, single-user apps. WAL mode for concurrency. Switch to Postgres for production scale.
Dependencies
Use latest stable versions. Fix breaking changes—don't avoid upgrades. Check official registries.
Django
DRF serializers for validation. ViewSets for CRUD. Token/JWT auth. Pagination on list endpoints.
One migration per change. Never edit applied migrations. Use RunPython for data migrations. Squash when large.
select_related/prefetch_related for joins. Use F() and Q() objects. Avoid N+1 with django-debug-toolbar.
Forms/ModelForms for input validation. Clean methods for business logic. Never trust request.POST directly.
Elixir
LiveView for server-rendered reactive UIs. No JavaScript needed for most interactions. Use hooks for JS interop. Streams for large lists.
Ecto schemas for data modeling. Changesets for validation. Repo for queries. Migrations for schema changes. Use preload for associations.
Phoenix for web apps. LiveView for interactive UIs without JavaScript. Channels for real-time. PubSub for broadcasting.
mix.exs for config, mix.lock for deps. `mix deps.get`, `mix test`. Umbrella apps for monorepos.
Pattern matching in function heads, case, with. Destructure data. Multiple function clauses over conditionals.
GenServers for state, Supervisors for fault tolerance. Let it crash philosophy. Use Application for startup.
ExUnit with describe/test. Doctests for documentation. Mox for mocking behaviors. async: true for speed.
Express
Middleware order matters. Error handlers last with 4 params. Use helmet, cors, compression.
Wrap async handlers or use express-async-errors. Central error handler. Return proper status codes.
Router for route groups. Mount with `app.use('/api', router)`. Keep routes thin, logic in services.
Fastapi
Async drivers: asyncpg (Postgres), SQLAlchemy 2.0 async. Match FastAPI's async nature.
Explicit Pydantic models for all inputs/outputs. Auto-validation, docs, type safety.
Use `Depends()` for db sessions, auth, config, services. Composable and testable.
Flask
`create_app()` factory pattern. Config from env. Blueprints for modularity. Flask extensions for features.
Flask-SQLAlchemy for ORM. Flask-Migrate for migrations. Scoped sessions. Avoid circular imports.
Formatting
Prettier for JS/TS/CSS/HTML/JSON. Minimal config. Run on save. Pre-commit hook. CI check.
isort for import ordering. Profile: black. Run before Black. Or use Ruff which includes isort.
StandardRB: no config, no debates. RuboCop with opinionated defaults. Just run `standardrb --fix`.
Git
Small, focused commits. Use: feat/fix/refactor/test/docs/chore. Each commit independently deployable.
Go
Check every error return. No `_` for errors. Wrap with context: `fmt.Errorf("doing x: %w", err)`.
`gofmt` is non-negotiable. `golangci-lint run` in CI. Enable: errcheck, govet, staticcheck.
Built-in testing package. Table-driven tests. `go test -v ./...`. Use testify for assertions if needed.
Use `log/slog` (Go 1.21+) for structured logging. JSON handler in production. slog.With() for context. Avoid fmt.Printf for logs.
`context.Context` as first param. Propagate through call chain. Use `context.WithTimeout` for I/O.
go.mod and go.sum at repo root. `go mod tidy` before commit. Pin versions for reproducibility.
range over integers, range over func. Enhanced HTTP routing patterns. for loop variable fix. Go 1.22+ minimum.
Accept interfaces, return structs. Small interfaces (1-2 methods). Define interfaces where used, not implemented.
Java
JUnit 5 (Jupiter) for all tests. Use `@Test`, `@BeforeEach`, `@ParameterizedTest`. Mockito for mocking.
`Optional<T>` for nullable returns. Never return null from methods. Use `@Nullable` annotations if needed.
Virtual threads for scalable concurrency. Pattern matching for switch. Record patterns. Sequenced collections. Java 21 LTS minimum.
Gradle for flexibility, Maven for simplicity. Lock dependency versions. Use wrapper scripts (gradlew/mvnw).
Spotless for consistent formatting. Google Java Format or custom. `spotlessApply` before commit.
Checkstyle for style enforcement. Google or Sun conventions. Integrate with build (Maven/Gradle).
SpotBugs (FindBugs successor) for bug patterns. High-confidence issues only. Part of CI pipeline.
Classes: PascalCase. Methods/variables: camelCase. Constants: SCREAMING_SNAKE_CASE. Packages: lowercase.
Final fields, immutable collections. Use records for data classes (Java 16+). Builder pattern for complex objects.
Javascript
TypeScript, not JS. Enable `strict: true`, `noImplicitAny`, `strictNullChecks`.
async/await for all async code. Easier to read, proper try/catch, works with loops.
`const` by default, `let` for reassignment. Never `var` (hoisting bugs).
ESLint + @typescript-eslint recommended rules. No `any`, no unused vars. Run in CI.
Kotlin
Data classes, extension functions, scope functions (let/apply/run). Null safety with `?` and `!!`.
`suspend` functions, `Flow` for streams. Structured concurrency with scopes. No blocking in coroutines.
ktlint for consistent style. Android Kotlin style guide. Run in CI with `--reporter=checkstyle`.
Kotest for expressive specs, JUnit 5 for familiarity. MockK for Kotlin-native mocking.
Laravel
Eager loading with `with()`. Scopes for reusable queries. Mass assignment protection with $fillable.
API Resources for JSON responses. Transform models consistently. Collection resources for lists. Conditional attributes.
Pest is Laravel's default testing framework. Expressive syntax. Use arch tests for architecture rules. describe/it blocks.
Queue jobs for email, notifications, processing. Use Horizon for monitoring. Retry with backoff.
Form Requests for complex validation. Validate all input. Custom rules when needed. Authorize in Form Request.
Migrations for schema. Seeders for test data. Factories for dynamic data. Never edit applied migrations.
Bind interfaces to implementations in providers. Constructor injection. Use app() only in service providers. Contextual binding when needed.
Linting
RuboCop with .rubocop.yml config. Inherit from rubocop-rails, rubocop-minitest. Auto-correct safe cops.
mypy with --strict mode. Check all files. Fix type errors, don't ignore. Part of CI pipeline.
Biome: fast all-in-one linter+formatter. Replaces ESLint+Prettier. Single config. Rust-based.
pyright: fast type checker. VS Code Pylance backend. strict or basic mode. Better inference than mypy.
flake8 for style checks. Use with .flake8 config. max-line-length: 88 (Black compat). Ruff is faster alternative.
Llm Behavior
User and tests define done. Don't redefine scope or declare partial progress as complete.
No excuses: 'pre-existing', 'unrelated', 'tedious', 'for now'. Recognize and continue working.
You wrote every line. No 'pre-existing issues'—only issues you haven't fixed yet.
Nestjs
Feature modules for domain boundaries. CoreModule for singletons. SharedModule for reusable providers.
Constructor injection. @Injectable() on services. Custom providers for factories. Scope: DEFAULT unless needed.
ValidationPipe with class-validator DTOs. Guards for auth. Interceptors for transforms. Exception filters.
Nextjs
Fetch in Server Components. Use `cache: 'force-cache'` or `revalidate`. generateStaticParams for static paths.
Server Actions for mutations. `'use server'` directive. Form actions. Revalidate with revalidatePath/revalidateTag.
App Router over Pages Router (Next.js 13+). Server Components by default. 'use client' only when needed.
next/image for optimized images. next/link for client navigation. Automatic prefetching.
Package Manager
package-lock.json (commit it). `npm ci` in CI. Audit for vulnerabilities. Scripts for common tasks.
Gemfile + Gemfile.lock. `bundle install --deployment` in prod. Groups for dev/test. `bundle exec` for commands.
pnpm: fast, disk-efficient. pnpm-lock.yaml. Workspaces for monorepos. Strict node_modules by default.
pyproject.toml + poetry.lock. `poetry install --no-dev` for prod. Groups for dev deps. Prefer uv for speed.
requirements.txt with pinned versions. `pip freeze > requirements.txt`. Virtual environments. Prefer uv.
yarn.lock (commit it). Workspaces for monorepos. `yarn install --frozen-lockfile` in CI. Yarn Berry for PnP.
Php
composer.json and composer.lock. `composer install --no-dev` for prod. PSR-4 autoloading.
PSR-12 coding style, PSR-4 autoloading. Use php-cs-fixer in CI. Strict types: `declare(strict_types=1)`.
PHPStan level 9 (max) for strict analysis. Type declarations on all methods. Use baseline for legacy code migration.
PHPUnit for all tests. Use data providers for parameterized tests. Pest for simpler syntax if preferred.
Symfony for enterprise PHP. Bundles for modularity. Flex for recipes. Doctrine for ORM. Use makers for scaffolding.
Constructor promotion, named arguments, match, attributes, typed properties, enums, readonly classes. PHP 8.3+ for typed class constants.
Python
Use `uv` (not pip/poetry/pipenv). 100x faster, lockfile support, built-in venv. Commands: `uv add`, `uv run`.
logging module, not print. Configure at app entry point. Use __name__ for logger names. Structured logging with extra dict or structlog.
with statement for files, connections, locks. contextlib for custom managers. Never leave resources open. async with for async resources.
Async for HTTP (httpx), database (asyncpg), files (aiofiles). Don't mix sync/async in same service.
pyproject.toml for deps, uv.lock (commit it), .python-version, src/ layout. Python 3.12+.
Alembic for SQLAlchemy migrations. `alembic revision --autogenerate`. Review generated migrations. Stamp for sync.
Type hints on all functions and class attributes. Enables mypy/pyright static analysis.
Pydantic BaseModel for validation. Field types with constraints. Serialization built-in. FastAPI integration.
SQLAlchemy 2.0 style. Declarative models. Session management. Async with asyncio extension.
Ruff for both linting and formatting (replaces flake8/isort/pylint/Black). Single tool, 100x faster. `ruff check` and `ruff format`.
boto3 for AWS services. Use resource API for simple ops, client for advanced. Handle pagination.
Dataclasses for simple containers, Pydantic for validation. No plain dicts for structured data.
Define entry points in `[project.scripts]`. Run any command with `uv run <cmd>`. CI: `uv sync --frozen`.
Use `[tool.uv.workspace]` for multi-package repos. Single lockfile, editable local packages.
pytest with fixtures and parametrize. Simple asserts, rich plugin ecosystem.
Celery for distributed task queue. Redis or RabbitMQ broker. Beat for scheduling. Flower for monitoring.
pipenv is slow and unmaintained. Migrate to uv for speed and modern features. `uv init` from existing.
unittest for simple cases or legacy code. TestCase classes. setUp/tearDown. Prefer pytest for new projects.
pylint for comprehensive checks. Disable noisy rules in pylintrc. Ruff is faster alternative.
httpx for async HTTP client. Requests-compatible API. Async context manager. HTTP/2 support.
requests for simple sync HTTP. Session for connection pooling. Timeout always. Use httpx for async.
Rails
Rails.application.credentials for secrets. `rails credentials:edit`. Environment-specific credentials. Never commit master.key.
Whitelist attributes explicitly. Never `.permit!`. Use `require(:model).permit(:field)`.
Use `has_secure_password`, `authenticate_by`, `generates_token_for`. Never use Devise/Sorcery/Clearance.
Pundit policies for authorization. `authorize @record` in controllers. Policy classes match models.
SolidQueue: Rails 8 default job backend. Database-backed. No Redis needed. Mission Control for monitoring.
Service objects for multi-model operations. Single public method (call). Return Result/Response objects. Keep models focused on persistence.
Minitest only—no RSpec. Rails default, fast, simple, fixtures-integrated.
Sidekiq for heavy background work. Redis required. Use perform_later. Retries with exponential backoff.
Turbo Drive for SPA-like nav. Turbo Frames for partials. Turbo Streams for real-time. Stimulus for JS sprinkles.
Controllers: auth, params, call service, render. Logic in models/services/form objects/query objects.
Add NOT NULL, UNIQUE indexes, foreign keys, check constraints. Don't rely only on ActiveRecord validations.
Use `includes`/`preload`/`eager_load` for associations. Use Bullet gem in development.
Email, file processing, API calls, reports → background jobs (SolidQueue/Sidekiq). Keep requests <200ms.
Use Turbo (Drive/Frames/Streams) + Stimulus before React/Vue. JS frameworks only for complex client state.
Propshaft for asset pipeline. Simpler than Sprockets. No compilation, just fingerprinting. Rails 8 default.
SolidCable: database-backed pub/sub for Action Cable. No Redis needed. Rails 8 default.
Always `has_many :through` with explicit join model. Never `has_and_belongs_to_many`.
SolidCache: database-backed cache. No Redis needed. Good for most apps. Rails 8 default.
Active Storage for uploads. Direct uploads for large files. Variants for images. S3/GCS for production.
Action Cable for WebSockets. Channels for subscriptions. Redis adapter for multi-server. Use Turbo Streams when possible.
Define scopes for reusable queries. Enables chaining: `Order.recent.completed.for_user(user)`.
Importmap for JS without bundling. Pin packages from CDN or vendor. Works with Hotwire. No node_modules.
React
Server Components for data fetching. Actions for mutations. use() hook for promises. Optimistic updates. React Compiler for auto-memoization.
TanStack Query for server state. Auto caching/revalidation. useQuery for reads, useMutation for writes.
Functional components + hooks only. No class components. Hooks enable reuse without HOCs.
TanStack Query or SWR for fetching. Auto caching, revalidation, loading/error states. Not useEffect+fetch.
Zustand for simple global state. No boilerplate. Hooks-based. Persist middleware for storage.
Context for static data, Zustand/Jotai for simple state, Redux Toolkit for complex. No deep prop drilling.
Ruby
Favor modules and dependency injection over deep inheritance. Max 2-3 inheritance levels. Duck typing over type checking.
Add `# frozen_string_literal: true` to files. Prevents mutation bugs, improves performance.
Hanami for DDD-style Ruby apps. Explicit dependencies. Actions over controllers. Repositories for persistence.
Sinatra for microservices and simple APIs. Modular style for larger apps. Rack middleware compatible.
Return meaningful values or `self`. Predicates (`?`) return booleans. Bang methods (`!`) mutate or raise.
Use pattern matching, endless methods (`def x = ...`), hash shorthand `{x:}`, numbered params `_1`. Ruby 3.2+.
Rust
No `.unwrap()` in production. Use `?` operator, `map`, `and_then`. `expect()` only with good messages.
Tokio for async I/O. `#[tokio::main]` for entry point. Use tokio::spawn for concurrent tasks. Async channels for communication.
Cargo.toml for deps, Cargo.lock (commit it). `cargo add` for new deps. Workspace for monorepos.
`cargo test` for all tests. Doc tests in comments. Integration tests in tests/. `#[cfg(test)]` modules.
`anyhow::Result` for applications (easy error propagation). `thiserror` for libraries (custom error types). context() for error messages.
`cargo fmt` before commit. `cargo clippy` with `-D warnings` in CI. Fix all warnings.
Prefer borrowing (&T, &mut T) to avoid moves. Use ownership for transfers. Clone for small Copy types is fine. Lifetimes express relationships, don't avoid them.
`#[derive(Debug, Clone, PartialEq)]` for structs. Use `thiserror` for error types, `serde` for serialization.
Security
Auth: bcrypt/argon2 for passwords, rate limiting, secure sessions/tokens. Authz: check permissions on every request, use policy objects or middleware.
Force SSL, redirect HTTP→HTTPS, secure cookies (Secure/HttpOnly/SameSite), HSTS headers.
Allowlists, not denylists. Validate type/length/format. Sanitize HTML. Parameterized queries only.
Spring
Constructor injection (preferred). @Service, @Repository, @Controller. @Autowired optional with single constructor.
SecurityFilterChain bean. JWT or session auth. Method security with @PreAuthorize. CSRF for forms.
Repository interfaces. Query methods by naming. @Query for custom. Use projections for performance.
spring-boot-starter-* for dependencies. Auto-configuration. application.yml over .properties.
Spring Boot 3.x with Java 17+. Native compilation with GraalVM. Virtual threads support. Observability with Micrometer. Jakarta EE namespace.
Svelte
SvelteKit for routing, SSR, API routes. +page.svelte for pages. +layout.svelte for shared UI. Load functions for data.
$: for reactive statements. Stores for shared state. Runes ($state, $derived) in Svelte 5. Two-way binding with bind:.
Swift
SwiftUI for declarative UI. @State/@Binding for local state. @Observable (iOS 17+) for view models. NavigationStack for navigation.
async/await, actors for shared state. @MainActor for UI. Structured concurrency with task groups.
SPM over CocoaPods/Carthage. Package.swift for dependencies. `swift build`, `swift test`.
Enable Swift 6 language mode for full concurrency checking. Sendable for thread-safe types. Actors for shared mutable state.
Swift Testing (@Test, #expect) for new code. Cleaner syntax than XCTest. Parameterized tests with @Test(arguments:). Parallel by default.
Structs over classes unless you need reference semantics. Enums with associated values. Protocols for polymorphism.
Clarity over brevity. Omit needless words. Use grammatical English phrases. SwiftLint for enforcement.
Testing
Test real instances. Mocking the class under test hides bugs.
Fix failures immediately. No skipping, no "pre-existing issues." Own the codebase state—a test suite with ignored tests can't be trusted.
Test public interfaces, inputs/outputs. Tests must survive refactoring. Don't test private methods.
Playwright for multi-browser E2E. Auto-wait for elements. Trace viewer for debugging. Parallel execution.
Mock only: external HTTP APIs, time, filesystem side effects, third-party services. Use real implementations for internal services, database, and business logic.
1) Write failing test 2) Minimum code to pass 3) Refactor. Every line has a reason.
Cypress for E2E tests. Commands for reusable actions. cy.intercept() for network. Avoid flaky selectors.
Vitest for Vite-based projects. Jest-compatible API. Native ESM. Use @testing-library for components.
Jest for unit/integration tests. `describe`/`it` blocks. Mock with jest.mock(). Snapshot testing sparingly.
RSpec describe/context/it. let/let! for setup. FactoryBot for data. Avoid excessive mocking.
Mocha with Chai assertions. describe/it blocks. Sinon for mocking. async/await in tests.
Use consistent test data setup: fixtures for stable reference data, factories for dynamic scenarios. Avoid inline object creation scattered throughout tests.
Capybara for system tests. Use semantic selectors. Wait for async. Headless Chrome in CI.
One logical concept per test. Multiple asserts OK if same concept. Clear test names describing behavior.
Integration tests for full request/response cycle. Unit tests for complex logic, edge cases, performance.
Typescript
Zod schemas for runtime validation. Infer types from schemas. Composable. Works with React Hook Form.
Vue
Composition API over Options API. `<script setup>` for SFCs. Composables for reusable logic. ref/reactive for state.
Pinia over Vuex. Define stores with `defineStore`. Composable pattern. DevTools integration.
Named routes, navigation guards. Lazy-load routes with dynamic imports. Use route meta for permissions.
Workflow
Verify changes locally: run app, run tests, check for errors. CI catches environment issues, not basic bugs.
Format → Lint → Test before every commit. Never rely on CI for basic checks.
Use this Ruleset
Sign in to adopt or fork this ruleset
Sign in with GitHubStatistics
- Rules
- 214
- Standards
- 0
- Projects using
- 0
- Created
- Jan 15, 2026