Dev-Architecture — Module Boundaries & Structural Integrity

SkillFiles & storage

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, 모듈 경계, 순환 참조.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Dev-Architecture — Module Boundaries & Structural Integrity skill

What this skill tells your AI

The instructions your AI receives, as published by lidge-jun/codexclaw in plugins/codexclaw/skills/dev-architecture/SKILL.md and read by ahel’s review.

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. Read the dev skill first for universal development discipline 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 describes impact only when backed by a concrete failure. Style, coupling heuristics, and size limits follow dev §0.2 DEFAULT exceptions; uppercase severity alone does not turn a structural preference into a safety gate.

Modular References

FileWhen to ReadWhat It Covers
references/circular-dependencies.mdDetecting or fixing import cyclesDetection commands (madge/pydeps/go vet), fix strategies, real examples
references/coupling-taxonomy.mdReviewing code for hidden coupling8 coupling types, severity matrix, refactoring patterns, banned review responses
references/barrel-discipline.mdCreating/modifying index/barrel filesWhen 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

Structural Decision Gate

Severity: HIGH Rule (ARCH-DECISION-01): When changing module boundaries, seams, layering, shared packages, or public exports above C0/C1 local-patch scope, name the structural decision before editing.

Required decision record, inline in the plan or in the repo's ADR/source-of-truth file:

  • Context: what pressure forced the boundary change.
  • Rejected alternative: at least one plausible option not chosen, with why.
  • Chosen move: split, extract, merge, invert dependency, introduce adapter, or change public export.
  • Consequences: new dependency direction, public contract impact, migration cost, and follow-up verification.

Route durable, surprising, hard-to-reverse, or cross-team choices to the repo's ADR/current-architecture source of truth instead of leaving them only in chat. dev-scaffolding owns where those durable docs live; this skill owns the boundary decision content.

Pre-Change Structural Map

Severity: HIGH Rule (ARCH-MAP-01): Before recommending or applying a split/extract/merge for boundary work above C0/C1, produce a compact structural map from code evidence.

Map fields:

  • Core modules involved.
  • Direct dependents and dependencies.
  • Current and intended dependency direction.
  • Public boundaries touched (index.*, package exports, route/API contracts, CLI entry points).
  • Blast-radius class (local module, feature, package, app, cross-app/monorepo).

Do not choose the fix first and backfill the map. The map is the evidence that tells whether the right move is colocation, extraction, dependency inversion, adapter introduction, or no structural change.

Layered Architecture Boundaries

LayerMay ImportMUST NOT ImportExample
Presentation (UI/CLI/Controller)Application, DomainInfrastructure directlyReact component importing DB client
Application (Use Cases/Services)Domain, PortsPresentation, Infra adaptersService importing React component
Domain (Entities/Value Objects)Nothing (self-contained)Any other layerEntity importing Express
Infrastructure (Adapters/DB/HTTP)Domain (implements ports)Presentation, ApplicationDB adapter importing controller

When to Split a Module

Canonical file-size rule: >400 LOC -> split (DEFAULT). Deviations require a stated reason.

SignalAction
File exceeds 400 LOCSplit by responsibility (DEFAULT)
Module has 6+ direct dependentsExtract shared interface
Two unrelated features share a fileSeparate into own modules
Circular import detectedExtract shared types/interfaces to a third module
Module name contains "and" or "utils"Split by actual concern

Banned Patterns

BannedWhyFix
utils.ts / helpers.ts growing unboundedBecomes a coupling magnetSplit by domain: date-utils.ts, string-format.ts
Cross-layer direct importBreaks dependency directionUse ports/adapters or event bus
Shared mutable state between modulesHidden temporal couplingPass explicitly or use event system
God module (20+ exports)Everything depends on itExtract cohesive sub-modules

Module SSOT (Single Source of Truth)

Every concept, constant, type, or configuration value MUST have exactly one canonical owner module.

ConceptCanonical OwnerConsumers Do
Shared types / interfacestypes/ or contracts/ moduleImport, never redefine
Constants / magic valuesDomain-specific constants moduleImport the constant
Config / envCentral config moduleImport resolved values
Validation schemasBoundary module (API entry)Import schema, don't recreate
API contractsAPI layerImport types from API module
BannedWhyFix
Duplicating a type/constant in a consumerTwo sources of truth → driftImport from canonical owner
"Local copy for convenience"Convenience becomes divergenceImport the original
Re-deriving a value that has a canonical sourceSilent inconsistencyImport the derived value or computation

Deep Modules and Seams

Use this vocabulary when deciding whether an abstraction earns its keep:

