TypeScript
Always onThe rules every repo gets: architecture, style, documentation and unit testing.
- Rules
- 96
- Deterministic checks
- 51
- Agent checks
- 45
Architecture Decisions10
- modules-and-the-graduation-rulea folder created for a concept with no private companions, or a folder's internals reached from outside itAgentAdvises
- code-placement-philosophyshared code placed above the lowest level that reaches it, or landing in
common/as a folder-moduleAgentAdvises - thin-wrapper-functionsa function that only renames parameters or forwards to anotherAgentAdvises
- dead-exportan export nothing else referencesDeterministicBlocks
- premature-abstractionan abstraction built before two or three concrete uses called for itAgentAdvises
- type-alias-indirectiona file that exists only to alias another typeDeterministicAdvises
- circular-dependenciestwo modules that import each otherAgentAdvises
- duplicate-code-blockthe same block of code written out in two or more filesDeterministicAdvises
- duplicate-function-bodytwo functions with the same body under different variable namesDeterministicBlocks
- duplicate-export-namethe same export name declared in more than one placeDeterministicAdvises
Folder Structure16
- placementshared code sitting in a
common/above or below the lowest folder that holds everyone using itAgentAdvises - file-directly-in-commona file placed directly in
common/instead of under a type folderDeterministicAdvises - graduating-out-of-commona shared concept with companions only it uses left inside
common/instead of graduating to its own folderAgentAdvises - top-level-domain-nounsa top-level folder naming a technical layer rather than a capability the product hasAgentAdvises
- banned-folder-namea folder named for the kind of code it holds (
helpers/,utils/outsidecommon/) instead of the domain it servesDeterministicAdvises - growing-without-new-rulesa level grown by inventing a new kind of place instead of graduating a file or consolidating siblingsAgentAdvises
- folder-sizemore files sitting in one flat folder than the cap allowsDeterministicAdvises
- fractal-skeletona graduated feature folder inventing its own internal layoutAgentAdvises
- per-folder-readmesa folder README restating structure these rules already giveAgentAdvises
- folder-casinga folder whose casing is none of the three the document allowsDeterministicAdvises
- ungrouped-domain-utilstwo or more utils about the same subject left loose in
utils/instead of grouped in a folder named for itDeterministicAdvises - single-file-domain-foldera graduated domain folder holding one fileDeterministicAdvises
- case-collisiontwo sibling paths that differ only by casingDeterministicBlocks
- domain-folder-is-not-a-modulea domain folder under
common/carrying a file that serves only its siblingsAgentAdvises - cross-package-sharingcode two packages need duplicated per package instead of moved to the shared oneAgentAdvises
- shared-package-criteriaa framework-dependent or single-consumer item placed in the shared packageAgentAdvises
TypeScript Documentation Style Guide4
- when-to-documenta doc comment on an export whose name and types already say it, or an inline comment narrating the next lineAgentAdvises
- doc-elementsa tag carrying what its type already says, or a description repeating the codeAgentAdvises
- brittle-doc-tagsa doc comment carrying a tag git, TypeScript or the issue tracker already ownsDeterministicBlocks
- params-interface-docsa doc comment on a function's local Params interface, or on every property inside itAgentAdvises
Casing1
File Naming1
Lint, Formatting & Mechanically-Enforced Rules3
- formatter-configformatting written against a convention the project's own formatter config does not useAgentAdvises
- import-type-onlyan import used only in type positions declared without
import typeDeterministicBlocks - no-anyan
anyannotation with no lint-suppression comment licensing itDeterministicBlocks
Naming3
- naming-consistencya second convention introduced into a domain that already standardized on oneAgentAdvises
- synonym-export-nametwo exports that name the same concept with different verbs —
getUserbesidefetchUseris usually the same code written twiceDeterministicAdvises - naming-for-reusea name that says where a value is used rather than what it isAgentAdvises
Variable Declaration2
Classes6
- class-bright-linea class written where none of the four criteria that license one holdAgentAdvises
- banned-class-shapesa class that is only static members, or one stateless methodDeterministicAdvises
- class-syntaxa class whose constructor takes positional arguments, or whose methods declare separate param interfacesAgentAdvises
- class-graduationa folder created for a class that bundles no private companionsAgentAdvises
- class-surfacea method added to a class for logic that never touches the class's stateAgentAdvises
- class-inheritancea class extending anything other than an ErrorDeterministicAdvises
Functions5
- object-argsa function taking positional arguments where no external contract dictates the shapeAgentAdvises
- single-returnbusiness logic returning from several branches instead of once at the endAgentAdvises
- private-helper-colocationa helper a second file needs while it still sits unexported in the firstAgentAdvises
- function-sizea function, hook or component over its line capDeterministicAdvises
- file-sizea file over the standards line capDeterministicAdvises
Named Constants6
- bare-string-uniona string-literal union exported with no
constobject behind itDeterministicAdvises - named-constant-casinga named constant in camelCase, or a lone value constant in PascalCaseAgentAdvises
- named-constant-boundariesan incoming string cast into the union at a boundary instead of validated into itAgentAdvises
- discriminant-const-objecta discriminant field typed as a raw string literal instead of the
constobject's memberDeterministicAdvises - props-union-exemptiona domain value claiming the props exemption while it also appears in domain logicAgentAdvises
- derived-lookup-mapa constant that merely uses the union kept in the
constobject's fileAgentAdvises
Import Path Strategy2
Module Boundaries & Exports6
- import-through-indexan import through an
index.tsof the importer's own package instead of from the file that declares the nameDeterministicAdvises - module-exportsan item exported somewhere other than the line it is defined onAgentAdvises
- barrel-staran index file re-exporting with
export *instead of naming what it publishesDeterministicAdvises - internal-import-from-outsidea file inside an
internal/folder imported from outside the folder that holds itDeterministicAdvises - folder-index-filean
index.tsin a folder that is not a package's entryDeterministicAdvises - code-in-index-filean index file holding code instead of only re-export linesDeterministicAdvises
One Export Per File1
Type & Constant Placement2
Return Types — Explicit on Exports, Inferred Internally1
Type assertions (as)1
Unit Testing26
- precedence-in-repos-with-older-teststhese standards describe the tests you write, not a mandate to renovate the ones already thereAgentAdvises
- module-boundary-testingcoverage added file-by-file when driving the module's public API would pin the same behavior — boundary tests are the default, not the mandateAgentAdvises
- test-in-tests-foldera unit test in a separate tests directory instead of beside its subjectDeterministicAdvises
- test-not-beside-subjecta test file whose folder holds no source file it could be testingDeterministicAdvises
- test-only-exportan export only its own tests referenceDeterministicAdvises
- test-support-in-srcshared test fixtures or mocks living under
src/DeterministicAdvises - test-nested-describea nested
describeoutside thewhen …/for …exceptionDeterministicAdvises - files-that-must-not-have-dedicated-testsa dedicated test file on a source file that holds no runtime logicAgentAdvises
- test-file-sizea test file over the test-file line capDeterministicAdvises
- test-structure-arrange-act-assert-with-setup-factoriesa test that does not read as arrange, act, assert with its arrangement in a
setup()factoryAgentAdvises - test-shared-leta
letreassigned in a beforeEach — mutable state shared across testsDeterministicBlocks - test-assert-in-hookan assertion in a beforeEach instead of the test bodyDeterministicBlocks
- test-multiple-setupsmore than one setup factory call in one testDeterministicAdvises
- test-strict-equal-matcher
toStrictEqualwith an asymmetric matcher — strict in name onlyDeterministicBlocks - test-never-passing-assertionan assertion written so that it can never pass —
expect.objectContainingpairing a key withundefinedDeterministicBlocks - assertions-pin-contractsan assertion that restates the module under test instead of stating its contract independentlyAgentAdvises
- setup-factoriesa setup factory that arranges through anything but its own parametersAgentAdvises
- oversized-setup-factorya setup factory grown past its parameter capDeterministicAdvises
- test-mock-prefixa module-scope mock variable without the
mockprefix Jest hoisting needsDeterministicBlocks - test-mock-return-in-hooka mock return value set in a beforeEach instead of the setup factoryDeterministicAdvises
- test-mock-untypeda
jest.fn()with no generic, so the spy does not match the real signatureDeterministicBlocks - test-mock-wrapper-untypeda
jest.mockfactory wrapper that discards the arguments it is called withDeterministicBlocks - jest-spyon-vs-jest-mock
jest.mockwhere a spy on the object already held would do, or the reverseAgentAdvises - asyncan async unit arranged or asserted without the resolved/rejected forms the document namesAgentAdvises
- import-time-side-effectsa module that acts at import time tested without a fresh instance or its own environmentAgentAdvises
- test-manual-mock-cleanupmanual mock cleanup in a lifecycle hook, which the Jest config already doesDeterministicAdvises