DEV Community

Alexei Ledenev
Alexei Ledenev

Posted on

The Tests Are Green. The Architecture Is Not: Why I Built Archfit

The task looked routine.

Add an endpoint. Reuse an existing function. Write the tests. Open a pull request.

The coding agent found exactly what it needed inside another module's persistence layer. Importing it directly was the shortest path, so the agent did that, added coverage, and completed the task.

The code compiled.

The tests passed.

The linter was happy.

The architecture was not.

The new dependency bypassed the target module's public API and reached directly into its internals. Locally, the change was reasonable. Globally, it weakened a boundary that the rest of the system depended on.

This is the problem I built Archfit to address.

The problem is governance, not code generation

AI coding agents are very good at making locally correct changes. Give an agent a focused task, enough repository context, and a validation command, and it can often produce a working implementation quickly.

But software architecture is not local.

Architecture lives in relationships:

  • which modules may depend on each other;
  • which APIs are public and which are internal;
  • which direction dependencies should flow;
  • where coupling is acceptable;
  • which shortcuts would make future changes more expensive.

An agent optimizing for task completion may never see the complete system design. Even if it does, architectural intent is often scattered across diagrams, ADRs, conventions, code-review history, and the memories of senior engineers.

The result is rarely one dramatic failure. It is usually a sequence of convenient decisions:

One shortcut is harmless. Twenty shortcuts become the architecture.

AI did not create architecture erosion. Humans have always made local compromises under delivery pressure. AI changes the rate: code can now change faster than humans can evaluate its structural consequences.

I tried the obvious things

The first response is usually to improve the prompt:

Respect module boundaries. Use public APIs. Follow the architecture.

That helps, but prompts are guidance, not enforcement.

Repository instructions such as AGENTS.md, architecture documents, and examples also help. But documentation can become stale, and an agent still has to retrieve the correct rule at the correct moment and interpret it correctly.

Linters and tests are essential, but they answer different questions. Tests show that observed behavior matches expectations. A linter checks a set of code-level rules. Neither necessarily knows that orders must not import payments/internal.

Code review remains important, but review capacity does not automatically scale with agent-generated change volume.

What was missing was an executable feedback loop for architecture.

Architecture as an executable constraint

Archfit starts with a simple idea:

  1. Declare the architectural boundaries you intend.
  2. Observe how the code is actually connected.
  3. Compare the two deterministically.
  4. Return a decision that CI, humans, and coding agents can act on.

Archfit reads architectural intent from .archfit.yaml. Language adapters collect dependency facts using ecosystem tools such as go list, dependency-cruiser, ast-grep, and grimp. A deterministic, LLM-free core classifies those dependencies and evaluates the configured gates.

It can detect structural problems such as:

  • forbidden dependencies;
  • cross-module access to internal APIs;
  • incorrect layer direction;
  • dependency cycles;
  • configured coupling or score regressions.

The result is not only a raw metric. It leads with a decision, separates blocking findings from advisory ones, and explains what needs attention.

ARCHFIT RESULT

Decision   ACCEPTABLE WITH WATCH ITEMS
Gate       PASS  ·  0 blocking
Warnings   55 advisory
Score      43 / 100  mixed

RECOMMENDATIONS

  MUST FIX
    none
  SHOULD FIX
    · bc/imbalanced_coupling — high fan-in into session state
  WATCH
    · lazy_cycle — lazy import SCC
Enter fullscreen mode Exit fullscreen mode

For a human review, run archfit analyze. For a CI gate with meaningful exit codes, run archfit check.

go install github.com/alexei-led/archfit/cmd/archfit@latest

archfit doctor
archfit config init --root .
archfit analyze
archfit check -c .archfit.yaml
Enter fullscreen mode Exit fullscreen mode

Archfit currently supports Go, TypeScript/JavaScript, Python, and Rust. A Docker image is also available with the supported analyzers bundled.

The failure should be useful to an agent

A CI failure written only for humans is not enough when an agent is part of the development loop.

If a boundary is violated, Archfit can produce a structured repair task:

{
  "rule_id": "no_internal_access",
  "goal": "Replace the internal-API access from pkg/a/a.go to pkg/b/internal/impl.go with b's public API.",
  "constraints": [
    "Use only the public API of module b",
    "public surface of \"b\": [pkg/b/api/**]"
  ],
  "files": [
    "pkg/a/a.go",
    "pkg/b/internal/impl.go"
  ],
  "validation": [
    "archfit check -c .archfit.yaml"
  ]
}
Enter fullscreen mode Exit fullscreen mode

This is more useful than a vague message such as architecture check failed.

The agent receives:

  • the goal;
  • the constraint it must preserve;
  • the relevant files;
  • the command that proves the repair.

That creates a bounded loop:

agent edits code
      ↓
