[{"title":"Separate internal codes from localized labels","rule":"Do not drive business logic with translated display strings. Keep stable internal codes for comparisons and map them to localized labels at the UI boundary.","apiRule":"Do not drive business logic with translated display strings. Keep stable internal codes for comparisons and map them to localized labels at the UI boundary. The same strings are serving both as domain identifiers and translated UI labels. That makes the code harder to understand outside one language context and couples logic changes to copy changes. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. This is especially important when the same domain value appears in APIs, analytics, persistence, and UI copy across multiple languages. Exceptions: Small one-off display-only mappings can stay inline, but the displayed label still should not become the source of truth for domain logic.","whyItMatters":"The same strings are serving both as domain identifiers and translated UI labels. That makes the code harder to understand outside one language context and couples logic changes to copy changes.","priority":"p1","scope":"universal"},{"title":"Optimize raster images before committing","rule":"Large PNG, JPEG, and similar raster assets should be compressed, converted, or resized before they land in the repo. Non-critical images should also avoid eager loading by default.","apiRule":"Large PNG, JPEG, and similar raster assets should be compressed, converted, or resized before they land in the repo. Non-critical images should also avoid eager loading by default. This commits a potentially oversized raster image with no optimization strategy and no loading hint. Heavy assets slow page loads, waste bandwidth, and often go unnoticed because the file already \"works\" locally. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Review both file format and display behavior. If the image is photographic or decorative, WebP/JPEG is often better than a raw PNG. Exceptions: Keep PNG or eager loading only when the content genuinely requires lossless transparency or when the image is critical to first paint.","whyItMatters":"This commits a potentially oversized raster image with no optimization strategy and no loading hint. Heavy assets slow page loads, waste bandwidth, and often go unnoticed because the file already \"works\" locally.","priority":"p1","scope":"stack-specific"},{"title":"Pause background refresh while the user is editing","rule":"Do not let polling or background refresh overwrite in-progress modal or form state. Suspend refresh or isolate draft state until editing is complete.","apiRule":"Do not let polling or background refresh overwrite in-progress modal or form state. Suspend refresh or isolate draft state until editing is complete. Background refresh keeps changing the source props while the user is editing, so the draft can reset unexpectedly. This creates data loss, confusing UI jumps, and modal behavior that feels broken even though the polling itself is technically working. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. If refresh must continue, keep server data and user draft in separate state channels and merge deliberately rather than resetting from props. Exceptions: Purely read-only surfaces can continue polling normally. The rule matters when refresh can overwrite in-progress user input or local editing state.","whyItMatters":"Background refresh keeps changing the source props while the user is editing, so the draft can reset unexpectedly. This creates data loss, confusing UI jumps, and modal behavior that feels broken even though the polling itself is technically working.","priority":"p1","scope":"stack-specific"},{"title":"Do not cast away nullable identifiers","rule":"When an identifier is nullable in the type system, narrow or reject it explicitly before use instead of hiding the risk with a cast.","apiRule":"When an identifier is nullable in the type system, narrow or reject it explicitly before use instead of hiding the risk with a cast. The cast suppresses a real possibility from the type system instead of handling it. If a nullable identifier leaks through, the code produces invalid selector items and the bug becomes harder to trace because the compiler was explicitly silenced. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Prefer narrowing at the boundary where nullable data first enters the flow. If null is a legitimate domain case, model it explicitly instead of coercing it away. Exceptions: A cast is only acceptable when an earlier runtime invariant already guarantees non-null and that invariant is immediately visible in the same code path.","whyItMatters":"The cast suppresses a real possibility from the type system instead of handling it. If a nullable identifier leaks through, the code produces invalid selector items and the bug becomes harder to trace because the compiler was explicitly silenced.","priority":"p1","scope":"stack-specific"},{"title":"Use runtime server config for deployment-specific values","rule":"Do not rely on build-time public environment variables for values that differ by deployed environment. Resolve deploy-specific origins and flags at runtime on the server boundary.","apiRule":"Do not rely on build-time public environment variables for values that differ by deployed environment. Resolve deploy-specific origins and flags at runtime on the server boundary. This assumes the public environment value will match the deployed runtime, but public env values are often inlined at build time. The result can silently drift across environments and produce broken URLs or feature flags after deployment. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. This applies to hostnames, preview origins, public asset bases, and feature flags that may differ between deployed environments. Exceptions: Purely static public values that are intentionally identical in every build can stay as build-time public env vars.","whyItMatters":"This assumes the public environment value will match the deployed runtime, but public env values are often inlined at build time. The result can silently drift across environments and produce broken URLs or feature flags after deployment.","priority":"p1","scope":"stack-specific"},{"title":"Prefer CSS clamping over JS truncation for text previews","rule":"When the UI only needs a collapsed preview with optional expansion, use native CSS clamping instead of custom JS truncation logic, especially for rich text.","apiRule":"When the UI only needs a collapsed preview with optional expansion, use native CSS clamping instead of custom JS truncation logic, especially for rich text. This adds custom truncation logic to solve a visual presentation problem. It becomes harder to maintain when the content contains HTML, line wrapping changes across breakpoints, or the preview needs to stay consistent with real layout behavior. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Prefer native CSS/platform behavior first. Fall back to JS only when the product truly needs content-aware truncation that CSS cannot express. Exceptions: If the requirement depends on semantic content boundaries, server-side summaries, or exact character budgets, a JS or backend solution may still be necessary.","whyItMatters":"This adds custom truncation logic to solve a visual presentation problem. It becomes harder to maintain when the content contains HTML, line wrapping changes across breakpoints, or the preview needs to stay consistent with real layout behavior.","priority":"p1","scope":"stack-specific"},{"title":"Review and verify code before opening a PR when explicitly requested","rule":"Run the project's lint/tests and review the diff for duplication, weak abstractions, dead code, fragile logic, and pattern mismatches only when the user explicitly asks for a pre-PR check.","apiRule":"Run the project's lint/tests and review the diff for duplication, weak abstractions, dead code, fragile logic, and pattern mismatches only when the user explicitly asks for a pre-PR check. This runs an expensive review workflow even though the user did not ask for it. It burns time and tokens on routine tasks, and it makes the assistant feel heavier than necessary. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. If the user explicitly asks for a pre-PR review, inspect the changed files for repeated code, bad naming, unnecessary complexity, weak validation, inconsistent patterns, and missing cleanup, not just test pass/fail status. Exceptions: You may run a narrower check without being asked only when a task specifically depends on one local verification step, such as confirming a build or lint fix. The full pre-PR audit remains opt-in.","whyItMatters":"This runs an expensive review workflow even though the user did not ask for it. It burns time and tokens on routine tasks, and it makes the assistant feel heavier than necessary.","priority":"p1","scope":"universal"},{"title":"Prefer refs for latest state in stable callbacks","rule":"When a useCallback only needs the latest state for a lookup or imperative action, read it from a ref instead of depending on the whole state collection.","apiRule":"When a useCallback only needs the latest state for a lookup or imperative action, read it from a ref instead of depending on the whole state collection. Depending on the whole state collection recreates the callback every time that collection changes, even when the callback only needs the latest item lookup at call time. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. Use this pattern when the callback only needs read access to the latest value at execution time. This is especially useful for event handlers, mutation callbacks, or async actions passed to children. Exceptions: Keep state in the dependency list when the callback should intentionally be recreated as that state changes, or when the state participates in derived logic that should stay inside React's dependency model.","whyItMatters":"Depending on the whole state collection recreates the callback every time that collection changes, even when the callback only needs the latest item lookup at call time.","priority":"p2","scope":"stack-specific"},{"title":"Validate request payloads before side effects","rule":"Validation prevents bad data from reaching the database and gives clients clear errors.","apiRule":"Validation prevents bad data from reaching the database and gives clients clear errors. The handler trusts the request body shape. Malformed payloads can cause runtime errors or corrupt data. Priority: P0. Scope: universal.","whenToApply":"Priority: P0. Scope: universal. Use when you encounter code similar to the bad example.","whyItMatters":"The handler trusts the request body shape. Malformed payloads can cause runtime errors or corrupt data.","priority":"p0","scope":"universal"},{"title":"Prefer early returns over deep nesting","rule":"Early returns keep control flow easy to read and reduce indentation.","apiRule":"Early returns keep control flow easy to read and reduce indentation. Nested branches add noise and make the function harder to scan. As conditions grow, the code becomes harder to maintain. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. Use when you encounter code similar to the bad example.","whyItMatters":"Nested branches add noise and make the function harder to scan. As conditions grow, the code becomes harder to maintain.","priority":"p1","scope":"universal"},{"title":"Extract duplicated UI primitives into shared components","rule":"If identical presentational blocks appear in multiple files, move them into a shared component instead of duplicating markup and styling contracts.","apiRule":"If identical presentational blocks appear in multiple files, move them into a shared component instead of duplicating markup and styling contracts. The same UI primitive is implemented in multiple places. That increases maintenance cost and makes style or behavior fixes easy to miss in one of the copies. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. This is especially useful for repeated row items, pills, action rows, badges, and small drawer/card primitives. Exceptions: Do not extract if the shared abstraction would be more complex than the duplication or if the duplicated blocks are already diverging in structure.","whyItMatters":"The same UI primitive is implemented in multiple places. That increases maintenance cost and makes style or behavior fixes easy to miss in one of the copies.","priority":"p1","scope":"universal"},{"title":"Memoize stable callback props with useCallback","rule":"When passing non-trivial handlers to hooks or child components, extract them and wrap them in useCallback so they are not recreated on every render.","apiRule":"When passing non-trivial handlers to hooks or child components, extract them and wrap them in useCallback so they are not recreated on every render. The callback is recreated on every render and hides reusable logic inside an inline prop. That makes dependency behavior harder to reason about and can trigger unnecessary downstream updates. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. Prefer this when the callback contains merge logic, side effects, or is passed into custom hooks. Tiny one-line DOM event handlers inside JSX do not always need extraction. Exceptions: Inline callbacks are acceptable when they are trivial, local to a leaf component, and not passed into memoized/custom-hook APIs.","whyItMatters":"The callback is recreated on every render and hides reusable logic inside an inline prop. That makes dependency behavior harder to reason about and can trigger unnecessary downstream updates.","priority":"p1","scope":"universal"},{"title":"Prefer semantic disambiguation around destructuring","rule":"If destructuring would collide with existing names, rename the surrounding locals based on their source or role, not with vague prefixes like current or updated.","apiRule":"If destructuring would collide with existing names, rename the surrounding locals based on their source or role, not with vague prefixes like current or updated. The destructured response fields are fine, but the surrounding local names are vague. Prefixes like current do not explain where the value comes from or why it differs from the response value. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. Use when you encounter code similar to the bad example. Exceptions: Use current only when it is a real domain term, such as currentUser or currentRoute, not just as a generic collision workaround.","whyItMatters":"The destructured response fields are fine, but the surrounding local names are vague. Prefixes like current do not explain where the value comes from or why it differs from the response value.","priority":"p2","scope":"stack-specific"},{"title":"Document non-obvious !important overrides","rule":"When !important is genuinely needed to beat framework-injected styles, explain the conflict in a short comment.","apiRule":"When !important is genuinely needed to beat framework-injected styles, explain the conflict in a short comment. Without context, the override looks accidental. Reviewers cannot tell whether it solves a real framework specificity issue or is masking dead CSS. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. Use when you encounter code similar to the bad example. Exceptions: If the override can be removed by switching to the library API or theme override cleanly in the same change, prefer removing it over documenting it.","whyItMatters":"Without context, the override looks accidental. Reviewers cannot tell whether it solves a real framework specificity issue or is masking dead CSS.","priority":"p2","scope":"stack-specific"},{"title":"Name overlay components after their actual UI behavior","rule":"Overlay component names should match what they render: Popover, Drawer, Dialog, or a neutral Panel when needed.","apiRule":"Overlay component names should match what they render: Popover, Drawer, Dialog, or a neutral Panel when needed. The name says Modal, but the component behaves like a popover. That misleads readers about interaction, accessibility, and stacking behavior. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. Use when you encounter code similar to the bad example. Exceptions: A neutral name like Panel is acceptable when the component intentionally abstracts over multiple overlay types across breakpoints.","whyItMatters":"The name says Modal, but the component behaves like a popover. That misleads readers about interaction, accessibility, and stacking behavior.","priority":"p2","scope":"stack-specific"},{"title":"Prefer callback parameter destructuring for simple property reads","rule":"If a collection callback only reads one property from its item, destructure that property in the parameter list.","apiRule":"If a collection callback only reads one property from its item, destructure that property in the parameter list. The callback parameter name adds no value when the body only reads one field. It makes the callback slightly noisier than necessary. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. Use when you encounter code similar to the bad example. Exceptions: Keep the full parameter when the callback uses multiple fields, when the object itself is passed onward, or when destructuring hurts clarity.","whyItMatters":"The callback parameter name adds no value when the body only reads one field. It makes the callback slightly noisier than necessary.","priority":"p2","scope":"stack-specific"},{"title":"Extract complex generic callback signatures into named type aliases","rule":"Do not inline non-trivial generic callback types in props or exported interfaces.","apiRule":"Do not inline non-trivial generic callback types in props or exported interfaces. Inlining a generic callback signature makes the public contract harder to scan, harder to reuse, and more tedious to discuss in review. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. Use when you encounter code similar to the bad example. Exceptions: Keep simple inline function types inline when they have no generics and remain easy to read.","whyItMatters":"Inlining a generic callback signature makes the public contract harder to scan, harder to reuse, and more tedious to discuss in review.","priority":"p2","scope":"stack-specific"},{"title":"Destructure repeatedly used fields from the same object","rule":"When several fields from one object are used together, destructure them once instead of repeating property access.","apiRule":"When several fields from one object are used together, destructure them once instead of repeating property access. Repeated property access creates noise and makes related fields harder to scan as one group. It also makes follow-up edits easier to miss. Priority: P2. Scope: universal.","whenToApply":"Priority: P2. Scope: universal. Use when you encounter code similar to the bad example. Exceptions: Keep direct property access when only one field is used once, or when destructuring would make a rename less clear.","whyItMatters":"Repeated property access creates noise and makes related fields harder to scan as one group. It also makes follow-up edits easier to miss.","priority":"p2","scope":"universal"},{"title":"Use API time-series values as the chart x-axis source of truth","rule":"If the backend already returns timestamps for graph points, the chart should use those timestamps directly instead of synthesizing index-based x-axis values.","apiRule":"If the backend already returns timestamps for graph points, the chart should use those timestamps directly instead of synthesizing index-based x-axis values. The UI discards the real timestamp and invents a synthetic x-axis. That can mislabel ticks and drift away from the actual backend data. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. Use when you encounter code similar to the bad example. Exceptions: A synthetic x-axis is acceptable only when the backend truly does not provide a stable domain value and the approximation is documented.","whyItMatters":"The UI discards the real timestamp and invents a synthetic x-axis. That can mislabel ticks and drift away from the actual backend data.","priority":"p1","scope":"universal"},{"title":"Extract duplicated frontend helpers into shared utilities","rule":"If two frontend files need the same helper or constants, move them to a shared utility instead of duplicating or mirroring them.","apiRule":"If two frontend files need the same helper or constants, move them to a shared utility instead of duplicating or mirroring them. The same date helpers live in multiple places, so future fixes drift and one consumer eventually behaves differently from the other. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. Use when you encounter code similar to the bad example.","whyItMatters":"The same date helpers live in multiple places, so future fixes drift and one consumer eventually behaves differently from the other.","priority":"p1","scope":"universal"},{"title":"Prefer API contract fixes over client-side metadata backfills","rule":"When the UI needs data that the API could reasonably return, improve the API contract instead of layering extra client fetches as the default solution.","apiRule":"When the UI needs data that the API could reasonably return, improve the API contract instead of layering extra client fetches as the default solution. The frontend is compensating for an incomplete API contract with extra fetches and merge logic. That increases coupling and hides a backend data-shape gap. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. Use when you encounter code similar to the bad example. Exceptions: Keep a temporary client fallback only when the backend change is blocked and the feature must ship; add a TODO that names the API contract to improve.","whyItMatters":"The frontend is compensating for an incomplete API contract with extra fetches and merge logic. That increases coupling and hides a backend data-shape gap.","priority":"p1","scope":"universal"},{"title":"Import shared utilities from their source module","rule":"Do not re-export shared helpers through feature-specific constants files unless you are intentionally creating a public module boundary.","apiRule":"Do not re-export shared helpers through feature-specific constants files unless you are intentionally creating a public module boundary. The feature constants file becomes an accidental barrel for unrelated shared utilities. That hides the real dependency and makes refactors noisier. Priority: P2. Scope: universal.","whenToApply":"Priority: P2. Scope: universal. Use when you encounter code similar to the bad example. Exceptions: A dedicated index barrel is fine when the module is intentionally exposing a stable public surface.","whyItMatters":"The feature constants file becomes an accidental barrel for unrelated shared utilities. That hides the real dependency and makes refactors noisier.","priority":"p2","scope":"universal"},{"title":"Do not wrap synchronous work in fake async helpers","rule":"Avoid Promise.resolve, Promise.all, or async function signatures when the underlying work is synchronous.","apiRule":"Avoid Promise.resolve, Promise.all, or async function signatures when the underlying work is synchronous. The code pretends there is asynchronous coordination, but the setters are synchronous. That makes control flow harder to read and review. Priority: P2. Scope: universal.","whenToApply":"Priority: P2. Scope: universal. Use when you encounter code similar to the bad example. Exceptions: Use Promise.all only when every entry is genuinely asynchronous and concurrency is desired.","whyItMatters":"The code pretends there is asynchronous coordination, but the setters are synchronous. That makes control flow harder to read and review.","priority":"p2","scope":"universal"},{"title":"Extract heavy component logic into hooks or helpers","rule":"When a React component starts owning substantial form state, derived values, validation, and side effects, move that logic into a custom hook or helper.","apiRule":"When a React component starts owning substantial form state, derived values, validation, and side effects, move that logic into a custom hook or helper. The component mixes rendering, state machine logic, validation, derived values, and side effects. It becomes hard to review, test, and change safely. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. Use when you encounter code similar to the bad example. Exceptions: Do not extract tiny components that only have a couple of local state values and no real business logic.","whyItMatters":"The component mixes rendering, state machine logic, validation, derived values, and side effects. It becomes hard to review, test, and change safely.","priority":"p1","scope":"universal"},{"title":"Do not keep empty callbacks to satisfy component APIs","rule":"If a callback does nothing, remove it and make the receiving prop optional instead of passing no-op handlers around.","apiRule":"If a callback does nothing, remove it and make the receiving prop optional instead of passing no-op handlers around. The callback exists only to satisfy the prop contract. It adds noise and hides the fact that opening has no behavior. Priority: P2. Scope: universal.","whenToApply":"Priority: P2. Scope: universal. Use when you encounter code similar to the bad example. Exceptions: A no-op is acceptable only when integrating with a third-party API that strictly requires a callable and cannot be wrapped locally.","whyItMatters":"The callback exists only to satisfy the prop contract. It adds noise and hides the fact that opening has no behavior.","priority":"p2","scope":"universal"},{"title":"Document historical data assumptions in enrichment fallbacks","rule":"When adding enrichment logic for missing names or metadata, encode and document the data assumption it protects against.","apiRule":"When adding enrichment logic for missing names or metadata, encode and document the data assumption it protects against. The code may be correct, but the intent is hidden. Reviewers cannot tell whether this handles stale data, deleted holdings, pagination gaps, or something else. Priority: P2. Scope: universal.","whenToApply":"Priority: P2. Scope: universal. Prefer intent-revealing names and one short comment when the fallback exists because current-state data differs from historical-state data. Exceptions: Do not add comments for obvious transformations; use this only when the data assumption would otherwise be unclear in review.","whyItMatters":"The code may be correct, but the intent is hidden. Reviewers cannot tell whether this handles stale data, deleted holdings, pagination gaps, or something else.","priority":"p2","scope":"universal"},{"title":"Keep frontend write contracts limited to the current UI scope","rule":"Do not expose backend fields in frontend create or update payloads unless the current UI actually reads, edits, or intentionally forwards them.","apiRule":"Do not expose backend fields in frontend create or update payloads unless the current UI actually reads, edits, or intentionally forwards them. The frontend widens its write surface to include DB or tracking fields that the product is not using. That adds maintenance cost and review noise without user value. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. Separate response completeness from write scope. The backend may return more fields than the frontend should be allowed to send back. Exceptions: Include hidden or passthrough fields only when there is an explicit product or backend requirement and that requirement is documented in code.","whyItMatters":"The frontend widens its write surface to include DB or tracking fields that the product is not using. That adds maintenance cost and review noise without user value.","priority":"p1","scope":"universal"},{"title":"Remove dead fallback and recovery logic","rule":"Fallbacks, retries, and recovery branches must correspond to a real failure mode in the current implementation, not to a past or assumed one.","apiRule":"Fallbacks, retries, and recovery branches must correspond to a real failure mode in the current implementation, not to a past or assumed one. The branch looks thoughtful, but it is dead if the request no longer depends on a refreshable token. Dead recovery code increases complexity and misleads reviewers about the real runtime behavior. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. When implementation assumptions change, revisit related retry, refresh, fallback, and normalization branches in the same PR. Exceptions: Keep dormant recovery logic only if it is intentionally part of an approved near-term rollout and clearly documented as such.","whyItMatters":"The branch looks thoughtful, but it is dead if the request no longer depends on a refreshable token. Dead recovery code increases complexity and misleads reviewers about the real runtime behavior.","priority":"p1","scope":"universal"},{"title":"Classify backend fields by contract scope","rule":"For every backend field, decide whether it belongs in response only, create payload, update payload, or nowhere in the frontend contract.","apiRule":"For every backend field, decide whether it belongs in response only, create payload, update payload, or nowhere in the frontend contract. The frontend copies response fields into create and update payloads without deciding whether the UI actually owns them. That creates bloated write contracts and unnecessary validation work. Priority: P0. Scope: universal.","whenToApply":"Priority: P0. Scope: universal. Use this decision order for every new backend field: response only, create only, update only, create and update, or not in frontend contracts at all. Exceptions: Mirrored create and update payloads are acceptable when the UI truly owns the same field set in both flows and that symmetry reduces complexity.","whyItMatters":"The frontend copies response fields into create and update payloads without deciding whether the UI actually owns them. That creates bloated write contracts and unnecessary validation work.","priority":"p0","scope":"universal"},{"title":"Trace auth ownership through the full call chain","rule":"Before adding auth props or headers, trace the whole request path and use only the auth mechanism actually consumed by the target route or service.","apiRule":"Before adding auth props or headers, trace the whole request path and use only the auth mechanism actually consumed by the target route or service. The code pushes authToken through the stack without checking whether the route actually needs it. This creates redundant props, dead validation, and misleading auth behavior. Priority: P0. Scope: universal.","whenToApply":"Priority: P0. Scope: universal. Explicitly identify whether auth is handled by session cookie, auth token, server-side service credentials, or no auth at all before adding headers or props. Exceptions: If a route intentionally accepts multiple auth mechanisms, that must be visible in the handler contract and documented in code.","whyItMatters":"The code pushes authToken through the stack without checking whether the route actually needs it. This creates redundant props, dead validation, and misleading auth behavior.","priority":"p0","scope":"universal"},{"title":"Only add token refresh logic to token-auth flows","rule":"Do not dispatch token refresh or retry behavior unless the failing request actually depends on a refreshable token.","apiRule":"Do not dispatch token refresh or retry behavior unless the failing request actually depends on a refreshable token. This assumes every 401 is solved by refreshing a token. If the route is session-based, the retry event is dead behavior and hides the real failure path. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. Treat token refresh as part of the auth contract, not as a generic 401 reaction. Exceptions: If the product intentionally centralizes 401 recovery through a shared retry system, document that convention and apply it consistently.","whyItMatters":"This assumes every 401 is solved by refreshing a token. If the route is session-based, the retry event is dead behavior and hides the real failure path.","priority":"p1","scope":"universal"},{"title":"Define the minimal request contract before threading props","rule":"Before adding hook props, fetch args, or service params, define the actual inputs the flow needs and pass only those through the stack.","apiRule":"Before adding hook props, fetch args, or service params, define the actual inputs the flow needs and pass only those through the stack. The flow accepts and forwards props before verifying they are truly needed. This creates dead parameters, broader interfaces, and review churn later. Priority: P0. Scope: universal.","whenToApply":"Priority: P0. Scope: universal. For new data flows, decide these first: required inputs, auth mechanism, pagination ownership, error handling, and response shape. Exceptions: Temporary over-provisioning is acceptable only when a follow-up change in the same PR uses the extra prop and the intent is documented.","whyItMatters":"The flow accepts and forwards props before verifying they are truly needed. This creates dead parameters, broader interfaces, and review churn later.","priority":"p0","scope":"universal"},{"title":"Only create types that narrow or clarify","rule":"A new type alias should either add compile-time constraints or preserve domain meaning. If it does neither, use the primitive directly.","apiRule":"A new type alias should either add compile-time constraints or preserve domain meaning. If it does neither, use the primitive directly. The alias adds no safety because it is still just string, and the optional-plus-null fields add extra states without different semantics. The contract becomes weaker, not clearer. Priority: P0. Scope: universal.","whenToApply":"Priority: P0. Scope: universal. Before adding a new alias, ask: does this constrain values, document a domain boundary, or improve call-site clarity? Exceptions: A domain alias is still useful when it reuses a shared primitive but keeps a meaningful API surface, for example `type PortfolioTransactionOrder = SortOrderUpper`.","whyItMatters":"The alias adds no safety because it is still just string, and the optional-plus-null fields add extra states without different semantics. The contract becomes weaker, not clearer.","priority":"p0","scope":"universal"},{"title":"Prefer configuration maps for tabular UI variants","rule":"When table headers or similarly repetitive UI only differ by labels, alignment, or classes per tab, define a tab-to-config map instead of repeating markup branches.","apiRule":"When table headers or similarly repetitive UI only differ by labels, alignment, or classes per tab, define a tab-to-config map instead of repeating markup branches. The component is really rendering data-driven column definitions, but the implementation duplicates markup for each tab variant. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. This rule fits best for headers, filters, and small variant-based layouts. Use stable keys in the config instead of array indexes. Exceptions: Do not force config maps when each branch has substantially different structure or behavior; in those cases separate components or explicit branches may be clearer.","whyItMatters":"The component is really rendering data-driven column definitions, but the implementation duplicates markup for each tab variant.","priority":"p1","scope":"universal"},{"title":"Optional reference IDs must be omitted when absent and positive when present","rule":"Foreign-key style IDs should not accept 0 or null as a meaningful value unless the API explicitly defines that behavior.","apiRule":"Foreign-key style IDs should not accept 0 or null as a meaningful value unless the API explicitly defines that behavior. Allowing 0 for reference IDs weakens the contract and creates an unnecessary extra state. Optional IDs should usually be omitted when absent. Priority: P0. Scope: universal.","whenToApply":"Priority: P0. Scope: universal. Use this especially for fields like `portfolioId`, `transactionId`, `dividendId`, `instrumentId`, and similar relation IDs. Exceptions: Only allow 0 or null if the backend contract explicitly documents them as valid domain values with different meaning from omission.","whyItMatters":"Allowing 0 for reference IDs weakens the contract and creates an unnecessary extra state. Optional IDs should usually be omitted when absent.","priority":"p0","scope":"universal"},{"title":"Reuse existing shared types, enums, and helpers before adding new ones","rule":"Before creating a new type, enum, interface, or validator, search the codebase for an existing shared version and reuse it when the semantics already match.","apiRule":"Before creating a new type, enum, interface, or validator, search the codebase for an existing shared version and reuse it when the semantics already match. This redefines concepts that often already exist elsewhere in the codebase, such as shared sort-order enums or paginated response shapes. Repeating them increases drift and review noise. Priority: P0. Scope: universal.","whenToApply":"Priority: P0. Scope: universal. Search for an existing project abstraction first. Only add a new one when the semantics are genuinely different. Exceptions: A new local type or helper is acceptable when the existing shared abstraction would hide meaningful domain differences or create awkward coupling.","whyItMatters":"This redefines concepts that often already exist elsewhere in the codebase, such as shared sort-order enums or paginated response shapes. Repeating them increases drift and review noise.","priority":"p0","scope":"universal"},{"title":"Keep domain aliases while reusing shared primitives","rule":"When a shared primitive already exists, reuse it under a domain-specific alias so the code stays both consistent and readable.","apiRule":"When a shared primitive already exists, reuse it under a domain-specific alias so the code stays both consistent and readable. This reuses the primitive, but it also removes domain context from the API surface. Readers have to infer what the order refers to. Priority: P2. Scope: universal.","whenToApply":"Priority: P2. Scope: universal. Prefer this pattern when the shape is shared but the meaning is domain-specific. Exceptions: If the alias adds no clarity and is only a thin rename with no meaningful usage boundary, the shared primitive can be used directly.","whyItMatters":"This reuses the primitive, but it also removes domain context from the API surface. Readers have to infer what the order refers to.","priority":"p2","scope":"universal"},{"title":"Extract duplicated API route validators into shared utilities","rule":"When the same request parsing or validation helper appears in multiple routes with the same behavior, move it to a shared utility.","apiRule":"When the same request parsing or validation helper appears in multiple routes with the same behavior, move it to a shared utility. Copying identical validators across route files makes maintenance harder and increases the chance of inconsistent behavior later. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. A good trigger is 3 or more identical copies in the same feature or route group. Exceptions: Keep a validator local if the behavior is intentionally route-specific or the shared version would become too generic to understand.","whyItMatters":"Copying identical validators across route files makes maintenance harder and increases the chance of inconsistent behavior later.","priority":"p1","scope":"universal"},{"title":"Validate and sandbox third-party API consumption","rule":"Treat third-party API responses as untrusted input: validate, sanitize, limit, and avoid following redirects blindly.","apiRule":"Treat third-party API responses as untrusted input: validate, sanitize, limit, and avoid following redirects blindly. Untrusted third-party data can carry injection payloads; unencrypted transport, missing validation, redirect following, and no timeouts increase exposure and DoS risk. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. Also: limit response sizes, handle partial outages gracefully, and assess providers' security posture. Treat provider names/fields as untrusted (avoid building SQL/commands from them).","whyItMatters":"Untrusted third-party data can carry injection payloads; unencrypted transport, missing validation, redirect following, and no timeouts increase exposure and DoS risk.","priority":"p1","scope":"universal"},{"title":"Avoid security misconfiguration (TLS, headers, CORS, safe errors, hardened defaults)","rule":"Harden the entire API stack with consistent configs, minimal surface area, and non-leaky error handling.","apiRule":"Harden the entire API stack with consistent configs, minimal surface area, and non-leaky error handling. Leaky errors, permissive CORS, missing TLS/security headers, and default-enabled features increase the chance of exploitation and data exposure. Priority: P0. Scope: universal.","whenToApply":"Priority: P0. Scope: universal. Also: ensure uniform request handling across proxies (prevent desync), set Cache-Control for private data, restrict content types, validate response schemas (including errors), and keep environments hardened and repeatable via automation.","whyItMatters":"Leaky errors, permissive CORS, missing TLS/security headers, and default-enabled features increase the chance of exploitation and data exposure.","priority":"p0","scope":"universal"},{"title":"Prevent SSRF when fetching user-supplied URLs","rule":"When the backend fetches a URL from client input (webhooks, previews, imports), validate with allowlists and block internal networks.","apiRule":"When the backend fetches a URL from client input (webhooks, previews, imports), validate with allowlists and block internal networks. Blindly fetching client-supplied URLs enables SSRF (internal port scanning, metadata theft like 169.254.169.254, proxying). Priority: P0. Scope: universal.","whenToApply":"Priority: P0. Scope: universal. Also: use a well-tested URL parser, sanitize inputs, isolate fetchers in the network, and never allow metadata services or localhost. Consider denylisting RFC1918, link-local, loopback, and cluster ranges.","whyItMatters":"Blindly fetching client-supplied URLs enables SSRF (internal port scanning, metadata theft like 169.254.169.254, proxying).","priority":"p0","scope":"universal"},{"title":"Maintain API inventory, documentation, and version retirement","rule":"Track all API hosts/versions and data flows; retire old deployments and avoid exposing unprotected beta/staging APIs.","apiRule":"Track all API hosts/versions and data flows; retire old deployments and avoid exposing unprotected beta/staging APIs. Undocumented or forgotten API hosts/versions expand the attack surface. Attackers commonly find weaker controls on beta or older endpoints. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. Also: document auth, errors, redirects, rate limits, CORS, endpoints; generate docs via OpenAPI in CI; avoid prod data in non-prod; apply external protections (WAF/API gateway) to all exposed versions.","whyItMatters":"Undocumented or forgotten API hosts/versions expand the attack surface. Attackers commonly find weaker controls on beta or older endpoints.","priority":"p1","scope":"universal"},{"title":"Enforce function-level authorization (BFLA) with deny-by-default","rule":"Every endpoint must check role/permission for the action; do not rely on URL conventions to separate admin vs user functions.","apiRule":"Every endpoint must check role/permission for the action; do not rely on URL conventions to separate admin vs user functions. If privileged endpoints lack role/permission checks, regular users can call them directly, guess URLs, or switch HTTP methods to perform unauthorized actions. Priority: P0. Scope: universal.","whenToApply":"Priority: P0. Scope: universal. Do not assume an endpoint is admin-only based on path. Ensure regular controllers cannot perform admin actions without checks. Consider policy-based access control (RBAC/ABAC) and consistent middleware.","whyItMatters":"If privileged endpoints lack role/permission checks, regular users can call them directly, guess URLs, or switch HTTP methods to perform unauthorized actions.","priority":"p0","scope":"universal"},{"title":"Protect sensitive business flows from automation and abuse","rule":"Identify critical workflows (purchase, reservations, referrals, posting) and add anti-automation + per-flow limits.","apiRule":"Identify critical workflows (purchase, reservations, referrals, posting) and add anti-automation + per-flow limits. High-value flows without abuse protections can be automated (scalping, spam, reservation hoarding, referral farming) and harm the business even if technical impact is low. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. Options include device fingerprinting, captcha/biometrics, non-human timing heuristics, IP reputation/Tor blocking, and stricter protection for machine-consumed APIs (B2B/dev).","whyItMatters":"High-value flows without abuse protections can be automated (scalping, spam, reservation hoarding, referral farming) and harm the business even if technical impact is low.","priority":"p1","scope":"universal"},{"title":"Enforce object-level authorization on every record access (BOLA)","rule":"Any endpoint that uses a client-supplied object ID must verify the user is allowed to access that specific object.","apiRule":"Any endpoint that uses a client-supplied object ID must verify the user is allowed to access that specific object. The handler fetches a record by a user-controlled ID without verifying ownership or policy access. Attackers can enumerate or guess IDs and access other users' data. Priority: P0. Scope: universal.","whenToApply":"Priority: P0. Scope: universal. Do not rely on comparing the session userId to an ID parameter as a universal fix. Enforce per-object access checks (ownership, org membership, ACLs, roles) for every action (read/update/delete). Prefer unpredictable IDs (UUID/GUID), but never treat them as authorization.","whyItMatters":"The handler fetches a record by a user-controlled ID without verifying ownership or policy access. Attackers can enumerate or guess IDs and access other users' data.","priority":"p0","scope":"universal"},{"title":"Harden authentication and account recovery against takeover","rule":"Protect login and recovery flows with strong validation, rate limiting, secure tokens, and re-auth for sensitive actions.","apiRule":"Protect login and recovery flows with strong validation, rate limiting, secure tokens, and re-auth for sensitive actions. Weak password handling, missing anti-bruteforce protections, overly permissive token practices, and allowing sensitive changes without re-auth make account takeover easier. Priority: P0. Scope: universal.","whenToApply":"Priority: P0. Scope: universal. Also: never send auth tokens/passwords in URLs; reject unsigned/weak JWTs; validate issuer/audience/expiry; treat forgot/reset like login (rate limit + lockout); implement weak-password checks; prefer MFA where possible; be careful with GraphQL batching bypassing rate limits.","whyItMatters":"Weak password handling, missing anti-bruteforce protections, overly permissive token practices, and allowing sensitive changes without re-auth make account takeover easier.","priority":"p0","scope":"universal"},{"title":"Prevent sensitive field exposure and mass assignment (BOPLA)","rule":"Return only allowed fields and accept only an allowlist of writable fields; never bind request bodies directly to models.","apiRule":"Return only allowed fields and accept only an allowlist of writable fields; never bind request bodies directly to models. Returning full objects can leak sensitive fields. Updating with req.body enables mass assignment (e.g., role/isAdmin/blocked/price) if the ORM accepts unknown fields. Priority: P0. Scope: universal.","whenToApply":"Priority: P0. Scope: universal. Avoid generic toJSON/toString serialization. Keep payloads minimal. For GraphQL, enforce field-level auth and schema-based response validation. Treat 'hidden' fields as server-owned, never client-controlled.","whyItMatters":"Returning full objects can leak sensitive fields. Updating with req.body enables mass assignment (e.g., role/isAdmin/blocked/price) if the ORM accepts unknown fields.","priority":"p0","scope":"universal"},{"title":"Limit resource consumption (rate limits, paging, batching, timeouts)","rule":"Protect APIs from DoS and cost blowups by enforcing rate limits, pagination caps, batching limits, and execution timeouts.","apiRule":"Protect APIs from DoS and cost blowups by enforcing rate limits, pagination caps, batching limits, and execution timeouts. Unbounded pagination and unrestricted batching can starve CPU/memory and create large bills (e.g., SMS/email providers, image processing, storage egress). Priority: P0. Scope: universal.","whenToApply":"Priority: P0. Scope: universal. Also: enforce upload size limits, memory/CPU limits via containers/serverless, per-user/per-operation throttles (OTP, password reset, purchases), and configure third-party spending limits or billing alerts.","whyItMatters":"Unbounded pagination and unrestricted batching can starve CPU/memory and create large bills (e.g., SMS/email providers, image processing, storage egress).","priority":"p0","scope":"universal"},{"title":"Always use braces for control flow blocks","rule":"Use braced blocks for if/for/while to prevent accidental logic bugs.","apiRule":"Use braced blocks for if/for/while to prevent accidental logic bugs. Unbraced blocks are easy to misread and can introduce bugs when lines are added or indentation lies. Priority: P0. Scope: universal.","whenToApply":"Priority: P0. Scope: universal. A single-line if may be allowed if it truly fits on one line, but braces are safer and preferred.","whyItMatters":"Unbraced blocks are easy to misread and can introduce bugs when lines are added or indentation lies.","priority":"p0","scope":"universal"},{"title":"Do not use require-style imports in TypeScript","rule":"Avoid import x = require('...'); use ES module syntax instead.","apiRule":"Avoid import x = require('...'); use ES module syntax instead. require-style imports are less compatible with modern module tooling and can complicate interoperability. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. Use when you encounter code similar to the bad example. Exceptions: Only in legacy interop edge cases that you cannot avoid (prefer wrapping such code).","whyItMatters":"require-style imports are less compatible with modern module tooling and can complicate interoperability.","priority":"p1","scope":"universal"},{"title":"Use === and !== (avoid == and !=)","rule":"Avoid implicit coercion; only allow == null to match null or undefined together.","apiRule":"Avoid implicit coercion; only allow == null to match null or undefined together. Loose equality performs coercions that are hard to predict and can hide bugs. Priority: P0. Scope: universal.","whenToApply":"Priority: P0. Scope: universal. Use when you encounter code similar to the bad example. Exceptions: Allow `x == null` / `x != null` when intentionally matching both null and undefined.","whyItMatters":"Loose equality performs coercions that are hard to predict and can hide bugs.","priority":"p0","scope":"universal"},{"title":"Do not use default exports","rule":"Use named exports to keep imports consistent and avoid ambiguous naming.","apiRule":"Use named exports to keep imports consistent and avoid ambiguous naming. Default exports have no canonical name, which makes refactors and code search harder and allows confusing import renames. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. Use when you encounter code similar to the bad example. Exceptions: Only use default exports if you are forced by an external ecosystem you cannot change (rare).","whyItMatters":"Default exports have no canonical name, which makes refactors and code search harder and allows confusing import renames.","priority":"p1","scope":"universal"},{"title":"Only throw (or reject) Error objects","rule":"Throwing non-Errors loses stack traces and makes debugging harder.","apiRule":"Throwing non-Errors loses stack traces and makes debugging harder. Throwing strings/objects can lose stack trace information and leads to inconsistent error handling. Priority: P0. Scope: universal.","whenToApply":"Priority: P0. Scope: universal. When catching unknown, narrow to Error (e.g., asserts) rather than defensive handling of random types. Exceptions: Only when integrating with a known bad API that throws non-Errors; add a comment identifying the source.","whyItMatters":"Throwing strings/objects can lose stack trace information and leads to inconsistent error handling.","priority":"p0","scope":"universal"},{"title":"Use import type for type-only imports","rule":"Use import type when the symbol is only used as a type to support isolated transpilation.","apiRule":"Use import type when the symbol is only used as a type to support isolated transpilation. In some build modes (isolated transpilation), type-only imports can require explicit annotation to avoid runtime import expectations. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. You can also use: import {type User, valueThing} from './mod'; Exceptions: Use regular imports when you need the value at runtime or for side effects.","whyItMatters":"In some build modes (isolated transpilation), type-only imports can require explicit annotation to avoid runtime import expectations.","priority":"p1","scope":"stack-specific"},{"title":"Use const/let; never use var","rule":"Prefer const by default and let only when reassignment is required.","apiRule":"Prefer const by default and let only when reassignment is required. var is function-scoped and can cause subtle bugs with hoisting and unexpected captures. Priority: P0. Scope: universal.","whenToApply":"Priority: P0. Scope: universal. Also: do not use variables before their declaration.","whyItMatters":"var is function-scoped and can cause subtle bugs with hoisting and unexpected captures.","priority":"p0","scope":"universal"},{"title":"Declare one variable per statement","rule":"Avoid comma-separated declarations for clarity and simpler diffs.","apiRule":"Avoid comma-separated declarations for clarity and simpler diffs. Multiple declarations in one statement make diffs noisier and hide intent when one variable changes. Priority: P2. Scope: universal.","whenToApply":"Priority: P2. Scope: universal. Use when you encounter code similar to the bad example.","whyItMatters":"Multiple declarations in one statement make diffs noisier and hide intent when one variable changes.","priority":"p2","scope":"universal"},{"title":"Use ES modules, not TypeScript namespaces","rule":"Organize code with files + imports/exports; avoid namespace Foo { ... }.","apiRule":"Organize code with files + imports/exports; avoid namespace Foo { ... }. Namespaces make dependency boundaries less explicit and don't compose well with modern tooling and bundlers. Priority: P0. Scope: universal.","whenToApply":"Priority: P0. Scope: universal. Use when you encounter code similar to the bad example. Exceptions: Only when interfacing with external third-party code that requires namespaces.","whyItMatters":"Namespaces make dependency boundaries less explicit and don't compose well with modern tooling and bundlers.","priority":"p0","scope":"universal"},{"title":"Do not use @ts-ignore / @ts-nocheck / @ts-expect-error in production code","rule":"Fix the underlying type issue; suppressions make types unpredictable and hide real bugs.","apiRule":"Fix the underlying type issue; suppressions make types unpredictable and hide real bugs. Suppression hides type problems and can cause runtime bugs by making surrounding types unclear. Priority: P0. Scope: universal.","whenToApply":"Priority: P0. Scope: universal. If you must do something unsafe (rare), prefer narrowing, unknown, or local casts with an explanatory comment. Exceptions: Unit tests may use @ts-expect-error sparingly, but prefer safer alternatives even there.","whyItMatters":"Suppression hides type problems and can cause runtime bugs by making surrounding types unclear.","priority":"p0","scope":"universal"},{"title":"Do not rely on Automatic Semicolon Insertion","rule":"Always end statements with semicolons to avoid ASI edge-case bugs.","apiRule":"Always end statements with semicolons to avoid ASI edge-case bugs. ASI can turn valid-looking code into different statements depending on formatting, creating hard-to-spot bugs. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. Use when you encounter code similar to the bad example.","whyItMatters":"ASI can turn valid-looking code into different statements depending on formatting, creating hard-to-spot bugs.","priority":"p1","scope":"universal"},{"title":"Avoid synchronous I/O in web request paths","rule":"Sync APIs block the event loop and can stall all requests; use async APIs instead.","apiRule":"Sync APIs block the event loop and can stall all requests; use async APIs instead. Sync file reads block the single-threaded event loop, causing slow or stuck requests under load. Priority: P0. Scope: stack specific.","whenToApply":"Priority: P0. Scope: stack specific. Even during startup, aim for fast boot times to support log-exit-restart and autoscaling patterns. Exceptions: Short-lived CLI scripts can use sync APIs when user experience is simpler and concurrency is not required.","whyItMatters":"Sync file reads block the single-threaded event loop, causing slow or stuck requests under load.","priority":"p0","scope":"stack-specific"},{"title":"Run Node services with a restart policy (process management)","rule":"Use OS/container orchestration restart policies; avoid ad-hoc scripts that hide failures.","apiRule":"Use OS/container orchestration restart policies; avoid ad-hoc scripts that hide failures. A naive loop can mask crashes, remove backoff, and make it harder to observe and manage failures properly. Priority: P0. Scope: stack specific.","whenToApply":"Priority: P0. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"A naive loop can mask crashes, remove backoff, and make it harder to observe and manage failures properly.","priority":"p0","scope":"stack-specific"},{"title":"Vet npm packages before adopting them","rule":"Check tests/CI, license, issues, maintainers, usage, docs, and code before adding a dependency.","apiRule":"Check tests/CI, license, issues, maintainers, usage, docs, and code before adding a dependency. Adding packages blindly increases the chance of pulling in unmaintained, insecure, or incompatible code. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. Use when you encounter code similar to the bad example.","whyItMatters":"Adding packages blindly increases the chance of pulling in unmaintained, insecure, or incompatible code.","priority":"p1","scope":"universal"},{"title":"Do not keep a Node process alive after an uncaught exception","rule":"Log, exit, and rely on a restart policy; recovery after uncaught exceptions can leave unknown state.","apiRule":"Log, exit, and rely on a restart policy; recovery after uncaught exceptions can leave unknown state. Continuing after an uncaught exception can leave the process in a corrupted or unsafe state, causing subtle data bugs. Priority: P0. Scope: stack specific.","whenToApply":"Priority: P0. Scope: stack specific. Use when you encounter code similar to the bad example. Exceptions: For CLI tools, you may handle and report errors without exiting immediately, but servers should prefer log-exit-restart.","whyItMatters":"Continuing after an uncaught exception can leave the process in a corrupted or unsafe state, causing subtle data bugs.","priority":"p0","scope":"stack-specific"},{"title":"Use Node.js LTS and plan safe upgrades","rule":"Run the latest Node LTS and roll upgrades through smoke tests before production.","apiRule":"Run the latest Node LTS and roll upgrades through smoke tests before production. Not pinning a supported runtime makes deployments inconsistent across machines and increases the chance of breaking changes hitting production unexpectedly. Priority: P0. Scope: stack specific.","whenToApply":"Priority: P0. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"Not pinning a supported runtime makes deployments inconsistent across machines and increases the chance of breaking changes hitting production unexpectedly.","priority":"p0","scope":"stack-specific"},{"title":"Explicitly declare dependencies and avoid relying on global installs","rule":"Deploys should work from a clean install using only declared dependencies.","apiRule":"Deploys should work from a clean install using only declared dependencies. Relying on undeclared or globally installed packages makes builds and deployments fragile and non-reproducible. Priority: P0. Scope: universal.","whenToApply":"Priority: P0. Scope: universal. Use when you encounter code similar to the bad example.","whyItMatters":"Relying on undeclared or globally installed packages makes builds and deployments fragile and non-reproducible.","priority":"p0","scope":"universal"},{"title":"Store config in environment variables","rule":"Anything that varies per deployment (DB URLs, secrets, external services) must live in env, not code.","apiRule":"Anything that varies per deployment (DB URLs, secrets, external services) must live in env, not code. Hardcoding secrets and environment-specific values leaks sensitive data and makes deployments painful and error-prone. Priority: P0. Scope: universal.","whenToApply":"Priority: P0. Scope: universal. Use when you encounter code similar to the bad example. Exceptions: A config file is OK only if you can switch it per deployment without changing code (e.g., env points to a file path).","whyItMatters":"Hardcoding secrets and environment-specific values leaks sensitive data and makes deployments painful and error-prone.","priority":"p0","scope":"universal"},{"title":"Keep dev, staging, and prod as similar as possible","rule":"Avoid environment-only behavior; differences should be config and data, not code paths.","apiRule":"Avoid environment-only behavior; differences should be config and data, not code paths. When dev behaves fundamentally differently, you can’t trust staging results and you ship surprises to production. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. This comes from the 12-Factor recommendation to keep dev/staging/prod as similar as possible. Exceptions: Development-only tooling is OK (debug logs, hot reload), but business logic must remain consistent.","whyItMatters":"When dev behaves fundamentally differently, you can’t trust staging results and you ship surprises to production.","priority":"p1","scope":"universal"},{"title":"Declare dependencies explicitly and use a dependency manager","rule":"Everything the app needs must be declared in package.json and installed deterministically.","apiRule":"Everything the app needs must be declared in package.json and installed deterministically. Relying on globally installed tools or undeclared packages makes builds non-reproducible. CI or another developer machine will fail unpredictably. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. Matches the 12-Factor point on explicitly declaring dependencies and not relying on system-wide packages.","whyItMatters":"Relying on globally installed tools or undeclared packages makes builds non-reproducible. CI or another developer machine will fail unpredictably.","priority":"p1","scope":"universal"},{"title":"Keep one codebase and deploy it to multiple environments","rule":"No separate repos or branches for 'local' vs 'cloud'—use config to change behavior per env.","apiRule":"No separate repos or branches for 'local' vs 'cloud'—use config to change behavior per env. Environment branching inside code encourages drift and makes it harder to reason about what runs in production. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. Use when you encounter code similar to the bad example.","whyItMatters":"Environment branching inside code encourages drift and makes it harder to reason about what runs in production.","priority":"p1","scope":"universal"},{"title":"Export services via port binding","rule":"The app should be self-contained and listen on a configurable port (PORT), not rely on external web servers.","apiRule":"The app should be self-contained and listen on a configurable port (PORT), not rely on external web servers. Hardcoding ports breaks container and platform deployments where the runtime assigns the port. It also makes parallel local dev harder. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. From the 12-Factor point on exporting services via port binding.","whyItMatters":"Hardcoding ports breaks container and platform deployments where the runtime assigns the port. It also makes parallel local dev harder.","priority":"p1","scope":"universal"},{"title":"Treat logs as event streams (stdout/stderr)","rule":"Apps should not manage log files—write structured logs to stdout/stderr and let the platform route them.","apiRule":"Apps should not manage log files—write structured logs to stdout/stderr and let the platform route them. Managing log files inside the app complicates deployments and breaks common production logging pipelines. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. Use when you encounter code similar to the bad example.","whyItMatters":"Managing log files inside the app complicates deployments and breaks common production logging pipelines.","priority":"p1","scope":"universal"},{"title":"Use stable keys for dynamic lists","rule":"Stable keys preserve correct component state when lists reorder, filter, or mutate.","apiRule":"Stable keys preserve correct component state when lists reorder, filter, or mutate. Using array indices as keys breaks reconciliation when items reorder or are inserted/removed. Inputs can appear to 'swap' values or keep the wrong state. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. If you don't have stable IDs, generate them at the data layer (not at render time) so they remain stable across renders. Exceptions: Indices are acceptable only for fully static lists that never reorder, insert, or delete items.","whyItMatters":"Using array indices as keys breaks reconciliation when items reorder or are inserted/removed. Inputs can appear to 'swap' values or keep the wrong state.","priority":"p1","scope":"stack-specific"},{"title":"Colocate state to reduce re-render blast radius","rule":"Keep state close to where it's used to avoid rerendering large trees unnecessarily.","apiRule":"Keep state close to where it's used to avoid rerendering large trees unnecessarily. A small UI toggle causes the entire layout (including heavy main content) to re-render. This increases work and can hurt performance as the app grows. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. If state must be shared across distant components, prefer splitting providers, selectors, or composition to keep updates localized. Exceptions: If the main content must react to the state change (e.g., layout shift, resize), lifting state is valid. Still isolate heavy children where possible.","whyItMatters":"A small UI toggle causes the entire layout (including heavy main content) to re-render. This increases work and can hurt performance as the app grows.","priority":"p1","scope":"universal"},{"title":"Use portals for overlays to avoid clipping and stacking bugs","rule":"Portals prevent modals/tooltips from being clipped by overflow and stacking contexts.","apiRule":"Portals prevent modals/tooltips from being clipped by overflow and stacking contexts. Overlays rendered inside containers can be clipped by `overflow: hidden` or appear behind other stacking contexts. This produces flaky UI across layouts. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Portals keep React event bubbling intact. Still manage focus trapping and ESC/backdrop handling for accessibility (prefer headless UI libs for complex overlays). Exceptions: Simple inline popovers inside unconstrained containers may not need portals, but verify overflow and z-index behavior across breakpoints.","whyItMatters":"Overlays rendered inside containers can be clipped by `overflow: hidden` or appear behind other stacking contexts. This produces flaky UI across layouts.","priority":"p1","scope":"stack-specific"},{"title":"Prefer declarative props over imperative refs","rule":"Use refs for DOM integration only when necessary; avoid imperative APIs that bypass React data flow.","apiRule":"Use refs for DOM integration only when necessary; avoid imperative APIs that bypass React data flow. Imperative DOM mutations inside effects can be fragile and hard to reuse. As UI changes, these assumptions break and produce subtle bugs. Priority: P2. Scope: universal.","whenToApply":"Priority: P2. Scope: universal. For reusable imperative APIs (focus/scroll), consider exposing a small `useImperativeHandle` surface from a dedicated component instead of mutating many DOM nodes from outside. Exceptions: Refs are appropriate for focus management, measuring layout, integrating non-React libraries, and escape hatches where declarative patterns are not possible.","whyItMatters":"Imperative DOM mutations inside effects can be fragile and hard to reuse. As UI changes, these assumptions break and produce subtle bugs.","priority":"p2","scope":"universal"},{"title":"Memoize for outcomes, not habit","rule":"Use memoization when it prevents real work; avoid blanket useMemo/useCallback everywhere.","apiRule":"Use memoization when it prevents real work; avoid blanket useMemo/useCallback everywhere. Memoization adds code and cognitive overhead. If it doesn't prevent expensive rerenders or heavy computation, it becomes performance theater. Priority: P2. Scope: universal.","whenToApply":"Priority: P2. Scope: universal. Good reasons to memoize: stabilizing Context value, preventing rerenders of memoized children, expensive derived computations, and stable dependencies for effects. Exceptions: Memoization can be used proactively in hot paths you already know are expensive, but avoid spreading it across the entire codebase without evidence.","whyItMatters":"Memoization adds code and cognitive overhead. If it doesn't prevent expensive rerenders or heavy computation, it becomes performance theater.","priority":"p2","scope":"universal"},{"title":"Do not define React components inside other components","rule":"Inline component definitions create new component identities and can cause remounts and lost state.","apiRule":"Inline component definitions create new component identities and can cause remounts and lost state. Defining a component inside another component creates a new component type on each render. This can trigger remounting of the subtree, losing state and input focus. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Use when you encounter code similar to the bad example. Exceptions: Inline render helpers are fine if they are not React components (do not use hooks, do not hold state) and you accept they are not visible in DevTools as components.","whyItMatters":"Defining a component inside another component creates a new component type on each render. This can trigger remounting of the subtree, losing state and input focus.","priority":"p1","scope":"stack-specific"},{"title":"Avoid stale closures in long-lived callbacks","rule":"Timers and event listeners should not read stale state; use refs or explicit dependencies.","apiRule":"Timers and event listeners should not read stale state; use refs or explicit dependencies. The interval callback captures the initial `count` value. The counter can get stuck or behave unexpectedly because the closure is stale. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. For event listeners reading changing values, combine a stable handler with a ref that stores the latest value. Exceptions: If you intentionally want a value frozen at subscription time, document the intent so readers don't 'fix' it incorrectly.","whyItMatters":"The interval callback captures the initial `count` value. The counter can get stuck or behave unexpectedly because the closure is stale.","priority":"p1","scope":"stack-specific"},{"title":"Split Context by responsibility","rule":"Separate frequently-changing state from stable actions to reduce consumer re-renders.","apiRule":"Separate frequently-changing state from stable actions to reduce consumer re-renders. Unrelated concerns are bundled into one Context. Any change (toast, sidebar) updates the same value and forces all consumers to re-render, even those only needing one part. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. If splitting into multiple contexts is too noisy, consider a reducer-based API where actions are stable and state selectors are scoped. Exceptions: For small apps with a handful of consumers, a single context may be simpler and acceptable.","whyItMatters":"Unrelated concerns are bundled into one Context. Any change (toast, sidebar) updates the same value and forces all consumers to re-render, even those only needing one part.","priority":"p1","scope":"stack-specific"},{"title":"Guard against stale async responses","rule":"Only commit async results if they belong to the latest request identity.","apiRule":"Only commit async results if they belong to the latest request identity. If userId changes quickly (navigation), an older request can resolve last and overwrite the newer profile. The UI shows the wrong user data. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Prefer AbortController when possible. Use identity guards when abort isn't available or when multiple async sources can resolve out of order.","whyItMatters":"If userId changes quickly (navigation), an older request can resolve last and overwrite the newer profile. The UI shows the wrong user data.","priority":"p1","scope":"stack-specific"},{"title":"Avoid request waterfalls in UI data fetching","rule":"Start independent requests in parallel to reduce total loading time.","apiRule":"Start independent requests in parallel to reduce total loading time. Orders are fetched only after the user request completes. For pages with multiple independent data sources, this sequential loading increases total time to interactive. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. In SSR frameworks (Next.js), prefer parallel fetching on the server (route handlers/loaders) where possible to avoid client waterfalls. Exceptions: If one request truly depends on the result of another (needs an ID), sequential fetching is required.","whyItMatters":"Orders are fetched only after the user request completes. For pages with multiple independent data sources, this sequential loading increases total time to interactive.","priority":"p1","scope":"universal"},{"title":"Cancel in-flight requests with AbortController","rule":"Abort old requests on unmount or param changes to prevent race conditions and wasted work.","apiRule":"Abort old requests on unmount or param changes to prevent race conditions and wasted work. Fast query changes can cause responses to resolve out of order. Older responses may overwrite newer results, and network work continues after the component is no longer relevant. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. If you also have timeouts or parent signals, merge signals (e.g., AbortSignal.any) and still handle AbortError explicitly. Exceptions: Do not abort fire-and-forget requests that must complete (e.g., audit logs). Decouple them from UI state instead.","whyItMatters":"Fast query changes can cause responses to resolve out of order. Older responses may overwrite newer results, and network work continues after the component is no longer relevant.","priority":"p1","scope":"universal"},{"title":"Memoize Context provider values","rule":"Stable provider values prevent unnecessary re-renders across all Context consumers.","apiRule":"Stable provider values prevent unnecessary re-renders across all Context consumers. The provider recreates its value object and functions on every render. That changes the Context value reference, forcing all consumers to re-render even if they don't depend on the changed parts. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Use when you encounter code similar to the bad example. Exceptions: If the provider has very few consumers and re-renders are rare, memoization may be unnecessary. Confirm with profiling before adding complexity.","whyItMatters":"The provider recreates its value object and functions on every render. That changes the Context value reference, forcing all consumers to re-render even if they don't depend on the changed parts.","priority":"p1","scope":"stack-specific"},{"title":"Render SSR-safe defaults for client measurement logic","rule":"When UI depends on client-only measurements, render a stable SSR fallback and enhance on the client.","apiRule":"When UI depends on client-only measurements, render a stable SSR fallback and enhance on the client. The initial SSR output cannot know the container width, so the first render may show the wrong number of tabs and then visibly jump after hydration/measurement. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. For critical layouts, consider CSS solutions (container queries, overflow) before JavaScript measurement. If you must measure, keep the SSR default visually acceptable. Exceptions: If the entire route is client-only and not SSR rendered, SSR defaults are less critical (but still consider first paint UX).","whyItMatters":"The initial SSR output cannot know the container width, so the first render may show the wrong number of tabs and then visibly jump after hydration/measurement.","priority":"p1","scope":"stack-specific"},{"title":"Prefer useEffect over useLayoutEffect by default","rule":"useLayoutEffect can block paint; reserve it for layout measurement that must run before paint.","apiRule":"useLayoutEffect can block paint; reserve it for layout measurement that must run before paint. useLayoutEffect runs before paint and can contribute to jank if it does non-trivial work. For scroll listeners and simple state updates, useEffect is typically sufficient. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. If you must measure layout and immediately apply style changes to avoid flicker, useLayoutEffect is appropriate. Document why. Exceptions: useLayoutEffect is acceptable for reading layout (getBoundingClientRect) and synchronously applying changes that prevent a visible jump.","whyItMatters":"useLayoutEffect runs before paint and can contribute to jank if it does non-trivial work. For scroll listeners and simple state updates, useEffect is typically sufficient.","priority":"p2","scope":"stack-specific"},{"title":"Keep component implementation files under 300 lines and colocated in their component folder","rule":"React component implementation files must stay under 300 lines, and the main component entry should live inside its own component folder. If hooks, effects, handlers, JSX, or hard-coded copy make a component grow, split the logic into hooks, child components, and dedicated constants/copy files instead of keeping one large TSX file or a stray sibling component file outside the folder.","apiRule":"React component implementation files must stay under 300 lines, and the main component entry should live inside its own component folder. If hooks, effects, handlers, JSX, or hard-coded copy make a component grow, split the logic into hooks, child components, and dedicated constants/copy files instead of keeping one large TSX file or a stray sibling component file outside the folder. This keeps the main component outside the folder that already owns its related files, and the component itself is oversized. Ownership becomes scattered across two locations and the implementation remains too large to read comfortably. Priority: P0. Scope: project specific.","whenToApply":"Priority: P0. Scope: project specific. Treat this as one of the strictest frontend rules in the project. When a component starts collecting too many hooks, effects, handlers, conditional branches, or text blocks, refactor early instead of waiting for a large cleanup later. Prefer colocated hooks folders and nearby child components, and keep the main exported component entry inside that same component folder so ownership stays obvious. Exceptions: This limit applies to component implementation files, not every TypeScript file. Files that are mostly props, types, filter definitions, large option lists, copy dictionaries, mappings, config objects, or generated data may exceed 300 lines when that is the cleanest representation. Those exceptions do not justify oversized TSX component files, and they do not justify leaving the main component entry outside the folder that owns the rest of the component.","whyItMatters":"This keeps the main component outside the folder that already owns its related files, and the component itself is oversized. Ownership becomes scattered across two locations and the implementation remains too large to read comfortably.","priority":"p0","scope":"project-specific"},{"title":"Avoid blocking work on the request path","rule":"Do not run heavy or blocking operations inside request handlers; offload to async jobs.","apiRule":"Do not run heavy or blocking operations inside request handlers; offload to async jobs. Heavy work in the request path increases latency and can cause timeouts under load. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. Use when you encounter code similar to the bad example. Exceptions: Small, fast operations can run inline, but anything heavy should be queued or precomputed.","whyItMatters":"Heavy work in the request path increases latency and can cause timeouts under load.","priority":"p1","scope":"universal"},{"title":"Avoid N+1 queries with batching or preloading","rule":"Batch related data fetching to prevent N+1 query patterns.","apiRule":"Batch related data fetching to prevent N+1 query patterns. This executes one query for posts plus one query per post (N+1), which is slow and expensive at scale. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. Use when you encounter code similar to the bad example. Exceptions: Small, bounded lists may be acceptable, but measure and document the tradeoff.","whyItMatters":"This executes one query for posts plus one query per post (N+1), which is slow and expensive at scale.","priority":"p1","scope":"universal"},{"title":"Use transactions for multi-step writes","rule":"Wrap multi-step write operations in a transaction to prevent partial updates.","apiRule":"Wrap multi-step write operations in a transaction to prevent partial updates. If any write fails, earlier writes remain, leaving the system in an inconsistent state. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. Use when you encounter code similar to the bad example. Exceptions: If you intentionally use a saga/outbox with compensating actions, document it and ensure consistency guarantees.","whyItMatters":"If any write fails, earlier writes remain, leaving the system in an inconsistent state.","priority":"p1","scope":"universal"},{"title":"Never hardcode secrets; validate config at startup","rule":"Load secrets from environment/config and validate them on boot; never commit keys in code.","apiRule":"Load secrets from environment/config and validate them on boot; never commit keys in code. Hardcoded secrets leak in repos, are difficult to rotate, and often end up in logs or client bundles. Priority: P0. Scope: universal.","whenToApply":"Priority: P0. Scope: universal. Use when you encounter code similar to the bad example. Exceptions: Public, non-secret values (public keys, URLs) can be hardcoded, but secrets must not.","whyItMatters":"Hardcoded secrets leak in repos, are difficult to rotate, and often end up in logs or client bundles.","priority":"p0","scope":"universal"},{"title":"Enforce authorization for protected actions","rule":"Require explicit authn/authz checks before reading or mutating protected data.","apiRule":"Require explicit authn/authz checks before reading or mutating protected data. Without an explicit guard, any caller can update any user record. Priority: P0. Scope: universal.","whenToApply":"Priority: P0. Scope: universal. Use when you encounter code similar to the bad example. Exceptions: Public, read-only endpoints can skip auth checks, but must be explicitly documented as public.","whyItMatters":"Without an explicit guard, any caller can update any user record.","priority":"p0","scope":"universal"},{"title":"Validate external inputs at boundaries","rule":"Validate all external inputs at the boundary and return explicit errors before using them.","apiRule":"Validate all external inputs at the boundary and return explicit errors before using them. External input can be missing or malformed. Using it directly risks crashes and corrupted data. Priority: P0. Scope: universal.","whenToApply":"Priority: P0. Scope: universal. Use when you encounter code similar to the bad example. Exceptions: If an upstream boundary already validates this input, you may skip duplicate validation but must document the trust boundary.","whyItMatters":"External input can be missing or malformed. Using it directly risks crashes and corrupted data.","priority":"p0","scope":"universal"},{"title":"Use named functions in useEffect","rule":"Name effect functions to improve stack traces and make intent clearer during reviews and debugging.","apiRule":"Name effect functions to improve stack traces and make intent clearer during reviews and debugging. Anonymous effect functions are harder to identify in stack traces and code reviews. Priority: P2. Scope: project specific.","whenToApply":"Priority: P2. Scope: project specific. Use when you encounter code similar to the bad example. Exceptions: If the effect is truly trivial (1–2 lines), an anonymous function is acceptable.","whyItMatters":"Anonymous effect functions are harder to identify in stack traces and code reviews.","priority":"p2","scope":"project-specific"},{"title":"Avoid inline event handlers in JSX","rule":"Extract event handlers into named functions to improve readability, reuse, and debugging.","apiRule":"Extract event handlers into named functions to improve readability, reuse, and debugging. Inline handlers hide intent and make it harder to reuse or test the handler logic. Priority: P2. Scope: project specific.","whenToApply":"Priority: P2. Scope: project specific. Use when you encounter code similar to the bad example. Exceptions: Tiny components can keep inline handlers if extraction hurts readability.","whyItMatters":"Inline handlers hide intent and make it harder to reuse or test the handler logic.","priority":"p2","scope":"project-specific"},{"title":"Place one-off components near their usage","rule":"Store single-use components in a _components folder at the highest level where they are used to keep structure local and navigable.","apiRule":"Store single-use components in a _components folder at the highest level where they are used to keep structure local and navigable. One-off components living in global folders make the structure noisy and harder to navigate. It becomes unclear which components are reusable. Priority: P2. Scope: project specific.","whenToApply":"Priority: P2. Scope: project specific. Use when you encounter code similar to the bad example.","whyItMatters":"One-off components living in global folders make the structure noisy and harder to navigate. It becomes unclear which components are reusable.","priority":"p2","scope":"project-specific"},{"title":"Standardize forms on React Hook Form + Zod","rule":"Use React Hook Form for state management and Zod for schemas so validation and error handling are consistent.","apiRule":"Use React Hook Form for state management and Zod for schemas so validation and error handling are consistent. Hand-rolled validation logic diverges across forms and is easy to forget. It also makes type inference and error handling inconsistent. Priority: P1. Scope: project specific.","whenToApply":"Priority: P1. Scope: project specific. Prefer validation on submit + blur (mode: onSubmit, reValidateMode: onBlur). Exceptions: Live validation is acceptable when UX requires immediate feedback.","whyItMatters":"Hand-rolled validation logic diverges across forms and is easy to forget. It also makes type inference and error handling inconsistent.","priority":"p1","scope":"project-specific"},{"title":"Split server and client API boundaries","rule":"GETs go through api.server.ts; non-GETs go through the proxy + api.client.ts to keep boundaries explicit and auditable.","apiRule":"GETs go through api.server.ts; non-GETs go through the proxy + api.client.ts to keep boundaries explicit and auditable. Mixing GETs and non-GETs in the same client layer blurs boundaries and makes auditing/SSR harder. It also bypasses the intended proxy route for non-GETs. Priority: P1. Scope: project specific.","whenToApply":"Priority: P1. Scope: project specific. Non-GET requests must go through src/app/api/proxy/[...catchAll]/route.ts via api.client.ts.","whyItMatters":"Mixing GETs and non-GETs in the same client layer blurs boundaries and makes auditing/SSR harder. It also bypasses the intended proxy route for non-GETs.","priority":"p1","scope":"project-specific"},{"title":"Default GETs to SSR data access","rule":"Route GET requests through the server-side data layer by default to keep data fetching centralized, cacheable, and consistent.","apiRule":"Route GET requests through the server-side data layer by default to keep data fetching centralized, cacheable, and consistent. Client-side GETs spread data fetching logic across the app and make caching and error handling inconsistent. This often increases waterfall requests and hurts performance. Priority: P1. Scope: project specific.","whenToApply":"Priority: P1. Scope: project specific. Most GETs should go through src/utils/api.server.ts. Exceptions: Use client fetching only for purely client-interactive or real-time flows where SSR is not practical.","whyItMatters":"Client-side GETs spread data fetching logic across the app and make caching and error handling inconsistent. This often increases waterfall requests and hurts performance.","priority":"p1","scope":"project-specific"},{"title":"Enforce test discipline for critical paths","rule":"Identify critical flows and require explicit unit/integration/e2e coverage in CI so regressions are caught quickly.","apiRule":"Identify critical flows and require explicit unit/integration/e2e coverage in CI so regressions are caught quickly. Critical behavior is untested, so regressions can ship silently. Teams learn about failures from production incidents. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. Document which paths are critical (auth, billing, onboarding) and require tests for every change. Exceptions: Pure prototypes or throwaway spikes can skip this, but production systems should not.","whyItMatters":"Critical behavior is untested, so regressions can ship silently. Teams learn about failures from production incidents.","priority":"p1","scope":"universal"},{"title":"Define performance budgets for critical paths","rule":"Set explicit thresholds for latency, bundle size, and CLS so regressions are caught before they reach users.","apiRule":"Set explicit thresholds for latency, bundle size, and CLS so regressions are caught before they reach users. Without budgets, regressions go unnoticed until users complain. Teams cannot tell if a change is acceptable. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. Budgets can target TTFB, CLS, LCP, bundle size, or p95 latency—pick what matters to the product. Exceptions: Early prototypes can skip budgets, but add them before public launch.","whyItMatters":"Without budgets, regressions go unnoticed until users complain. Teams cannot tell if a change is acceptable.","priority":"p1","scope":"universal"},{"title":"Standardize error handling across boundaries","rule":"Use a consistent error strategy (typed errors or Result objects) so callers can handle failures predictably and avoid ad‑hoc patterns.","apiRule":"Use a consistent error strategy (typed errors or Result objects) so callers can handle failures predictably and avoid ad‑hoc patterns. The function throws inconsistent error types, so callers must guess how to handle failures. This leads to fragile error handling and missed cases. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. Result/Either patterns are also valid if your codebase avoids exceptions. Exceptions: Very small scripts may skip this, but shared modules should standardize errors.","whyItMatters":"The function throws inconsistent error types, so callers must guess how to handle failures. This leads to fragile error handling and missed cases.","priority":"p1","scope":"universal"},{"title":"Define API boundaries with dedicated DTOs","rule":"Do not expose internal domain models directly through APIs; map to explicit DTOs at the boundary to keep contracts stable and enforce validation.","apiRule":"Do not expose internal domain models directly through APIs; map to explicit DTOs at the boundary to keep contracts stable and enforce validation. The API returns the raw domain entity, so internal fields and schema changes leak to consumers. This couples clients to the database shape and makes safe refactors harder. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. Apply this to both HTTP APIs and internal service boundaries (RPC, queues). Exceptions: For quick prototypes you may skip DTOs, but document the risk and plan to add them before production.","whyItMatters":"The API returns the raw domain entity, so internal fields and schema changes leak to consumers. This couples clients to the database shape and makes safe refactors harder.","priority":"p1","scope":"universal"},{"title":"Avoid mixing optional and nullable types","rule":"Do not combine optional syntax (?) with null types in TypeScript definitions, as it creates ambiguous and redundant 'missing' states.","apiRule":"Do not combine optional syntax (?) with null types in TypeScript definitions, as it creates ambiguous and redundant 'missing' states. Using both `?` (undefined) and `| null` creates two ways to represent 'no value'. This forces the consumer to handle both `undefined` and `null`, implies a semantic difference that rarely exists, and makes the type definition confusing. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"Using both `?` (undefined) and `| null` creates two ways to represent 'no value'. This forces the consumer to handle both `undefined` and `null`, implies a semantic difference that rarely exists, and makes the type definition confusing.","priority":"p1","scope":"stack-specific"},{"title":"Prefer strict required fields over optional properties","rule":"Avoid marking all interface properties as optional; use strict types to guarantee the existence of core fields.","apiRule":"Avoid marking all interface properties as optional; use strict types to guarantee the existence of core fields. Marking essential fields like `id` or `username` as optional (`?`) weakens the type definition. It implies these fields might be missing entirely, forcing consumers to add redundant checks for data that is actually guaranteed. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Only use `?` if the API might omit the key entirely. If the key is always sent but might be empty, use `| null`.","whyItMatters":"Marking essential fields like `id` or `username` as optional (`?`) weakens the type definition. It implies these fields might be missing entirely, forcing consumers to add redundant checks for data that is actually guaranteed.","priority":"p1","scope":"stack-specific"},{"title":"Prefer explicit null over conditional object spreading","rule":"Ensure consistent object shapes by assigning `null` to missing properties instead of conditionally omitting the key using spread syntax.","apiRule":"Ensure consistent object shapes by assigning `null` to missing properties instead of conditionally omitting the key using spread syntax. Using conditional spreading `...(value ? { key } : {})` results in an inconsistent object shape where keys may be missing entirely. This forces consumers to check for key existence instead of just value nullability and prevents JavaScript engines from optimizing object structures (hidden classes). Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"Using conditional spreading `...(value ? { key } : {})` results in an inconsistent object shape where keys may be missing entirely. This forces consumers to check for key existence instead of just value nullability and prevents JavaScript engines from optimizing object structures (hidden classes).","priority":"p1","scope":"stack-specific"},{"title":"Flatten complex conditional rendering with variables","rule":"Avoid nested or chained ternary operators in JSX for multi-state rendering; assign content to a variable or use early returns.","apiRule":"Avoid nested or chained ternary operators in JSX for multi-state rendering; assign content to a variable or use early returns. Nested ternaries inside JSX make the render logic difficult to scan, format, and debug. It forces the reader to parse multiple levels of indentation and conditions just to understand the visual hierarchy. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. Use this pattern when only a section of the UI changes (e.g., inside a layout). If the entire component changes, prefer 'Early Returns' instead.","whyItMatters":"Nested ternaries inside JSX make the render logic difficult to scan, format, and debug. It forces the reader to parse multiple levels of indentation and conditions just to understand the visual hierarchy.","priority":"p2","scope":"stack-specific"},{"title":"Decouple presentational components from domain entities","rule":"Avoid passing raw API objects to UI components; pass explicit, pre-formatted props to improve reusability and separation of concerns.","apiRule":"Avoid passing raw API objects to UI components; pass explicit, pre-formatted props to improve reusability and separation of concerns. The component takes a raw API object (`article`) and performs data transformation (formatting names, dates, fallbacks) internally. This couples the UI to the backend schema, making the component hard to reuse with different data sources and harder to test. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. If passing many props becomes cumbersome, define a specific UI interface (e.g., `ArticleUiModel`) that is distinct from the API entity.","whyItMatters":"The component takes a raw API object (`article`) and performs data transformation (formatting names, dates, fallbacks) internally. This couples the UI to the backend schema, making the component hard to reuse with different data sources and harder to test.","priority":"p1","scope":"stack-specific"},{"title":"Prefer shorthand axis utilities in Tailwind","rule":"Combine individual top/bottom or left/right utilities into single axis utilities (px, py, mx, my) when values are identical.","apiRule":"Combine individual top/bottom or left/right utilities into single axis utilities (px, py, mx, my) when values are identical. The code uses separate classes for top/bottom and left/right spacing with identical values (`pt-6 pb-6`, `pl-4 pr-4`). This is verbose and harder to scan. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. Use when you encounter code similar to the bad example. Exceptions: Only use axis shorthands when the values match; keep explicit sides when they differ.","whyItMatters":"The code uses separate classes for top/bottom and left/right spacing with identical values (`pt-6 pb-6`, `pl-4 pr-4`). This is verbose and harder to scan.","priority":"p2","scope":"stack-specific"},{"title":"Prefer logical AND for single-branch JSX rendering","rule":"Use the logical AND operator (&&) instead of the ternary operator for conditional rendering when there is no fallback UI.","apiRule":"Use the logical AND operator (&&) instead of the ternary operator for conditional rendering when there is no fallback UI. Using a ternary operator with `null` as the fallback is unnecessarily verbose when you only need to render content for the truthy case. It adds visual noise without adding value. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. Be cautious with numeric values (e.g., `0`), as React renders them directly. When checking numbers, convert them to booleans (`!!count` or `count > 0`) before using `&&`. Exceptions: Use a ternary when the falsy case must render UI or when values like 0 should still display.","whyItMatters":"Using a ternary operator with `null` as the fallback is unnecessarily verbose when you only need to render content for the truthy case. It adds visual noise without adding value.","priority":"p2","scope":"stack-specific"},{"title":"Prefer descriptive names over generic verbs in effects","rule":"Avoid generic function names like 'run' or 'start' inside useEffect; use names that describe the specific action.","apiRule":"Avoid generic function names like 'run' or 'start' inside useEffect; use names that describe the specific action. The name 'run' is generic and fails to describe what the function actually does. It forces the reader to parse the function body to understand the intent. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"The name 'run' is generic and fails to describe what the function actually does. It forces the reader to parse the function body to understand the intent.","priority":"p2","scope":"stack-specific"},{"title":"Prefer AbortController over manual flags for effect cleanup","rule":"Use AbortController to handle async cleanup in useEffect instead of manual boolean flags like isActive or isMounted.","apiRule":"Use AbortController to handle async cleanup in useEffect instead of manual boolean flags like isActive or isMounted. Using a manual `isActive` flag alongside `AbortController` is redundant. The flag adds imperative complexity solely to prevent state updates on unmounted components, which `AbortController` can already handle. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. Use when you encounter code similar to the bad example. Exceptions: If AbortController isn't available in the target runtime, a simple mounted flag can be acceptable. For complex multi-step cancellations, a higher-level abstraction may be clearer.","whyItMatters":"Using a manual `isActive` flag alongside `AbortController` is redundant. The flag adds imperative complexity solely to prevent state updates on unmounted components, which `AbortController` can already handle.","priority":"p2","scope":"stack-specific"},{"title":"Avoid redundant variable aliases","rule":"Do not create variables that merely alias existing state or props without adding logic or semantic value.","apiRule":"Do not create variables that merely alias existing state or props without adding logic or semantic value. The variable `visible` is an unnecessary alias for `results`. It introduces visual noise and confuses the reader into thinking there might be a difference between the two variables when there isn't. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. If you need to rename a variable for clarity, rename the original state or prop instead of creating an alias.","whyItMatters":"The variable `visible` is an unnecessary alias for `results`. It introduces visual noise and confuses the reader into thinking there might be a difference between the two variables when there isn't.","priority":"p1","scope":"universal"},{"title":"Consolidate effects with identical dependencies","rule":"Merge multiple useEffect hooks that share the same dependency array and domain concern into a single hook to improve readability and maintainability.","apiRule":"Merge multiple useEffect hooks that share the same dependency array and domain concern into a single hook to improve readability and maintainability. Using two separate effects with the exact same dependency array (`[imageUrl]`) adds unnecessary boilerplate. It separates logically related operations (resetting state and validating the image), making the code harder to scan. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. Exceptions apply if the effects handle completely unrelated concerns (e.g., one for analytics and one for DOM manipulation), but generally, related logic sharing triggers should be colocated.","whyItMatters":"Using two separate effects with the exact same dependency array (`[imageUrl]`) adds unnecessary boilerplate. It separates logically related operations (resetting state and validating the image), making the code harder to scan.","priority":"p2","scope":"stack-specific"},{"title":"Define distinct types for distinct data shapes","rule":"Avoid reusing a single type for multiple API responses that have differing structures; create specific types to ensure strict type safety.","apiRule":"Avoid reusing a single type for multiple API responses that have differing structures; create specific types to ensure strict type safety. Reusing a single type for different API endpoints forces fields to be optional to accommodate both shapes. This leads to a weak type definition where developers cannot rely on the existence of specific properties without manual checks. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"Reusing a single type for different API endpoints forces fields to be optional to accommodate both shapes. This leads to a weak type definition where developers cannot rely on the existence of specific properties without manual checks.","priority":"p1","scope":"stack-specific"},{"title":"Prefer synchronous DOM access over unnecessary microtasks","rule":"Avoid wrapping DOM logic in microtasks if the element is already mounted and accessible.","apiRule":"Avoid wrapping DOM logic in microtasks if the element is already mounted and accessible. Wrapping immediate DOM interactions in a microtask adds unnecessary complexity and async behavior. Since `useEffect` runs after the component has mounted and the ref is populated, deferral is not needed for static elements. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Only use microtasks or timeouts if you are waiting for a browser paint, a layout recalculation, or an element that renders asynchronously (e.g., inside a just-opened portal or dialog).","whyItMatters":"Wrapping immediate DOM interactions in a microtask adds unnecessary complexity and async behavior. Since `useEffect` runs after the component has mounted and the ref is populated, deferral is not needed for static elements.","priority":"p1","scope":"stack-specific"},{"title":"Localize browser patches with scoped listeners","rule":"Fix element-specific browser quirks using scoped event listeners on the target element instead of global interaction tracking.","apiRule":"Fix element-specific browser quirks using scoped event listeners on the target element instead of global interaction tracking. This code introduces complex global state and window listeners solely to manage the behavior of a single input element. It pollutes the component with unrelated concerns and renders the logic fragile and hard to maintain. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"This code introduces complex global state and window listeners solely to manage the behavior of a single input element. It pollutes the component with unrelated concerns and renders the logic fragile and hard to maintain.","priority":"p2","scope":"stack-specific"},{"title":"Prefer activeElement checks over interaction listeners for autofocus detection","rule":"Detect browser autofocus by checking document.activeElement on mount instead of tracking global user interaction events.","apiRule":"Detect browser autofocus by checking document.activeElement on mount instead of tracking global user interaction events. Tracking global events to determine if a focus was user-initiated is fragile and adds unnecessary complexity. It pollutes the component with global side effects solely to infer a state that is already available in the DOM. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"Tracking global events to determine if a focus was user-initiated is fragile and adds unnecessary complexity. It pollutes the component with global side effects solely to infer a state that is already available in the DOM.","priority":"p1","scope":"stack-specific"},{"title":"Configure mixed test environments using Vitest projects","rule":"Use the projects configuration in Vitest to isolate Node-based unit tests from browser-based integration tests in a single file.","apiRule":"Use the projects configuration in Vitest to isolate Node-based unit tests from browser-based integration tests in a single file. Using a single environment for all tests forces compromises. Logic tests run slower in 'jsdom', while browser tests might fail or require excessive mocking if forced into 'node'. It treats all tests as the same type. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. Use when you encounter code similar to the bad example.","whyItMatters":"Using a single environment for all tests forces compromises. Logic tests run slower in 'jsdom', while browser tests might fail or require excessive mocking if forced into 'node'. It treats all tests as the same type.","priority":"p1","scope":"universal"},{"title":"Prefer accessibility locators for user interaction tests","rule":"Query elements by accessible roles (e.g., button, option) to ensure tests resemble user behavior and verify accessibility.","apiRule":"Query elements by accessible roles (e.g., button, option) to ensure tests resemble user behavior and verify accessibility. Relying on CSS classes, IDs, or internal data attributes creates brittle tests that break when implementation details change (e.g., refactoring CSS). It also fails to verify that the element is correctly exposed to assistive technologies (screen readers). Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. Use when you encounter code similar to the bad example.","whyItMatters":"Relying on CSS classes, IDs, or internal data attributes creates brittle tests that break when implementation details change (e.g., refactoring CSS). It also fails to verify that the element is correctly exposed to assistive technologies (screen readers).","priority":"p1","scope":"universal"},{"title":"Prefer remember utility for global singletons","rule":"Use a dedicated utility to persist singleton instances across module reloads instead of manual globalThis assignments.","apiRule":"Use a dedicated utility to persist singleton instances across module reloads instead of manual globalThis assignments. Manually assigning properties to `globalThis` to preserve instances across hot reloads is verbose, error-prone, and clutters the global namespace with custom property names. Priority: P1. Scope: project specific.","whenToApply":"Priority: P1. Scope: project specific. This pattern is essential for database connections (Prisma), query clients, or any heavy object that should not be re-initialized during development HMR cycles.","whyItMatters":"Manually assigning properties to `globalThis` to preserve instances across hot reloads is verbose, error-prone, and clutters the global namespace with custom property names.","priority":"p1","scope":"project-specific"},{"title":"Prefer invariant over manual error throwing","rule":"Use invariant utilities to assert conditions and narrow types instead of verbose manual checks.","apiRule":"Use invariant utilities to assert conditions and narrow types instead of verbose manual checks. Manual checks using 'if' statements and 'throw new Error' are verbose and add visual noise. They require reading the negative condition to understand the requirement. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Popular libraries include 'tiny-invariant' for frontend/general use and 'node:assert' for Node.js backends.","whyItMatters":"Manual checks using 'if' statements and 'throw new Error' are verbose and add visual noise. They require reading the negative condition to understand the requirement.","priority":"p1","scope":"stack-specific"},{"title":"Infer loader data types from implementation","rule":"Avoid manually defining loader return types; infer them directly from the loader function using TypeScript.","apiRule":"Avoid manually defining loader return types; infer them directly from the loader function using TypeScript. Manually defining the `LoaderData` interface creates a maintenance burden. If the `loader` function implementation changes (e.g., adding a field), the type must be manually updated, leading to potential desynchronization and bugs. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. This also applies to `useRouteLoaderData` where you should import the loader from the parent route and use `typeof importedLoader` instead of recreating the type locally.","whyItMatters":"Manually defining the `LoaderData` interface creates a maintenance burden. If the `loader` function implementation changes (e.g., adding a field), the type must be manually updated, leading to potential desynchronization and bugs.","priority":"p1","scope":"stack-specific"},{"title":"Assume standard global objects exist","rule":"Do not use typeof checks for standard global objects like AbortSignal or URL; only feature-detect new methods on them if needed.","apiRule":"Do not use typeof checks for standard global objects like AbortSignal or URL; only feature-detect new methods on them if needed. Checking `typeof AbortSignal !== 'undefined'` is defensive noise. Modern environments (browsers/Node.js) guarantee the existence of standard classes like `AbortSignal`, `URL`, or `Promise`. Priority: P2. Scope: universal.","whenToApply":"Priority: P2. Scope: universal. Use when you encounter code similar to the bad example.","whyItMatters":"Checking `typeof AbortSignal !== 'undefined'` is defensive noise. Modern environments (browsers/Node.js) guarantee the existence of standard classes like `AbortSignal`, `URL`, or `Promise`.","priority":"p2","scope":"universal"},{"title":"Prefer instanceof over Boolean for type narrowing","rule":"Use instanceof checks in filter callbacks to allow TypeScript to automatically infer types and avoid manual type predicates.","apiRule":"Use instanceof checks in filter callbacks to allow TypeScript to automatically infer types and avoid manual type predicates. Using `Boolean(signal)` relies on generic truthiness and requires a manual type predicate (`signal is AbortSignal`) to ensure the array type is narrowed correctly. It incorrectly suggests that any truthy value is valid. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Inferred type predicates from filter functions were introduced in TypeScript 5.5.","whyItMatters":"Using `Boolean(signal)` relies on generic truthiness and requires a manual type predicate (`signal is AbortSignal`) to ensure the array type is narrowed correctly. It incorrectly suggests that any truthy value is valid.","priority":"p1","scope":"stack-specific"},{"title":"Validate event targets with runtime checks","rule":"Avoid unsafe type casting on event.target; use instanceof checks or invariants to guarantee type safety at runtime.","apiRule":"Avoid unsafe type casting on event.target; use instanceof checks or invariants to guarantee type safety at runtime. Using `as AbortSignal` silences TypeScript without verifying the actual object at runtime. If `event.target` is null or a different type, the code will fail unexpectedly downstream. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"Using `as AbortSignal` silences TypeScript without verifying the actual object at runtime. If `event.target` is null or a different type, the code will fail unexpectedly downstream.","priority":"p1","scope":"stack-specific"},{"title":"Merge cancellation signals with AbortSignal.any","rule":"Combine multiple cancellation sources (timeouts, user actions) into a single signal using AbortSignal.any to ensure robust resource cleanup.","apiRule":"Combine multiple cancellation sources (timeouts, user actions) into a single signal using AbortSignal.any to ensure robust resource cleanup. By using only the local `timeoutCtrl.signal`, the function ignores the `parentSignal` passed from the caller (e.g., a component unmounting or user navigation). This leads to 'zombie' requests that continue even after they are no longer needed. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. Modern browsers and Node.js (v20+) support `AbortSignal.any()` natively. For older environments, use a utility function that gracefully falls back to a manual `AbortController` implementation with proper event cleanup.","whyItMatters":"By using only the local `timeoutCtrl.signal`, the function ignores the `parentSignal` passed from the caller (e.g., a component unmounting or user navigation). This leads to 'zombie' requests that continue even after they are no longer needed.","priority":"p1","scope":"universal"},{"title":"Prefer intermediate unknown cast for diverging generics","rule":"Cast raw data to unknown before asserting specific generic types to handle diverging type paths safely.","apiRule":"Cast raw data to unknown before asserting specific generic types to handle diverging type paths safely. Directly casting the response to `TData` assumes the raw shape matches the input type. This causes type conflicts when the function needs to return `TResult` (which might differ from `TData`), forcing unsafe double assertions later. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"Directly casting the response to `TData` assumes the raw shape matches the input type. This causes type conflicts when the function needs to return `TResult` (which might differ from `TData`), forcing unsafe double assertions later.","priority":"p1","scope":"stack-specific"},{"title":"Integrate QueryClient in fetch utilities","rule":"Pass QueryClient and query keys to fetch utilities to enable caching and deduplication instead of raw fetching.","apiRule":"Pass QueryClient and query keys to fetch utilities to enable caching and deduplication instead of raw fetching. The fetch function blindly executes a network request every time it is called. It fails to utilize any caching layer, leading to redundant data fetching and slower user experiences when navigating between known states. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"The fetch function blindly executes a network request every time it is called. It fails to utilize any caching layer, leading to redundant data fetching and slower user experiences when navigating between known states.","priority":"p1","scope":"stack-specific"},{"title":"Inject fresh dependencies in test utilities","rule":"Always create fresh instances of stateful dependencies (like QueryClient or Stores) inside test helpers to ensure test isolation.","apiRule":"Always create fresh instances of stateful dependencies (like QueryClient or Stores) inside test helpers to ensure test isolation. Using a global singleton in test helpers causes state (like cache) to persist between tests. This leads to flaky tests where the outcome depends on the execution order of previous tests. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. This applies to any stateful dependency injected via Context, such as Redux stores, Apollo clients, or React Router contexts.","whyItMatters":"Using a global singleton in test helpers causes state (like cache) to persist between tests. This leads to flaky tests where the outcome depends on the execution order of previous tests.","priority":"p1","scope":"universal"},{"title":"Integrate caching and dependency injection into fetch utilities","rule":"Encapsulate caching logic (like ensureQueryData) and dependency injection within fetch helpers to reduce boilerplate and ensure consistent signal handling.","apiRule":"Encapsulate caching logic (like ensureQueryData) and dependency injection within fetch helpers to reduce boilerplate and ensure consistent signal handling. The fetch utility is too simple, forcing every consumer to manually wrap calls with `ensureQueryData`, manage query keys, and manually pass abort signals. This creates repetitive boilerplate and increases the risk of forgetting to forward signals. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Pass `QueryClient` as an argument (dependency injection) instead of importing a global instance to ensure compatibility with Server-Side Rendering (SSR) and isolation in tests.","whyItMatters":"The fetch utility is too simple, forcing every consumer to manually wrap calls with `ensureQueryData`, manage query keys, and manually pass abort signals. This creates repetitive boilerplate and increases the risk of forgetting to forward signals.","priority":"p1","scope":"stack-specific"},{"title":"Merge AbortSignals when bridging async contexts","rule":"Combine cancellation signals when using async libraries (like React Query) within framework lifecycles (like React Router) to ensure redundant requests are properly aborted.","apiRule":"Combine cancellation signals when using async libraries (like React Query) within framework lifecycles (like React Router) to ensure redundant requests are properly aborted. This code only respects the signal from the query library (React Query). If the user navigates away (triggering React Router's `request.signal`), the network request continues running unnecessarily because that signal is ignored. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Modern environments support `AbortSignal.any()`, but a polyfill or custom helper is needed for broader compatibility.","whyItMatters":"This code only respects the signal from the query library (React Query). If the user navigates away (triggering React Router's `request.signal`), the network request continues running unnecessarily because that signal is ignored.","priority":"p1","scope":"stack-specific"},{"title":"Prefer explicit boolean literals for discriminated unions","rule":"Use strict `true` and `false` literals instead of `undefined` for boolean discriminants in unions to improve clarity and type safety.","apiRule":"Use strict `true` and `false` literals instead of `undefined` for boolean discriminants in unions to improve clarity and type safety. Using `undefined` as a discriminant implies a missing value rather than a specific state. It makes the union harder to reason about and type-check strictly. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"Using `undefined` as a discriminant implies a missing value rather than a specific state. It makes the union harder to reason about and type-check strictly.","priority":"p1","scope":"stack-specific"},{"title":"Align I/O type assertions with return generics","rule":"When parsing external data in generic functions, cast directly to the return type generic to avoid type mismatches or suppressed errors.","apiRule":"When parsing external data in generic functions, cast directly to the return type generic to avoid type mismatches or suppressed errors. Casting the raw response to the input type (`TIn`) creates a type conflict when the function needs to return the output type (`TOut`) in the absence of a transformer. This forces developers to suppress errors, hiding potential issues. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"Casting the raw response to the input type (`TIn`) creates a type conflict when the function needs to return the output type (`TOut`) in the absence of a transformer. This forces developers to suppress errors, hiding potential issues.","priority":"p1","scope":"stack-specific"},{"title":"Prefer inferred type predicates in filter","rule":"Avoid defining manual type predicates in array filter callbacks when TypeScript can infer narrowing automatically.","apiRule":"Avoid defining manual type predicates in array filter callbacks when TypeScript can infer narrowing automatically. Manually defining the type predicate (`: s is Type`) is verbose and redundant in modern TypeScript. It also creates a maintenance burden, as the type definition must be manually synced with the implementation logic. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. This behavior requires TypeScript 5.5 or later (Inferred Type Predicates).","whyItMatters":"Manually defining the type predicate (`: s is Type`) is verbose and redundant in modern TypeScript. It also creates a maintenance burden, as the type definition must be manually synced with the implementation logic.","priority":"p2","scope":"stack-specific"},{"title":"Invalidate HMR singletons on config change","rule":"Include configuration data in singleton cache keys to ensure instances are recreated when settings change during Hot Module Replacement.","apiRule":"Include configuration data in singleton cache keys to ensure instances are recreated when settings change during Hot Module Replacement. Using a static key for a persisted singleton means the instance survives HMR even if you change its configuration code. If you modify `staleTime`, the old client (with the old time) is still returned, confusing the developer. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. Also prefer using exported interfaces like `QueryClientConfig` over complex utility types like `ConstructorParameters<typeof QueryClient>[0]` for better readability.","whyItMatters":"Using a static key for a persisted singleton means the instance survives HMR even if you change its configuration code. If you modify `staleTime`, the old client (with the old time) is still returned, confusing the developer.","priority":"p1","scope":"universal"},{"title":"Prefer single inferred useLoaderData call","rule":"Avoid calling useLoaderData multiple times or manual casting to merge types; rely on automatic type inference.","apiRule":"Avoid calling useLoaderData multiple times or manual casting to merge types; rely on automatic type inference. Calling useLoaderData multiple times creates redundant runtime execution solely to satisfy TypeScript. It implies a misunderstanding of the data flow or type system. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"Calling useLoaderData multiple times creates redundant runtime execution solely to satisfy TypeScript. It implies a misunderstanding of the data flow or type system.","priority":"p2","scope":"stack-specific"},{"title":"Use numeric separators for large numbers","rule":"Use underscores (_) as numeric separators for large numbers and time calculations to improve visual parsing and reduce errors.","apiRule":"Use underscores (_) as numeric separators for large numbers and time calculations to improve visual parsing and reduce errors. Writing large numbers or unit conversions without separators makes them difficult to scan visually. It is easy to miss a zero or misinterpret the magnitude (e.g., confusing 10000 with 1000). Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. This is especially useful for milliseconds calculations (e.g., `1_000 * 60` for one minute).","whyItMatters":"Writing large numbers or unit conversions without separators makes them difficult to scan visually. It is easy to miss a zero or misinterpret the magnitude (e.g., confusing 10000 with 1000).","priority":"p1","scope":"universal"},{"title":"Prefer React Router Context for loader dependencies","rule":"Inject dependencies into loaders using React Router's context middleware instead of closures or globals to improve testability and type safety.","apiRule":"Inject dependencies into loaders using React Router's context middleware instead of closures or globals to improve testability and type safety. Using closures or factory functions to inject dependencies like `QueryClient` creates unnecessary boilerplate and complicates route definitions. It also makes type inference for loader data more difficult. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. This pattern requires enabling `v8_middleware: true` in the React Router configuration and defining the `Future` interface in TypeScript.","whyItMatters":"Using closures or factory functions to inject dependencies like `QueryClient` creates unnecessary boilerplate and complicates route definitions. It also makes type inference for loader data more difficult.","priority":"p1","scope":"stack-specific"},{"title":"Prefer curried event handlers for parameterized callbacks","rule":"Use curried functions to pass arguments to event handlers instead of inline anonymous wrappers to improve JSX readability.","apiRule":"Use curried functions to pass arguments to event handlers instead of inline anonymous wrappers to improve JSX readability. Using an inline arrow function wrapper `() => handleSort(...)` adds visual noise to the JSX and mixes logic definition with usage. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. This pattern is functionally equivalent to arrow wrappers regarding re-renders (a new function is created each time), but it is often preferred for style and cleaner JSX syntax.","whyItMatters":"Using an inline arrow function wrapper `() => handleSort(...)` adds visual noise to the JSX and mixes logic definition with usage.","priority":"p2","scope":"stack-specific"},{"title":"Use CSS for visual reordering instead of conditional JSX","rule":"Avoid duplicating JSX elements or using complex conditionals just to change visual order; use CSS flex-direction or order instead.","apiRule":"Avoid duplicating JSX elements or using complex conditionals just to change visual order; use CSS flex-direction or order instead. Conditionally rendering the same component in different positions based on a flag creates unnecessary duplication and bloats the JSX. It mixes visual layout concerns with the component's structural logic. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"Conditionally rendering the same component in different positions based on a flag creates unnecessary duplication and bloats the JSX. It mixes visual layout concerns with the component's structural logic.","priority":"p2","scope":"stack-specific"},{"title":"Encapsulate internal types and helpers","rule":"Do not export types, functions, or constants that are only used locally within a module to avoid polluting the public API.","apiRule":"Do not export types, functions, or constants that are only used locally within a module to avoid polluting the public API. The interface `IInternalUserState` is implementation detail relevant only to this file. Exporting it creates a messy API surface and encourages other modules to depend on what should be private logic. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"The interface `IInternalUserState` is implementation detail relevant only to this file. Exporting it creates a messy API surface and encourages other modules to depend on what should be private logic.","priority":"p2","scope":"stack-specific"},{"title":"Prefer focal-point cropping over blind resizing","rule":"Avoid simple center-cropping or scaling for editorial images; use focal-point (AOI) metadata to ensure the subject remains visible.","apiRule":"Avoid simple center-cropping or scaling for editorial images; use focal-point (AOI) metadata to ensure the subject remains visible. Simply resizing or center-cropping an image ignores its content. This often leads to awkward crops where heads, text, or key details are cut off, degrading the user experience. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. Use when you encounter code similar to the bad example.","whyItMatters":"Simply resizing or center-cropping an image ignores its content. This often leads to awkward crops where heads, text, or key details are cut off, degrading the user experience.","priority":"p1","scope":"universal"},{"title":"Encapsulate transformation logic in domain models","rule":"Avoid transforming or formatting entity data inside controllers or routes; encapsulate this logic as computed properties or methods within the entity class itself.","apiRule":"Avoid transforming or formatting entity data inside controllers or routes; encapsulate this logic as computed properties or methods within the entity class itself. The controller manually formats the avatar URL and full name using external helpers. This leads to anemic models and scattered logic; if another endpoint needs the same user data, you must duplicate this mapping logic. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. If multiple subclasses share the same transformation logic (e.g., `Agenda` and `AgendaExternal`), implement the logic once in the abstract base class (`AgendaBase`) instead of duplicating it or handling it externally.","whyItMatters":"The controller manually formats the avatar URL and full name using external helpers. This leads to anemic models and scattered logic; if another endpoint needs the same user data, you must duplicate this mapping logic.","priority":"p1","scope":"stack-specific"},{"title":"Avoid array indices as React keys","rule":"Use stable, unique IDs from the data instead of array indices for React keys to ensure correct reconciliation and state preservation.","apiRule":"Use stable, unique IDs from the data instead of array indices for React keys to ensure correct reconciliation and state preservation. Using the array index as a key is unsafe if the list can be reordered, filtered, or mutated. If the order changes, React may incorrectly preserve component state (like inputs) or fail to update the DOM efficiently. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Indices are only acceptable if the list is completely static (read-only, fixed length, never reordered). Even then, prefer unique IDs if available.","whyItMatters":"Using the array index as a key is unsafe if the list can be reordered, filtered, or mutated. If the order changes, React may incorrectly preserve component state (like inputs) or fail to update the DOM efficiently.","priority":"p1","scope":"stack-specific"},{"title":"Prefer components over render helper functions","rule":"Avoid using helper functions to return JSX; extract them into standalone React components for better debugging and performance.","apiRule":"Avoid using helper functions to return JSX; extract them into standalone React components for better debugging and performance. Using a helper function (`renderStatusDot`) to return JSX bypasses React's component system. It doesn't show up in React DevTools, cannot use hooks efficiently, and treats the returned JSX as part of the parent's render output rather than a distinct unit. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"Using a helper function (`renderStatusDot`) to return JSX bypasses React's component system. It doesn't show up in React DevTools, cannot use hooks efficiently, and treats the returned JSX as part of the parent's render output rather than a distinct unit.","priority":"p1","scope":"stack-specific"},{"title":"Prefer function references over wrapper lambdas","rule":"Pass function references directly when arguments match, avoiding unnecessary anonymous wrapper functions.","apiRule":"Pass function references directly when arguments match, avoiding unnecessary anonymous wrapper functions. The anonymous function `(newPage) => setPage(newPage)` creates unnecessary verbosity. It simply forwards the argument to `setPage` without adding any logic. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. Be careful when passing functions that accept more arguments than intended (e.g., `['1', '2'].map(parseInt)` fails because `map` passes index). For state setters, this is safe.","whyItMatters":"The anonymous function `(newPage) => setPage(newPage)` creates unnecessary verbosity. It simply forwards the argument to `setPage` without adding any logic.","priority":"p2","scope":"stack-specific"},{"title":"Replace repetitive switch rendering with configuration maps","rule":"Avoid repeating identical JSX structures inside switch statements; use a configuration object to map state to visual attributes.","apiRule":"Avoid repeating identical JSX structures inside switch statements; use a configuration object to map state to visual attributes. The code repeats the entire JSX structure for every case, changing only the class modifier and text. If the HTML structure changes (e.g., adding an icon or aria-label), you must update every case manually. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"The code repeats the entire JSX structure for every case, changing only the class modifier and text. If the HTML structure changes (e.g., adding an icon or aria-label), you must update every case manually.","priority":"p2","scope":"stack-specific"},{"title":"Derive state defaults from option constants","rule":"Avoid hardcoding initial state values that must correspond to a specific list of options; reference the options source directly.","apiRule":"Avoid hardcoding initial state values that must correspond to a specific list of options; reference the options source directly. Hardcoding the initial value creates a hidden dependency. If the configuration constant (`PAGE_SIZES`) changes, the hardcoded default might become invalid (e.g., if 10 is removed), leading to bugs or inconsistent UI states. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"Hardcoding the initial value creates a hidden dependency. If the configuration constant (`PAGE_SIZES`) changes, the hardcoded default might become invalid (e.g., if 10 is removed), leading to bugs or inconsistent UI states.","priority":"p1","scope":"stack-specific"},{"title":"Avoid naming string identifiers as 'index'","rule":"Do not use the name 'index' for variables that hold string identifiers or keys, as it implies numeric position and order dependence.","apiRule":"Do not use the name 'index' for variables that hold string identifiers or keys, as it implies numeric position and order dependence. The variable name 'index' incorrectly suggests a numeric array position. This confuses the reader into thinking the logic depends on the order of elements, leading to unnecessary concerns about code fragility when reordering items. Priority: P2. Scope: universal.","whenToApply":"Priority: P2. Scope: universal. Use when you encounter code similar to the bad example.","whyItMatters":"The variable name 'index' incorrectly suggests a numeric array position. This confuses the reader into thinking the logic depends on the order of elements, leading to unnecessary concerns about code fragility when reordering items.","priority":"p2","scope":"universal"},{"title":"Prefer named keys over array indices for table columns","rule":"Avoid using numeric indices to identify columns during rendering; use stable string keys or enums to ensure readability and prevent regression when reordering.","apiRule":"Avoid using numeric indices to identify columns during rendering; use stable string keys or enums to ensure readability and prevent regression when reordering. Using a magic number (`index === 2`) to identify a specific column is brittle and unreadable. If the column order changes or a new column is inserted, the logic will apply to the wrong data or break completely. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"Using a magic number (`index === 2`) to identify a specific column is brittle and unreadable. If the column order changes or a new column is inserted, the logic will apply to the wrong data or break completely.","priority":"p2","scope":"stack-specific"},{"title":"Prefer objects over tuples for structured data","rule":"Use objects with named keys instead of arrays (tuples) to represent structured data rows to improve readability and maintainability.","apiRule":"Use objects with named keys instead of arrays (tuples) to represent structured data rows to improve readability and maintainability. Using arrays (tuples) to represent rows of data relies on implicit knowledge of index positions (e.g., index 0 is the date). This is brittle; adding or reordering columns breaks the logic and makes the code hard to read without constant reference to the mapping function. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"Using arrays (tuples) to represent rows of data relies on implicit knowledge of index positions (e.g., index 0 is the date). This is brittle; adding or reordering columns breaks the logic and makes the code hard to read without constant reference to the mapping function.","priority":"p2","scope":"stack-specific"},{"title":"Extract data preparation from JSX loops","rule":"Avoid performing complex data formatting or object creation inside JSX map callbacks; extract this logic into a separate helper function or prepare the data beforehand.","apiRule":"Avoid performing complex data formatting or object creation inside JSX map callbacks; extract this logic into a separate helper function or prepare the data beforehand. Defining variables and performing formatting logic inside the JSX `map` callback clutters the render method. It mixes data transformation with UI structure, making the component harder to read and the logic harder to test independently. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"Defining variables and performing formatting logic inside the JSX `map` callback clutters the render method. It mixes data transformation with UI structure, making the component harder to read and the logic harder to test independently.","priority":"p2","scope":"stack-specific"},{"title":"Extract complex modal content into dedicated components","rule":"Move complex modal UI and its specific data transformation logic into separate components to improve readability and testability.","apiRule":"Move complex modal UI and its specific data transformation logic into separate components to improve readability and testability. Defining the Modal UI and its helper functions (like `getDetailRows`) inside the parent component bloats the file. It mixes the list logic with the detail view logic, making the parent hard to read and the modal hard to test in isolation. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"Defining the Modal UI and its helper functions (like `getDetailRows`) inside the parent component bloats the file. It mixes the list logic with the detail view logic, making the parent hard to read and the modal hard to test in isolation.","priority":"p1","scope":"stack-specific"},{"title":"Freeze static collections with const assertions","rule":"Use `as const` on static arrays and objects to infer literal types and read-only tuples, preventing type widening.","apiRule":"Use `as const` on static arrays and objects to infer literal types and read-only tuples, preventing type widening. Defining an array without `as const` widens the type to `string[]`, losing the specific values and allowing mutation. This makes it impossible to use the values as a source of truth for types. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"Defining an array without `as const` widens the type to `string[]`, losing the specific values and allowing mutation. This makes it impossible to use the values as a source of truth for types.","priority":"p2","scope":"stack-specific"},{"title":"Extract generic UI patterns into reusable components","rule":"Avoid implementing generic UI behaviors (like Modals, Drawers, or Tooltips) inline within feature components; extract them into reusable components.","apiRule":"Avoid implementing generic UI behaviors (like Modals, Drawers, or Tooltips) inline within feature components; extract them into reusable components. The component mixes domain logic (displaying invoices) with generic UI mechanics (handling overlay clicks, stopping propagation, positioning). This makes the code repetitive if you need another modal elsewhere and clutters the component. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"The component mixes domain logic (displaying invoices) with generic UI mechanics (handling overlay clicks, stopping propagation, positioning). This makes the code repetitive if you need another modal elsewhere and clutters the component.","priority":"p2","scope":"stack-specific"},{"title":"Extract imperative DOM logic into custom hooks","rule":"Move direct DOM manipulations like scroll locking or event listeners into named custom hooks to separate side effects from UI logic.","apiRule":"Move direct DOM manipulations like scroll locking or event listeners into named custom hooks to separate side effects from UI logic. The component directly manipulates the global `document` object inside `useEffect`. This mixes imperative side effects with declarative UI code, making the component harder to read and the logic impossible to reuse. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"The component directly manipulates the global `document` object inside `useEffect`. This mixes imperative side effects with declarative UI code, making the component harder to read and the logic impossible to reuse.","priority":"p2","scope":"stack-specific"},{"title":"Prefer ReactNode props over render functions for static content","rule":"Define props as ReactNode or JSX.Element instead of render functions or Component types when the content does not rely on the component's internal state.","apiRule":"Define props as ReactNode or JSX.Element instead of render functions or Component types when the content does not rely on the component's internal state. Defining props as render functions (`() => JSX.Element`) or `ComponentType` creates unnecessary boilerplate at the usage site when no data needs to be passed from the child to the parent. It forces the consumer to wrap logic in functions or strictly match component interfaces. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. Only use render props (functions) if you need to pass internal state from the child component back to the injected content (e.g., renderItem={(item) => ...}).","whyItMatters":"Defining props as render functions (`() => JSX.Element`) or `ComponentType` creates unnecessary boilerplate at the usage site when no data needs to be passed from the child to the parent. It forces the consumer to wrap logic in functions or strictly match component interfaces.","priority":"p2","scope":"stack-specific"},{"title":"Push subclass-specific logic down from base classes","rule":"Avoid defining methods in an abstract base class if they are only used by specific subclasses.","apiRule":"Avoid defining methods in an abstract base class if they are only used by specific subclasses. The base class contains methods that are only relevant to one specific subclass. This pollutes the interface of other subclasses (like SMSNotification) with unrelated logic and violates separation of concerns. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"The base class contains methods that are only relevant to one specific subclass. This pollutes the interface of other subclasses (like SMSNotification) with unrelated logic and violates separation of concerns.","priority":"p1","scope":"stack-specific"},{"title":"Create dedicated transformation methods for distinct contexts","rule":"Do not reuse specific data transformation methods for unrelated use cases; create dedicated methods to ensure decoupling.","apiRule":"Do not reuse specific data transformation methods for unrelated use cases; create dedicated methods to ensure decoupling. The `asWidgetData` method reuses `asWebTeaser`, creating a hidden dependency. If the web teaser logic changes (e.g., to return a different image size), the widget logic breaks or behaves incorrectly. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Avoid mapping one DTO to another (e.g., `WebDTO` -> `WidgetDTO`). Always derive specific DTOs directly from the source entity to maintain clear separation of concerns.","whyItMatters":"The `asWidgetData` method reuses `asWebTeaser`, creating a hidden dependency. If the web teaser logic changes (e.g., to return a different image size), the widget logic breaks or behaves incorrectly.","priority":"p1","scope":"stack-specific"},{"title":"Derive union types from constant objects","rule":"Use `keyof typeof` to generate union types from constant objects to ensure a single source of truth and prevent synchronization errors.","apiRule":"Use `keyof typeof` to generate union types from constant objects to ensure a single source of truth and prevent synchronization errors. Manually defining a union type that mirrors the keys of an object creates a double maintenance burden. If the object changes but the type is not updated, it leads to bugs and inconsistency. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"Manually defining a union type that mirrors the keys of an object creates a double maintenance burden. If the object changes but the type is not updated, it leads to bugs and inconsistency.","priority":"p1","scope":"stack-specific"},{"title":"Prefer specific union keys over generic string index signatures","rule":"Avoid typing maps as `Record<string, T>` when the keys are a finite known set; use `as const` or explicit unions to enforce strict key validation.","apiRule":"Avoid typing maps as `Record<string, T>` when the keys are a finite known set; use `as const` or explicit unions to enforce strict key validation. Using `Record<string, string>` tells TypeScript that *any* string is a valid key. This disables type checking for typos or missing keys, leading to potential runtime errors when accessing undefined properties. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"Using `Record<string, string>` tells TypeScript that *any* string is a valid key. This disables type checking for typos or missing keys, leading to potential runtime errors when accessing undefined properties.","priority":"p1","scope":"stack-specific"},{"title":"Extract side effects and data fetching into custom hooks","rule":"Move data fetching, subscriptions, and complex state logic into custom hooks to keep components focused on presentation.","apiRule":"Move data fetching, subscriptions, and complex state logic into custom hooks to keep components focused on presentation. The component mixes imperative data fetching logic, state management, and error handling with declarative UI rendering. This makes it harder to read, test, and reuse the fetching logic elsewhere. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"The component mixes imperative data fetching logic, state management, and error handling with declarative UI rendering. This makes it harder to read, test, and reuse the fetching logic elsewhere.","priority":"p2","scope":"stack-specific"},{"title":"Define explicit return types for custom hooks","rule":"Always define and export an explicit interface for custom hook return values using the `[HookName]Payload` naming convention.","apiRule":"Always define and export an explicit interface for custom hook return values using the `[HookName]Payload` naming convention. Relying on type inference for the return value makes the API contract unclear. Changes to the internal logic might accidentally change the return type, causing issues for consumers. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"Relying on type inference for the return value makes the API contract unclear. Changes to the internal logic might accidentally change the return type, causing issues for consumers.","priority":"p2","scope":"stack-specific"},{"title":"Identify aborted requests by error type","rule":"Check for 'AbortError' explicitly in catch blocks instead of relying on signal state to handle cancellations.","apiRule":"Check for 'AbortError' explicitly in catch blocks instead of relying on signal state to handle cancellations. Checking `!signal.aborted` inside `.catch()` is semantically incorrect and potentially unreliable. It relies on shared state rather than the specific error object that caused the rejection. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. In `.then()` blocks, checking `!signal.aborted` is still acceptable to prevent state updates after unmount if the promise resolved before abortion.","whyItMatters":"Checking `!signal.aborted` inside `.catch()` is semantically incorrect and potentially unreliable. It relies on shared state rather than the specific error object that caused the rejection.","priority":"p1","scope":"stack-specific"},{"title":"Abstract date parsing logic into utilities","rule":"Avoid manually parsing date strings with `new Date()` or `split()` due to timezone ambiguity and fragility. Use centralized utilities instead.","apiRule":"Avoid manually parsing date strings with `new Date()` or `split()` due to timezone ambiguity and fragility. Use centralized utilities instead. Using `new Date(string)` on purely date strings (YYYY-MM-DD) creates timezone ambiguity (UTC vs Local), often leading to off-by-one errors. Manual string splitting is brittle and verbose. Priority: P2. Scope: universal.","whenToApply":"Priority: P2. Scope: universal. Libraries like `date-fns` or `dayjs` are recommended over native `Date` manipulation for complex logic.","whyItMatters":"Using `new Date(string)` on purely date strings (YYYY-MM-DD) creates timezone ambiguity (UTC vs Local), often leading to off-by-one errors. Manual string splitting is brittle and verbose.","priority":"p2","scope":"universal"},{"title":"Consolidate global type augmentations","rule":"Centralize global type declarations in a single file and only define properties that are strictly consumed.","apiRule":"Centralize global type declarations in a single file and only define properties that are strictly consumed. Creating a separate file for a minor augmentation clutters the project structure. Defining the entire shape of the external object (including unused fields like 'email' or 'preferences') increases maintenance burden without adding value. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"Creating a separate file for a minor augmentation clutters the project structure. Defining the entire shape of the external object (including unused fields like 'email' or 'preferences') increases maintenance burden without adding value.","priority":"p1","scope":"stack-specific"},{"title":"Avoid defensive checks for required globals in effects","rule":"useEffect runs only in the browser, so checking for window is redundant. Avoid silently skipping logic if required globals are missing; rely on strict types or fail loudly.","apiRule":"useEffect runs only in the browser, so checking for window is redundant. Avoid silently skipping logic if required globals are missing; rely on strict types or fail loudly. Checking 'typeof window' inside useEffect is redundant code bloat. Additionally, wrapping critical logic in defensive 'if' checks causes the feature to fail silently if the configuration is missing, hiding integration bugs. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. If a global property is truly optional, mark it as optional in the TypeScript interface definition (e.g., `user?: User`) instead of adding runtime checks that contradict the types.","whyItMatters":"Checking 'typeof window' inside useEffect is redundant code bloat. Additionally, wrapping critical logic in defensive 'if' checks causes the feature to fail silently if the configuration is missing, hiding integration bugs.","priority":"p1","scope":"stack-specific"},{"title":"Prefer null over sentinel values for missing state","rule":"Use null or undefined to explicitly represent missing data instead of magic strings to simplify truthiness checks and avoid ambiguity.","apiRule":"Use null or undefined to explicitly represent missing data instead of magic strings to simplify truthiness checks and avoid ambiguity. Using a magic string like '#' to represent the absence of a value is brittle and requires verbose checks (`!== '#'`). It confuses valid data with placeholder values. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"Using a magic string like '#' to represent the absence of a value is brittle and requires verbose checks (`!== '#'`). It confuses valid data with placeholder values.","priority":"p1","scope":"stack-specific"},{"title":"Explicitly type useState with null initial value","rule":"Prevent type narrowing to 'null' by explicitly defining the generic type when initializing state with null.","apiRule":"Prevent type narrowing to 'null' by explicitly defining the generic type when initializing state with null. When `useState` is initialized with `null` without a generic type, TypeScript infers the state type as strictly `null`. This makes it impossible to update the state with actual data later, causing type errors. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"When `useState` is initialized with `null` without a generic type, TypeScript infers the state type as strictly `null`. This makes it impossible to update the state with actual data later, causing type errors.","priority":"p1","scope":"stack-specific"},{"title":"Assume universal support for AbortController","rule":"Avoid defensive feature detection for AbortController as it is supported in all modern browsers and Node.js.","apiRule":"Avoid defensive feature detection for AbortController as it is supported in all modern browsers and Node.js. Defensively checking for 'AbortController' is outdated and adds unnecessary noise. All modern browsers and active Node.js versions support it natively. Priority: P2. Scope: universal.","whenToApply":"Priority: P2. Scope: universal. Use when you encounter code similar to the bad example.","whyItMatters":"Defensively checking for 'AbortController' is outdated and adds unnecessary noise. All modern browsers and active Node.js versions support it natively.","priority":"p2","scope":"universal"},{"title":"Rely on TypeScript inference for primitive state initialization","rule":"Avoid explicit generic type arguments for `useState` when the type can be correctly inferred from the initial primitive value.","apiRule":"Avoid explicit generic type arguments for `useState` when the type can be correctly inferred from the initial primitive value. Explicitly adding generic types like `<number>` or `<string>` is redundant when initializing with a primitive. It adds visual noise without improving type safety. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. Explicit types are only necessary when the initial value is `null`, `undefined`, or an empty array (where inference might be `never[]`), or when using Union types.","whyItMatters":"Explicitly adding generic types like `<number>` or `<string>` is redundant when initializing with a primitive. It adds visual noise without improving type safety.","priority":"p2","scope":"stack-specific"},{"title":"Prefer a compute helper over raw IIFEs in JSX","rule":"Use a utility function to execute inline logic immediately instead of noisy IIFE syntax inside JSX props.","apiRule":"Use a utility function to execute inline logic immediately instead of noisy IIFE syntax inside JSX props. Using a raw Immediately Invoked Function Expression (IIFE) inside JSX creates visual clutter with trailing parentheses `()`, making the code harder to read. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. While extracting complex logic into named variables is often preferred, `compute` is an excellent alternative when the logic is strictly presentational and should remain co-located with the JSX.","whyItMatters":"Using a raw Immediately Invoked Function Expression (IIFE) inside JSX creates visual clutter with trailing parentheses `()`, making the code harder to read.","priority":"p2","scope":"stack-specific"},{"title":"Enforce timeouts on network requests","rule":"Always attach an AbortController signal to fetch requests to prevent indefinite hanging on slow networks.","apiRule":"Always attach an AbortController signal to fetch requests to prevent indefinite hanging on slow networks. The fetch request lacks a timeout mechanism. If the server hangs or the connection drops silently, the application will wait indefinitely, leading to frozen UI states and resource exhaustion. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. Use when you encounter code similar to the bad example.","whyItMatters":"The fetch request lacks a timeout mechanism. If the server hangs or the connection drops silently, the application will wait indefinitely, leading to frozen UI states and resource exhaustion.","priority":"p1","scope":"universal"},{"title":"Validate dependencies inside useEffect","rule":"Always add guard clauses inside useEffect to check if required dependencies exist before performing expensive operations like data fetching.","apiRule":"Always add guard clauses inside useEffect to check if required dependencies exist before performing expensive operations like data fetching. The effect runs immediately when the component mounts or `userId` changes, even if `userId` is missing. This results in unnecessary network requests (e.g., fetching `/api/users/undefined`) and potential 404 errors. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"The effect runs immediately when the component mounts or `userId` changes, even if `userId` is missing. This results in unnecessary network requests (e.g., fetching `/api/users/undefined`) and potential 404 errors.","priority":"p1","scope":"stack-specific"},{"title":"Avoid naming files 'Types' if they export runtime values","rule":"Files named '*.types.ts' should strictly contain type definitions. If they export runtime values, rename them to '*.constants.ts' or the domain name.","apiRule":"Files named '*.types.ts' should strictly contain type definitions. If they export runtime values, rename them to '*.constants.ts' or the domain name. The filename 'ArticleTypes' suggests it only contains TypeScript definitions and will be erased at runtime. Including runtime constants like 'ARTICLE_CATEGORIES' violates this expectation. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"The filename 'ArticleTypes' suggests it only contains TypeScript definitions and will be erased at runtime. Including runtime constants like 'ARTICLE_CATEGORIES' violates this expectation.","priority":"p2","scope":"stack-specific"},{"title":"Validate inputs and handle upstream 404s in providers","rule":"Always validate required arguments before using them in external requests and explicitly handle upstream 404 responses to distinguish missing data from errors.","apiRule":"Always validate required arguments before using them in external requests and explicitly handle upstream 404 responses to distinguish missing data from errors. The code constructs a URL and fetches data without validating the input `id` or checking the response status. If `id` is missing or the upstream service returns a 404, the application may crash during JSON parsing or return invalid data. Priority: P0. Scope: stack specific.","whenToApply":"Priority: P0. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"The code constructs a URL and fetches data without validating the input `id` or checking the response status. If `id` is missing or the upstream service returns a 404, the application may crash during JSON parsing or return invalid data.","priority":"p0","scope":"stack-specific"},{"title":"Terminate invalid requests with explicit HTTP errors","rule":"Return a 4xx response immediately when validation fails instead of calling next(), which passes control to subsequent handlers.","apiRule":"Return a 4xx response immediately when validation fails instead of calling next(), which passes control to subsequent handlers. Calling `next()` when input validation fails causes the request to fall through to subsequent routes or the 404 handler. This obscures the actual error (bad input) and confuses the client. Priority: P0. Scope: stack specific.","whenToApply":"Priority: P0. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"Calling `next()` when input validation fails causes the request to fall through to subsequent routes or the 404 handler. This obscures the actual error (bad input) and confuses the client.","priority":"p0","scope":"stack-specific"},{"title":"Safeguard URL instantiation with try-catch","rule":"Wrap `new URL()` calls in try-catch blocks to prevent runtime crashes from malformed strings.","apiRule":"Wrap `new URL()` calls in try-catch blocks to prevent runtime crashes from malformed strings. Directly calling `new URL()` with dynamic input allows invalid strings to throw an error. This unhandled exception bubbles up and can crash the entire component tree or application. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. Even with protocol checks (like regex), `new URL()` can still fail on specific malformed characters or incomplete strings.","whyItMatters":"Directly calling `new URL()` with dynamic input allows invalid strings to throw an error. This unhandled exception bubbles up and can crash the entire component tree or application.","priority":"p1","scope":"universal"},{"title":"Handle fallible derivations safely outside JSX","rule":"Wrap logic that might throw errors (like URL parsing) in safe handlers outside JSX to prevent component crashes.","apiRule":"Wrap logic that might throw errors (like URL parsing) in safe handlers outside JSX to prevent component crashes. Calling functions that can throw exceptions (like `new URL()`) directly inside JSX is unsafe. If the input is invalid, the error will bubble up and unmount the entire component tree (or crash the app), offering no fallback UI. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"Calling functions that can throw exceptions (like `new URL()`) directly inside JSX is unsafe. If the input is invalid, the error will bubble up and unmount the entire component tree (or crash the app), offering no fallback UI.","priority":"p1","scope":"stack-specific"},{"title":"Prefer visual loading states over null","rule":"Always render a visual indicator (spinner or skeleton) during data fetching instead of returning null to improve user experience.","apiRule":"Always render a visual indicator (spinner or skeleton) during data fetching instead of returning null to improve user experience. Returning null during a loading state causes the component to vanish, potentially causing layout shifts or making the app appear unresponsive/broken. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Skeletons are generally preferred over spinners for larger content blocks as they reduce the perceived load time by mimicking the final layout.","whyItMatters":"Returning null during a loading state causes the component to vanish, potentially causing layout shifts or making the app appear unresponsive/broken.","priority":"p1","scope":"stack-specific"},{"title":"Include context in error logs","rule":"Always include relevant identifiers (IDs, slugs, keys) in error messages to facilitate debugging and reproduction.","apiRule":"Always include relevant identifiers (IDs, slugs, keys) in error messages to facilitate debugging and reproduction. The error message is generic. If this error appears in logs, you won't know which specific product ID caused the failure, making reproduction difficult. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. Use when you encounter code similar to the bad example.","whyItMatters":"The error message is generic. If this error appears in logs, you won't know which specific product ID caused the failure, making reproduction difficult.","priority":"p1","scope":"universal"},{"title":"Encapsulate data fetching in dedicated Providers","rule":"Centralize data access and transformation logic within dedicated Provider classes instead of loose functions or inline logic.","apiRule":"Centralize data access and transformation logic within dedicated Provider classes instead of loose functions or inline logic. Using loose functions for data fetching scatters logic across the codebase, making it harder to maintain state, handle configuration, or mock dependencies for testing. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"Using loose functions for data fetching scatters logic across the codebase, making it harder to maintain state, handle configuration, or mock dependencies for testing.","priority":"p1","scope":"stack-specific"},{"title":"Encapsulate strictly coupled components","rule":"Wrap components that must always be used together into a single parent component to enforce consistency and prevent usage errors.","apiRule":"Wrap components that must always be used together into a single parent component to enforce consistency and prevent usage errors. The dependency between the feature flag and the ticker component is implicit and exposed to the consumer. If a developer adds `LiveTicker` but forgets `FeatureFlag`, the functionality may break silently or behave unpredictably. This violates the DRY principle. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"The dependency between the feature flag and the ticker component is implicit and exposed to the consumer. If a developer adds `LiveTicker` but forgets `FeatureFlag`, the functionality may break silently or behave unpredictably. This violates the DRY principle.","priority":"p1","scope":"stack-specific"},{"title":"Prefer optional chaining for nested property access","rule":"Use the optional chaining operator (?.) to access deeply nested properties instead of verbose logical AND checks.","apiRule":"Use the optional chaining operator (?.) to access deeply nested properties instead of verbose logical AND checks. Using repeated logical AND (&&) operators to guard against null/undefined values is verbose and repetitive. It creates visual noise and makes the code harder to read and modify. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"Using repeated logical AND (&&) operators to guard against null/undefined values is verbose and repetitive. It creates visual noise and makes the code harder to read and modify.","priority":"p2","scope":"stack-specific"},{"title":"Prefer readable steps over complex single-line returns","rule":"Avoid dense one-liners involving multiple logic branches or string manipulations; break them into distinct, named steps for clarity.","apiRule":"Avoid dense one-liners involving multiple logic branches or string manipulations; break them into distinct, named steps for clarity. This one-liner attempts to normalize the protocol, check for existing query parameters, and append a new one all at once. It is visually dense, hard to debug, and prone to syntax errors like missing delimiters. Priority: P2. Scope: universal.","whenToApply":"Priority: P2. Scope: universal. Use when you encounter code similar to the bad example.","whyItMatters":"This one-liner attempts to normalize the protocol, check for existing query parameters, and append a new one all at once. It is visually dense, hard to debug, and prone to syntax errors like missing delimiters.","priority":"p2","scope":"universal"},{"title":"Avoid magic strings in control flow","rule":"Use named constants or enums instead of raw string literals in conditional logic to prevent typos and ease refactoring.","apiRule":"Use named constants or enums instead of raw string literals in conditional logic to prevent typos and ease refactoring. Hardcoding string literals like 'bjellesauerApp' directly in the control flow makes the code error-prone. A single typo will break the logic, and searching for all occurrences during refactoring is manual and risky. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"Hardcoding string literals like 'bjellesauerApp' directly in the control flow makes the code error-prone. A single typo will break the logic, and searching for all occurrences during refactoring is manual and risky.","priority":"p2","scope":"stack-specific"},{"title":"Prefer URL object for query parameter construction","rule":"Use the native URL and URLSearchParams APIs to build query strings instead of manual string concatenation or array joining.","apiRule":"Use the native URL and URLSearchParams APIs to build query strings instead of manual string concatenation or array joining. Manual string concatenation is brittle and error-prone. You must remember to manually encode every value using `encodeURIComponent`, and managing delimiters (`?`, `&`) becomes messy as complexity grows. Priority: P2. Scope: universal.","whenToApply":"Priority: P2. Scope: universal. Note: Do not manually `encodeURIComponent` when using `url.searchParams.set()`, as the method performs encoding automatically. Doing so would result in double encoding.","whyItMatters":"Manual string concatenation is brittle and error-prone. You must remember to manually encode every value using `encodeURIComponent`, and managing delimiters (`?`, `&`) becomes messy as complexity grows.","priority":"p2","scope":"universal"},{"title":"Prefer null over defaults for missing data","rule":"Use null to explicitly represent missing values in API responses instead of ambiguous defaults like empty strings or zeroes.","apiRule":"Use null to explicitly represent missing values in API responses instead of ambiguous defaults like empty strings or zeroes. Using empty strings (`''`) or zeroes (`0`) as fallbacks for missing data creates ambiguity. The consumer cannot distinguish between a legitimate empty value (e.g., a free product) and missing data (e.g., price not set). Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. Use when you encounter code similar to the bad example.","whyItMatters":"Using empty strings (`''`) or zeroes (`0`) as fallbacks for missing data creates ambiguity. The consumer cannot distinguish between a legitimate empty value (e.g., a free product) and missing data (e.g., price not set).","priority":"p1","scope":"universal"},{"title":"Avoid specialized repository methods for filter combinations","rule":"Do not create specialized repository methods for specific filter combinations; rely on chainable (fluent) methods and a single terminal execution method.","apiRule":"Do not create specialized repository methods for specific filter combinations; rely on chainable (fluent) methods and a single terminal execution method. This approach leads to method explosion (e.g., `getTeasersByCategory`, `getTeasersByTag`). It duplicates the terminal logic (fetching and mapping) in every specialized method, making the class harder to maintain. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. In a Repository pattern, separate 'criteria building' (chainable methods that return `this`) from 'execution' (methods that return data).","whyItMatters":"This approach leads to method explosion (e.g., `getTeasersByCategory`, `getTeasersByTag`). It duplicates the terminal logic (fetching and mapping) in every specialized method, making the class harder to maintain.","priority":"p1","scope":"stack-specific"},{"title":"Encapsulate specific query and mapping chains in repositories","rule":"Avoid manually chaining query filters and mapping logic inside controllers; extract them into specific repository methods.","apiRule":"Avoid manually chaining query filters and mapping logic inside controllers; extract them into specific repository methods. The controller knows too much about how to fetch and transform data. It manually chains filter methods and maps the result to a DTO, making this logic hard to reuse and test in isolation. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"The controller knows too much about how to fetch and transform data. It manually chains filter methods and maps the result to a DTO, making this logic hard to reuse and test in isolation.","priority":"p1","scope":"stack-specific"},{"title":"Enforce consistent query behavior across data sources","rule":"Ensure primary and fallback data providers use identical matching logic (exact vs. fuzzy) to prevent unpredictable results.","apiRule":"Ensure primary and fallback data providers use identical matching logic (exact vs. fuzzy) to prevent unpredictable results. The primary source uses strict exact matching, while the fallback uses loose fuzzy matching. This creates inconsistent behavior where the result set's quality changes drastically depending solely on which provider answers the request. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. This often occurs when falling back from a specialized search engine (like Elasticsearch) to a general-purpose database (like SQL). Explicitly test the fallback path to ensure semantics align.","whyItMatters":"The primary source uses strict exact matching, while the fallback uses loose fuzzy matching. This creates inconsistent behavior where the result set's quality changes drastically depending solely on which provider answers the request.","priority":"p1","scope":"stack-specific"},{"title":"Prefer method chaining for fluent APIs","rule":"Utilize method chaining when working with fluent interfaces (like query builders) to avoid unnecessary intermediate variables.","apiRule":"Utilize method chaining when working with fluent interfaces (like query builders) to avoid unnecessary intermediate variables. This code creates an intermediate variable solely to mutate its state step-by-step. It breaks the visual flow and adds imperative noise to what should be a declarative query definition. Priority: P2. Scope: universal.","whenToApply":"Priority: P2. Scope: universal. Use when you encounter code similar to the bad example.","whyItMatters":"This code creates an intermediate variable solely to mutate its state step-by-step. It breaks the visual flow and adds imperative noise to what should be a declarative query definition.","priority":"p2","scope":"universal"},{"title":"Design clean, semantic API routes","rule":"Avoid file extensions in URL paths and organize routes by access scope (public vs. private) to improve maintainability and security.","apiRule":"Avoid file extensions in URL paths and organize routes by access scope (public vs. private) to improve maintainability and security. The route uses a file extension (`.json`), which is a legacy practice, and is placed in a generic `commonRouter`. This mixes public and private logic and creates brittle, static-file-like URLs. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Only use file extensions in URLs if the endpoint explicitly supports multiple return formats (e.g., .xml vs .json) dynamically. Otherwise, rely on 'Accept' headers.","whyItMatters":"The route uses a file extension (`.json`), which is a legacy practice, and is placed in a generic `commonRouter`. This mixes public and private logic and creates brittle, static-file-like URLs.","priority":"p1","scope":"stack-specific"},{"title":"Extract derived values into named variables","rule":"Compute derived values (like URLs, labels, or config) in the component body instead of embedding logic or anonymous functions inside JSX props.","apiRule":"Compute derived values (like URLs, labels, or config) in the component body instead of embedding logic or anonymous functions inside JSX props. Defining logic inside the JSX prop (especially anonymous functions) mixes calculation with presentation. It makes the component harder to read and the URL calculation cannot be reused or easily tested. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"Defining logic inside the JSX prop (especially anonymous functions) mixes calculation with presentation. It makes the component harder to read and the URL calculation cannot be reused or easily tested.","priority":"p2","scope":"stack-specific"},{"title":"Use anchor tags for navigation, buttons for actions","rule":"If an element opens a URL or navigates the user, use an anchor tag instead of a button with an onClick handler.","apiRule":"If an element opens a URL or navigates the user, use an anchor tag instead of a button with an onClick handler. Using a `<button>` with `onClick` for navigation prevents users from middle-clicking to open in a new tab, seeing the URL on hover, or using assistive technology correctly. Screen readers announce 'button' (implies in-page action) instead of 'link' (implies navigation). Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. For internal navigation in SPAs (React Router, Next.js), use the framework's `<Link>` component, which renders an accessible `<a>` tag under the hood.","whyItMatters":"Using a `<button>` with `onClick` for navigation prevents users from middle-clicking to open in a new tab, seeing the URL on hover, or using assistive technology correctly. Screen readers announce 'button' (implies in-page action) instead of 'link' (implies navigation).","priority":"p1","scope":"stack-specific"},{"title":"Trigger side effects in event handlers, not useEffect","rule":"Place logic triggered by user interactions directly in event handlers instead of watching state with useEffect.","apiRule":"Place logic triggered by user interactions directly in event handlers instead of watching state with useEffect. Using useEffect to sync state with callbacks creates a 'waterfall' of updates. The state changes, React renders, then the effect runs. This makes code harder to follow and can trigger redundant renders or behaviors. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. See React docs: 'You Might Not Need an Effect'. If you need to handle the initial state (on mount), separate that logic or use a specific initialization effect, but keep user-triggered updates in handlers.","whyItMatters":"Using useEffect to sync state with callbacks creates a 'waterfall' of updates. The state changes, React renders, then the effect runs. This makes code harder to follow and can trigger redundant renders or behaviors.","priority":"p1","scope":"stack-specific"},{"title":"Decouple analytics context from reusable components","rule":"Avoid hardcoding specific tracking values inside reusable UI components to ensure they remain context-agnostic.","apiRule":"Avoid hardcoding specific tracking values inside reusable UI components to ensure they remain context-agnostic. The component hardcodes the tracking type to 'offers', assuming it will only ever be used in that context. If this popup is later used on a 'profile' or 'article' page, the analytics data will be incorrect. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"The component hardcodes the tracking type to 'offers', assuming it will only ever be used in that context. If this popup is later used on a 'profile' or 'article' page, the analytics data will be incorrect.","priority":"p2","scope":"stack-specific"},{"title":"Encapsulate third-party integration logic in standalone functions","rule":"Avoid inline interaction with third-party globals (like analytics) inside components; extract them into named service functions.","apiRule":"Avoid inline interaction with third-party globals (like analytics) inside components; extract them into named service functions. The component directly accesses global window objects and constructs complex payloads inline. This makes the code hard to read, impossible to reuse, and tightly coupled to the specific analytics vendor. Priority: P2. Scope: universal.","whenToApply":"Priority: P2. Scope: universal. Use when you encounter code similar to the bad example.","whyItMatters":"The component directly accesses global window objects and constructs complex payloads inline. This makes the code hard to read, impossible to reuse, and tightly coupled to the specific analytics vendor.","priority":"p2","scope":"universal"},{"title":"Prefer anchor tags for navigation over buttons","rule":"Use <a> tags for navigation instead of buttons with onClick handlers to ensure accessibility, SEO, and native browser behavior.","apiRule":"Use <a> tags for navigation instead of buttons with onClick handlers to ensure accessibility, SEO, and native browser behavior. Using a button with an imperative `onClick` handler (like `window.open`) breaks native browser features such as 'Open in new tab', status bar URL preview, and SEO crawling. It also confuses screen readers by announcing an action rather than navigation. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"Using a button with an imperative `onClick` handler (like `window.open`) breaks native browser features such as 'Open in new tab', status bar URL preview, and SEO crawling. It also confuses screen readers by announcing an action rather than navigation.","priority":"p1","scope":"stack-specific"},{"title":"Prefer explicit checks for non-default variants","rule":"Check explicitly for specific variants instead of relying on negative checks against a default. This ensures a safe fallback for undefined or future values.","apiRule":"Check explicitly for specific variants instead of relying on negative checks against a default. This ensures a safe fallback for undefined or future values. Using a negative check (`!== 'Finansavisen'`) means that if `product` is undefined, null, or a new future value, the code incorrectly defaults to the specific variant logic ('KapitalLogo'). Priority: P2. Scope: universal.","whenToApply":"Priority: P2. Scope: universal. Use when you encounter code similar to the bad example.","whyItMatters":"Using a negative check (`!== 'Finansavisen'`) means that if `product` is undefined, null, or a new future value, the code incorrectly defaults to the specific variant logic ('KapitalLogo').","priority":"p2","scope":"universal"},{"title":"Replace nested ternaries with configuration helpers","rule":"Avoid complex nested ternary operators for value selection by using a helper function that accepts a configuration object.","apiRule":"Avoid complex nested ternary operators for value selection by using a helper function that accepts a configuration object. Nested ternaries make the code hard to read and force you to repeat the conditional logic for every prop. Adding a new case requires modifying every usage site. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. This pattern is especially useful when switching themes, brands, or configurations based on a global state.","whyItMatters":"Nested ternaries make the code hard to read and force you to repeat the conditional logic for every prop. Adding a new case requires modifying every usage site.","priority":"p2","scope":"stack-specific"},{"title":"Extract complex render props into named components","rule":"Avoid defining complex UI logic inline within render props. Extract them into separate components to improve readability and maintainability.","apiRule":"Avoid defining complex UI logic inline within render props. Extract them into separate components to improve readability and maintainability. Defining the tooltip UI inline within the render prop clutters the parent component, making it hard to read and maintain. The logic for rendering the tooltip is mixed with the chart configuration, and the anonymous function is redefined on every render. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"Defining the tooltip UI inline within the render prop clutters the parent component, making it hard to read and maintain. The logic for rendering the tooltip is mixed with the chart configuration, and the anonymous function is redefined on every render.","priority":"p2","scope":"stack-specific"},{"title":"Centralize component configuration","rule":"Move static visual parameters, magic numbers, and style constants into a dedicated configuration object to separate concerns and improve maintainability.","apiRule":"Move static visual parameters, magic numbers, and style constants into a dedicated configuration object to separate concerns and improve maintainability. Visual constants like colors, font sizes, and margins are hardcoded directly into the JSX. This makes the component code noisy and difficult to maintain if design tokens change. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"Visual constants like colors, font sizes, and margins are hardcoded directly into the JSX. This makes the component code noisy and difficult to maintain if design tokens change.","priority":"p2","scope":"stack-specific"},{"title":"Prefer React Router state over manual URL parsing","rule":"Use hooks like `useMatches` or `useMatch` with stable route IDs to detect active routes instead of manually parsing the `pathname` string.","apiRule":"Use hooks like `useMatches` or `useMatch` with stable route IDs to detect active routes instead of manually parsing the `pathname` string. Manually parsing `pathname` using Regex or string methods is fragile and duplicates routing logic. If the route path changes in the configuration, this logic will silently break. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. Ensure you explicitly assign unique `id` properties to your route definitions in `createBrowserRouter` or similar configs for this pattern to work reliably.","whyItMatters":"Manually parsing `pathname` using Regex or string methods is fragile and duplicates routing logic. If the route path changes in the configuration, this logic will silently break.","priority":"p2","scope":"stack-specific"},{"title":"Prevent Flash of Unwanted Content by defaulting to hidden","rule":"Set initial element visibility to hidden when controlling it via client-side JavaScript to avoid UI flickering.","apiRule":"Set initial element visibility to hidden when controlling it via client-side JavaScript to avoid UI flickering. The element is visible by default in the HTML. When the JavaScript runs (hydration/effect), it hides the element, but users perceive a jarring 'flash' of the content disappearing immediately after page load. Priority: P2. Scope: universal.","whenToApply":"Priority: P2. Scope: universal. Alternatively, moving the element entirely into the React component tree (instead of manipulating the DOM externally) is often a cleaner architectural solution if possible.","whyItMatters":"The element is visible by default in the HTML. When the JavaScript runs (hydration/effect), it hides the element, but users perceive a jarring 'flash' of the content disappearing immediately after page load.","priority":"p2","scope":"universal"},{"title":"Prefer route IDs over pathnames for route matching","rule":"Use stable route IDs to identify routes programmatically instead of relying on brittle URL path strings.","apiRule":"Use stable route IDs to identify routes programmatically instead of relying on brittle URL path strings. The logic relies on checking `pathname` strings to determine the active route. This is brittle because changing the URL structure (e.g., renaming `/user` to `/account`) breaks the logic without warning, and partial matching can lead to false positives. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"The logic relies on checking `pathname` strings to determine the active route. This is brittle because changing the URL structure (e.g., renaming `/user` to `/account`) breaks the logic without warning, and partial matching can lead to false positives.","priority":"p2","scope":"stack-specific"},{"title":"Compute derived values outside useEffect","rule":"Calculate derived conditions in the render scope and pass primitives to useEffect to prevent unnecessary re-runs.","apiRule":"Calculate derived conditions in the render scope and pass primitives to useEffect to prevent unnecessary re-runs. The logic is inside the effect, forcing it to depend on the entire `location` object. Since objects often change reference on every render, the effect runs more often than necessary. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"The logic is inside the effect, forcing it to depend on the entire `location` object. Since objects often change reference on every render, the effect runs more often than necessary.","priority":"p1","scope":"stack-specific"},{"title":"Avoid generic variable names like 'data'","rule":"Variable names must describe the specific domain concept they hold. Generic terms like 'data', 'info', or 'items' fail to convey intent and make code harder to understand.","apiRule":"Variable names must describe the specific domain concept they hold. Generic terms like 'data', 'info', or 'items' fail to convey intent and make code harder to understand. The names 'data' and 'hasData' are semantically empty because everything in software is data. They force the reader to check the assignment source to understand what strictly domain-specific information is being handled. Priority: P2. Scope: universal.","whenToApply":"Priority: P2. Scope: universal. Use when you encounter code similar to the bad example.","whyItMatters":"The names 'data' and 'hasData' are semantically empty because everything in software is data. They force the reader to check the assignment source to understand what strictly domain-specific information is being handled.","priority":"p2","scope":"universal"},{"title":"Omit default type attributes in HTML5","rule":"Do not specify `type=\"text/javascript\"` for scripts or `type=\"text/css\"` for styles, as they are implied defaults in HTML5.","apiRule":"Do not specify `type=\"text/javascript\"` for scripts or `type=\"text/css\"` for styles, as they are implied defaults in HTML5. The `type` attribute is explicitly set to the default values. This is legacy syntax that adds unnecessary verbosity to the markup without adding any value. Priority: P2. Scope: universal.","whenToApply":"Priority: P2. Scope: universal. Use when you encounter code similar to the bad example.","whyItMatters":"The `type` attribute is explicitly set to the default values. This is legacy syntax that adds unnecessary verbosity to the markup without adding any value.","priority":"p2","scope":"universal"},{"title":"Always wrap React root in StrictMode","rule":"Wrap the application root in React.StrictMode to catch potential problems, unsafe lifecycles, and impure renderers during development.","apiRule":"Wrap the application root in React.StrictMode to catch potential problems, unsafe lifecycles, and impure renderers during development. Mounting the application without StrictMode hides potential issues like impure side effects, deprecated lifecycle usage, or unsafe contexts, making debugging harder. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. StrictMode checks run only in development mode; they do not impact production performance.","whyItMatters":"Mounting the application without StrictMode hides potential issues like impure side effects, deprecated lifecycle usage, or unsafe contexts, making debugging harder.","priority":"p2","scope":"stack-specific"},{"title":"Prefer enums or constants over magic strings","rule":"Replace opaque literal values (magic strings/numbers) with named constants or enums to make code self-documenting and maintainable.","apiRule":"Replace opaque literal values (magic strings/numbers) with named constants or enums to make code self-documenting and maintainable. The code uses 'magic strings' (hardcoded IDs) inside the control flow. This forces the developer to write comments above each case to explain what the ID represents, which is prone to becoming outdated or missing. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"The code uses 'magic strings' (hardcoded IDs) inside the control flow. This forces the developer to write comments above each case to explain what the ID represents, which is prone to becoming outdated or missing.","priority":"p2","scope":"stack-specific"},{"title":"Prefer logical AND for conditional object properties","rule":"Use logical AND (&&) instead of the ternary operator when conditionally spreading properties into an object to reduce verbosity.","apiRule":"Use logical AND (&&) instead of the ternary operator when conditionally spreading properties into an object to reduce verbosity. The ternary syntax requires explicitly returning an empty object ({}) for the false case. This adds unnecessary visual noise and verbosity for a simple conditional inclusion. Priority: P2. Scope: universal.","whenToApply":"Priority: P2. Scope: universal. Use when you encounter code similar to the bad example. Exceptions: If falsy values like 0 are valid, use explicit checks or ternaries to avoid dropping them.","whyItMatters":"The ternary syntax requires explicitly returning an empty object ({}) for the false case. This adds unnecessary visual noise and verbosity for a simple conditional inclusion.","priority":"p2","scope":"universal"},{"title":"Wrap global mutations in try-finally during tests","rule":"When a test modifies the global environment (like prototypes or global variables), use a try-finally block to guarantee the environment is restored even if assertions fail.","apiRule":"When a test modifies the global environment (like prototypes or global variables), use a try-finally block to guarantee the environment is restored even if assertions fail. The cleanup logic is placed after the assertions. If an assertion fails, the test function exits immediately, leaving the global `String.prototype` modified. This pollutes the environment and causes subsequent tests to fail unpredictably. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. This pattern is preferred over `afterEach` for one-off, invasive mutations (like deleting a prototype method) to keep the test self-contained and avoid accidental pollution if lifecycle hooks are misconfigured.","whyItMatters":"The cleanup logic is placed after the assertions. If an assertion fails, the test function exits immediately, leaving the global `String.prototype` modified. This pollutes the environment and causes subsequent tests to fail unpredictably.","priority":"p1","scope":"universal"},{"title":"Wrap global mutations in try-finally for safe test cleanup","rule":"When modifying global objects (prototypes, window) in a test, use try-finally to guarantee cleanup even upon failure.","apiRule":"When modifying global objects (prototypes, window) in a test, use try-finally to guarantee cleanup even upon failure. If the assertion fails, the execution stops immediately, and the global `String.prototype` is never restored. This causes 'test pollution', where subsequent tests fail mysteriously because the environment remains broken. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. While `beforeEach`/`afterEach` hooks handle common setups, `try...finally` is often cleaner for single-test specific mutations like checking polyfill behavior.","whyItMatters":"If the assertion fails, the execution stops immediately, and the global `String.prototype` is never restored. This causes 'test pollution', where subsequent tests fail mysteriously because the environment remains broken.","priority":"p1","scope":"universal"},{"title":"Colocate polyfills with their dependent libraries","rule":"Wrap libraries that require polyfills in a dedicated module to encapsulate side effects and clarify dependencies.","apiRule":"Wrap libraries that require polyfills in a dedicated module to encapsulate side effects and clarify dependencies. The polyfill is imported in a generic utility file. It is unclear which library requires it, and if 'legacyLib' is removed in the future, the polyfill will likely remain as dead code because the relationship is not explicit. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. This pattern effectively implements the Facade pattern for external dependencies that require environment patching.","whyItMatters":"The polyfill is imported in a generic utility file. It is unclear which library requires it, and if 'legacyLib' is removed in the future, the polyfill will likely remain as dead code because the relationship is not explicit.","priority":"p1","scope":"universal"},{"title":"Prefer standard polyfill libraries","rule":"Avoid manually implementing polyfills in source code; use granular imports from established libraries like core-js to ensure correctness and maintainability.","apiRule":"Avoid manually implementing polyfills in source code; use granular imports from established libraries like core-js to ensure correctness and maintainability. Copying and pasting polyfill logic into the source code is risky. It creates unmaintained code that may miss edge cases, lacks updates, and clutters the repository. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. If a manual polyfill is strictly necessary (e.g., due to extreme bundle size constraints preventing even granular imports), you must explicitly document the source/origin of the implementation in a comment.","whyItMatters":"Copying and pasting polyfill logic into the source code is risky. It creates unmaintained code that may miss edge cases, lacks updates, and clutters the repository.","priority":"p1","scope":"universal"},{"title":"Avoid manual DOM focus hacks","rule":"Avoid imperative DOM calls like .blur() or .stopPropagation() to control focus; use semantic HTML or accessible libraries instead.","apiRule":"Avoid imperative DOM calls like .blur() or .stopPropagation() to control focus; use semantic HTML or accessible libraries instead. Manually calling `blur()` to prevent the mobile keyboard or manipulate focus flow is a brittle hack. It confuses screen readers, breaks standard keyboard navigation, and fights against the browser's native behavior. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Using 'readonly' on an input is sometimes acceptable, but if the element solely functions as a button to open something else, a `<button>` tag is semantically superior.","whyItMatters":"Manually calling `blur()` to prevent the mobile keyboard or manipulate focus flow is a brittle hack. It confuses screen readers, breaks standard keyboard navigation, and fights against the browser's native behavior.","priority":"p1","scope":"stack-specific"},{"title":"Target document for DOM events and window for viewport events","rule":"Attach listeners to `document` for content interactions (clicks, key presses) and reserve `window` for viewport events (resize, scroll) to maintain semantic clarity.","apiRule":"Attach listeners to `document` for content interactions (clicks, key presses) and reserve `window` for viewport events (resize, scroll) to maintain semantic clarity. Attaching DOM interaction events (like clicks or key presses) to `window` is semantically incorrect because `window` represents the global browser frame. It works due to bubbling but muddies the distinction between viewport state and document content. Priority: P2. Scope: universal.","whenToApply":"Priority: P2. Scope: universal. Use when you encounter code similar to the bad example.","whyItMatters":"Attaching DOM interaction events (like clicks or key presses) to `window` is semantically incorrect because `window` represents the global browser frame. It works due to bubbling but muddies the distinction between viewport state and document content.","priority":"p2","scope":"universal"},{"title":"Avoid triggering side effects on focus events","rule":"Do not trigger significant UI changes like opening modals on focus events to preserve keyboard navigation.","apiRule":"Do not trigger significant UI changes like opening modals on focus events to preserve keyboard navigation. Attaching action logic to `onFocus` forces the action to trigger merely by navigating to the element (e.g., via Tab). Immediately calling `blur()` further degrades accessibility by removing the focus indicator and preventing screen readers from announcing the element. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"Attaching action logic to `onFocus` forces the action to trigger merely by navigating to the element (e.g., via Tab). Immediately calling `blur()` further degrades accessibility by removing the focus indicator and preventing screen readers from announcing the element.","priority":"p1","scope":"stack-specific"},{"title":"Prefer AbortController for event cleanup","rule":"Use AbortController signals to manage and clean up event listeners instead of manually pairing addEventListener with removeEventListener.","apiRule":"Use AbortController signals to manage and clean up event listeners instead of manually pairing addEventListener with removeEventListener. Manually removing event listeners requires ensuring the function reference passed to removeEventListener is identical to the one added. This is verbose and error-prone, especially with complex closures. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. This pattern is standard in modern browsers and also works for cleaning up fetch requests in the same effect.","whyItMatters":"Manually removing event listeners requires ensuring the function reference passed to removeEventListener is identical to the one added. This is verbose and error-prone, especially with complex closures.","priority":"p1","scope":"stack-specific"},{"title":"Prefer headless UI libraries for complex interactive components","rule":"Use battle-tested headless libraries (like Radix UI or Headless UI) for complex interactive patterns to ensure robust accessibility and focus management.","apiRule":"Use battle-tested headless libraries (like Radix UI or Headless UI) for complex interactive patterns to ensure robust accessibility and focus management. Manually implementing interactive patterns (like a button wrapping an input) often leads to invalid HTML semantics, broken keyboard navigation, and confused screen readers. It forces you to reinvent focus management and ARIA support from scratch. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Common libraries include Radix UI, Headless UI, and React Aria. Use them for Modals, Popovers, Dropdowns, and Tabs.","whyItMatters":"Manually implementing interactive patterns (like a button wrapping an input) often leads to invalid HTML semantics, broken keyboard navigation, and confused screen readers. It forces you to reinvent focus management and ARIA support from scratch.","priority":"p1","scope":"stack-specific"},{"title":"Prefer AbortController for event listener cleanup","rule":"Use AbortController to manage event listener lifecycle instead of manually pairing addEventListener and removeEventListener.","apiRule":"Use AbortController to manage event listener lifecycle instead of manually pairing addEventListener and removeEventListener. Using removeEventListener requires passing the exact same function reference used in addEventListener. If the handler is anonymous or wrapped, the cleanup fails silently. It also requires repeating the event name and options. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. This pattern is supported in all modern browsers. It is especially useful in React useEffect hooks to avoid stale closures or complex dependency management for event handlers.","whyItMatters":"Using removeEventListener requires passing the exact same function reference used in addEventListener. If the handler is anonymous or wrapped, the cleanup fails silently. It also requires repeating the event name and options.","priority":"p2","scope":"stack-specific"},{"title":"Avoid redundant preventDefault","rule":"Do not use event.preventDefault() if declarative HTML attributes like readOnly or disabled already enforce the desired behavior.","apiRule":"Do not use event.preventDefault() if declarative HTML attributes like readOnly or disabled already enforce the desired behavior. Using preventDefault() here is unnecessary because the 'readOnly' attribute already prevents the user from modifying the value. It adds noise and suggests special handling that isn't actually occurring. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. If the intent is to create a button that looks like an input, consider using a semantic <button> styled as an input instead to ensure better accessibility and event handling.","whyItMatters":"Using preventDefault() here is unnecessary because the 'readOnly' attribute already prevents the user from modifying the value. It adds noise and suggests special handling that isn't actually occurring.","priority":"p1","scope":"stack-specific"},{"title":"Verify event target is a Node before DOM access","rule":"Use `instanceof Node` to safely narrow `event.target` types instead of unsafe casting with `as Node`.","apiRule":"Use `instanceof Node` to safely narrow `event.target` types instead of unsafe casting with `as Node`. Using `as Node` blindly forces TypeScript to accept the type, but it doesn't prevent runtime errors if `event.target` is null or not a DOM node (e.g., `window`). It bypasses type safety. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"Using `as Node` blindly forces TypeScript to accept the type, but it doesn't prevent runtime errors if `event.target` is null or not a DOM node (e.g., `window`). It bypasses type safety.","priority":"p1","scope":"stack-specific"},{"title":"Restrict boolean prefixes to boolean values","rule":"Variables named with boolean prefixes (is, has, should) must strictly hold boolean values to ensure code clarity.","apiRule":"Variables named with boolean prefixes (is, has, should) must strictly hold boolean values to ensure code clarity. The variable `hasLabel` uses the `has` prefix, which conventionally indicates a boolean, but it holds a string. This mismatch creates confusion about the variable's type. Additionally, `tickerCategory` describes where the data is used (ticker) rather than what the data is (article category), making it misleading. Priority: P2. Scope: universal.","whenToApply":"Priority: P2. Scope: universal. Use when you encounter code similar to the bad example.","whyItMatters":"The variable `hasLabel` uses the `has` prefix, which conventionally indicates a boolean, but it holds a string. This mismatch creates confusion about the variable's type. Additionally, `tickerCategory` describes where the data is used (ticker) rather than what the data is (article category), making it misleading.","priority":"p2","scope":"universal"},{"title":"Replace magic values with named collection constants","rule":"Avoid hardcoding specific string or number literals in conditional logic. Use named arrays or Sets to define the business rule, making the code more readable and extensible.","apiRule":"Avoid hardcoding specific string or number literals in conditional logic. Use named arrays or Sets to define the business rule, making the code more readable and extensible. Hardcoding specific values like '2025' directly in the control flow makes the code brittle and the intent unclear. If the list of excluded years grows, the condition becomes messy and hard to read. Priority: P2. Scope: universal.","whenToApply":"Priority: P2. Scope: universal. For small lists, an array with `.includes()` is sufficient. For large datasets, consider using a `Set` for O(1) lookup performance.","whyItMatters":"Hardcoding specific values like '2025' directly in the control flow makes the code brittle and the intent unclear. If the list of excluded years grows, the condition becomes messy and hard to read.","priority":"p2","scope":"universal"},{"title":"Prevent layout shifts with aspect-ratio on images","rule":"Always define `aspect-ratio` and use `max-width` for responsive images to reserve layout space and prevent Cumulative Layout Shift (CLS).","apiRule":"Always define `aspect-ratio` and use `max-width` for responsive images to reserve layout space and prevent Cumulative Layout Shift (CLS). Using only `width: 100%` and `height: auto` means the browser cannot calculate the image's height until the file is fully downloaded. This causes the page layout to jump (CLS) as the image loads. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. Use when you encounter code similar to the bad example. Exceptions: If your image component already reserves space via width/height attributes or intrinsic sizing, explicit aspect-ratio may be redundant.","whyItMatters":"Using only `width: 100%` and `height: auto` means the browser cannot calculate the image's height until the file is fully downloaded. This causes the page layout to jump (CLS) as the image loads.","priority":"p1","scope":"universal"},{"title":"Prefer max-width over width for fluid media","rule":"Use max-width to ensure images respond to container size without forcing them to stretch beyond their natural resolution.","apiRule":"Use max-width to ensure images respond to container size without forcing them to stretch beyond their natural resolution. Setting 'width: 100%' forces the element to fill the container width, causing pixelation if the image is naturally smaller. Adding 'max-width: 100%' becomes redundant because the width is already locked to 100%. Priority: P2. Scope: universal.","whenToApply":"Priority: P2. Scope: universal. Use when you encounter code similar to the bad example. Exceptions: If you intentionally want stretching (e.g., decorative backgrounds), width: 100% can be acceptable.","whyItMatters":"Setting 'width: 100%' forces the element to fill the container width, causing pixelation if the image is naturally smaller. Adding 'max-width: 100%' becomes redundant because the width is already locked to 100%.","priority":"p2","scope":"universal"},{"title":"Prefer concise alt text without redundancy","rule":"Ensure alt text accurately mirrors the image content or function without adding redundant context or filler words.","apiRule":"Ensure alt text accurately mirrors the image content or function without adding redundant context or filler words. The alt text includes filler words (\"Official list of\") that likely aren't part of the image's visual text. This adds noise for screen reader users who already know the site context. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"The alt text includes filler words (\"Official list of\") that likely aren't part of the image's visual text. This adds noise for screen reader users who already know the site context.","priority":"p1","scope":"stack-specific"},{"title":"Prefer clsx for conditional class names","rule":"Use the `clsx` utility to construct className strings conditionally instead of manual string concatenation.","apiRule":"Use the `clsx` utility to construct className strings conditionally instead of manual string concatenation. Manually concatenating strings for class names is error-prone. It is easy to miss a leading space (e.g., resulting in 'btn-primaryactive' instead of 'btn-primary active') and the syntax becomes messy with multiple conditions. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. Always install `clsx` if not present. It is lighter than `classnames` but serves the same purpose. Exceptions: If the class list is static or the project already has a tiny utility, adding clsx may be unnecessary.","whyItMatters":"Manually concatenating strings for class names is error-prone. It is easy to miss a leading space (e.g., resulting in 'btn-primaryactive' instead of 'btn-primary active') and the syntax becomes messy with multiple conditions.","priority":"p2","scope":"stack-specific"},{"title":"Minimize code churn by avoiding unnecessary reordering","rule":"Avoid moving blocks of code unless strictly necessary for logic or scoping, as it creates noisy diffs and complicates code reviews.","apiRule":"Avoid moving blocks of code unless strictly necessary for logic or scoping, as it creates noisy diffs and complicates code reviews. The developer moved the `pageData` definition above `logAnalytics` without any dependency requirement. This generates a confusing diff where lines appear deleted and added elsewhere, obscuring the fact that only `newField` was added. Priority: P2. Scope: universal.","whenToApply":"Priority: P2. Scope: universal. Only move code if variable dependencies (scoping) require it. If you must refactor/reorder, do it in a separate commit from logic changes.","whyItMatters":"The developer moved the `pageData` definition above `logAnalytics` without any dependency requirement. This generates a confusing diff where lines appear deleted and added elsewhere, obscuring the fact that only `newField` was added.","priority":"p2","scope":"universal"},{"title":"Maintain a single source of truth for domain configuration","rule":"Centralize domain definitions (like lists or resources) into a single shared configuration to prevent duplication and inconsistency across environments.","apiRule":"Centralize domain definitions (like lists or resources) into a single shared configuration to prevent duplication and inconsistency across environments. The list definitions are duplicated across multiple environment files, making them hard to keep in sync. Additionally, business logic relies on hardcoded strings (`slug === 'top-400'`) instead of configuration metadata. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. In large monorepos, use a shared package or a common configuration folder that both 'apps' (environments) can import from.","whyItMatters":"The list definitions are duplicated across multiple environment files, making them hard to keep in sync. Additionally, business logic relies on hardcoded strings (`slug === 'top-400'`) instead of configuration metadata.","priority":"p1","scope":"universal"},{"title":"Match alt text literally to text within the image","rule":"For images containing text, the alt attribute must mirror that text exactly to ensure equivalent access for screen readers.","apiRule":"For images containing text, the alt attribute must mirror that text exactly to ensure equivalent access for screen readers. The `alt` text describes the resource conceptually or uses a translation that doesn't match the visual text. Screen reader users will hear something different from what is displayed. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. If the image is purely decorative and contains no meaningful text, use an empty alt attribute (alt=\"\").","whyItMatters":"The `alt` text describes the resource conceptually or uses a translation that doesn't match the visual text. Screen reader users will hear something different from what is displayed.","priority":"p1","scope":"stack-specific"},{"title":"Optimize SVG assets before committing","rule":"Always optimize SVG files using tools like SVGO to remove unnecessary metadata and reduce bundle size.","apiRule":"Always optimize SVG files using tools like SVGO to remove unnecessary metadata and reduce bundle size. Raw SVG exports often contain editor metadata, comments, hidden groups, and verbose attributes that are unnecessary for rendering in the browser. This bloats the application bundle. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. You can run 'pnpm dlx svgo path/to/file.svg' or use an online optimizer like SVGOMG before adding the file to the project.","whyItMatters":"Raw SVG exports often contain editor metadata, comments, hidden groups, and verbose attributes that are unnecessary for rendering in the browser. This bloats the application bundle.","priority":"p1","scope":"universal"},{"title":"Prefer const assertions for literals over explicit type annotations","rule":"Use `as const` for literals in conditionals to preserve their specific type, enabling cleaner type inference without manual annotations.","apiRule":"Use `as const` for literals in conditionals to preserve their specific type, enabling cleaner type inference without manual annotations. Manually annotating the variable type (`: AccessLevel`) adds noise and redundancy. It forces you to maintain the type definition on the variable, rather than letting TypeScript verify compatibility through inference. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"Manually annotating the variable type (`: AccessLevel`) adds noise and redundancy. It forces you to maintain the type definition on the variable, rather than letting TypeScript verify compatibility through inference.","priority":"p1","scope":"stack-specific"},{"title":"Prefer declarative serialization for response shaping","rule":"Use a configuration-based serializer to handle edge cases (like omitting fields) instead of hardcoding conditional logic in controllers.","apiRule":"Use a configuration-based serializer to handle edge cases (like omitting fields) instead of hardcoding conditional logic in controllers. The controller contains ad-hoc imperative logic to mutate the response for specific cases. This mixes business rules with data retrieval and makes the API response shape unpredictable and harder to test. Priority: P0. Scope: stack specific.","whenToApply":"Priority: P0. Scope: stack specific. Commonly used for handling backward compatibility or hiding specific fields (e.g., sensitive data or empty values) for certain resource versions.","whyItMatters":"The controller contains ad-hoc imperative logic to mutate the response for specific cases. This mixes business rules with data retrieval and makes the API response shape unpredictable and harder to test.","priority":"p0","scope":"stack-specific"},{"title":"Prefer Object.groupBy for data grouping","rule":"Use the standardized Object.groupBy method instead of manual iteration to group array items.","apiRule":"Use the standardized Object.groupBy method instead of manual iteration to group array items. Manually initializing an object, checking for key existence, and pushing to an array is imperative boilerplate. It creates noise and increases the chance of minor bugs. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. Requires 'ES2024' or newer in your tsconfig 'lib' setting. Use Map.groupBy if you need keys that are not strings. Exceptions: Use Map.groupBy when you need non-string keys, or stick with manual grouping if you must support runtimes before ES2024.","whyItMatters":"Manually initializing an object, checking for key existence, and pushing to an array is imperative boilerplate. It creates noise and increases the chance of minor bugs.","priority":"p2","scope":"stack-specific"},{"title":"Limit the scope of try-catch blocks","rule":"Wrap only unstable code (like I/O) in try-catch blocks, keeping data transformation logic outside.","apiRule":"Wrap only unstable code (like I/O) in try-catch blocks, keeping data transformation logic outside. The entire logic is wrapped in `try-catch`. If the mapping logic throws a `TypeError` (e.g., accessing properties on undefined), it is caught and treated as a fetch failure, hiding the actual bug. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. Use when you encounter code similar to the bad example.","whyItMatters":"The entire logic is wrapped in `try-catch`. If the mapping logic throws a `TypeError` (e.g., accessing properties on undefined), it is caught and treated as a fetch failure, hiding the actual bug.","priority":"p1","scope":"universal"},{"title":"Extract complex mapping logic into helper functions","rule":"Avoid complex inline callbacks in array methods like .map(); extract the logic into named helper functions for better readability.","apiRule":"Avoid complex inline callbacks in array methods like .map(); extract the logic into named helper functions for better readability. The mapping logic is defined inline, making the main flow of the data transformation hard to follow. If the logic grows, this block becomes cluttered and harder to test. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. Use when you encounter code similar to the bad example.","whyItMatters":"The mapping logic is defined inline, making the main flow of the data transformation hard to follow. If the logic grows, this block becomes cluttered and harder to test.","priority":"p2","scope":"stack-specific"},{"title":"Prefer Record over Map for simple indexing","rule":"Use plain objects (Record) instead of Maps for simple ID-based lookups unless specific Map features are required.","apiRule":"Use plain objects (Record) instead of Maps for simple ID-based lookups unless specific Map features are required. Using a `Map` for simple string or number keys adds unnecessary complexity (`.get`, `.set`) and isn't natively serializable to JSON. It is overkill for simple lookups. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. Use `Map` only when keys are objects, frequent additions/removals affect performance significantly, or key insertion order matters. Exceptions: Use Map when keys aren't strings, insertion order matters, or you need frequent mutations.","whyItMatters":"Using a `Map` for simple string or number keys adds unnecessary complexity (`.get`, `.set`) and isn't natively serializable to JSON. It is overkill for simple lookups.","priority":"p1","scope":"stack-specific"},{"title":"Prefer const with IIFE over mutable let","rule":"Avoid using 'let' for variables that are only reassigned during initialization; use IIFEs or helper functions to keep them 'const'.","apiRule":"Avoid using 'let' for variables that are only reassigned during initialization; use IIFEs or helper functions to keep them 'const'. Using 'let' allows the variable to be mutated anywhere in the scope, making the data flow harder to trace. The logic is fragmented across declaration and initialization. Priority: P2. Scope: stack specific.","whenToApply":"Priority: P2. Scope: stack specific. If your project has a dedicated 'compute' utility (e.g. `const val = compute(() => ...)`), prefer using that over raw IIFEs for better readability.","whyItMatters":"Using 'let' allows the variable to be mutated anywhere in the scope, making the data flow harder to trace. The logic is fragmented across declaration and initialization.","priority":"p2","scope":"stack-specific"},{"title":"Prefer flatMap for combined mapping and filtering","rule":"Use flatMap to transform and filter items in a single pass instead of chaining map and filter.","apiRule":"Use flatMap to transform and filter items in a single pass instead of chaining map and filter. Chaining .map() and .filter() creates an intermediate array and iterates over the collection twice. This separates the transformation logic from the validation logic, which can be less efficient and harder to follow for complex types. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. While map().filter() is readable for very simple cases, flatMap is generally preferred when the transformation might result in invalid states that need immediate removal.","whyItMatters":"Chaining .map() and .filter() creates an intermediate array and iterates over the collection twice. This separates the transformation logic from the validation logic, which can be less efficient and harder to follow for complex types.","priority":"p1","scope":"stack-specific"},{"title":"Enforce consistent pluralization for REST API resources","rule":"Always use consistent plural nouns for resource collections in API routes to ensure predictability.","apiRule":"Always use consistent plural nouns for resource collections in API routes to ensure predictability. The API routes mix plural ('instruments') and singular ('instrument') for the same resource collection. This makes the API endpoints difficult to predict and confusing for consumers. Priority: P1. Scope: universal.","whenToApply":"Priority: P1. Scope: universal. A common standard is to always use plural for collections (e.g., /users, /users/:id). Avoid mixing /users for lists and /user/:id for details.","whyItMatters":"The API routes mix plural ('instruments') and singular ('instrument') for the same resource collection. This makes the API endpoints difficult to predict and confusing for consumers.","priority":"p1","scope":"universal"},{"title":"Prefer React Context over global window events","rule":"Avoid using global window events for component communication; use React Context to share state and actions instead.","apiRule":"Avoid using global window events for component communication; use React Context to share state and actions instead. Using global window events creates hidden coupling between components and bypasses React's unidirectional data flow. It makes the application fragile, hard to debug (unknown triggers), and difficult to test since it relies on the global window object. Priority: P1. Scope: stack specific.","whenToApply":"Priority: P1. Scope: stack specific. For complex state, consider using a reducer within the Context provider to handle state transitions effectively.","whyItMatters":"Using global window events creates hidden coupling between components and bypasses React's unidirectional data flow. It makes the application fragile, hard to debug (unknown triggers), and difficult to test since it relies on the global window object.","priority":"p1","scope":"stack-specific"}]