Stack conventions
Angular
Angular applications with standalone components, signals and OnPush change detection, tested with TestBed and component harnesses.
Angular applications with standalone components, signals and OnPush change detection, tested with TestBed and component harnesses.
Used for: angular.
Principles
- Write standalone components, directives and pipes; do not add new NgModules unless the repo's existing structure requires them.
- Use signals for component state (
signal,computed,linkedSignal) and signal-based APIs (input(),output(),model(),viewChild()) in new code. - Use
ChangeDetectionStrategy.OnPushfor every new component (or the zoneless set-up the app has adopted). - Use the built-in control flow (
@if,@forwith atrackexpression,@switch,@defer) instead of structural directives in new templates. - Inject dependencies with
inject(); keep components thin and move business logic and HTTP access into services. - Every RxJS subscription has an owner that ends it: prefer the
asyncpipe ortoSignal, otherwisetakeUntilDestroyed(). - Keep templates declarative: no complex logic or function calls that do work in templates — use
computedsignals or pure pipes. - Follow the repo's file naming; for new projects the current style guide uses hyphenated names and drops redundant type suffixes.
- Use typed reactive forms for non-trivial forms, with validation in the form model.
- Keep the compiler in strict mode and let types describe the real shape of data.
- Never use any in new code; use unknown and narrow it, or write the precise type.
- Validate data that crosses a trust boundary (HTTP, storage, environment) at runtime before giving it a static type.
Conventions
Accessibility
- Use native elements and Angular CDK a11y utilities (
FocusTrap,LiveAnnouncer) for dialogs and announcements. - Every form control has a label; bind ARIA attributes with
[attr.aria-*]only when native semantics are insufficient. - Restore focus after route changes and dialog close; keep a logical heading order.
- When the change renders UI, use semantic HTML elements and accessible names for controls.
Concurrency
- Choose the flattening operator deliberately (
switchMap,mergeMap,concatMap,exhaustMap) and explain the choice when it is not obvious. - Avoid nested subscribes; compose streams with operators.
- Use async and await consistently; do not mix callbacks and promises in one flow.
- Bound parallel requests to external services and handle partial failure with Promise.allSettled where appropriate.
- Pass AbortSignal to long-running operations so callers can cancel them.
Dependencies
- Upgrade with
ng updatefollowing the Angular update guide; keep all@angular/*packages on the same version. - Prefer Angular CDK/Material or the repo's design system over additional UI libraries.
- Add packages with the repository's package manager and commit its lock file.
- Prefer packages that ship their own types; add @types packages as dev dependencies only.
- Do not add a dependency for a few lines of standard code.
Documentation
- Document public inputs/outputs of shared components with short comments; keep component usage examples in Storybook where the repo has it.
- Document exported functions and types with TSDoc comments that state behaviour, parameters and thrown errors.
- Let types document shapes; comment the reasons behind non-obvious decisions.
- Update the README or docs when commands, configuration or public APIs change.
Errors
- Map HTTP errors to user-visible states in services or interceptors; do not let observables error silently.
- Provide a global
ErrorHandlerfor reporting unexpected errors. - Throw Error objects (or subclasses), never strings or plain objects.
- Narrow caught values from unknown before reading their properties.
- Await or explicitly handle every promise; do not leave floating promises.
- Add context when re-throwing by using the cause option of Error.
Logging
- No
console.login committed code; use the app's logging/monitoring service. - Use the repository's logger rather than console in application code.
- Log structured fields (ids, counts, durations) rather than whole objects.
- Never log tokens, passwords, cookies or personal data.
Naming
- Selectors use the app prefix in kebab-case (
app-user-card); directive selectors in camelCase with the prefix. - Class names in PascalCase; signals and observables named for their value (
user,users$only if the repo uses the$suffix). - One concept per file; file names in kebab-case matching the class.
- Use camelCase for variables and functions, PascalCase for types, classes and enums, and UPPER_SNAKE_CASE for true constants.
- Do not prefix interfaces with I or types with T.
- Name files the way the repository already does (kebab-case or camelCase), consistently.
Performance
- OnPush plus signals so change detection checks only what changed.
- Always give
@fora meaningfultrack(an id), never$indexfor mutable lists. - Use pure pipes or
computedrather than method calls in templates. - Avoid blocking the event loop with synchronous I/O or heavy CPU work in request handlers.
- Run independent async operations concurrently with Promise.all, with a bound on concurrency.
- Avoid repeated work inside loops and render paths; memoise only after measuring.
Security
- Rely on Angular's built-in sanitisation; never call
bypassSecurityTrust*with untrusted values. - Do not use
innerHTMLbindings with user content unless sanitised; avoid direct DOM manipulation viaElementRef.nativeElement. - Use
HttpClientXSRF support for cookie-authenticated APIs; keep tokens out of local storage where the app can use HttpOnly cookies. - Validate and limit all request input with a schema before use.
- Never pass user input to eval, new Function, child_process with a shell, or dynamic require.
- Use parameterised queries or the ORM's query builder for databases.
- Read secrets from environment or a secret store and never ship them to browser bundles.
- Escape or sanitise data rendered as HTML; prefer the framework's safe rendering.
Structure
- Organise by feature area, not by type; keep a component's template, styles and spec beside it.
- Use
providedIn: 'root'for app-wide services; provide feature-scoped services in the route'sproviders. - Configure the app with
bootstrapApplicationandprovide*functions inapp.config.ts. - Keep one responsibility per module and export only what other modules need.
- Use import type for imports used only as types.
- Prefer named exports over default exports unless the framework requires a default.
- Follow the repository's path aliases and module system (ESM or CommonJS); do not mix them.
Testing
- Use TestBed with standalone components (
imports: [MyComponent]) andprovideHttpClientTesting()for HTTP. - Prefer component harnesses (Angular CDK/Material) or role-based queries (Testing Library) over CSS selectors.
- Test signals and services directly without TestBed where they have no Angular dependencies.
- Unit-test services, pipes and signals without TestBed where possible; integration-test components with their template, child components and
provideHttpClientTesting(). - Spec files sit beside the code (
user-card.component.spec.ts) and test titles state the behaviour. - Done means tested: new or changed behaviour ships with tests in the same change, and a fixed bug gets a regression test that fails without the fix.
- Use the repository's test runner (for example Vitest or Jest) and its file naming.
- Test behaviour through public functions; avoid testing private details.
Review checklist
- Are new components standalone and OnPush?
- Do new components use
input()/output()/signals rather than decorators and mutable fields? - Do templates use
@if/@forwith a meaningfultrackexpression? - Does every subscription end with the component (async pipe, toSignal, takeUntilDestroyed)?
- Are there no nested subscribes and is the flattening operator appropriate?
- Is business logic and HTTP access in services rather than components?
- Are template expressions free of method calls that do work?
- Is
bypassSecurityTrust*absent or justified with constant input? - Are feature routes lazy-loaded where appropriate?
- Do tests use TestBed with standalone imports or harnesses and avoid CSS-selector coupling?
- Are form controls labelled and dialogs focus-managed?
- Does the change compile under the repository's strict compiler settings without new any or ts-ignore?
Quality checks (run when your repository has the tool configured)
| Check | Kind | Tool | Blocks the pull request |
|---|---|---|---|
ng-lint |
lint | ng lint | no |
tsc |
typecheck | tsc | no |
eslint |
lint | eslint | no |
biome |
lint | biome | no |
prettier |
format | prettier | no |
npm-audit |
security | npm audit | no |
gitleaks |
security | gitleaks | no |