angular/angular
Patterns and conventions
What review enforces. The canonical doc is contributing-docs/coding-standards.md; this page summarizes the patterns most commonly cited in PR feedback.
Code style
- Base style: Google JavaScript / TypeScript Style Guide.
- Wrap at 100 columns.
- Format with
prettier. CI fails ifpnpm ng-dev format changed --checkfails. - Lint with
pnpm lint(tslint + format-check). Thetslintruleset is intentional — replacement with ESLint has been considered but not adopted.
Comments
The team's published rule: comments that say what are nice; comments that say why are invaluable. Useless example:
// Set default tabindex.
if (!attributes['tabindex']) {
element.setAttribute('tabindex', '-1');
}Useful example:
// Unless the user specifies otherwise, the calendar should not be a tab stop.
// This prevents ngAria from overzealously adding a tabindex to anything with an ng-model.
if (!attributes['tabindex']) {
element.setAttribute('tabindex', '-1');
}JSDoc (/** */) for public API descriptions; // for explanations and asides.
API design
Avoid boolean arguments
Don't add boolean arguments that switch behavior; split into two functions. Exception: framework hot paths where a boolean parameter shaves bytes off the bundle.
// Avoid
function getTargetElement(createIfNotFound = false) {
/* ... */
}
// Prefer
function getExistingTargetElement() {
/* ... */
}
function createTargetElement() {
/* ... */
}Optional arguments
Use sparingly. Only when the API genuinely has an optional input — never for implementation convenience.
Getters and setters
Avoid getters/setters except:
- on
@Inputproperties that need a setter, - when API compatibility requires it,
- never longer than three lines (extract to a method).
A getter without a setter is a readonly property in disguise. Use readonly directly.
TypeScript
- Avoid
any. Reach for a generic orunknownfirst. consteverywhere by default. Useletonly where mutation is necessary. Avoidvar.- Use
readonlyfor class members that don't change. try/catchonly when handling a legitimately unexpected error. Eachtry/catchmust be commented with the specific error and why prevention isn't possible.
Naming
- Names describe what the code does, not how it's used.
NonStoringRouteReuseStrategyoverDefaultRouteReuseStrategy. - Prefer full words over abbreviations.
- Booleans use
is/hasprefixes, except on@Input()which uses the natural English form. - Don't suffix Observables with
$. - Classes: PascalCase. Don't end with
Impl. - Interfaces: don't prefix with
I, don't suffix withInterface. - Functions/methods: camelCase. Names describe the action performed (
activateRipple()nothandleClick()). - Constants and DI tokens:
UPPER_SNAKE_CASE. - Test fixtures: descriptive names (
FormGroupWithCheckboxAndRadiosoverComp).
RxJS
- Don't suffix Observables with
$. - When importing
of, alias it:import {of as observableOf} from 'rxjs';to avoid clashing with the destructuredofkeyword in template syntax discussions. - Prefer signals for new reactive state. RxJS remains the standard for HTTP, router, and any observable stream that crosses time.
Iteration
Prefer for and for…of over Array.prototype.forEach. forEach makes debugger stepping harder and adds function-call overhead.
Errors and warnings
- Use
formatRuntimeError(code, message)frompackages/core/src/errors.tsfor runtime issues. The numeric code links the message to its angular.dev documentation page. - New error codes are allocated sequentially in the existing range. Don't reuse retired codes.
- Throw
RuntimeErrorfor hard failures, log viaconsole.warnfor recoverable ones.
Test naming
// Prefer
describe('Router', () => {
describe('with the default route reuse strategy', () => {
it('should not reuse routes upon location change', () => {
/* ... */
});
});
});
// Avoid
describe('Router', () => {
describe('default strategy', () => {
it('should work', () => {
/* ... */
});
});
});Test names should read as a sentence. The standard it('should …', …) form is enforced by review.
Public API surface
Adding or changing anything that lands in a public index.ts of a package requires:
- A JSDoc on the symbol describing what it does and what each parameter is.
- An update to the matching
goldens/public-api/<package>.api.md(pnpm public-api:update). - A
BREAKING CHANGE:footer if the change is breaking.
Removing anything from the public API requires:
- A deprecation in a previous major (
@deprecatedJSDoc +DEPRECATED:footer in commit). - An automated migration in
packages/core/schematics/migrations/if the change requires user code edits. - The actual removal in the next major.
The full process is documented in contributing-docs/public-api-surface.md.
@experimental and @developerPreview
@developerPreview— a feature exposed for early adoption. Public API rules apply but the team can change it without a deprecation cycle.@experimental— older marker, similar semantics, used less often now.
The full distinction is in contributing-docs/dev_preview_and_experimental.md.
Cross-cutting "do not" list
Things that come up in review repeatedly:
- Don't import from another package's
src/directly. Cross-package imports must go through the publicindex.ts. - Don't use
privateto mean "internal to the framework". TypeScript'sprivateis enforced at compile time only; framework-internal symbols use theɵprefix or thecore_private_export.tsre-export pattern. - Don't add new
.ngfactoryreferences. They're View Engine residue. - Don't introduce new circular module imports.
pnpm ts-circular-deps:checkruns in CI. - Don't write
it('works', …). Use a sentence.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.