Skip to main content

ADR Drift

Deterministic architecture tests for any language. ADRs rot the moment they merge — this keeps them mechanically true. Define layers and constraints once, then adr-drift check fails CI when code drifts from the decisions.

The problem

"The moment an ADR gets merged, it decays into a passive historical record unless something mechanical forces it to stay alive."

Developers — and now AI coding agents — merge boundary violations because nothing checks decisions against code. ArchUnit is Java-only, deptrac is PHP-only. ADR Drift is language-agnostic, repo-local, and CI-friendly.

Install

git clone <repo> && cd adr-drift
pip install .   # provides the `adr-drift` console script

Or run without installing: python3 -m adr_drift from the repo root.

Quick start

cd your-repo
python3 -m adr_drift init      # scaffold arch.yaml + sample ADR from folder layout
# tighten the rules in arch.yaml, add ## Constraints to your ADRs
python3 -m adr_drift check     # verify; exit 1 on violations (CI-ready)
# repo already has legacy violations? grandfather them, then enforce:
python3 -m adr_drift baseline                    # record current violations
python3 -m adr_drift check --baseline .adr-baseline.json

Commands

Command What it does
init Infer layers from folder layout, write arch.yaml + sample ADR + index
check Verify the repo against arch.yaml + every ADR's ## Constraints (--baseline FILE grandfathers recorded violations)
baseline Write current violations to .adr-baseline.json (default) for check --baseline
drift --since HEAD~5 Report violations introduced since a git ref, plus violations fixed since then (pre-existing ones ignored)
new "title" Create the next-numbered ADR from a template
index Regenerate the one-sentence-per-ADR index (docs/adr/README.md)

The constraint DSL

Rules live in arch.yaml and in any ADR's ## Constraints section:

forbid-import: handlers -> db.drivers     # layering: handlers may not import drivers
forbid-pattern: src/**.py !~ \beval\(     # no eval() anywhere in src/
require-pattern: src/handlers/**.py =~ authenticate\(   # handlers must authenticate

from/to match layer names or module prefixes (db.drivers matches db.drivers.pg); * wildcards supported. Comments start with #. To-side patterns resolve against layer paths too: in a src/-layout repo both db.drivers and src.db.drivers spellings match a db layer mapped to src/db/**.

How it checks

  • Python is parsed with the real ast — no regex false positives from comments or strings.
  • JS/TS/Go/Rust/Java/Ruby/PHP use a heuristic regex import extractor (documented as such; --strict-py-only disables it).
  • drift compares violations at HEAD against the base ref via git show — it never touches your working tree, and git blame tags each new violation with its introducing commit.

CI

- run: python3 -m adr_drift check --format json --fail-on violations

--fail-on none reports without failing. JSON output is stable for tooling.

Honest limits

  • The regex fallback for non-Python languages is heuristic — it can miss exotic import forms. Python is precise; everything else is best-effort.
  • drift needs a git repo; check works anywhere.
  • Rules are only as good as the constraints you write — init scaffolds, you tighten.

Tests

55/55 pass: python3 -m unittest discover -s tests

Metadata

Release files for adr-drift 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for adr-drift 0.1.0
File Size Uploaded Provenance
adr_drift-0.1.0.tar.gz 23.2 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for adr-drift 0.1.0
File Interpreter ABI Platform
adr_drift-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 42.6 kB

Release files / adr_drift-0.1.0.tar.gz

Download URL adr_drift-0.1.0.tar.gz
Size 23.2 kB
Tags Source
SHA-256 checksum
How to use checksums
4cfd86c2d6cf163f30a447533e799c1ccc57deadd27b455bf3d7306efd954906
BLAKE2b-256 checksum
How to use checksums
a07f3c3547386b22bcd11ef7687941019cde974d002305b5ce6cc5747ab6e449
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Release files / adr_drift-0.1.0-py3-none-any.whl

Download URL adr_drift-0.1.0-py3-none-any.whl
Size 19.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
135d814dba9a87cfe5c2235b1402d6df474da6e70eb823450c73c9245a422991
BLAKE2b-256 checksum
How to use checksums
c2e24c16229ee93e3b793fcc3f133bdc316c0c99be0405284c565b58f2a42231
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Security for adr-drift 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page