Code Style
Naming Conventions
- Variables and functions: camelCase
- Classes, interfaces, and types: PascalCase
- Constants: UPPER_SNAKE_CASE
- Files: kebab-case for modules, PascalCase for components
- Booleans: prefix with is, has, can, should (e.g., isActive, hasPermission) — makes conditionals read like English
Functions
- Keep functions under 30 lines — longer functions are harder to test and reason about
BAD: A 60-line function doing validation + transformation + persistence GOOD: Three focused functions: validate(), transform(), persist()
- Single responsibility: one function does one thing
- Pure functions preferred: same input always produces same output — easier to test and cache
- Limit parameters to 3 — use an options object for more
BAD: createUser(name, email, role, team, isActive, notify)
GOOD: createUser({ name, email, role, team, isActive, notify })
Control Flow
- Use early returns and guard clauses to handle edge cases at the top of functions — keeps the happy path at the outer scope and reduces nesting
- Prefer exhaustive pattern matching (switch/when with sealed types) over if-else chains — the compiler enforces that all cases are handled
- Avoid deep nesting: if indentation exceeds 3 levels, refactor — use early returns, extract functions, or invert conditions
Immutability by Default
- Treat data as immutable unless mutation is explicitly required — create new objects instead of modifying existing ones
- Use language-level immutability features: const, readonly, frozen, final — mutation should require a conscious choice
File Organization
- One primary export per file where practical — simplifies imports and dependency tracking
- Group imports: external libraries, path-aliased modules, relative modules, types
- Order imports alphabetically within each group
- Keep files focused on a single concern
- Never use deep relative imports (2+ levels of
../) — use path aliases (#src/*,@/*) instead; deep traversals are fragile and unreadable
Type Discipline
- Treat type errors as design feedback, not noise — fix the design, don’t suppress the error
- Automate type generation in CI where applicable (API schemas, database types, GraphQL)
Complexity Metrics
- Track cognitive complexity, not just line count — a 25-line function with 4 levels of nesting is harder to read than a 40-line flat function
- Configure linters to warn on cognitive complexity thresholds (e.g., SonarQube default of 15)
Linting
- Always fix linting errors before moving to other tasks — never leave broken lint for later
- Never suppress linter warnings without a documented reason and ticket reference
Error Handling
- Never silently swallow errors (empty catch blocks) — hidden failures cause hard-to-debug production issues
- Handle errors at the appropriate level, not everywhere
- Use typed errors when the language supports them
- Always clean up resources in finally blocks
Dead Code
- Remove unused functions, imports, and variables — dead code misleads readers and increases maintenance burden
- Remove stale feature flags after rollout is complete — lingering flags accumulate as tech debt
No Hardcoding — Single Source of Truth
- Never hardcode values that are defined or derivable elsewhere — duplicated lists drift and cause bugs
- Magic numbers and magic strings must be named constants with clear intent
- Dynamic lists (supported agents, preset names, template catalogs, flag keys) must be derived from their source of truth, not duplicated in arrays or sets
- Configuration values belong in environment variables, config files, or constants modules — not scattered in business logic
- If you find yourself copying a list from one file to another, extract it to a shared source and import it
BAD: const presets = ['minimal', 'balanced', 'strict'] (duplicates the preset registry)
GOOD: const presets = getPresetNames() (derived from the registry)
BAD: if (status === 3) (what does 3 mean?)
GOOD: if (status === STATUS.APPROVED) (self-documenting constant)
Comments
- Write self-documenting code first
- Comment the WHY, not the WHAT — the code already shows what it does
BAD: // increment counter above counter++
GOOD: // retry count resets after successful auth per RFC 6749 §4.1.3
- Remove commented-out code — version control has the history
- Use TODO/FIXME with ticket references, not open-ended notes