Dev-Architecture — Module Boundaries & Structural Integrity
C0/C1 work (small local patches): See dev §0.0 Work Classifier + §0.1 Patch Fast-Path before reading references.
dev is canonical: dev §0.2 Rule Classes, §3 Verification Gate, and §5 Safety Rules apply to all work governed by this skill.
Always read dev/SKILL.md first for project-wide conventions before applying architecture rules.
Enforces architectural rules that prevent structural decay: circular dependencies, implicit coupling, barrel abuse, and misplaced validation. These rules are mechanical — an AI coding agent can follow them without subjective judgment.
Severity mapping (dev §0.2): Severity: CRITICAL/HIGH ⇒ STRICT; MEDIUM ⇒ DEFAULT.
Modular References
| File |
When to Read |
What It Covers |
references/circular-dependencies.md |
Detecting or fixing import cycles |
Detection commands (madge/pydeps/go vet), fix strategies, real examples |
references/coupling-taxonomy.md |
Reviewing code for hidden coupling |
8 coupling types, severity matrix, refactoring patterns, banned review responses |
references/barrel-discipline.md |
Creating/modifying index/barrel files |
When barrels OK vs banned, tree-shaking impact, safe barrel template |
External/current architecture evidence
Architecture rules in this skill are local and mechanical. When an architectural
decision depends on current framework guidance, cloud/provider reference
architecture, package deprecation, platform limits, or public source evidence,
read the active search skill and follow its query-rewrite, source-fetch, and
evidence-status rules. Use browser verification only after candidate URLs exist.
1. Module Boundaries
Layered Architecture Boundaries
| Layer |
May Import |
MUST NOT Import |
Example |
| Presentation (UI/CLI/Controller) |
Application, Domain |
Infrastructure directly |
React component importing DB client |
| Application (Use Cases/Services) |
Domain, Ports |
Presentation, Infra adapters |
Service importing React component |
| Domain (Entities/Value Objects) |
Nothing (self-contained) |
Any other layer |
Entity importing Express |
| Infrastructure (Adapters/DB/HTTP) |
Domain (implements ports) |
Presentation, Application |
DB adapter importing controller |
When to Split a Module
Canonical file-size rule: >400 LOC -> split (DEFAULT). Deviations require a stated reason.
| Signal |
Action |
| File exceeds 400 LOC |
Split by responsibility (DEFAULT) |
| Module has 6+ direct dependents |
Extract shared interface |
| Two unrelated features share a file |
Separate into own modules |
| Circular import detected |
Extract shared types/interfaces to a third module |
| Module name contains "and" or "utils" |
Split by actual concern |
| 3+ apps/services import the same feature folder |
Promote to a monorepo package with its own manifest + public boundary |
| Package needs its own release cadence, CI matrix, or version |
Split at package level, not folder level |
Banned Patterns
| Banned |
Why |
Fix |
utils.ts / helpers.ts growing unbounded |
Becomes a coupling magnet |
Split by domain: date-utils.ts, string-format.ts |
| Cross-layer direct import |
Breaks dependency direction |
Use ports/adapters or event bus |
| Shared mutable state between modules |
Hidden temporal coupling |
Pass explicitly or use event system |
| God module (20+ exports) |
Everything depends on it |
Extract cohesive sub-modules |
Module SSOT (Single Source of Truth)
Every concept, constant, type, or configuration value MUST have exactly one canonical owner module.
| Concept |
Canonical Owner |
Consumers Do |
| Shared types / interfaces |
types/ or contracts/ module |
Import, never redefine |
| Constants / magic values |
Domain-specific constants module |
Import the constant |
| Config / env |
Central config module |
Import resolved values |
| Validation schemas |
Boundary module (API entry) |
Import schema, don't recreate |
| API contracts |
API layer |
Import types from API module |
| Banned |
Why |
Fix |
| Duplicating a type/constant in a consumer |
Two sources of truth → drift |
Import from canonical owner |
| "Local copy for convenience" |
Convenience becomes divergence |
Import the original |
| Re-deriving a value that has a canonical source |
Silent inconsistency |
Import the derived value or computation |
Deep Modules and Seams
Use this vocabulary when deciding whether an abstraction earns its keep:
| Term |
Meaning |
| Module |
A cohesive unit with a named responsibility and public interface |
| Interface |
The small surface consumers depend on |
| Implementation |
The hidden work behind that surface |
| Depth |
Large useful behavior hidden behind a small interface |
| Seam |
A boundary where alternative implementations are real or likely |
| Adapter |
Code translating one external shape into the module's interface |
| Leverage |
How much change the abstraction absorbs for its callers |
| Locality |
How close related behavior stays to its owning concept |
Frontend depth means small props/events hiding complex rendering, state management,
data transformation, or integration behavior. One adapter usually means hypothetical
indirection; two adapters, or a near-term second adapter, is evidence of a real seam.
Do not expose internals only for tests; test through the public interface or add a
boundary-owned diagnostic hook with production value.
2. Circular Dependency Detection & Prevention
Severity: CRITICAL
Rule: No circular dependency may exist between modules. Every detected cycle MUST be resolved before merge.
Required Agent Workflow
| Phase |
Required Action |
Pass Condition |
| 1. Detect |
Run ecosystem-specific detection command |
Command exits clean (no cycles reported) |
| 2. Classify |
Identify cycle type: direct A<->B or transitive A->B->C->A |
Type documented |
| 3. Analyze |
Determine root cause: shared type? callback? event? |
Root interface identified |
| 4. Fix |
Apply appropriate fix strategy (see references/) |
Detection command passes |
| 5. Verify |
Re-run detection + confirm no regressions |
Zero cycles in report |
Detection commands are ecosystem-specific. See references/circular-dependencies.md
for command templates, examples, and verification details.
Banned Patterns
| Banned Pattern |
Why Banned |
Required Fix |
| A imports B, B imports A (direct cycle) |
Compile failures, bundler issues, test fragility |
Extract shared interface to C |
Type-only cycle (import type both ways) |
Still signals wrong boundary |
Move shared types to types/ module |
| Barrel re-export creating hidden cycle |
Index file masks real dependency graph |
Remove barrel, use direct imports |
Lazy import to "break" cycle (require() inside function) |
Hides the problem, breaks tree-shaking |
Fix the architecture, not the symptom |
| "It works in runtime" as justification |
Fragile, bundler-dependent, blocks refactoring |
Must pass static analysis |
| Circular via test file importing source that imports test helper |
Test infra leaking into production graph |
Isolate test helpers in __test_utils__/ |
Fix Guidance
| Situation |
Preferred Fix |
| Two modules share types |
Extract types.ts or contracts/ module both import |
| Module A calls back into B |
Dependency inversion: A defines interface, B implements |
| Event producer and consumer import each other |
Event bus / mediator pattern |
| Circular at package level (monorepo) |
Introduce shared or contracts package |
| UI component imports its container |
Lift shared state to context or prop drilling |
| Service layer cycle |
Extract orchestrator service or use events |
3. Implicit Coupling Taxonomy
Severity: CRITICAL
Rule: Every coupling instance in a code review MUST be classified by type. Coupling severity determines whether the code can merge.
Coupling Types (ordered by severity, worst first)
| # |
Type |
Definition |
Severity |
Fix Pattern |
| 1 |
Content |
Module reaches into another's internals |
CRITICAL |
Expose via public API/method |
| 2 |
Common |
Multiple modules share global mutable state |
CRITICAL |
Dependency injection, immutable config |
| 3 |
Control |
Module passes flag to control another's logic |
HIGH |
Polymorphism, strategy pattern |
| 4 |
Stamp |
Module passes large struct when only one field needed |
HIGH |
Pass only needed fields |
| 5 |
External |
Multiple modules depend on same external format |
HIGH |
Single parser module, shared schema |
| 6 |
Temporal |
Modules must execute in specific order |
MEDIUM |
Make ordering explicit (state machine, builder) |
| 7 |
Sequential |
Output of A is input of B |
LOW |
Document the contract, validate at boundary |
| 8 |
Functional |
Modules share a well-defined interface |
LOW |
This is GOOD coupling — the target state |
See references/coupling-taxonomy.md for examples, detection signals,
refactoring patterns, and banned review responses.
Review Decision Matrix
| Severity |
Merge? |
Action Required |
| CRITICAL (Content, Common) |
BLOCK |
Must refactor before merge |
| HIGH (Control, Stamp, External) |
BLOCK unless justified |
Require tech-debt ticket if merged |
| MEDIUM (Temporal) |
Allowed with documentation |
Add ordering comments or state assertions |
| LOW (Sequential, Functional) |
ALLOWED |
No action needed |
4. Boundary-Only Defensive Programming
Severity: CRITICAL
Rule: Validation and defensive checks belong ONLY at system boundaries. Internal module boundaries MUST trust their callers.
Ownership split: placement (validation happens at the boundary, nowhere else) is owned
by this section; what the validation schema enforces (content/policy) is owned by
dev-security §1.
Validation Location Matrix
| Location |
Validate? |
Rationale |
Example |
| HTTP/API controller input |
YES |
Untrusted external data |
Zod schema, JSON schema |
| CLI argument parsing |
YES |
Untrusted user input |
yargs/commander validation |
| File system reads |
YES |
External data, may be corrupt |
Parse + validate structure |
| Database query results |
YES at ORM-untyped/raw-query boundaries (shape only); NO when a typed schema/ORM guarantees the shape |
Untyped results may drift; typed guarantees are trusted (see Banned Patterns) |
Check raw-query nulls/shape; trust typed ORM results |
| Message queue consumer |
YES |
Cross-process boundary |
Validate message schema |
| Internal function params |
NO |
Caller is trusted code you control |
Type system handles this |
| Private method args |
NO |
Same module, same author |
Redundant — types suffice |
| Service-to-service in same process |
NO |
In-process calls share type system |
Interface contracts handle this |
Banned Patterns
| Banned Pattern |
Why Banned |
Fix |
if (!param) throw at start of every internal function |
Redundant with type system, clutters code |
Remove — let TypeScript/types enforce |
| Runtime type checks in typed language internals |
Duplicates compiler work, adds noise |
Trust the type system |
assert(x !== null) in module-internal code |
If x can be null, fix the type; if it can't, the assert is noise |
Fix type signature or remove assert |
| Validation in domain entity constructor for in-process callers |
Entities should be created from validated data |
Validate at boundary, trust domain layer |
| Try-catch around every internal call |
Hides bugs, makes debugging harder |
Let errors propagate, catch at boundary |
| Null checks after DB query that schema guarantees NOT NULL |
Distrusts your own schema |
Trust schema, validate at migration time |
Allowed Defensive Checks (Exceptions)
| Situation |
Why Allowed |
Pattern |
| Security-critical path (auth, crypto) |
Defense in depth required by policy |
Double-check even internal calls |
| Data from deserialization (JSON.parse) |
Runtime data, types lost |
Validate with schema (Zod/io-ts) |
| Plugin/extension boundary |
Third-party code, untrusted |
Validate at plugin interface |
| Across deployment boundary (microservice call) |
Network = system boundary |
Full validation required |
| Feature flags / A-B test paths |
Runtime variation, not type-safe |
Guard with runtime check |
Fix Guidance
| Smell |
Diagnosis |
Fix |
10+ if (!x) throw in one file |
Over-defensive internal code |
Remove guards, fix types |
| Every function starts with parameter validation |
Boundary confusion |
Move all validation to entry point |
try { } catch { return null } everywhere |
Error suppression |
Let errors bubble, handle at boundary |
typeof x === 'string' in TypeScript |
Distrusting compiler |
Remove, or fix the type to be accurate |
| Same validation in controller AND service |
Duplicated boundary |
Validate once at controller, service trusts |
5. Barrel/Re-export Discipline
Severity: HIGH
Rule: Barrel files (index.ts/index.js/init.py) are ONLY allowed at public boundaries — package APIs and feature public boundary exports. Internal convenience barrels are banned.
Barrel Policy Matrix
| Context |
Barrel Allowed? |
Rationale |
Library/package public API (packages/ui/index.ts) |
YES |
Single entry point for consumers |
Framework plugin entry (plugin/index.ts) |
YES |
Plugin contract requires it |
Feature public boundary export (features/auth/index.ts as the feature's single external entry) |
YES |
Public Boundary Export (dev-scaffolding §1); external consumers import the boundary |
| Feature internal convenience barrel (re-exporting siblings for imports inside the feature) |
NO |
Hides internal structure, breaks tree-shaking |
Utility folder (utils/index.ts) |
NO |
Creates coupling magnet |
| Component folder re-exporting siblings |
NO |
Direct imports are clearer |
Monorepo package boundary (@org/shared/index.ts) |
YES |
Cross-package contract |
See references/barrel-discipline.md for import examples, tree-shaking
details, ESLint enforcement, and the safe barrel template.
6. Review Integration
Architecture Review Checklist (for code-reviewer)
When reviewing any PR that adds/modifies module structure, verify:
Automated Enforcement (CI Recommendations)
| Check |
Tool |
CI Command |
| Layer/dependency rules (preferred CI gate) |
dependency-cruiser |
npx depcruise --validate .dependency-cruiser.cjs src/ |
| Import boundaries |
eslint-plugin-boundaries |
ESLint with boundaries config |
| Circular deps (quick visualization) |
madge |
npx madge --circular --extensions ts,tsx src/ && echo "OK" |
| Barrel abuse |
Biome noBarrelFile or ESLint no-restricted-imports |
pattern for internal index files |
| Dead files/exports/deps |
knip |
npx knip |
| Monorepo package consistency |
sherif |
npx sherif |
| Module size |
custom script |
find src -name '*.ts' -exec wc -l {} + | awk '$1 > 400' |
Tool roles verified 2026-07-02 (Sources: references/circular-dependencies.md).
Cross-Skill References
- Observability: Trace emission at module boundaries is a production/long-lived-runtime concern (DEFAULT there, not universal). See
dev-backend/references/core/observability.md for the canonical OTel setup.
- Security: Validate at every trust/process/external boundary (HTTP entry, IPC, file/CLI input, third-party responses). Intra-trust-domain module calls follow §4 boundary-only defense — do not re-validate already-trusted data. See
dev-security/SKILL.md for input validation and auth patterns.
Quick Decision Trees
"Should I create a new module?"
Does the code serve a distinct responsibility?
NO -> Keep in existing module
YES -> Is it used by 3+ other modules?
NO -> Co-locate with primary consumer
YES -> Create dedicated module with clear interface
"Is this coupling acceptable?"
What type? (see taxonomy above)
Content/Common -> BLOCK, refactor now
Control/Stamp/External -> BLOCK unless tech-debt ticket created
Temporal -> ALLOW with documentation
Sequential/Functional -> ALLOW
"Where does this validation go?"
Is the data source external (HTTP, file, queue, DB, user input)?
YES -> Validate here (boundary)
NO -> Is this a security-critical path?
YES -> Validate (defense in depth)
NO -> Trust the type system, no validation needed
- Coupling and boundary review: see
dev-code-reviewer.
- Debugging escalation for boundary or coupling issues: see
dev-debugging.
- Infrastructure architecture and deployment boundaries: see
dev-devops.
Structural Index Concept (ARCH-INDEX-01, DEFAULT)
Source: sol research (wednesday-solutions/ai-agent-skills AST dependency graph).
Instead of reconstructing a module map for every task, maintain a lightweight
structural index that agents can query:
- Use
cli-jaw map <dir> for on-demand symbol-level maps (already shipped).
- For larger repos, consider a persistent dependency graph artifact (e.g.,
dependency-cruiser JSON, Nx project graph, or a custom SQLite index).
- The index should track: module → exports, module → imports, symbol → callers.
- Freshness: re-generate on significant structural changes (new modules, moved files).
- Query before editing: "what depends on this module?" should be answerable from
the index without a full codebase scan.
This is a guidance concept, not a shipped tool. The agent should check for existing
index artifacts before running ad-hoc scans.
Architecture Conformance Tests (ARCH-CONFORMANCE-01, DEFAULT)
Source: sol research (HoangNguyen0403/agent-skills-standard compliance auditing).
Architecture rules that exist only as prose are invisible to CI. For C3+ work
where boundary violations would cause real harm:
- Generate tool-specific configs from architecture decisions (dependency-cruiser
rules, ESLint boundaries plugin, Nx enforce-module-boundaries, Go
depguard).
- Include at least one allowed-edge and one forbidden-edge test fixture.
- The CI gate should FAIL on new violations while allowing a baselined set of
legacy violations (ratcheting: new cycles fail, old ones are migrated).
- Return a machine-readable report (JSON or SARIF) that agents can consume.
1---2name: jaw-dev-architecture3description: MUST USE for module boundary work, circular dependency detection, coupling review, barrel or re-export changes, and validation placement decisions. Triggers: circular import, module split, layer violation, dependency direction, utils growth, barrel file, re-export, boundary review, architecture refactor, 모듈 경계, 순환 참조.4---56# Dev-Architecture — Module Boundaries & Structural Integrity78> **C0/C1 work (small local patches):** See `dev` §0.0 Work Classifier + §0.1 Patch Fast-Path before reading references.910> **`dev` is canonical:** `dev` §0.2 Rule Classes, §3 Verification Gate, and §5 Safety Rules apply to all work governed by this skill.11> **Always read `dev/SKILL.md` first** for project-wide conventions before applying architecture rules.1213Enforces architectural rules that prevent structural decay: circular dependencies, implicit coupling, barrel abuse, and misplaced validation. These rules are mechanical — an AI coding agent can follow them without subjective judgment.1415Severity mapping (dev §0.2): `Severity: CRITICAL`/`HIGH` ⇒ STRICT; `MEDIUM` ⇒ DEFAULT.1617## Modular References1819| File | When to Read | What It Covers |20|------|--------------|----------------|21| `references/circular-dependencies.md` | Detecting or fixing import cycles | Detection commands (madge/pydeps/go vet), fix strategies, real examples |22| `references/coupling-taxonomy.md` | Reviewing code for hidden coupling | 8 coupling types, severity matrix, refactoring patterns, banned review responses |23| `references/barrel-discipline.md` | Creating/modifying index/barrel files | When barrels OK vs banned, tree-shaking impact, safe barrel template |2425## External/current architecture evidence2627Architecture rules in this skill are local and mechanical. When an architectural28decision depends on current framework guidance, cloud/provider reference29architecture, package deprecation, platform limits, or public source evidence,30read the active `search` skill and follow its query-rewrite, source-fetch, and31evidence-status rules. Use browser verification only after candidate URLs exist.3233---3435## 1. Module Boundaries3637### Layered Architecture Boundaries3839| Layer | May Import | MUST NOT Import | Example |40|-------|-----------|-----------------|---------|41| Presentation (UI/CLI/Controller) | Application, Domain | Infrastructure directly | React component importing DB client |42| Application (Use Cases/Services) | Domain, Ports | Presentation, Infra adapters | Service importing React component |43| Domain (Entities/Value Objects) | Nothing (self-contained) | Any other layer | Entity importing Express |44| Infrastructure (Adapters/DB/HTTP) | Domain (implements ports) | Presentation, Application | DB adapter importing controller |4546### When to Split a Module4748Canonical file-size rule: **>400 LOC -> split (DEFAULT)**. Deviations require a stated reason.4950| Signal | Action |51|--------|--------|52| File exceeds 400 LOC | Split by responsibility (DEFAULT) |53| Module has 6+ direct dependents | Extract shared interface |54| Two unrelated features share a file | Separate into own modules |55| Circular import detected | Extract shared types/interfaces to a third module |56| Module name contains "and" or "utils" | Split by actual concern |57| 3+ apps/services import the same feature folder | Promote to a monorepo package with its own manifest + public boundary |58| Package needs its own release cadence, CI matrix, or version | Split at package level, not folder level |5960### Banned Patterns6162| Banned | Why | Fix |63|--------|-----|-----|64| `utils.ts` / `helpers.ts` growing unbounded | Becomes a coupling magnet | Split by domain: `date-utils.ts`, `string-format.ts` |65| Cross-layer direct import | Breaks dependency direction | Use ports/adapters or event bus |66| Shared mutable state between modules | Hidden temporal coupling | Pass explicitly or use event system |67| God module (20+ exports) | Everything depends on it | Extract cohesive sub-modules |6869### Module SSOT (Single Source of Truth)7071Every concept, constant, type, or configuration value MUST have exactly one canonical owner module.7273| Concept | Canonical Owner | Consumers Do |74|---------|----------------|--------------|75| Shared types / interfaces | `types/` or `contracts/` module | Import, never redefine |76| Constants / magic values | Domain-specific constants module | Import the constant |77| Config / env | Central config module | Import resolved values |78| Validation schemas | Boundary module (API entry) | Import schema, don't recreate |79| API contracts | API layer | Import types from API module |8081| Banned | Why | Fix |82|--------|-----|-----|83| Duplicating a type/constant in a consumer | Two sources of truth → drift | Import from canonical owner |84| "Local copy for convenience" | Convenience becomes divergence | Import the original |85| Re-deriving a value that has a canonical source | Silent inconsistency | Import the derived value or computation |8687### Deep Modules and Seams8889Use this vocabulary when deciding whether an abstraction earns its keep:9091| Term | Meaning |92|------|---------|93| Module | A cohesive unit with a named responsibility and public interface |94| Interface | The small surface consumers depend on |95| Implementation | The hidden work behind that surface |96| Depth | Large useful behavior hidden behind a small interface |97| Seam | A boundary where alternative implementations are real or likely |98| Adapter | Code translating one external shape into the module's interface |99| Leverage | How much change the abstraction absorbs for its callers |100| Locality | How close related behavior stays to its owning concept |101102Frontend depth means small props/events hiding complex rendering, state management,103data transformation, or integration behavior. One adapter usually means hypothetical104indirection; two adapters, or a near-term second adapter, is evidence of a real seam.105Do not expose internals only for tests; test through the public interface or add a106boundary-owned diagnostic hook with production value.107108---109110## 2. Circular Dependency Detection & Prevention111112**Severity: CRITICAL**113**Rule:** No circular dependency may exist between modules. Every detected cycle MUST be resolved before merge.114115### Required Agent Workflow116117| Phase | Required Action | Pass Condition |118|-------|-----------------|----------------|119| 1. Detect | Run ecosystem-specific detection command | Command exits clean (no cycles reported) |120| 2. Classify | Identify cycle type: direct A<->B or transitive A->B->C->A | Type documented |121| 3. Analyze | Determine root cause: shared type? callback? event? | Root interface identified |122| 4. Fix | Apply appropriate fix strategy (see references/) | Detection command passes |123| 5. Verify | Re-run detection + confirm no regressions | Zero cycles in report |124125Detection commands are ecosystem-specific. See `references/circular-dependencies.md`126for command templates, examples, and verification details.127128### Banned Patterns129130| Banned Pattern | Why Banned | Required Fix |131|----------------|-----------|--------------|132| A imports B, B imports A (direct cycle) | Compile failures, bundler issues, test fragility | Extract shared interface to C |133| Type-only cycle (`import type` both ways) | Still signals wrong boundary | Move shared types to `types/` module |134| Barrel re-export creating hidden cycle | Index file masks real dependency graph | Remove barrel, use direct imports |135| Lazy import to "break" cycle (`require()` inside function) | Hides the problem, breaks tree-shaking | Fix the architecture, not the symptom |136| "It works in runtime" as justification | Fragile, bundler-dependent, blocks refactoring | Must pass static analysis |137| Circular via test file importing source that imports test helper | Test infra leaking into production graph | Isolate test helpers in `__test_utils__/` |138139### Fix Guidance140141| Situation | Preferred Fix |142|-----------|---------------|143| Two modules share types | Extract `types.ts` or `contracts/` module both import |144| Module A calls back into B | Dependency inversion: A defines interface, B implements |145| Event producer and consumer import each other | Event bus / mediator pattern |146| Circular at package level (monorepo) | Introduce `shared` or `contracts` package |147| UI component imports its container | Lift shared state to context or prop drilling |148| Service layer cycle | Extract orchestrator service or use events |149150---151152## 3. Implicit Coupling Taxonomy153154**Severity: CRITICAL**155**Rule:** Every coupling instance in a code review MUST be classified by type. Coupling severity determines whether the code can merge.156157### Coupling Types (ordered by severity, worst first)158159| # | Type | Definition | Severity | Fix Pattern |160|---|------|-----------|----------|-------------|161| 1 | **Content** | Module reaches into another's internals | CRITICAL | Expose via public API/method |162| 2 | **Common** | Multiple modules share global mutable state | CRITICAL | Dependency injection, immutable config |163| 3 | **Control** | Module passes flag to control another's logic | HIGH | Polymorphism, strategy pattern |164| 4 | **Stamp** | Module passes large struct when only one field needed | HIGH | Pass only needed fields |165| 5 | **External** | Multiple modules depend on same external format | HIGH | Single parser module, shared schema |166| 6 | **Temporal** | Modules must execute in specific order | MEDIUM | Make ordering explicit (state machine, builder) |167| 7 | **Sequential** | Output of A is input of B | LOW | Document the contract, validate at boundary |168| 8 | **Functional** | Modules share a well-defined interface | LOW | This is GOOD coupling — the target state |169170See `references/coupling-taxonomy.md` for examples, detection signals,171refactoring patterns, and banned review responses.172173### Review Decision Matrix174175| Severity | Merge? | Action Required |176|----------|--------|-----------------|177| CRITICAL (Content, Common) | BLOCK | Must refactor before merge |178| HIGH (Control, Stamp, External) | BLOCK unless justified | Require tech-debt ticket if merged |179| MEDIUM (Temporal) | Allowed with documentation | Add ordering comments or state assertions |180| LOW (Sequential, Functional) | ALLOWED | No action needed |181182---183184## 4. Boundary-Only Defensive Programming185186**Severity: CRITICAL**187**Rule:** Validation and defensive checks belong ONLY at system boundaries. Internal module boundaries MUST trust their callers.188189Ownership split: **placement** (validation happens at the boundary, nowhere else) is owned190by this section; **what the validation schema enforces** (content/policy) is owned by191`dev-security` §1.192193### Validation Location Matrix194195| Location | Validate? | Rationale | Example |196|----------|-----------|-----------|---------|197| HTTP/API controller input | YES | Untrusted external data | Zod schema, JSON schema |198| CLI argument parsing | YES | Untrusted user input | yargs/commander validation |199| File system reads | YES | External data, may be corrupt | Parse + validate structure |200| Database query results | YES at ORM-untyped/raw-query boundaries (shape only); NO when a typed schema/ORM guarantees the shape | Untyped results may drift; typed guarantees are trusted (see Banned Patterns) | Check raw-query nulls/shape; trust typed ORM results |201| Message queue consumer | YES | Cross-process boundary | Validate message schema |202| **Internal function params** | **NO** | Caller is trusted code you control | Type system handles this |203| **Private method args** | **NO** | Same module, same author | Redundant — types suffice |204| **Service-to-service in same process** | **NO** | In-process calls share type system | Interface contracts handle this |205206### Banned Patterns207208| Banned Pattern | Why Banned | Fix |209|----------------|-----------|-----|210| `if (!param) throw` at start of every internal function | Redundant with type system, clutters code | Remove — let TypeScript/types enforce |211| Runtime type checks in typed language internals | Duplicates compiler work, adds noise | Trust the type system |212| `assert(x !== null)` in module-internal code | If x can be null, fix the type; if it can't, the assert is noise | Fix type signature or remove assert |213| Validation in domain entity constructor for in-process callers | Entities should be created from validated data | Validate at boundary, trust domain layer |214| Try-catch around every internal call | Hides bugs, makes debugging harder | Let errors propagate, catch at boundary |215| Null checks after DB query that schema guarantees NOT NULL | Distrusts your own schema | Trust schema, validate at migration time |216217### Allowed Defensive Checks (Exceptions)218219| Situation | Why Allowed | Pattern |220|-----------|-------------|---------|221| Security-critical path (auth, crypto) | Defense in depth required by policy | Double-check even internal calls |222| Data from deserialization (JSON.parse) | Runtime data, types lost | Validate with schema (Zod/io-ts) |223| Plugin/extension boundary | Third-party code, untrusted | Validate at plugin interface |224| Across deployment boundary (microservice call) | Network = system boundary | Full validation required |225| Feature flags / A-B test paths | Runtime variation, not type-safe | Guard with runtime check |226227### Fix Guidance228229| Smell | Diagnosis | Fix |230|-------|-----------|-----|231| 10+ `if (!x) throw` in one file | Over-defensive internal code | Remove guards, fix types |232| Every function starts with parameter validation | Boundary confusion | Move all validation to entry point |233| `try { } catch { return null }` everywhere | Error suppression | Let errors bubble, handle at boundary |234| `typeof x === 'string'` in TypeScript | Distrusting compiler | Remove, or fix the type to be accurate |235| Same validation in controller AND service | Duplicated boundary | Validate once at controller, service trusts |236237---238239## 5. Barrel/Re-export Discipline240241**Severity: HIGH**242**Rule:** Barrel files (index.ts/index.js/__init__.py) are ONLY allowed at public boundaries — package APIs and feature public boundary exports. Internal convenience barrels are banned.243244### Barrel Policy Matrix245246| Context | Barrel Allowed? | Rationale |247|---------|-----------------|-----------|248| Library/package public API (`packages/ui/index.ts`) | YES | Single entry point for consumers |249| Framework plugin entry (`plugin/index.ts`) | YES | Plugin contract requires it |250| Feature public boundary export (`features/auth/index.ts` as the feature's single external entry) | YES | Public Boundary Export (dev-scaffolding §1); external consumers import the boundary |251| Feature internal convenience barrel (re-exporting siblings for imports inside the feature) | NO | Hides internal structure, breaks tree-shaking |252| Utility folder (`utils/index.ts`) | NO | Creates coupling magnet |253| Component folder re-exporting siblings | NO | Direct imports are clearer |254| Monorepo package boundary (`@org/shared/index.ts`) | YES | Cross-package contract |255256See `references/barrel-discipline.md` for import examples, tree-shaking257details, ESLint enforcement, and the safe barrel template.258259---260261## 6. Review Integration262263### Architecture Review Checklist (for code-reviewer)264265When reviewing any PR that adds/modifies module structure, verify:266267- [ ] **No new circular dependencies** — run `madge --circular` or equivalent268- [ ] **Layer violations** — no upward imports (infra->domain OK, domain->infra BLOCKED)269- [ ] **Coupling classified** — any new cross-module dependency has coupling type identified270- [ ] **No CRITICAL/HIGH coupling without justification** — Content/Common/Control coupling blocked271- [ ] **Barrel files** — no new internal barrels; existing public barrels use named exports only272- [ ] **Validation placement** — new validation is at system boundary, not internal functions273- [ ] **Module size** — new/modified modules under 400 LOC274- [ ] **No "utils" growth** — shared code placed in domain-specific module, not catch-all utils275- [ ] **Dependency direction** — dependencies point inward toward Domain: outer layers depend on inner layers (Presentation/Application/Infrastructure -> Domain), and inner layers never import outward276- [ ] **No lazy-import hacks** — no `require()` inside function body to hide circular deps277278### Automated Enforcement (CI Recommendations)279280| Check | Tool | CI Command |281|-------|------|------------|282| Layer/dependency rules (preferred CI gate) | dependency-cruiser | `npx depcruise --validate .dependency-cruiser.cjs src/` |283| Import boundaries | eslint-plugin-boundaries | ESLint with boundaries config |284| Circular deps (quick visualization) | madge | `npx madge --circular --extensions ts,tsx src/ && echo "OK"` |285| Barrel abuse | Biome `noBarrelFile` or ESLint `no-restricted-imports` | pattern for internal index files |286| Dead files/exports/deps | knip | `npx knip` |287| Monorepo package consistency | sherif | `npx sherif` |288| Module size | custom script | `find src -name '*.ts' -exec wc -l {} + \| awk '$1 > 400'` |289290Tool roles verified 2026-07-02 (Sources: `references/circular-dependencies.md`).291292---293294## Cross-Skill References295296- **Observability**: Trace emission at module boundaries is a production/long-lived-runtime concern (DEFAULT there, not universal). See `dev-backend/references/core/observability.md` for the canonical OTel setup.297- **Security**: Validate at every trust/process/external boundary (HTTP entry, IPC, file/CLI input, third-party responses). Intra-trust-domain module calls follow §4 boundary-only defense — do not re-validate already-trusted data. See `dev-security/SKILL.md` for input validation and auth patterns.298299---300301## Quick Decision Trees302303### "Should I create a new module?"304305```306Does the code serve a distinct responsibility? 307 NO -> Keep in existing module308 YES -> Is it used by 3+ other modules?309 NO -> Co-locate with primary consumer310 YES -> Create dedicated module with clear interface311```312313### "Is this coupling acceptable?"314315```316What type? (see taxonomy above)317 Content/Common -> BLOCK, refactor now318 Control/Stamp/External -> BLOCK unless tech-debt ticket created319 Temporal -> ALLOW with documentation320 Sequential/Functional -> ALLOW321```322323### "Where does this validation go?"324325```326Is the data source external (HTTP, file, queue, DB, user input)?327 YES -> Validate here (boundary)328 NO -> Is this a security-critical path?329 YES -> Validate (defense in depth)330 NO -> Trust the type system, no validation needed331```332- Coupling and boundary review: see `dev-code-reviewer`.333- Debugging escalation for boundary or coupling issues: see `dev-debugging`.334- Infrastructure architecture and deployment boundaries: see `dev-devops`.335336## Structural Index Concept (ARCH-INDEX-01, DEFAULT)337338Source: sol research (wednesday-solutions/ai-agent-skills AST dependency graph).339340Instead of reconstructing a module map for every task, maintain a lightweight341structural index that agents can query:342343- Use `cli-jaw map <dir>` for on-demand symbol-level maps (already shipped).344- For larger repos, consider a persistent dependency graph artifact (e.g.,345 `dependency-cruiser` JSON, Nx project graph, or a custom SQLite index).346- The index should track: module → exports, module → imports, symbol → callers.347- Freshness: re-generate on significant structural changes (new modules, moved files).348- Query before editing: "what depends on this module?" should be answerable from349 the index without a full codebase scan.350351This is a guidance concept, not a shipped tool. The agent should check for existing352index artifacts before running ad-hoc scans.353354## Architecture Conformance Tests (ARCH-CONFORMANCE-01, DEFAULT)355356Source: sol research (HoangNguyen0403/agent-skills-standard compliance auditing).357358Architecture rules that exist only as prose are invisible to CI. For C3+ work359where boundary violations would cause real harm:360361- Generate tool-specific configs from architecture decisions (dependency-cruiser362 rules, ESLint boundaries plugin, Nx enforce-module-boundaries, Go `depguard`).363- Include at least one allowed-edge and one forbidden-edge test fixture.364- The CI gate should FAIL on new violations while allowing a baselined set of365 legacy violations (ratcheting: new cycles fail, old ones are migrated).366- Return a machine-readable report (JSON or SARIF) that agents can consume.