Skip to content

Architecture checks for large codebases

Turn implicit architecture knowledge into deterministic checks.

Maat helps teams capture the coupling, boundaries, and codebase rules that usually live in a few developers' heads.

Maat is not a linter, an AI reviewer, or a diagram generator. It is a way to turn architectural observations into collectors, deterministic rules, and a ledger of explicit decisions about findings.

Why this exists

Large codebases collect rules that are hard to see from one file: which modules may talk to each other, which data shapes are public, which duplicated policies must stay consistent, and which shortcuts have become dangerous.

Those rules often live in review comments, ADRs, debugging sessions, and the memory of people who know the codebase well. Maat gives teams a path to make that knowledge explicit.

A team can write collectors for the facts its codebase needs, write rules for the policies it cares about, and keep the accepted exceptions in version control.

The invisible coupling

The hardest coupling isn't in import graphs. It's in semantic patterns: multiple code paths executing the same domain operation with different invariants, shared data structures carrying incompatible assumptions across bounded contexts, configuration surfaces that govern the same action from disconnected panels.

These patterns are invisible to linters, type checkers, SAST, and unit tests. Each function works correctly in isolation. Types compile. Tests pass. The damage accumulates silently — orphaned data, broken audit trails, compliance gaps that surface months later.

Maat was born to make these patterns detectable. The direction is clear: specialized collectors (including LLM-assisted ones) gather semantic facts from code, comments, and commit history. Deterministic rules check those facts against policies the team chooses to encode. LLMs for gathering judgment. Rules for guarantees.

New and existing codebases

Greenfield

Write the rules before the shortcuts settle in.

New systems can encode package boundaries, layer rules, and purity constraints from the first commit. maat check fails the pull request before active findings become normal ledger state.

  • Prevent accidental dependencies before they become precedent.
  • Keep domain code independent from infrastructure and framework details.
  • Review architecture rules as code.

Brownfield

Start from what the codebase already taught you.

Existing systems can turn repeated review notes and manual architecture analysis into checks, baseline existing findings in dedicated ledger PRs, and keep fixed fingerprints from quietly regressing.

  • Separate new violations from existing debt.
  • Track accepted exceptions in the same repository history as the code.
  • Turn repeated review comments into checks.

Read more about greenfield and brownfield workflows

Design choices

Deterministic analysis

Same collected facts, same findings. No hidden state, no randomness, no LLM judgment inside the gate.

File-based history

The ledger records findings and state changes in a file that can be committed with the codebase. Maat stores the decision; repository history stores who made it.

Incremental adoption

The tool should work before the codebase is clean. Existing violations can be baselined or resolved over time.

Official rules from the maat-tools/maat repository are deterministic by guarantee. Third-party plugins are supported through public interfaces, but their behavior is the responsibility of the package author and the team that installs them.

Read more about the determinism guarantee and the plugin system.

What happens when it runs

Collect factsCollectors turn repository structure, metadata, or team-specific signals into facts.
Check rulesRules compare those facts with the policies the team chose to encode.
Report findingsViolations are shown with stable fingerprints.
Update ledgerAccepted findings and decisions are stored with the repository.

Configuration example

Maat ships with a TypeScript collector and built-in rules for package and layer boundaries. Teams can add their own collectors and rules for codebase-specific problems.

ts
import { defineConfig } from '@maat-tools/core'
import { layer } from '@maat-tools/coupling-rules'
import { Pure } from '@maat-tools/coupling-rules/roles'

export default defineConfig({
  check: { strict: true },
  collectors: [['@maat-tools/collector-ts', { tsConfigFilePath: './tsconfig.json' }]],
  rules: [
    layer('@myapp/domain').is(Pure).allows('@myapp/contracts'),
    layer('@myapp/infra').allows('@myapp/domain', '@myapp/contracts'),
  ],
})

Manual claims

bash
maat axiom declare \
  --id "domain-purity" \
  --scope "@myapp/domain" \
  --claim "The domain layer has no infrastructure dependencies." \
  --note "Keeps the domain testable without spinning up real I/O."

Fitness functions

Maat rules implement the fitness function concept from Building Evolutionary Architectures (Ford, Parsons, Kua, Sadalage): automated checks that measure how well a system adheres to its intended architectural characteristics.

Each rule evaluates collected facts against a policy the team chose to encode. When a rule fails, the finding signals that the codebase is drifting from the shape the team wants to preserve. Over time, adding and refining rules guides the architecture's evolution in a deliberate direction.

Read more about how Maat implements fitness functions

FAQ

Is Maat a linter?

No. Linters usually check local syntax, style, or common code patterns. Maat is for architecture rules that need facts from more than one place in the repository.

How is it different from dependency rules?

Dependency rules are one use case. Maat can express package and layer boundaries, but the collector model is broader: rules can run over any fact type a collector provides.

How is it different from architecture unit tests?

Architecture tests usually pass or fail at one point in time. Maat adds lifecycle: baseline existing findings and mark fixed fingerprints as resolved so exact regressions are caught later.

Are Maat rules fitness functions?

Yes. In the vocabulary of Building Evolutionary Architectures (Ford, Parsons, Kua, Sadalage), Maat rules are fitness functions: automated, objective checks that verify architectural characteristics. Collectors gather the facts, rules evaluate them, and findings report deviations from the desired shape.

Can we write our own checks?

Yes. Maat is built around plugins: collectors gather facts, rules evaluate them, and the kernel keeps the check path deterministic.

Status

Maat is pre-1.0. The CLI can run checks, sync findings with the ledger, and move decisions through baseline and resolve flows.

The model is stable enough to inspect and experiment with, but package APIs can still change while the collector and rule interfaces settle.

Released under the Apache-2.0 License.GitHub