TermMeaning
ModuleA cohesive unit with a named responsibility and public interface
InterfaceThe small surface consumers depend on
ImplementationThe hidden work behind that surface
DepthLarge useful behavior hidden behind a small interface
SeamA boundary where alternative implementations are real or likely
AdapterCode translating one external shape into the module's interface
LeverageHow much change the abstraction absorbs for its callers
LocalityHow 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

PhaseRequired ActionPass Condition
1. DetectRun ecosystem-specific detection commandCommand exits clean (no cycles reported)
2. ClassifyIdentify cycle type: direct A<->B or transitive A->B->C->AType documented
3. AnalyzeDetermine root cause: shared type? callback? event?Root interface identified
4. FixApply appropriate fix strategy (see references/)Detection command passes
5. VerifyRe-run detection + confirm no regressionsZero cycles in report

Detection commands are ecosystem-specific. See references/circular-dependencies.md for command templates, examples, and verification details.

Banned Patterns

Banned PatternWhy BannedRequired Fix
A imports B, B imports A (direct cycle)Compile failures, bundler issues, test fragilityExtract shared interface to C
Type-only cycle (import type both ways)Still signals wrong boundaryMove shared types to types/ module
Barrel re-export creating hidden cycleIndex file masks real dependency graphRemove barrel, use direct imports
Lazy import to "break" cycle (require() inside function)Hides the problem, breaks tree-shakingFix the architecture, not the symptom
"It works in runtime" as justificationFragile, bundler-dependent, blocks refactoringMust pass static analysis
Circular via test file importing source that imports test helperTest infra leaking into production graphIsolate test helpers in __test_utils__/

Fix Guidance

SituationPreferred Fix
Two modules share typesExtract types.ts or contracts/ module both import
Module A calls back into BDependency inversion: A defines interface, B implements
Event producer and consumer import each otherEvent bus / mediator pattern
Circular at package level (monorepo)Introduce shared or contracts package
UI component imports its containerLift shared state to context or prop drilling
Service layer cycleExtract 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)

#TypeDefinitionExampleSeverityFix Pattern
1ContentModule reaches into another's internalsAccessing private fields, reading internal stateCRITICALExpose via public API/method
2CommonMultiple modules share global mutable stateGlobal config object mutated by servicesCRITICALDependency injection, immutable config
3ControlModule passes flag to control another's logicprocessOrder(order, isRetry=true)HIGHPolymorphism, strategy pattern
4StampModule passes large struct when only one field neededrenderHeader(entireUserObject)HIGHPass only needed fields
5ExternalMultiple modules depend on same external formatBoth parse same CSV format independentlyHIGHSingle parser module, shared schema
6TemporalModules must execute in specific orderinit() must run before process()MEDIUMMake ordering explicit (state machine, builder)
7SequentialOutput of A is input of B (pipeline)ETL stagesLOWDocument the contract, validate at boundary
8FunctionalModules share a well-defined interfaceFunction call with typed params/returnLOWThis is GOOD coupling — the target state

Review Decision Matrix

SeverityMerge?Action Required
CRITICAL (Content, Common)BLOCKMust refactor before merge
HIGH (Control, Stamp, External)BLOCK unless justifiedRequire tech-debt ticket if merged
MEDIUM (Temporal)Allowed with documentationAdd ordering comments or state assertions
LOW (Sequential, Functional)ALLOWEDNo action needed

See references/coupling-taxonomy.md for examples, detection signals, refactoring patterns, and banned review responses.


4. Boundary-Only Defensive Programming

Severity: CRITICAL Rule: Parse untrusted data at trust boundaries and avoid repeating shape validation inside one trusted typed boundary. Domain invariants (valid ranges, state transitions, relational constraints) belong to the domain owner even for in-process callers. Authorization and assertions for genuinely reachable invalid states remain allowed.

Ownership: this section distinguishes ingress shape parsing, domain invariants and reachable-state assertions. dev-security owns security validation and authorization policy; placement must not erase a business invariant or required defense-in-depth.

Validation Location Matrix

LocationValidate?RationaleExample
HTTP/API controller inputYESUntrusted external dataZod schema, JSON schema
CLI argument parsingYESUntrusted user inputyargs/commander validation
File system readsYESExternal data, may be corruptParse + validate structure
Database query resultsYES at ORM-untyped/raw-query boundaries (shape only); NO when a typed schema/ORM guarantees the shapeUntyped results may drift; typed guarantees are trusted (see Banned Patterns)Check raw-query nulls/shape; trust typed ORM results
Message queue consumerYESCross-process boundaryValidate message schema
Internal function paramsNo repeated shape parsing; domain constraints may applyTypes prove shape, not every business invariantDomain owner checks start <= end
Private method argsNo repeated shape parsing; invariants may applyTypes do not prove every valid stateEnforce the private method's real domain constraints
Service-to-service in same processNo repeated trusted shape parsing; enforce domain/security rulesIn-process is not a waiver for invariants or authorizationValidate the actual boundary/constraint

