Architectural Discipline (First Principles)
Core Directives
- Patternization: A unified, simpler whole beats a fragmented system of
locally perfect solutions. Accept local suboptimality for universal
patterns.
- Minimalism: Smallest viable solution. ZERO speculative extensibility.
- Traceability: Names reflect architectural layer, domain role, and
technical purpose.
- Dependency Discipline: Graphs MUST be directed, acyclic, shallow. Cycles
forbidden. Depth is cost.
1. Minimalism & Abstraction
- YAGNI: No speculative features or extensibility hooks.
- Rule of 3: Wait for three proven instances before abstracting. Prefer
copying < 20 lines over premature abstraction.
- DRY (knowledge, not shape): A business rule, constant, or schema has
exactly one authoritative representation. Code-shape duplication defers to
Rule of 3.
- Frame-check before execute: When an issue, spec, or PRD prescribes
implementation steps (a numbered "Implementation Approach" section, a
multi-step task list, a build/CI plumbing plan), DO NOT start by executing
step 1. First run the necessity gate from
functionality-complexity-tradeoff
§1 against the framing the steps assume — what problem is this code actually
addressing, does that problem still occur in this stack, is it already owned
by another layer? Issue authors prescribe solutions; the gate asks whether the
prescription matches a problem we have. A prescribed plan exceeding 3 steps or
introducing a novel abstraction is the strongest trigger for this check.
2. Consistency & Coupling
- Eventual Consistency by Default: Strong consistency couples components.
Accept idempotency / compensation to preserve modularity.
- Full Migration: When adopting a new pattern, migrate all sibling
components in the same PR — but always ask the user and pick a pattern
that fits both new and existing logic.
- Dependency Inversion: Domain logic depends on abstractions, never concrete
implementations.
3. Functional Core
- Pure Domain Logic, I/O at the Edges: Business logic is pure, side-effect
free, environment-agnostic. External systems live at the edges.
- Testability: A pure core is unit-testable without mocks. If the domain
needs mocks, purity has been violated.
4. Modularity
- SoC: One concern per module; cross-cutting concerns are extracted, not
interleaved.
- SRP: One reason to change per module. Two forces of change → split.
- Capability Boundary = Module Boundary: A capability with its own domain
name, lifecycle, dependency surface, test surface, or reason to change gets
its own module directory. Do not group multiple atomic capabilities in one
subsystem directory unless they form a higher-level capability with a single
public interface and shared change reason.
- High Cohesion, Loose Coupling: Internals tightly related; external
dependencies minimized and abstracted.
- Interface Discipline:
- Caller: depend on the contract, never the implementation.
- Module: internals encapsulated; the interface is the only access point.
- Designer: expose everything every caller needs and only what every
caller needs.
Review check: If a directory contains multiple named capabilities, require
one of: a single facade/interface proving they are one higher-level capability,
or a split into capability-named module directories.
5. Resilience
- Fail Fast: Validate and sanitize inputs at all system or atomicity
boundaries.
- Idempotency: Safe under repeated execution; succeeds when the desired
state already holds. Does not suppress errors that prevent reaching it.
- Statelessness: Prefer stateless services.
- Failure Classification: Categorize each external call as hard (blocks
subsequent steps) or best-effort (logged, no cascade) before
implementation.
- Atomicity: Decide whether partial success is acceptable or rollback is
required.
- State Visibility: Log decision and outcome at each step.
6. Naming & Traceability
- Domain-Driven Names: Every function, variable, directory reveals
architectural layer, domain role, and technical purpose.
utils / helpers
fail this test.
- Self-Documenting Structure: Directory and filename alone should reveal
architectural boundaries and business rules.
7. Concurrency & Shared Mutable State
Every shared mutable state must declare its concurrency model:
- Per-instance (multi-tab) → use
navigator.locks or BroadcastChannel.
- Per-tab → fine; document in JSDoc.
- Global → atomic writes or locks.
Review check: if state is modified after an await, ask: "is this guarded
against concurrent mutation?"
8. Layer Self-Sufficiency
- Controls complete at their own layer: a control a layer owns must hold on
that layer alone. One that works only because a lower layer limits who can
reach it is inherited, not implemented.
- Assume the layer below is absent: every authentication and authorization
decision must hold with the endpoint publicly reachable. Network isolation,
private connectivity, and firewall placement are additional layers, never the
control.
- Ambient guarantees are invisible dependencies: the assumption lives
outside the codebase, so the deployment or infrastructure change that
invalidates it never appears in a diff, review, or test of the code relying
on it.
- Name the reason for every gap: when a control is weakened, deferred, or
dropped, state why. A reason that cites a property of a lower layer or the
deployment environment means the control is missing, not satisfied.
Review check: for each control, ask "does this still hold when the layer
below it disappears?" A "no" is a defect in the layer under review, never a
requirement on the layer below.
9. Integration Discipline
Rules for edges that cross an application boundary: a different deployable,
data store, team, or lifecycle.
- Smart endpoints, dumb pipes: The transport (gateway, proxy, queue,
topic, bus) decouples parties in location and time and does nothing else.
Business logic, validation, enrichment, transformation, storage, replay,
and access decisions live in the endpoints, never in the pipe.
- Producers are consumer-agnostic; consumers transform: A producer
publishes its own domain representation once. Each consumer maps it into
its own model. A canonical or unified intermediate model shared across
applications couples every party to every change; do not introduce one.
- No peer internals: An application reaches another only through that
application's published contract (API, event, queue, file exchange), never
its database, file system, or internal modules. Reaching past the contract
is the integration form of the interface violation in §4.
- Untrusted network: Every cross-application edge is designed for the
open internet. The receiving endpoint authenticates, authorizes, and
validates on its own; §8 governs what the pipe's placement can and cannot
satisfy.
- Asynchronous where the caller can continue: Use a queue or topic when
the caller does not need the answer to proceed; reserve synchronous
request-response for when it does. Fire-and-forget only where loss is
acceptable and stated.
- Contracts expose domain, not implementation: Payloads carry domain
entities, never storage rows, join tables, or internal identifiers. A change
to a shared payload shape follows
evolutionary-database-design.
Review check: every rule above needs a question, or the check passes while
the rule fails. For each cross-application edge, ask all four:
- "Which side transforms, and what does the pipe do besides carry?" An
answer that names the pipe is a defect in the endpoint.
- "Does the receiver authenticate, authorize, and validate on its own, as if
the network were public?"
- "Does the caller wait for an answer it does not need, and what happens to
the message when the pipe fails after the caller's own write?" A
synchronous edge that leaves a stored record with no way to complete or
retry it is an unrecoverable half-state, not a failed call.
- "Does the edge reach past the peer's published contract, and does the
payload carry domain entities rather than storage rows or internal
identifiers?"
[!IMPORTANT] Complexity Warning: If a solution violates any guideline
above, state: "Complexity Warning: introduces [X]. A simpler alternative is
[Y]." If the violation is non-trivial, see structural-simplification §8
Decision Protocol for a per-axis comparison before accepting it.
10. Enforcement Handoff
Use architecture-as-code only for constraints that can be enforced as import
or dependency rules. Do not duplicate this skill's principles there; hand off
the specific rule to encode.
Examples:
Principle: DI / functional core
Constraint: domain must not import infrastructure
Enforcement: add architecture rule: forbid <domain-component> -> <infra-component>
Principle: interface discipline
Constraint: external callers use the facade only
Enforcement: add architecture rule: forbid * -> <module-internal-*>, except <module-*>
Principle: integration discipline
Constraint: no module reaches a peer application's internals
Enforcement: add architecture rule: forbid * -> <peer-storage-client | peer-internal-*>, except <integration-adapter-*>
A principle can also settle as no handoff. Record that outcome rather than
omitting it:
Principle: layer self-sufficiency
Constraint: the control holds with the layer below absent
Enforcement: none - not an import or dependency edge; verify by exercising the
component without that layer (`defect-shift-left`)
11. Output Contract
When this skill changes or rejects a design, emit a coder-facing decision
record:
Subject: <module / service / abstraction / PR / code path>
Decision: Proceed | Simplify | Split | Inline | Reject | Defer
Principle: <YAGNI | Rule of 3 | DRY | SoC | SRP | capability-boundary | DI | fail-fast | idempotency | atomicity | integration | layer-self-sufficiency | naming | concurrency>
Evidence: <callers, imports, tests, runtime invariant, or file paths checked>
Enforcement: <none | add architecture rule: constraint | update architecture rule: constraint>
Next action: <edit, delete, extract, add test, add lint rule, or ask user>
Verification: <command / review check / Not run + reason>
See also
functionality-complexity-tradeoff — necessity gate and worth ledger applied to individual decisions.
structural-simplification — per-axis complexity comparison (D, K, P, n).
morphogenetic-architecture — declared placement, observed coupling fields, and topology evolution.
architecture-as-code — consumes explicit Enforcement handoffs and turns enforceable dependency constraints into lint rules.
evolutionary-database-design — staged, compatible change to a payload or stored shape shared across an integration edge.
1---2name: architecture-guidelines3description: First-principles architectural rules for module/service/abstraction design: minimalism, modularity, functional core, resilience, layer self-sufficiency, integration, naming, and concurrency. TRIGGER when introducing a module/service/abstraction, refactoring across module boundaries, applying SOLID, deciding whether a control may rely on the layer beneath it, designing an integration edge between applications or services, or reviewing architectural concerns (purity, idempotency, naming, fail-fast). SKIP for bug fixes within an existing module, content/copy edits, CSS-only changes, dependency bumps, and trivial renames. Emits an `Enforcement` handoff to `architecture-as-code` when a design decision yields an enforceable dependency constraint.4---56# Architectural Discipline (First Principles)78> **Core Directives**9>10> - **Patternization**: A unified, simpler whole beats a fragmented system of11> locally perfect solutions. Accept local suboptimality for universal12> patterns.13> - **Minimalism**: Smallest viable solution. ZERO speculative extensibility.14> - **Traceability**: Names reflect architectural layer, domain role, and15> technical purpose.16> - **Dependency Discipline**: Graphs MUST be directed, acyclic, shallow. Cycles17> forbidden. Depth is cost.1819## 1. Minimalism & Abstraction2021- **YAGNI**: No speculative features or extensibility hooks.22- **Rule of 3**: Wait for three proven instances before abstracting. Prefer23 copying < 20 lines over premature abstraction.24- **DRY (knowledge, not shape)**: A business rule, constant, or schema has25 exactly one authoritative representation. Code-shape duplication defers to26 Rule of 3.27- **Frame-check before execute**: When an issue, spec, or PRD prescribes28 implementation steps (a numbered "Implementation Approach" section, a29 multi-step task list, a build/CI plumbing plan), DO NOT start by executing30 step 1. First run the necessity gate from `functionality-complexity-tradeoff`31 §1 against the _framing_ the steps assume — what problem is this code actually32 addressing, does that problem still occur in this stack, is it already owned33 by another layer? Issue authors prescribe solutions; the gate asks whether the34 prescription matches a problem we have. A prescribed plan exceeding 3 steps or35 introducing a novel abstraction is the strongest trigger for this check.3637## 2. Consistency & Coupling3839- **Eventual Consistency by Default**: Strong consistency couples components.40 Accept idempotency / compensation to preserve modularity.41- **Full Migration**: When adopting a new pattern, migrate all sibling42 components in the same PR — but **always ask the user** and pick a pattern43 that fits both new and existing logic.44- **Dependency Inversion**: Domain logic depends on abstractions, never concrete45 implementations.4647## 3. Functional Core4849- **Pure Domain Logic, I/O at the Edges**: Business logic is pure, side-effect50 free, environment-agnostic. External systems live at the edges.51- **Testability**: A pure core is unit-testable without mocks. If the domain52 needs mocks, purity has been violated.5354## 4. Modularity5556- **SoC**: One concern per module; cross-cutting concerns are extracted, not57 interleaved.58- **SRP**: One reason to change per module. Two forces of change → split.59- **Capability Boundary = Module Boundary**: A capability with its own domain60 name, lifecycle, dependency surface, test surface, or reason to change gets61 its own module directory. Do not group multiple atomic capabilities in one62 subsystem directory unless they form a higher-level capability with a single63 public interface and shared change reason.64- **High Cohesion, Loose Coupling**: Internals tightly related; external65 dependencies minimized and abstracted.66- **Interface Discipline**:67 - _Caller_: depend on the contract, never the implementation.68 - _Module_: internals encapsulated; the interface is the only access point.69 - _Designer_: expose everything every caller needs and only what every70 caller needs.7172**Review check:** If a directory contains multiple named capabilities, require73one of: a single facade/interface proving they are one higher-level capability,74or a split into capability-named module directories.7576## 5. Resilience7778- **Fail Fast**: Validate and sanitize inputs at all system or atomicity79 boundaries.80- **Idempotency**: Safe under repeated execution; succeeds when the desired81 state already holds. Does not suppress errors that prevent reaching it.82- **Statelessness**: Prefer stateless services.83- **Failure Classification**: Categorize each external call as **hard** (blocks84 subsequent steps) or **best-effort** (logged, no cascade) before85 implementation.86- **Atomicity**: Decide whether partial success is acceptable or rollback is87 required.88- **State Visibility**: Log decision and outcome at each step.8990## 6. Naming & Traceability9192- **Domain-Driven Names**: Every function, variable, directory reveals93 architectural layer, domain role, and technical purpose. `utils` / `helpers`94 fail this test.95- **Self-Documenting Structure**: Directory and filename alone should reveal96 architectural boundaries and business rules.9798## 7. Concurrency & Shared Mutable State99100Every shared mutable state must declare its concurrency model:101102- **Per-instance** (multi-tab) → use `navigator.locks` or `BroadcastChannel`.103- **Per-tab** → fine; document in JSDoc.104- **Global** → atomic writes or locks.105106**Review check:** if state is modified after an `await`, ask: _"is this guarded107against concurrent mutation?"_108109## 8. Layer Self-Sufficiency110111- **Controls complete at their own layer**: a control a layer owns must hold on112 that layer alone. One that works only because a lower layer limits who can113 reach it is inherited, not implemented.114- **Assume the layer below is absent**: every authentication and authorization115 decision must hold with the endpoint publicly reachable. Network isolation,116 private connectivity, and firewall placement are additional layers, never the117 control.118- **Ambient guarantees are invisible dependencies**: the assumption lives119 outside the codebase, so the deployment or infrastructure change that120 invalidates it never appears in a diff, review, or test of the code relying121 on it.122- **Name the reason for every gap**: when a control is weakened, deferred, or123 dropped, state why. A reason that cites a property of a lower layer or the124 deployment environment means the control is missing, not satisfied.125126**Review check:** for each control, ask _"does this still hold when the layer127below it disappears?"_ A "no" is a defect in the layer under review, never a128requirement on the layer below.129130## 9. Integration Discipline131132Rules for edges that cross an application boundary: a different deployable,133data store, team, or lifecycle.134135- **Smart endpoints, dumb pipes**: The transport (gateway, proxy, queue,136 topic, bus) decouples parties in location and time and does nothing else.137 Business logic, validation, enrichment, transformation, storage, replay,138 and access decisions live in the endpoints, never in the pipe.139- **Producers are consumer-agnostic; consumers transform**: A producer140 publishes its own domain representation once. Each consumer maps it into141 its own model. A canonical or unified intermediate model shared across142 applications couples every party to every change; do not introduce one.143- **No peer internals**: An application reaches another only through that144 application's published contract (API, event, queue, file exchange), never145 its database, file system, or internal modules. Reaching past the contract146 is the integration form of the interface violation in §4.147- **Untrusted network**: Every cross-application edge is designed for the148 open internet. The receiving endpoint authenticates, authorizes, and149 validates on its own; §8 governs what the pipe's placement can and cannot150 satisfy.151- **Asynchronous where the caller can continue**: Use a queue or topic when152 the caller does not need the answer to proceed; reserve synchronous153 request-response for when it does. Fire-and-forget only where loss is154 acceptable and stated.155- **Contracts expose domain, not implementation**: Payloads carry domain156 entities, never storage rows, join tables, or internal identifiers. A change157 to a shared payload shape follows `evolutionary-database-design`.158159**Review check:** every rule above needs a question, or the check passes while160the rule fails. For each cross-application edge, ask all four:1611621. _"Which side transforms, and what does the pipe do besides carry?"_ An163 answer that names the pipe is a defect in the endpoint.1642. _"Does the receiver authenticate, authorize, and validate on its own, as if165 the network were public?"_1663. _"Does the caller wait for an answer it does not need, and what happens to167 the message when the pipe fails after the caller's own write?"_ A168 synchronous edge that leaves a stored record with no way to complete or169 retry it is an unrecoverable half-state, not a failed call.1704. _"Does the edge reach past the peer's published contract, and does the171 payload carry domain entities rather than storage rows or internal172 identifiers?"_173174> [!IMPORTANT] **Complexity Warning**: If a solution violates any guideline175> above, state: _"Complexity Warning: introduces [X]. A simpler alternative is176> [Y]."_ If the violation is non-trivial, see `structural-simplification` §8177> Decision Protocol for a per-axis comparison before accepting it.178179## 10. Enforcement Handoff180181Use `architecture-as-code` only for constraints that can be enforced as import182or dependency rules. Do not duplicate this skill's principles there; hand off183the specific rule to encode.184185Examples:186187```188Principle: DI / functional core189Constraint: domain must not import infrastructure190Enforcement: add architecture rule: forbid <domain-component> -> <infra-component>191```192193```194Principle: interface discipline195Constraint: external callers use the facade only196Enforcement: add architecture rule: forbid * -> <module-internal-*>, except <module-*>197```198199```200Principle: integration discipline201Constraint: no module reaches a peer application's internals202Enforcement: add architecture rule: forbid * -> <peer-storage-client | peer-internal-*>, except <integration-adapter-*>203```204205A principle can also settle as no handoff. Record that outcome rather than206omitting it:207208```209Principle: layer self-sufficiency210Constraint: the control holds with the layer below absent211Enforcement: none - not an import or dependency edge; verify by exercising the212 component without that layer (`defect-shift-left`)213```214215## 11. Output Contract216217When this skill changes or rejects a design, emit a coder-facing decision218record:219220```221Subject: <module / service / abstraction / PR / code path>222Decision: Proceed | Simplify | Split | Inline | Reject | Defer223Principle: <YAGNI | Rule of 3 | DRY | SoC | SRP | capability-boundary | DI | fail-fast | idempotency | atomicity | integration | layer-self-sufficiency | naming | concurrency>224Evidence: <callers, imports, tests, runtime invariant, or file paths checked>225Enforcement: <none | add architecture rule: constraint | update architecture rule: constraint>226Next action: <edit, delete, extract, add test, add lint rule, or ask user>227Verification: <command / review check / Not run + reason>228```229230## See also231232- **`functionality-complexity-tradeoff`** — necessity gate and worth ledger applied to individual decisions.233- **`structural-simplification`** — per-axis complexity comparison (`D, K, P, n`).234- **`morphogenetic-architecture`** — declared placement, observed coupling fields, and topology evolution.235- **`architecture-as-code`** — consumes explicit `Enforcement` handoffs and turns enforceable dependency constraints into lint rules.236- **`evolutionary-database-design`** — staged, compatible change to a payload or stored shape shared across an integration edge.