DEV Community

Archrad
Archrad

Posted on

Every CI pipeline checks code. Nobody checks architecture. Here's why that matters.

Originally built this as a side project — sharing what I've learned along the way.


Your CI pipeline probably does a lot.

It runs unit tests, scans for vulnerabilities, lints your API spec, builds and pushes a container image.

What it almost certainly doesn't do is check whether your architecture is still what you agreed to build.

The problem isn’t that teams don’t care.
It’s that architecture lives in docs — and CI can’t read docs.

The gap nobody talks about

When a team agrees on a service boundary — no direct database access, auth required on every HTTP entry — that agreement lives in a Confluence doc or a Miro board.

Nothing in your pipeline can read those. So nothing enforces them.

A direct DB connection can merge. A missing auth boundary can ship. Not because nobody cared. Because nothing was gating on it.

Code review is the current answer. But code review happens after implementation. The architecture decision has already been made and built. At that point you're reviewing a fait accompli.

The post-code tools don't close this gap

ArchUnit, Spectral, Axivion, OPA — all solid tools. All operating on code, specs, or running systems that already exist.

The intervention point is after the first commit.

None of them can catch an architecture violation before a line of code is written.

What a pre-code gate looks like

Your architecture is a graph — services are nodes, dependencies are edges. Express it as a machine-readable IR. Run a deterministic compiler on it before code generation begins.

# Use your existing OpenAPI spec — no IR authoring required
npx @archrad/deterministic ingest openapi --spec your-api.yaml --out graph.json
npx @archrad/deterministic validate --ir graph.json
Enter fullscreen mode Exit fullscreen mode

No security definitions in your spec → IR-LINT-MISSING-AUTH-010 fires automatically:

⚠️  IR-LINT-MISSING-AUTH-010: HTTP entry node "payment-api" has no auth node
    or auth config in its immediate graph neighbourhood
    Fix: Add an auth/middleware node or set config.authRequired: false for
    intentionally public endpoints.
    Impact: Unauthenticated HTTP entry points are a compliance gap in regulated
    environments and a common attack surface.
Enter fullscreen mode Exit fullscreen mode

Direct DB connection in the graph → IR-LINT-DIRECT-DB-ACCESS-002:

⚠️  IR-LINT-DIRECT-DB-ACCESS-002: API node "payment-api" connects directly
    to datastore node "card-db"
    Fix: Introduce a service or domain layer between HTTP handlers and persistence.
    Impact: Harder to test, swap storage, or enforce invariants at a single boundary.
Enter fullscreen mode Exit fullscreen mode

Same graph. Same compiler version. Same findings. Every time.

High level flow

graph IR (JSON/YAML)
        ↓
archrad validate  ← blocks on violations
        ↓
archrad export
        ↓
code + OpenAPI + Docker
        ↓
archrad validate-drift ← blocks on divergence
        ↓
rest of CI (tests, security scans)

Enter fullscreen mode Exit fullscreen mode

Machine-readable JSON output. CI-gateable. Blocks the PR.

Honest limits

Cold start is real. You have to get your architecture into graph IR format. OpenAPI ingestion helps for teams that already have specs. IaC ingestion — Terraform, CloudFormation — is roadmap, not shipped.

This doesn't prove semantic correctness. Drift detection means "code matches what the IR would generate" — not that your code is correct or matches production behavior. That's your tests.

No round-trip. Once you edit generated code there's no built-in path back to the IR. Think of it as scaffold plus contract validation, not full lifecycle sync.

The CI pipeline this enables

graph.json committed to repo
        ↓
archrad validate --fail-on-warning   ← blocks PR if architecture violated
        ↓
archrad validate-drift               ← blocks PR if code drifted from IR
        ↓
Spectral                             ← lint generated OpenAPI spec
        ↓
Snyk / Semgrep                       ← scan generated code
        ↓
Merge → deploy → operate
Enter fullscreen mode Exit fullscreen mode

ArchRad runs first because structural violations should block before other tools waste time scanning code that shouldn't exist yet.

Try it

OSS under Apache-2.0. No IR authoring needed to get started:

# Ingest your existing OpenAPI spec
npx @archrad/deterministic ingest openapi --spec your-api.yaml --out graph.json
npx @archrad/deterministic validate --ir graph.json

# Or run against the built-in ecommerce fixture
npx @archrad/deterministic validate --ir fixtures/ecommerce-with-warnings.json
Enter fullscreen mode Exit fullscreen mode

👉 github.com/archradhq/arch-deterministic
📦 npm install -g @archrad/deterministic


Closing question: does your CI pipeline gate on architecture today?

And if not — is that a tooling problem, a process problem, or just accepted as inevitable?

Top comments (0)