Banned Patterns

Banned PatternWhy BannedFix
if (!param) throw at start of every internal functionRedundant with type system, clutters codeRemove — let TypeScript/types enforce
Repeated runtime shape checks on already validated trusted valuesAdds noise without a new boundaryTrust the parsed shape; retain domain invariants and reachable-state checks
Assertions on a state proven impossible by the actual contractDistracts from reachable failuresFix types where sufficient; retain assertions for real domain/state constraints
Repeating the same input shape parser in every domain constructorDuplicates a trusted ingress contractParse shape once; enforce domain invariants in the entity/value-object owner
Try-catch around every internal callHides bugs, makes debugging harderLet errors propagate, catch at boundary
Null checks after DB query that schema guarantees NOT NULLDistrusts your own schemaTrust schema, validate at migration time

Allowed Defensive Checks (Exceptions)

SituationWhy AllowedPattern
Security-critical path (auth, crypto)Defense in depth required by policyDouble-check even internal calls
Data from deserialization (JSON.parse)Runtime data, types lostValidate with schema (Zod/io-ts)
Plugin/extension boundaryThird-party code, untrustedValidate at plugin interface
Across deployment boundary (microservice call)Network = system boundaryFull validation required
Feature flags / A-B test pathsRuntime variation, not type-safeGuard with runtime check

Fix Guidance

SmellDiagnosisFix
10+ if (!x) throw in one fileOver-defensive internal codeRemove guards, fix types
Every function starts with parameter validationBoundary confusionMove all validation to entry point
try { } catch { return null } everywhereError suppressionLet errors bubble, handle at boundary
typeof x === 'string' in TypeScriptDistrusting compilerRemove, or fix the type to be accurate
Same validation in controller AND serviceDuplicated boundaryValidate 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

ContextBarrel Allowed?Rationale
Library/package public API (packages/ui/index.ts)YESSingle entry point for consumers
Framework plugin entry (plugin/index.ts)YESPlugin contract requires it
Feature public boundary export (features/auth/index.ts as the feature's single external entry)YESPublic Boundary Export (dev-scaffolding §1); external consumers import the boundary
Feature internal convenience barrel (re-exporting siblings for imports inside the feature)NOHides internal structure, breaks tree-shaking
Utility folder (utils/index.ts)NOCreates coupling magnet
Component folder re-exporting siblingsNODirect imports are clearer
Monorepo package boundary (@org/shared/index.ts)YESCross-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:

  • No new circular dependencies — run madge --circular or equivalent
  • Layer violations — no upward imports (infra->domain OK, domain->infra BLOCKED)
  • Coupling classified — any new cross-module dependency has coupling type identified
  • No CRITICAL/HIGH coupling without justification — Content/Common/Control coupling blocked
  • Barrel files — no new internal barrels; existing public barrels use named exports only
  • Validation placement — parse untrusted shape at ingress; enforce domain invariants in their owner and preserve required security checks
  • Module size — review >400 LOC for cohesion; document a justified exception rather than blocking by size alone
  • No "utils" growth — shared code placed in domain-specific module, not catch-all utils
  • Dependency direction — dependencies point inward toward Domain: outer layers depend on inner layers (Presentation/Application/Infrastructure -> Domain), and inner layers never import outward
  • No lazy-import hacks — no require() inside function body to hide circular deps

Automated Enforcement (CI Recommendations)

CheckToolCI Command
Layer/dependency rules (preferred CI gate)dependency-cruisernpx depcruise --validate .dependency-cruiser.cjs src/
Circular deps (quick visualization)madgenpx madge --circular --extensions ts,tsx src/ && echo "OK"
Dead files/exports/depsknipnpx knip
Monorepo package consistencysherifnpx sherif
Import boundarieseslint-plugin-boundariesESLint with boundaries config
Layer violationsdependency-cruisernpx depcruise --validate .dependency-cruiser.cjs src/
Barrel abusecustom ESLint ruleno-restricted-imports pattern for internal index files
Module sizecustom script`find src -name '*.ts' -exec wc -l {} +

On Windows without Unix tools, use PowerShell equivalents: Get-ChildItem -Recurse, Measure-Object, Select-String.


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.
  • 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.

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 or a domain/state invariant?
    YES -> Enforce the relevant invariant/authorization in its owner
    NO  -> Avoid duplicating already-proven shape validation

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 cxc 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.

Signals

GitHub stars
37
Forks
7
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
cxc-dev-architecture
Source
github.com/lidge-jun/codexclaw