placement
shared code sitting in a `common/` above or below the lowest folder that holds everyone using it
Agent checkadvisory by defaultbasecode
The argument
Rules
- Keep
common/close to consumers — the lowest level where all dependents can reach it - Promote when reused — move to a parent
common/only when 2+ modules at that level need it - Avoid circular dependencies — update imports when promoting; verify no cycles
common/is always typed, never flat — every file lives under a type subfolder from the first file. The type vocabulary is a closed list:utils/,types/,constants/,services/, plus domain folders graduated per Domain Folders. Never invent a new type folder; never place a file directly incommon/.- Graduate, don't pre-build — a concept becomes a folder only when it needs private companions. This ceremony ban does not apply to
common/'s type subfolders: that skeleton is always built, so placement is a no-decision.
| Folder | Contents |
|---|---|
utils/ | Stateless functions — pure or IO-performing (formatDate(), loadConfig()) |
types/ | Type-level declarations (CopyResult) |
constants/ | Value and named constants (defaultConfig, Action) |
services/ | Stateful classes with methods (ApiClient) |
Example
src/
├─ common/ # shared across ALL modules
│ ├─ utils/ # (formatDate.ts)
│ ├─ types/
│ ├─ services/
│ ├─ formatting/ # domain folder: 2+ related pure functions
├─ featureA/
│ ├─ common/ # shared within featureA only
│ │ ├─ utils/
│ │ ├─ types/
│ └─ featureA.ts
Reading the hierarchy: src/common/ serves every feature; src/featureA/common/ serves only featureA. If a helper there is later needed by featureB, promote it to src/common/utils/.
A file in a folder's common/ may still be what the folder offers others — a type its exported functions take, say. Code outside the folder imports that from its own file like any other export, and it stays where it is. What moves is a general helper the folder merely happened to hold first: once a second folder needs it for its own work, it belongs in the common/ of the lowest folder containing both.
The proof
fail
import { round } from '../pay/common/utils/round';
export const bill = round;
pass
Turn it down
Both lines go in your lightsout.config.json.
"standards-checks": { "placement": "advisory" }"standards-checks": { "placement": "off" }