Coding Standards
Apply consistent naming, structure, and patterns so code is readable and maintainable across the team.
When to Activate
- Starting a new project or module
- Refactoring to match team conventions
- Setting up or updating lint/format/type-check rules
- Reviewing code for consistency
- Onboarding: documenting or applying coding conventions
- Enforcing naming, formatting, or structural consistency
Core Principles
- Readability first — Code is read more than written; clear names and structure beat clever tricks.
- KISS — Simplest solution that works; avoid over-engineering and premature optimization.
- DRY — Extract common logic into functions/modules; avoid copy-paste.
- YAGNI — Don't build for speculative future needs; add complexity when required.
- Immutability — Prefer const; avoid mutating arguments or shared state; use spread/copy where needed.
Work Process (when applying standards)
- Discover project conventions — Scan existing code: naming (camelCase vs snake_case), file layout, import style, test patterns. Check for CONTRIBUTING, .eslintrc, .prettierrc, or editorconfig.
- Identify violations — Compare changed or new code against those conventions and the rules below.
- Suggest concrete fixes — Rename symbols, extract functions, add types, fix formatting. Prefer one logical edit per suggestion.
- Document exceptions — If the project has an exception (e.g. "use any here for legacy"), note it rather than "fixing" it without context.
Naming Conventions
Variables and functions
// GOOD: Descriptive, verb-noun for functions
const marketSearchQuery = 'election';
const isUserAuthenticated = true;
async function fetchMarketData(marketId: string) {}
function calculateSimilarity(a: number[], b: number[]) {}
// BAD: Unclear or noun-only for actions
const q = 'election';
const flag = true;
async function market(id: string) {}
function similarity(a, b) {}
Constants
- UPPER_SNAKE for true constants (e.g.
MAX_RETRIES, API_BASE_URL).
- Or project convention (some codebases use camelCase for config objects).
Types and interfaces
- PascalCase:
User, OrderItem, ApiResponse<T>.
- Suffix with role if helpful:
CreateUserRequest, UserResponse.
Files
- Components: PascalCase (
Button.tsx, UserProfile.tsx).
- Utilities/hooks: camelCase (
formatDate.ts, useAuth.ts).
- Types: camelCase with
.types or .d as project uses (market.types.ts).
Other languages
The examples above are TypeScript; the rule is "follow the language's own published convention", and that convention differs. Each language's style guide wins over the casing shown here:
- Python (PEP 8) —
snake_case functions, variables, modules; PascalCase classes; UPPER_SNAKE constants; a leading _ marks non-public.
- Go (Effective Go) —
MixedCaps, never underscores; a capital initial is the export marker. The package name is part of the name: chi.NewRouter, not chi.NewChiRouter.
- Rust (RFC 430) —
snake_case functions, variables, modules; UpperCamelCase types and traits; SCREAMING_SNAKE_CASE consts and statics.
- Java / Kotlin —
camelCase methods and fields, PascalCase types, one public type per file named after it.
- C# —
PascalCase for methods, properties, and types; camelCase for locals and parameters; interfaces prefixed I.
Immutability (critical)
// GOOD: Spread and new references
const updatedUser = { ...user, name: 'New Name' };
const updatedArray = [...items, newItem];
// BAD: Direct mutation
user.name = 'New Name';
items.push(newItem);
Other languages — same rule, different mechanism:
- Python — never mutate an argument the caller still owns, and never use a mutable default (
def f(xs=[]) shares one list across calls; use None + build inside). Prefer tuples and frozenset for fixed collections; dataclasses.replace(obj, field=…) for a modified copy.
- Go — a slice you store keeps its caller's backing array, so
append can write through to it later; copy before retaining (slices.Clone). Value receivers for methods that shouldn't mutate.
- Rust — bindings are immutable by default and the borrow checker enforces it;
mut is the exception you justify, not the default you type.
- Java —
record for data carriers, List.copyOf/Map.copyOf for defensive copies at the boundary.
- C# —
record types plus with expressions; ImmutableArray/ImmutableList for shared collections.
Error Handling
// GOOD: Validate, throw or return with context
async function fetchData(url: string) {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
return await response.json();
} catch (error) {
console.error('Fetch failed:', error);
throw new Error('Failed to fetch data');
}
}
// BAD: No handling or swallowed errors
async function fetchData(url: string) {
const response = await fetch(url);
return response.json();
}
Other languages — the shape changes, "add context, never swallow" does not:
- Python — raise a specific exception type, never a bare
except: (it eats KeyboardInterrupt too). Re-raise with the chain intact: raise ParseError(...) from err.
- Go — errors are values: check every one, wrap with context using
%w so errors.Is/errors.As still work (fmt.Errorf("fetch %s: %w", url, err)). Discarding one with _ = needs a comment saying why it's safe.
- Rust — return
Result<T, E> and propagate with ?; a typed error enum (or thiserror) at library boundaries, anyhow only in the binary. unwrap()/expect() in library code is a panic you shipped.
- Java — one exception type per failure mode, never an empty
catch; keep the cause (throw new X(msg, err)).
- C# — catch the specific exception, rethrow with bare
throw; (not throw ex;, which resets the stack trace).
Async and concurrency
// GOOD: Parallel when independent
const [users, markets, stats] = await Promise.all([
fetchUsers(),
fetchMarkets(),
fetchStats(),
]);
// BAD: Sequential when unnecessary
const users = await fetchUsers();
const markets = await fetchMarkets();
const stats = await fetchStats();
Other languages — run independent work concurrently, and give every concurrent unit a way to be cancelled:
- Python —
asyncio.gather(*coros) for independent awaits; asyncio.TaskGroup (3.11+) when a failure should cancel the siblings. Blocking calls go to run_in_executor, never inline in a coroutine.
- Go —
errgroup.Group (golang.org/x/sync) for fan-out that must fail as a unit, sync.WaitGroup when it must not. context.Context is the first parameter of anything that can block, and it is passed down, not stored in a struct.
- Rust —
tokio::join! for independent futures, try_join! when the first error should abort; a CancellationToken or dropping the JoinHandle for teardown.
- Java —
CompletableFuture.allOf(...), or structured concurrency (StructuredTaskScope) where available.
- C# —
Task.WhenAll(...), with a CancellationToken threaded through every async signature.
Type Safety
// GOOD: Explicit types, no any
interface Market {
id: string;
name: string;
status: 'active' | 'resolved' | 'closed';
}
function getMarket(id: string): Promise<Market> { /* ... */ }
// BAD: any or untyped
function getMarket(id: any): Promise<any> { /* ... */ }
Other languages — the goal is the same: make an invalid value unrepresentable, and check it at the boundary rather than at every call site.
- Python — annotate every public signature and run
mypy/pyright in strict mode in CI; a type hint nothing checks is a comment. TypedDict/dataclass/Pydantic model at API and parse boundaries, not raw dict[str, Any].
- Go — concrete types over
any; accept interfaces, return structs. An any in a signature is a parse boundary, and it gets a type switch or errors.As immediately.
- Rust — newtypes over bare primitives (
struct UserId(u64), not u64) so the compiler catches a swapped argument; enums over stringly-typed state.
- Java / Kotlin — Kotlin's nullable types, or
Optional plus nullability annotations in Java; no Object in a public signature.
- C# — nullable reference types enabled project-wide (
<Nullable>enable</Nullable>), warnings as errors.
Dynamically-typed languages without a checker (plain JS, Ruby, PHP, Lua) do the same job at runtime: validate at the trust boundary with a schema, and let internal calls stay unguarded.
Comments and docs
- Explain why, not what — "Use exponential backoff to avoid overwhelming the API" not "Increment retry count." A comment that only restates the code is noise; delete it or rename the code so it isn't needed.
- Plain ASCII punctuation — Write comments the way a developer types them:
- not —, ... not …, straight quotes, no decorative glyphs or emoji. Typographic glyphs in a comment are an AI-generation tell, not house style. (Comment text only — never string literals, identifiers, or data.)
- Doc comments on public APIs — Summary, parameters, return, what it raises, optional example. Use the language's own format and match project style: JSDoc/TSDoc, Python docstrings (PEP 257, in the project's Google/NumPy/reST flavor), Go doc comments starting with the symbol name, Rust
/// with a # Examples section, Javadoc, XML doc comments in C#.
- No commented-out code — Remove or explain in a ticket; use version control for history.
That is the bar for code you are writing or touching now. For a repo-wide pass over existing comments — deciding what to delete, condense, or fix, with the false-positive boundaries and the behavior-preserving verification gate — use awesome-code-cleanup, which owns that procedure.
File and project structure
- Follow existing layout (e.g.
src/app/, src/components/, src/lib/).
- One main export per file unless the project uses barrel files or index re-exports.
- Group imports: stdlib → third-party → local; alphabetical or by path per project.
Backend layering and boundaries (when applicable)
- Three-model split — keep DTO/API models, domain models, and persistence/ORM models separate. One
User object flowing through transport, business logic, and storage traps API shape to table shape and makes every refactor touch every layer.
- Layer-placement heuristic — needs HTTP status codes → edge/controller; needs business rules → service; needs tables/indexes/ORM → repository. Flow one direction:
controller -> service -> repository -> gateway.
- Cross-cutting concerns once at the edge — auth, validation, rate-limit, request IDs, logging live in the HTTP pipeline (global middleware/hooks or route-scoped setup), never hand-copied into each handler. The rule: do not repeat policy by hand in every endpoint.
- Errors don't know transport — services and repositories throw domain errors; one global handler maps them to status codes.
- Contract-first — OpenAPI (or equivalent) is the single source of truth for request/response shapes; generate typed clients from it and fail CI on spec drift.
// GOOD: service throws a domain error; the global handler maps NotFoundError -> 404
throw new NotFoundError('market', id);
// BAD: business logic reaches into HTTP transport
return res.status(404).json({ error: 'not found' });
This section only places the layers. Designing the error envelope, HTTP status mapping, and retry policy in depth is awesome-error-standards' job — go there when the task is the error contract itself.
Code smells to fix
| Smell |
Action |
| Function > ~50 lines |
Split into smaller functions with clear names |
| Deep nesting (5+ levels) |
Use early returns or extract functions |
| Magic numbers |
Extract named constants (e.g. MAX_RETRIES, DEBOUNCE_MS) |
| Long parameter list |
Use options object or split into smaller types |
| Duplicate logic in two places |
Extract to shared function or module |
The smells above hold in any language. Numeric thresholds are a starting point, not a law; a documented repo standard always wins, and don't re-flag what a linter or type-checker already enforces. Before inventing a pattern, search the codebase — the problem is often already solved somewhere; reuse it rather than adding a second way to do the same thing.
Framework-specific correctness (when applicable)
Separate from the universal smells above: every framework has a short list of footguns that look correct and fail at runtime. Learn the list for the framework in front of you rather than assuming another framework's list transfers — the authority is that framework's own documentation (React's Rules of Hooks and "You Might Not Need an Effect", Vue's reactivity caveats, Angular's change-detection guide, Svelte's store and reactivity notes).
React, as the worked example:
| Smell |
Action |
{count && <X/>} in JSX |
Renders literal 0/NaN when falsy — use an explicit ternary count > 0 ? <X/> : null |
| Component defined inside another component |
Hoist it out — a nested definition is a new type each render and remounts, losing state |
State derivable from props/state kept in useState+useEffect |
Derive it during render (or use a keyed reset) — no effect needed |
The transferable part is the shape of the class, not these three rows: a falsy value rendering as visible output, an identity that changes every render and silently discards state, and state duplicated instead of derived. Look for that shape in whatever framework the project uses.
Frontend rendering and motion (when applicable)
- Animate only compositor properties —
transform and opacity. Never animate layout properties (width, height, top, margin) — they trigger reflow every frame; use the FLIP technique for position changes.
- Never interleave layout reads and writes in one frame — batch reads (
getBoundingClientRect, offsetWidth) before writes to avoid layout thrashing.
- Prefer
animation-timeline: view()/scroll() over JS scroll-event listeners for scroll-linked animation; use will-change surgically and remove it after.
- Sensible defaults:
text-balance on headings, text-pretty on body, tabular-nums for numeric columns, h-dvh over h-screen, interactions under ~200ms, one accent color per view, a fixed z-index scale.
Frontend architecture (when applicable)
- Feature-first folders — product code (pages, feature components, state, feature-scoped API adapters, tests) lives together in a feature/module folder. Shared folders hold only cross-app primitives: design system, app shell, routing bootstrap, global config, i18n. Anti-patterns: a
shared/common/utils bucket with no boundary; a giant global components/; a folder per one-file throwaway.
- Colocate — a component or hook that matters keeps its test, story, styles, and an
index.ts re-export next to it.
- Route-level code-splitting is the default perf win — lazy-load route chunks; keep dashboard-sized deps out of the initial bundle when the landing page doesn't need them. Profile before hand-optimizing components. (Distinct from the compositor/animation rules above — this is bundle shape, not frame budget.)
- Consume the API contract, don't re-type it — generate TypeScript types from the same OpenAPI spec the backend owns instead of hand-duplicating request/response shapes. Use a server-state library (e.g. TanStack Query) so loading/error/retry stay uniform. Map errors once (a single
parseApiError) and surface the server request_id in error UI so user reports line up with server logs.
Checklist (when enforcing)
Anti-patterns
| Anti-pattern |
Better approach |
| "It's just a small script" |
Apply same naming and structure; future readers will thank you |
| Commenting out code "for later" |
Delete; use git history or a ticket |
| Fixing only the file in scope |
If touching a pattern, suggest project-wide convention or follow-up |
| Adding style rules without tooling |
Put them in the language's formatter and linter so format is automatic (gofmt, ruff/black, rustfmt+clippy, ktlint, dotnet format, ESLint/Prettier), plus .editorconfig for what crosses languages |
Integration
- If the project has a style guide, CONTRIBUTING, or lint/format config, align with it first. Override only when the user explicitly asks.
- Suggest concrete edits (rename, extract function, add type) rather than only listing rules.
1---2name: awesome-code-standards3description: Universal coding standards — naming, structure, immutability, error handling, type safety, backend layering, frontend architecture — that fill the gaps after the project's own conventions. Use when starting a module, refactoring to team conventions, setting up lint rules, onboarding, or 'стандарты кода'. Do not use for a repo-wide comment cleanup pass (awesome-code-cleanup) or for designing the API error contract (awesome-error-standards).4license: MIT5---67# Coding Standards89Apply consistent naming, structure, and patterns so code is readable and maintainable across the team.1011## When to Activate1213- Starting a new project or module14- Refactoring to match team conventions15- Setting up or updating lint/format/type-check rules16- Reviewing code for consistency17- Onboarding: documenting or applying coding conventions18- Enforcing naming, formatting, or structural consistency1920## Core Principles21221. Readability first — Code is read more than written; clear names and structure beat clever tricks.232. KISS — Simplest solution that works; avoid over-engineering and premature optimization.243. DRY — Extract common logic into functions/modules; avoid copy-paste.254. YAGNI — Don't build for speculative future needs; add complexity when required.265. Immutability — Prefer const; avoid mutating arguments or shared state; use spread/copy where needed.2728## Work Process (when applying standards)29301. Discover project conventions — Scan existing code: naming (camelCase vs snake_case), file layout, import style, test patterns. Check for CONTRIBUTING, .eslintrc, .prettierrc, or editorconfig.312. Identify violations — Compare changed or new code against those conventions and the rules below.323. Suggest concrete fixes — Rename symbols, extract functions, add types, fix formatting. Prefer one logical edit per suggestion.334. Document exceptions — If the project has an exception (e.g. "use any here for legacy"), note it rather than "fixing" it without context.3435## Naming Conventions3637### Variables and functions3839```typescript40// GOOD: Descriptive, verb-noun for functions41const marketSearchQuery = 'election';42const isUserAuthenticated = true;43async function fetchMarketData(marketId: string) {}44function calculateSimilarity(a: number[], b: number[]) {}4546// BAD: Unclear or noun-only for actions47const q = 'election';48const flag = true;49async function market(id: string) {}50function similarity(a, b) {}51```5253### Constants5455- UPPER_SNAKE for true constants (e.g. `MAX_RETRIES`, `API_BASE_URL`).56- Or project convention (some codebases use camelCase for config objects).5758### Types and interfaces5960- PascalCase: `User`, `OrderItem`, `ApiResponse<T>`.61- Suffix with role if helpful: `CreateUserRequest`, `UserResponse`.6263### Files6465- Components: PascalCase (`Button.tsx`, `UserProfile.tsx`).66- Utilities/hooks: camelCase (`formatDate.ts`, `useAuth.ts`).67- Types: camelCase with `.types` or `.d` as project uses (`market.types.ts`).6869### Other languages7071The examples above are TypeScript; the rule is "follow the language's own published convention", and that convention differs. Each language's style guide wins over the casing shown here:7273- Python (PEP 8) — `snake_case` functions, variables, modules; `PascalCase` classes; `UPPER_SNAKE` constants; a leading `_` marks non-public.74- Go (Effective Go) — `MixedCaps`, never underscores; a capital initial *is* the export marker. The package name is part of the name: `chi.NewRouter`, not `chi.NewChiRouter`.75- Rust (RFC 430) — `snake_case` functions, variables, modules; `UpperCamelCase` types and traits; `SCREAMING_SNAKE_CASE` consts and statics.76- Java / Kotlin — `camelCase` methods and fields, `PascalCase` types, one public type per file named after it.77- C# — `PascalCase` for methods, properties, and types; `camelCase` for locals and parameters; interfaces prefixed `I`.7879## Immutability (critical)8081```typescript82// GOOD: Spread and new references83const updatedUser = { ...user, name: 'New Name' };84const updatedArray = [...items, newItem];8586// BAD: Direct mutation87user.name = 'New Name';88items.push(newItem);89```9091Other languages — same rule, different mechanism:9293- Python — never mutate an argument the caller still owns, and never use a mutable default (`def f(xs=[])` shares one list across calls; use `None` + build inside). Prefer tuples and `frozenset` for fixed collections; `dataclasses.replace(obj, field=…)` for a modified copy.94- Go — a slice you store keeps its caller's backing array, so `append` can write through to it later; copy before retaining (`slices.Clone`). Value receivers for methods that shouldn't mutate.95- Rust — bindings are immutable by default and the borrow checker enforces it; `mut` is the exception you justify, not the default you type.96- Java — `record` for data carriers, `List.copyOf`/`Map.copyOf` for defensive copies at the boundary.97- C# — `record` types plus `with` expressions; `ImmutableArray`/`ImmutableList` for shared collections.9899## Error Handling100101```typescript102// GOOD: Validate, throw or return with context103async function fetchData(url: string) {104 try {105 const response = await fetch(url);106 if (!response.ok) {107 throw new Error(`HTTP ${response.status}: ${response.statusText}`);108 }109 return await response.json();110 } catch (error) {111 console.error('Fetch failed:', error);112 throw new Error('Failed to fetch data');113 }114}115116// BAD: No handling or swallowed errors117async function fetchData(url: string) {118 const response = await fetch(url);119 return response.json();120}121```122123Other languages — the shape changes, "add context, never swallow" does not:124125- Python — raise a specific exception type, never a bare `except:` (it eats `KeyboardInterrupt` too). Re-raise with the chain intact: `raise ParseError(...) from err`.126- Go — errors are values: check every one, wrap with context using `%w` so `errors.Is`/`errors.As` still work (`fmt.Errorf("fetch %s: %w", url, err)`). Discarding one with `_ =` needs a comment saying why it's safe.127- Rust — return `Result<T, E>` and propagate with `?`; a typed error enum (or `thiserror`) at library boundaries, `anyhow` only in the binary. `unwrap()`/`expect()` in library code is a panic you shipped.128- Java — one exception type per failure mode, never an empty `catch`; keep the cause (`throw new X(msg, err)`).129- C# — catch the specific exception, rethrow with bare `throw;` (not `throw ex;`, which resets the stack trace).130131## Async and concurrency132133```typescript134// GOOD: Parallel when independent135const [users, markets, stats] = await Promise.all([136 fetchUsers(),137 fetchMarkets(),138 fetchStats(),139]);140141// BAD: Sequential when unnecessary142const users = await fetchUsers();143const markets = await fetchMarkets();144const stats = await fetchStats();145```146147Other languages — run independent work concurrently, and give every concurrent unit a way to be cancelled:148149- Python — `asyncio.gather(*coros)` for independent awaits; `asyncio.TaskGroup` (3.11+) when a failure should cancel the siblings. Blocking calls go to `run_in_executor`, never inline in a coroutine.150- Go — `errgroup.Group` (`golang.org/x/sync`) for fan-out that must fail as a unit, `sync.WaitGroup` when it must not. `context.Context` is the first parameter of anything that can block, and it is *passed down*, not stored in a struct.151- Rust — `tokio::join!` for independent futures, `try_join!` when the first error should abort; a `CancellationToken` or dropping the `JoinHandle` for teardown.152- Java — `CompletableFuture.allOf(...)`, or structured concurrency (`StructuredTaskScope`) where available.153- C# — `Task.WhenAll(...)`, with a `CancellationToken` threaded through every async signature.154155## Type Safety156157```typescript158// GOOD: Explicit types, no any159interface Market {160 id: string;161 name: string;162 status: 'active' | 'resolved' | 'closed';163}164function getMarket(id: string): Promise<Market> { /* ... */ }165166// BAD: any or untyped167function getMarket(id: any): Promise<any> { /* ... */ }168```169170Other languages — the goal is the same: make an invalid value unrepresentable, and check it at the boundary rather than at every call site.171172- Python — annotate every public signature and run `mypy`/`pyright` in strict mode in CI; a type hint nothing checks is a comment. `TypedDict`/`dataclass`/Pydantic model at API and parse boundaries, not raw `dict[str, Any]`.173- Go — concrete types over `any`; accept interfaces, return structs. An `any` in a signature is a parse boundary, and it gets a type switch or `errors.As` immediately.174- Rust — newtypes over bare primitives (`struct UserId(u64)`, not `u64`) so the compiler catches a swapped argument; enums over stringly-typed state.175- Java / Kotlin — Kotlin's nullable types, or `Optional` plus nullability annotations in Java; no `Object` in a public signature.176- C# — nullable reference types enabled project-wide (`<Nullable>enable</Nullable>`), warnings as errors.177178Dynamically-typed languages without a checker (plain JS, Ruby, PHP, Lua) do the same job at runtime: validate at the trust boundary with a schema, and let internal calls stay unguarded.179180## Comments and docs181182- Explain why, not what — "Use exponential backoff to avoid overwhelming the API" not "Increment retry count." A comment that only restates the code is noise; delete it or rename the code so it isn't needed.183- Plain ASCII punctuation — Write comments the way a developer types them: `-` not `—`, `...` not `…`, straight quotes, no decorative glyphs or emoji. Typographic glyphs in a comment are an AI-generation tell, not house style. (Comment text only — never string literals, identifiers, or data.)184- Doc comments on public APIs — Summary, parameters, return, what it raises, optional example. Use the language's own format and match project style: JSDoc/TSDoc, Python docstrings (PEP 257, in the project's Google/NumPy/reST flavor), Go doc comments starting with the symbol name, Rust `///` with a `# Examples` section, Javadoc, XML doc comments in C#.185- No commented-out code — Remove or explain in a ticket; use version control for history.186187That is the bar for code you are writing or touching now. For a repo-wide pass over existing comments — deciding what to delete, condense, or fix, with the false-positive boundaries and the behavior-preserving verification gate — use awesome-code-cleanup, which owns that procedure.188189## File and project structure190191- Follow existing layout (e.g. `src/app/`, `src/components/`, `src/lib/`).192- One main export per file unless the project uses barrel files or index re-exports.193- Group imports: stdlib → third-party → local; alphabetical or by path per project.194195## Backend layering and boundaries (when applicable)196197- Three-model split — keep DTO/API models, domain models, and persistence/ORM models separate. One `User` object flowing through transport, business logic, and storage traps API shape to table shape and makes every refactor touch every layer.198- Layer-placement heuristic — needs HTTP status codes → edge/controller; needs business rules → service; needs tables/indexes/ORM → repository. Flow one direction: `controller -> service -> repository -> gateway`.199- Cross-cutting concerns once at the edge — auth, validation, rate-limit, request IDs, logging live in the HTTP pipeline (global middleware/hooks or route-scoped setup), never hand-copied into each handler. The rule: do not repeat policy by hand in every endpoint.200- Errors don't know transport — services and repositories throw domain errors; one global handler maps them to status codes.201- Contract-first — OpenAPI (or equivalent) is the single source of truth for request/response shapes; generate typed clients from it and fail CI on spec drift.202203```typescript204// GOOD: service throws a domain error; the global handler maps NotFoundError -> 404205throw new NotFoundError('market', id);206207// BAD: business logic reaches into HTTP transport208return res.status(404).json({ error: 'not found' });209```210211This section only places the layers. Designing the error envelope, HTTP status mapping, and retry policy in depth is awesome-error-standards' job — go there when the task is the error contract itself.212213## Code smells to fix214215| Smell | Action |216|-------|--------|217| Function > ~50 lines | Split into smaller functions with clear names |218| Deep nesting (5+ levels) | Use early returns or extract functions |219| Magic numbers | Extract named constants (e.g. `MAX_RETRIES`, `DEBOUNCE_MS`) |220| Long parameter list | Use options object or split into smaller types |221| Duplicate logic in two places | Extract to shared function or module |222223The smells above hold in any language. Numeric thresholds are a starting point, not a law; a documented repo standard always wins, and don't re-flag what a linter or type-checker already enforces. Before inventing a pattern, search the codebase — the problem is often already solved somewhere; reuse it rather than adding a second way to do the same thing.224225## Framework-specific correctness (when applicable)226227Separate from the universal smells above: every framework has a short list of footguns that look correct and fail at runtime. Learn the list for the framework in front of you rather than assuming another framework's list transfers — the authority is that framework's own documentation (React's Rules of Hooks and "You Might Not Need an Effect", Vue's reactivity caveats, Angular's change-detection guide, Svelte's store and reactivity notes).228229React, as the worked example:230231| Smell | Action |232|-------|--------|233| `{count && <X/>}` in JSX | Renders literal `0`/`NaN` when falsy — use an explicit ternary `count > 0 ? <X/> : null` |234| Component defined inside another component | Hoist it out — a nested definition is a new type each render and remounts, losing state |235| State derivable from props/state kept in `useState`+`useEffect` | Derive it during render (or use a keyed reset) — no effect needed |236237The transferable part is the *shape* of the class, not these three rows: a falsy value rendering as visible output, an identity that changes every render and silently discards state, and state duplicated instead of derived. Look for that shape in whatever framework the project uses.238239## Frontend rendering and motion (when applicable)240241- Animate only compositor properties — `transform` and `opacity`. Never animate layout properties (`width`, `height`, `top`, `margin`) — they trigger reflow every frame; use the FLIP technique for position changes.242- Never interleave layout reads and writes in one frame — batch reads (`getBoundingClientRect`, `offsetWidth`) before writes to avoid layout thrashing.243- Prefer `animation-timeline: view()/scroll()` over JS scroll-event listeners for scroll-linked animation; use `will-change` surgically and remove it after.244- Sensible defaults: `text-balance` on headings, `text-pretty` on body, `tabular-nums` for numeric columns, `h-dvh` over `h-screen`, interactions under ~200ms, one accent color per view, a fixed z-index scale.245246## Frontend architecture (when applicable)247248- Feature-first folders — product code (pages, feature components, state, feature-scoped API adapters, tests) lives together in a feature/module folder. Shared folders hold only cross-app primitives: design system, app shell, routing bootstrap, global config, i18n. Anti-patterns: a `shared`/`common`/`utils` bucket with no boundary; a giant global `components/`; a folder per one-file throwaway.249- Colocate — a component or hook that matters keeps its test, story, styles, and an `index.ts` re-export next to it.250- Route-level code-splitting is the default perf win — lazy-load route chunks; keep dashboard-sized deps out of the initial bundle when the landing page doesn't need them. Profile before hand-optimizing components. (Distinct from the compositor/animation rules above — this is bundle shape, not frame budget.)251- Consume the API contract, don't re-type it — generate TypeScript types from the same OpenAPI spec the backend owns instead of hand-duplicating request/response shapes. Use a server-state library (e.g. TanStack Query) so loading/error/retry stay uniform. Map errors once (a single `parseApiError`) and surface the server `request_id` in error UI so user reports line up with server logs.252253## Checklist (when enforcing)254255- [ ] Naming matches project (camelCase/PascalCase/snake_case)256- [ ] No direct mutation of arguments or shared state257- [ ] Errors handled and propagated with context258- [ ] No unnecessary `any`; types explicit at boundaries259- [ ] Public APIs documented (the language's doc-comment format, or the project standard)260- [ ] Files and structure match existing layout261- [ ] No magic numbers; constants named262- [ ] Lint and format rules pass (if project has them)263264## Anti-patterns265266| Anti-pattern | Better approach |267|--------------|-----------------|268| "It's just a small script" | Apply same naming and structure; future readers will thank you |269| Commenting out code "for later" | Delete; use git history or a ticket |270| Fixing only the file in scope | If touching a pattern, suggest project-wide convention or follow-up |271| Adding style rules without tooling | Put them in the language's formatter and linter so format is automatic (`gofmt`, `ruff`/`black`, `rustfmt`+`clippy`, `ktlint`, `dotnet format`, ESLint/Prettier), plus `.editorconfig` for what crosses languages |272273## Integration274275- If the project has a style guide, CONTRIBUTING, or lint/format config, align with it first. Override only when the user explicitly asks.276- Suggest concrete edits (rename, extract function, add type) rather than only listing rules.