The Problem with Architecture Advice
Most architecture tools tell you what you already know. Linters catch style. Type checkers catch types. Complexity metrics give you a number without telling you what it means. What they don't catch is the kind of structural rot that makes a codebase unmaintainable over months and years.
And when you go looking for advice, you get dogmas. "Never use booleans in function signatures." "Always write tests." "Single responsibility, always." Nobody tells you when these rules start strangling the system. Nobody shows you the real failure cases.
ArchDogma is my attempt at something different. It's a static analysis tool for Python that catches architectural anti-patterns — not style, not formatting, but structure. And it tries to be honest about what it actually knows.
What It Does
Install it:
pip install archdogma
Run it on a Python file or directory:
archdogma probe mymodule/
archdogma probe src/api.py
It analyzes your Python code with AST-based detectors and flags architectural
issues with severity levels: critical, high, medium, low.
Output is clean JSON — pipe it into whatever you want.
The 11 Detectors in v0.1.0
These are the patterns ArchDogma catches today. Not aspirational — actually implemented, actually tested.
null-return
Functions that return None mixed with real values. The caller has to null-check
everywhere, or it crashes. Severity: high.
if-on-parameter
Functions that branch on a boolean parameter. This is a sign that you have two functions
masquerading as one. The classic: def process(data, dry_run=False) — split it.
Severity: medium.
magic-numbers
Numeric literals in logic that aren't 0 or 1. if retries > 3 — why 3?
What changes when the system scales? Magic numbers are undocumented assumptions.
Severity: medium.
dynamic-magic
Dynamic attribute creation at runtime via setattr, __dict__,
or vars(). Makes the object model impossible to reason about statically.
Severity: high.
function-complexity
Functions that do too many things: high cyclomatic complexity, too many parameters, too many lines. Each is a separate threshold with its own severity.
class-complexity
Classes with too many public methods. Usually a sign of a class that accumulated responsibilities over time rather than being designed with one clear purpose.
file-complexity
Files that span too many concerns: too many classes, too many top-level functions, too many imports. A useful proxy for "this file is doing too much."
circular-import
Circular import chains between modules. These cause subtle runtime errors and make the dependency graph impossible to understand.
module-docstring
Missing module-level docstrings. Low severity, but forces you to articulate what a module is actually for. If you can't write one sentence — it's a smell.
broad-except
except Exception: or bare except: blocks. You're catching
things you didn't intend to catch, hiding bugs, and making debugging impossible.
Severity: high.
mutable-default-arg
def foo(items=[]) is a Python classic that bites everyone eventually.
The default is shared across all calls. Use None and initialize inside.
Severity: high.
Why 281 Tests
Every detector has its own test suite. Each test proves one specific case: the pattern fires when it should, doesn't fire when it shouldn't. No magic coverage numbers — just scenarios that correspond to real code structures.
The test count matters because detectors break silently. A regex or AST traversal that worked for one code pattern can miss another. 281 tests means we've explored the space of inputs enough to trust the output.
It's also honest about what it can't do. ArchDogma doesn't analyze runtime behavior. It doesn't understand semantics. It catches structural patterns that tend to correlate with problems — it can't prove causation.
The Catalog of Dogmas
The other part of ArchDogma is the dogma catalog. Each architectural anti-pattern detected by the tool links to a catalog entry with:
- What the pattern is
- Under what conditions it actually causes problems
- Real failure cases with links to post-mortems
- Counter-dogmas — when the conventional wisdom is wrong
The rule: no link, no claim. If we can't find a real-world case where the dogma
failed, we mark it [NEED POSTMORTEMS] and don't pretend we know more
than we do. Three dogmas are fully filled with sources: microservices-for-everything,
TDD-as-law, DRY/premature-abstractions. The rest are drafts.
This is the part that makes ArchDogma different from other linters. Most tools tell you "this is bad." ArchDogma tries to tell you why, and under what conditions it might actually be fine.
How to Use It
Basic probe of a file or directory:
archdogma probe src/
Output issues filtered by severity:
archdogma probe src/ --min-severity high
JSON output for CI integration:
archdogma probe src/ --format json | jq '.issues[] | select(.severity == "critical")'
Browse the dogma catalog:
archdogma catalog
archdogma catalog --tag null-return
What's Next
v0.1.1 will add two more Tier 1 detectors: god-class (classes with too many responsibilities measured by method count + coupling) and deep-inheritance (inheritance chains that go more than 2-3 levels deep). These require ClassDef walker extensions to the AST analyzer that aren't in v0.1.0.
After that: repo-level analysis (import graphs, dependency cycles across packages), and better catalog coverage with more post-mortems. Eventually — integration with the Function Probe so the tool can say "this pattern appears in functions that also have X and Y, which historically correlates with production incidents."
But that's the roadmap. v0.1.0 is on PyPI, works, and is honest about what it catches.