9. Coding standards¶
Every rule on this page comes from a configuration file or workflow in the repository. Where a tool enforces the rule, run the tool; where only review enforces it, the reviewer checks it.
9.1. Formatting¶
Prettier formats both packages, with a different configuration in each. Run each package’s formatter only from inside that package. Running the client configuration over API files, or the reverse, rewrites every file it touches.
Setting |
|
|
|---|---|---|
Indentation |
2 spaces |
4 spaces |
Trailing commas |
none |
es5 |
Print width |
250 |
160 |
Arrow function parentheses |
always (default) |
avoid |
Quotes and semicolons |
single quotes, semicolons |
single quotes, semicolons |
Parser overrides |
|
none |
The commands are the same in both packages. Use --cache runs for speed and the check for verification:
npm run format
npm run format:check
api/.prettierignore excludes Services/migrations/, tls/, every .json, .yml, and .yaml file (which includes the OpenAPI contract), and the environment files. Migration SQL and the contract are therefore formatted by hand. client/.prettierignore excludes generated output, editor folders, and the lockfile.
Line endings are LF in the repository. There is no .gitattributes file, so Windows checkouts rely on core.autocrlf. The only .editorconfig is client/.editorconfig: two-space indentation, UTF-8, a final newline, trimmed trailing whitespace, and single quotes for TypeScript.
9.2. Linting¶
Both packages use ESLint flat configuration with eslint-config-prettier applied last, so formatting is never a lint error.
npm run lint
npm run lint:fix
Client rules that shape code, from client/eslint.config.mjs:
Rule |
Effect |
|---|---|
|
Element selectors, prefix |
|
Attribute selectors, prefix |
|
Classes end in |
|
Public static fields, then static fields, then instance fields, then public instance methods. |
|
Unused values are errors unless their name starts with |
|
A blank line after a group of declarations, before every |
|
Arrow functions drop braces when the body is a single expression. |
|
Templates use |
Turned off on purpose |
|
The client configuration is type-aware (parserOptions.project), so npm run lint needs the TypeScript project to compile.
API rules, from api/eslint.config.js: the ESLint recommended set, the eslint-plugin-n script preset, no-unused-vars with the same _ convention (which is why unused handler arguments are written _req, _res, _next), and n/exports-style, which is why every controller and service exports with module.exports.name = ... rather than a single object. healthcheck.js and utils/state.js are allowed to call process.exit. The ignore list covers tls/, .sonar/, the lockfile, and Services/migrations/lib/.
9.3. TypeScript settings¶
client/tsconfig.json sets strict to false and then turns on most of the individual checks. The ones that change how you write code:
Option |
Consequence |
|---|---|
|
Values behind an index signature are read with brackets: |
|
An indexed array or record value has type |
|
Unused symbols fail the build; prefix intentionally unused parameters with |
|
Every code path returns a value; |
|
Overriding members carry the |
|
Class fields may be declared without an initializer. |
|
Templates are type-checked; a wrong binding type is a build error. |
strictNullChecks is not enabled, so null and undefined are not tracked in ordinary types. Do not rely on the compiler to catch a missing null check.
9.4. Naming¶
Kind |
Convention |
|---|---|
Controllers |
|
Services |
|
Models |
|
Migrations |
|
Tests |
|
Kind |
Convention |
|---|---|
Components |
A kebab-case folder holding |
Selectors |
|
Routing |
One route file per feature: |
Services and models |
|
Utilities |
|
9.6. Angular rules¶
Every component is standalone and declares
changeDetection: ChangeDetectionStrategy.OnPush. Angular 22 defaults to OnPush; the declaration makes the intent visible.Anything a template reads is a signal. A plain field assigned inside an RxJS subscription does not re-render under OnPush; there is no error, only a stale view.
Dependencies are injected with
inject()in field initializers, not through constructor parameters.Subscriptions end with
takeUntilDestroyed(this.destroyRef).Templates use the built-in control flow (
@if,@for,@switch). No*ngIfor*ngForremains in the code base.PrimeNG components and the theme system come first. Do not add another component library.
Feature code lives in
pages/<feature>/; anything used by two features moves tocommon/.
See Frontend guide for the reasoning behind each rule.
9.7. Backend rules¶
Define or change the operation in
api/specification/C-PAT.yamlfirst. There are no Express routes outside the contract.Controllers stay thin: a
tryblock that calls one service function and sends the result, and acatchblock that callssendError.Business logic and SQL live in
Services/. SQL is parameterized with?placeholders and schema-qualified with${config.database.schema}. Never interpolate request data into a query.Multi-statement writes use
dbUtils.withTransaction.Failures throw a subclass of
SmErrorfromapi/utils/error.js; services do not return error objects.Log through
api/utils/logger.js. The logger replacesconsole.logand its siblings at startup and reports stray calls as errors.Configuration comes from environment variables through
api/utils/config.js. No configuration files, no deployment-specific code paths.
See Backend guide for the reasoning behind each rule.
9.8. Commits and branches¶
Commit subjects follow the Conventional Commits form, type: summary or type(scope): summary, with the types the release workflow recognizes: build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test. Scopes in use are api, client, and docs. The release workflow groups feat subjects under New Features, fix under Bug Fixes, and the rest under Other Changes, so the subject is written for a reader of the release notes. Nothing enforces the form automatically; reviewers do.
Branches start from development and are named for the change: feature/<name>, fix/<name>, refactor/<name>. See Contributing and Release process.
9.9. Pre-submission checklist¶
Run the following before opening a pull request. Every command must succeed.
cd client
npm run lint:fix && npm run format && npm run lint && npm run format:check
npm run test:run
npm run build
cd ../api
npm run format && npm run format:check
npm run lint
npm run lint:spec
npm test
If the change touches docs/, build the documentation as well. See Documentation.