Derived Modular Arch
Deep Dive

Why CLI, Not Only Linters

Architecture rules are graph properties. A single file cannot see them.

Short answer

ESLint, Biome, and Oxlint look at one file (or a string-import heuristic). Cycles, inbound predicates, and promotion are properties of the whole graph. That needs a separate analyzer — dma check.

Linters in the editor remain useful. But CI without the CLI is a hole in architecture.

What the linter sees

DMA file-scoped rules:

  • import in the wrong layer direction (layer-direction)
  • feature → feature (feature-to-feature)
  • import bypassing public/ (public-api)
  • barrel index with re-exports (no-barrel)

ESLint does this precisely — via @derived-modular/boundaries and path resolution. Biome — by heuristics on string patterns.

What the linter does not see

RuleWhy the graph is needed
no-cycleA cycle is a path A→B→C→A across multiple files
feature-has-inboundInbound is counted across all modules, not one import in one file
service-no-inboundA service without consumers is a subgraph property
dma doctorEvolution signals across the whole project

Example: a cycle services/billingservices/cartservices/inventoryservices/billing — in each file one import looks legal, while no-cycle only sees the full graph. Or catalog imports checkout directly — ESLint catches feature-to-feature in the file, but the inbound predicate feature-has-inbound and chains through several modules — only check.

DMA's hybrid model

Editor     →  linter (fast, file-scoped)
CI         →  dma check (full graph)
Locally    →  dma doctor (soft signals)

One analyzer (@derived-modular/cli) — source of truth. Linters are thin adapters on top of @derived-modular/boundaries, not a second source of truth.

"We configured ESLint — architecture is under control" is an illusion until you run dma check in CI.

Why not one mega-ESLint rule

In theory ESLint could analyze the whole project. In practice:

  • linters are optimized for per-file work and per-file cache;
  • a full graph pass on every save is expensive;
  • cycles and inbound are a separate class of algorithms, not the AST of one module.

DMA deliberately moved graph audit into the CLI. Linters are for the IDE feedback loop.

Coverage matrix

ConcernESLintBiomedma check
layer-directionheuristic
feature-to-featureheuristic
public-apiheuristic
no-barrelheuristic
no-cycle
inbound predicates
doctor

Practical recommendation

  1. Wire up ESLint (or Biome / Oxlint) in the editor
  2. Add npx @derived-modular/cli check . to CI
  3. Once per sprint — npx @derived-modular/cli doctor . for growth signals

On this page