Architecture checks for large codebases
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.
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 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.
Greenfield
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.
Brownfield
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.
Read more about greenfield and brownfield workflows
Same collected facts, same findings. No hidden state, no randomness, no LLM judgment inside the gate.
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.
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.
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.
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'),
],
})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."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
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.
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.
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.
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.
Yes. Maat is built around plugins: collectors gather facts, rules evaluate them, and the kernel keeps the check path deterministic.
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.