archfit check
      ↓
violation + repair task
      ↓
agent repairs the boundary
      ↓
archfit check
Enter fullscreen mode Exit fullscreen mode

The architectural rule becomes part of the same feedback cycle as compilation, testing, and linting.

Why the gate is deterministic

An architecture gate should not change its answer because a model produced a different interpretation today.

For the same code, configuration, and analyzer evidence, Archfit's decision path is deterministic. That makes it suitable for CI and prevents an LLM from becoming the authority that decides whether a build passes.

Optional AI features can summarize, explain, classify, or help draft configuration. They remain outside the gate. AI may help narrate the evidence; it does not own the verdict.

This separation matters:

  • analyzers collect facts;
  • the core evaluates declared rules;
  • renderers produce text, JSON, SARIF, Markdown, scorecards, and agent tasks;
  • optional AI enrichment stays advisory.

Modularity is context engineering

We usually describe modularity in terms of maintainability, ownership, testability, or independent change.

With coding agents, there is another benefit: modularity limits the context required to make a safe decision.

A module with a stable public contract tells both humans and agents:

You need to understand this interface. You do not need to understand every implementation detail behind it.

Weak boundaries do the opposite. To change one feature safely, the developer or agent must reason about a growing part of the repository. More context means more tokens, more opportunities to miss a constraint, and more retries when a locally plausible change breaks something elsewhere.

In that sense, modularity is a form of context engineering for code.

Coupling is not the enemy

Archfit is not based on the idea that all coupling is bad. Useful software requires components to collaborate.

The better question is whether the coupling is appropriate for the relationship.

Archfit's coupling scorecard is informed by Vlad Khononov's Balanced Coupling model, which considers three dimensions:

  • integration strength: how much knowledge crosses the boundary;
  • distance: how far apart the coupled components are;
  • volatility: how likely the connected components are to change.

A strong dependency inside one cohesive module may be entirely reasonable. The same dependency across independently deployed, frequently changing components may be a serious risk.

The score is therefore evidence for a conversation, not a universal definition of good architecture. Hard gates should protect explicit rules. Softer signals should reveal trends and areas worth reviewing.

What Archfit cannot govern

Archfit is a structural sensor, not an automated architect.

It cannot tell you that:

  • a business rule is conceptually wrong;
  • a technically valid public API is badly designed;
  • a configured architectural policy no longer matches reality;
  • a runtime interaction creates risks that static dependencies do not show;
  • the intended architecture itself should change.

Those remain engineering decisions.

The goal is not to remove architecture review. It is to make repeatable structural evidence cheap enough to collect on every relevant change.

Faster change needs faster feedback

When development accelerates, slow feedback does not merely delay delivery. It allows more incorrect assumptions to accumulate before anyone notices.

We already respond to faster code generation with automated tests, linters, security scans, and CI gates. Architectural constraints belong in that loop too—especially when coding agents are producing a growing share of the changes.

The core principle is simple:

If an architectural decision matters repeatedly, make its evidence executable.

Archfit is open source under the Apache-2.0 license. You can try it on an existing repository without turning on a CI gate: generate a starter configuration, run an analysis, and inspect what the dependency graph says about the architecture you actually have.

GitHub logo alexei-led / archfit

Architecture-fitness CLI for AI agents and CI: deterministic multi-language gates, Balanced Coupling scorecards, SARIF, and agent repair tasks.

archfit

CI Release Version GHCR Go Reference Go Report Card License

Does this change keep the architecture healthy? archfit answers — with a decision, not a number.

archfit is a one-command architecture-fitness CLI. It reads how your code is actually wired (from language analyzers like go list, dependency-cruiser ast-grep, grimp), checks it against the architecture you declared in .archfit.yaml, and gives you a clear verdict: a decision, a CI gate, a banded scorecard, and — when an AI agent breaks a boundary — a structured repair task.

Built for AI agent and CI workflows: deterministic output, pipe-friendly leads with what to do.

$ archfit
ARCHFIT RESULT

Decision   ACCEPTABLE WITH WATCH ITEMS
Gate       PASS  ·  0 blocking
Warnings   55 advisory
Score      43 / 100  mixed

Acceptable with watch items. Monitor flagged areas.

No blockers. Use this run for architecture-improvement planning,
not to stop development.

RECOMMENDATIONS

  MUST FIX
    none
  SHOULD FIX
    · bc/imbalanced_coupling — high fan-in into session state
  WATCH

The repository, documentation, examples, and installation instructions are available at github.com/alexei-led/archfit.

This article is adapted from my longer post, The Tests Are Green. The Architecture Is Not: Why I Built Archfit, originally published in ITNEXT on Medium.

How are you protecting architectural boundaries when agents can generate changes faster than humans can review them?

Top comments (0)