Build1 publisher3 min readPublished
The archrule glob makes your package tree the architecture spec
The New Stack's case for moving architecture rules out of the wiki and into pytest holds up on mechanism. The two sample rules also show the bill: enforcement runs on module-name globs, and the domain-purity check bans two libraries where its own docstring promises an allowlist.
The Engineer · Build desk

What happened
- The New Stack argues that AI agents merge functionally correct code which quietly crosses domain boundaries, and names the resulting gap between writing speed and human understanding comprehension debt.
- Its remedy is to write boundaries as pytest tests using pytest-archon, which the article presents as the Python counterpart to Java's ArchUnit, so that a violation fails the build.
- The worked example is a modular monolith e-commerce app with two boundaries: billing must never import shipping, and domain models must never import infrastructure such as the AWS SDK or SQLAlchemy.
- Adoption as shown is a dependency install plus a test_architecture.py holding archrule chains that match module names by glob, name a forbidden import prefix, and check one package root.
- The published text stops at a heading reading Step 3: Close, so the CI/CD pipeline wiring the piece builds toward never actually appears.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- constraint Because rules bind to dotted module paths, a team whose directory layout does not already match its domains must reorganise the repo before any rule fires at all, which puts the real adoption cost in file moves rather than in the test file.
- contradiction The sample purity rule's docstring promises standard library plus pydantic while its code denies two named packages, so anyone who copies it ships a gate materially weaker than the comment sitting above it.
- cost Someone now owns a denylist that has to grow with every new infrastructure dependency, and a rule that has fallen behind does not fail the build; it passes quietly.
- capability Once a boundary is a named test in the suite, a violation produces a machine-readable failure an agent can read and retry against, which is feedback a wiki page cannot give it.
Read the rule before the argument. The billing check the article prints is one chain: `archrule("billing_isolation").match("ecommerce.billing*").should_not_import("ecommerce.shipping*").check("ecommerce")` [13]. Every argument in it is a dotted module path with a trailing wildcard, and the same package root reappears in `check()` [16]. That fixes the enforcement surface. The rule fires on modules whose import-time names match the pattern, and on nothing else [16].
So if your billing logic sits in `ecommerce.services.billing_calc` and `ecommerce.api.orders`, the pattern `ecommerce.billing*` matches neither, and the suite is green because there was nothing to look at. The second rule is stricter still: `.match("ecommerce.*.models")` requires every domain to keep its models in a module actually named `models` [14]. Adopting this is not a `pip install` [11] followed by a test file [12]. It is a repo reorganisation, and that is the part that costs a sprint.
The same rule shows the other gap. Its docstring says domain models should depend only on the standard library and pydantic, while the code that runs denies `sqlalchemy*` and `boto3*` [14]. Those are different statements. An agent that reaches for httpx, or for any ORM the list does not name, satisfies the rule exactly as written [15]. A denylist of two encodes yesterday's infrastructure choices, and the stale entry never announces itself.
What the material does not settle is whether `should_not_import` looks only at direct imports or follows the graph [19]. That question is load-bearing given the author's own failure model, which is that an agent finding an easier route to its objective will take it, including skipping a service layer [5]. A direct-only check is cleared by one hop through a shared helper module. This is the first thing to read in the docs, and the article's Step 3 is headed "Close" with the text ending there, which is an awkward place for a CI tutorial to stop [17].
The piece asserts that reviewers cannot keep up with thousands of AI-generated pull requests, so architectural detours pass review unseen [7], and that a junior developer's bad code breaks the build while 500 correct lines that subtly cross a boundary merge cleanly [4]. It reports no violation rate and no before-and-after comparison [18]. The diagnosis rests on argument rather than measurement, and the conclusion transfers to your repo only if three conditions hold: merge volume has to exceed what your reviewers can actually read [7], boundaries have to be expressible as forbidden imports between named packages [9], and your package tree has to already draw the domains you think you have [16].
Where that holds, this is good, cheap engineering: the boundary becomes a test with a name, checked on the same pipeline as everything else [6]. Where it does not, you get a green suite that certifies two library bans. The thing the author is actually worried about, the team's model of why the codebase exists [2], lives outside the import graph, where a CI job cannot reach it.
What to watch
- Whether pytest-archon's own docs specify direct versus transitive import checking, which decides if one hop through a shared helper clears the rule.
- The missing Step 3: whether the recommended pipeline wiring makes archrule failures blocking or merely reported.
- Whether anyone publishes boundary-violation rates from agent-authored pull requests, the number this argument currently runs without.