Writing Architecture Rules That Governance Teams Can Enforce
Every architecture policy document has a section called something like "Principles," and every one of those sections contains a line that reads, roughly, "applications must not access databases directly across domain boundaries." It's a good sentence. It's also, in most organizations, fiction — not because anyone disagrees with it, but because nothing ever checks it. The application that violates it was built eighteen months ago, works fine, and nobody has looked at it since the review that approved the original design. The principle exists. The violation exists. They coexist peacefully because nothing connects them.
This is the gap between a principle and a rule. A principle is a sentence a governance board agreed on. A rule is a check that runs against every model change and produces a pass or a fail. The two look similar on paper — often literally the same sentence — but only one of them does anything after the meeting where it was approved ends. This article is about what separates the two, with worked examples of rules that hold up under mechanical checking and rules that only sound like they do, and about what changes in a governance function once "did anyone review this" stops being the only available question.
Why policy documents don't enforce anything
A principle written in a Word document or a wiki page is, structurally, a piece of prose aimed at a human reader who is expected to remember it at the moment it matters and apply judgment about whether it applies. That's a reasonable thing to ask of a reviewer sitting in a design authority meeting looking at one system. It's not a reasonable thing to ask of an organization with four hundred applications, four architects, and a release cadence that doesn't wait for anyone to reread the principles document before every change.
The failure mode isn't laziness. It's arithmetic. If a principle is only checked when a human happens to look at the right diagram at the right time, then the fraction of the portfolio that's actually in compliance with it is a function of review coverage, not of the principle's merit. A well-written principle that gets reviewed against ten percent of relevant changes produces roughly the same outcome as a badly written one: the organization believes it has a rule, and mostly doesn't have one. Governance teams tend to discover this the hard way — during an audit, an incident postmortem, or a new architect's first week, when someone asks "wait, is this actually true everywhere, or just in the diagram we usually show auditors?"
None of this is an argument against writing principles down. It's an argument for treating "written down" and "enforced" as two entirely different achievements, and for being honest about which one a given policy document has actually accomplished. A principle earns the second one only when it can be restated as something a machine can evaluate — which is a narrower, more specific thing than most principles are written to be.
What makes a rule genuinely enforceable
An enforceable rule has one defining property: it can be expressed as a deterministic query over the model. Not over intentions, not over documentation, not over what a reviewer believes to be true — over the actual elements, relationships, tags, and properties that exist in the architecture model at the moment the rule runs. If the answer to "does this pass or fail" depends on information the model doesn't contain, the rule isn't enforceable yet, no matter how sensible it sounds.
Breaking that down, three things have to be true simultaneously:
- The subject is identifiable by type or tag. The rule has to be able to say, unambiguously, which elements it applies to — "every ApplicationComponent tagged internet-facing," not "important customer-facing systems," which requires a judgment call about what counts as important.
- The condition is a structural fact, not an opinion. "Has an owner property set" is structural. "Has adequate technical documentation" is an opinion, and a reasonable one for two reviewers to disagree about looking at the same evidence.
- The check terminates with a boolean. A rule either passes or fails for a given element. There's no partial credit, no "mostly compliant," no result that still requires a person to interpret before anyone can act on it.
This is exactly why deterministic rules in Mooodels live separately from AI-assisted review, rather than being folded into it. An AI assistant is well suited to reading a rule violation and explaining what it means, or proposing a fix — it's poorly suited to being the thing that decides whether the violation exists in the first place, because that decision needs to be the same decision every time, for every reviewer, on every run, without drift. A rule engine that gives a different answer depending on model temperature or phrasing isn't a governance control; it's a second opinion that happens to sound authoritative. Deterministic rules and AI assistance solve different problems, and conflating them quietly weakens both.
Rules that actually work
The clearest way to see what "expressible as a query" means in practice is to work through rules that hold up, written the way they'd actually be expressed against a model — element types, relationship types, and properties, evaluated mechanically rather than argued about.
Public applications must use the approved gateway
The organization has decided that anything exposed to the public internet routes through one sanctioned API gateway — for rate limiting, for a single point of TLS termination, for one place to revoke access in an incident. Written as policy prose, this is a sentence in a security standard. Written as a rule, it's a check on the relationship graph: any ApplicationComponent tagged exposure:public must have its inbound traffic relationship originating from the approved gateway element, not from an arbitrary external actor or another application directly.
rule public_apps_use_approved_gateway {
for each ApplicationComponent where tag(exposure) == "public"
require exists incoming relationship
from Element where id == "gw-approved-api-gateway"
type == "serves"
}
This is checkable because "public" is a tag someone set deliberately (not inferred), the approved gateway is a specific, named element with a stable ID, and "serves" is a relationship type the model already distinguishes from other kinds of traffic. There's no interpretation required — either the incoming edge exists and points at the right element, or it doesn't.
Tier-1 applications must have an owner
This is the simplest useful rule in most governance frameworks, and also the one most often left unenforced purely through inertia — an application gets classified Tier-1 during an assessment, the owner field is left blank because nobody was sure who to put yet, and it's still blank two years later.
rule tier1_requires_owner {
for each ApplicationComponent where tag(criticality) == "tier-1"
require property(owner) is not empty
}
What makes this genuinely enforceable rather than aspirational is that it doesn't ask whether the owner is the right owner, or whether they're actually engaged with the system, or whether the ownership is documented anywhere outside the model — all reasonable governance concerns, none of them checkable by a machine. It asks only whether the field is populated. That's a narrower claim than "this system has proper ownership," and it's honest about being narrower. A narrow, true rule that runs on every change beats a broad, aspirational one that runs never.
Internet-facing services need authentication metadata
Security reviewers routinely need to answer "which of our externally reachable services have no documented authentication mechanism" — usually right before an audit, usually by hand, usually incompletely. As a rule, this is a property-presence check scoped by tag, similar in shape to the ownership rule but worth calling out separately because it composes naturally with the gateway rule above: an element can pass one and fail the other, and a governance dashboard benefits from being able to say which.
rule internet_facing_requires_auth_metadata {
for each ApplicationComponent where tag(exposure) == "public"
require property(auth_mechanism) in ["oauth2", "mtls", "api-key", "saml"]
}
Note the closed list. "Require an auth_mechanism property to exist" is weaker than it looks, because someone can satisfy it by typing "handled elsewhere" into the field and moving on. Constraining the value to a known set of acceptable mechanisms turns a box-ticking exercise back into an actual check — which is a distinction worth generalizing: a property-presence rule is only as strong as the values it accepts.
No direct cross-domain database access
This is the rule from the introduction, and it's worth showing what it looks like once it's actually enforceable, because the plain-English version conceals a structural question the rule has to answer: what counts as "direct," and what counts as "cross-domain."
rule no_direct_cross_domain_db_access {
for each relationship r
where r.type == "accesses"
and source(r).type == "ApplicationComponent"
and target(r).type == "Database"
and domain(source(r)) != domain(target(r))
require false
}
The mechanism this replaces — "an ApplicationComponent may reach a Database belonging to another domain only through an intermediary ApplicationService" — reads naturally as an intermediary requirement, but it's evaluated the same way: as a direct-edge prohibition between the two element types across domains, with the required pattern being a component-to-service-to-database chain instead of a direct component-to-database edge. What makes this rule powerful is what it makes visible: an integration that has quietly grown from "call the reporting service" to "just query the reporting database directly, it was faster" shows up as a rule violation on the next model change, not as a finding six months later when someone happens to trace the connection during an incident.
Rules that sound good but aren't actually checkable
Governance documents are full of principles that read well and fail the one-sentence test the moment you try to turn them into a query. Recognizing these before they're written into a rules engine — and failing loudly, or silently, every time they run — matters as much as writing the good rules above.
"Architecture should follow good practice"
This isn't a rule, it's a mission statement. It has no subject that can be identified by type, no structural condition, and no way to terminate in a boolean. There's nothing to convert here — it needs to be broken into the specific practices it's gesturing at, each of which might become a real rule in its own right, or might not survive being made specific enough to check.
"Systems should be well documented"
This one is closer, and it's a useful case because the fix is almost mechanical. "Well documented" isn't checkable — but "has a non-empty description property," "has at least one owner tag," and "has at least one view it appears in" are each checkable individually. The honest move is to stop pretending there's a single rule called "well documented" and instead run three or four narrow presence checks, each of which is true independent of the others. None of them, alone, proves the documentation is good. Together, they catch the common failure mode, which is documentation that doesn't exist at all rather than documentation that exists but isn't quite good enough — and that narrower failure mode is exactly what a linter, not a governance rule, is built to catch.
"Integrations should be loosely coupled"
Coupling is a real architectural property and a real concern, but "loosely coupled" as stated has no threshold. Turned into something checkable, it usually splits into two different things with two different owners: a structural sub-rule that can be enforced ("no synchronous relationship type between elements tagged as belonging to different bounded contexts, without an intermediary"), and a metric that's worth surfacing on a dashboard without being a pass/fail gate ("fan-in and fan-out counts per application, flagged when unusually high relative to the rest of the portfolio"). The first is a rule. The second is exactly the kind of signal that belongs in graph analysis and linting output rather than being forced into a boolean it can't honestly produce.
"New designs should be reviewed by architecture before build starts"
This is a process principle, not a model rule, and it's worth naming as its own category rather than trying to force it into the model-query shape. It's genuinely important, and it's genuinely not the kind of thing a rule engine evaluating element types and relationships can check — there's no reliable structural signal in the model itself for "a human looked at this before anyone wrote code." Trying to fake a proxy for it (a "reviewed" tag someone sets by hand) just relocates the enforcement gap one level down: now the rule about whether review happened is only as trustworthy as whoever remembers to flip the tag, which is precisely the manual-review problem this whole exercise was trying to get away from.
"Reduce technical debt"
Almost every architecture strategy document contains this phrase, and it's the purest example of a principle with no queryable subject at all. What's usually meant by it, on inspection, decomposes into several genuinely checkable things: applications with no maintained owner, components built on end-of-life platforms tagged as such, relationships that bypass an approved pattern. Each of those is a rule. "Reduce technical debt" as written is a slogan wrapping several rules that haven't been separated out yet — and the separating-out is usually where most of the actual governance value gets created, because it forces someone to say specifically what "debt" means in this portfolio rather than gesturing at the idea of it.
| Sounds like a rule | What's actually missing | What to do with it |
|---|---|---|
| "Should follow good practice" | No queryable subject at all | Discard, or decompose into named practices |
| "Should be well documented" | "Well" has no threshold | Split into presence checks; run as lint, not a gate |
| "Should be loosely coupled" | Coupling is a spectrum, not boolean | Structural sub-rule + a surfaced metric, not one rule |
| "Reviewed before build starts" | Not observable in the model | Process control, not a model rule — track outside the tool |
| "Reduce technical debt" | No agreed definition of "debt" | Decompose into the specific rules it's standing in for |
Rules versus linting: two different jobs, worth keeping separate
It's tempting, once a governance team has a rules engine, to route everything through it — including things that are really quality signals rather than pass/fail gates. Resisting that temptation matters, because the two have different consequences when they fire.
A rule violation is a governance finding: a Tier-1 system with no owner, a public application bypassing the gateway, a cross-domain database edge that shouldn't exist. It's specific, it's binary, and it usually implies someone is accountable for fixing it on a timeline.
A lint warning is a quality signal: an orphaned element nothing references, a component with no description, a naming inconsistency between two views of the same domain. These are worth surfacing — a model full of undescribed, unreferenced elements is a model nobody trusts — but treating every one of them as a governance failure with the same weight as a security control being bypassed trains people to ignore the whole list, rule violations included. The fix that reads badly on a dashboard ("47 warnings!") but is actually correct governance design is separating the count that requires sign-off from the count that's advisory. Mooodels keeps these as genuinely separate outputs for exactly this reason — a rule failure and a lint warning look different, are triaged differently, and don't get to cannibalize each other's urgency by sharing one undifferentiated red badge.
How automatic evaluation changes the governance conversation
The practical effect of moving from principle to rule isn't just that violations get caught earlier — it's that the question a governance board is actually answering changes shape.
The old question is "did anyone review this?" It's a question about process, and it has an uncomfortable property: the honest answer is often "someone reviewed something, at some point, that was probably related." It's satisfied by a checkbox in a change ticket, a name in a sign-off field, a meeting that happened. None of those actually confirm the architecture is compliant — they confirm that a human process occurred, which is a weaker claim than most governance frameworks pretend it is.
The new question is "did it pass?" That's a question about the model, and it has a much less forgiving property: it's either true or it isn't, and it's true or false about the actual current state, not about the state at the time someone last looked. A change either introduces a direct cross-domain database edge or it doesn't. A Tier-1 application either has an owner populated right now or it doesn't. There's no "reviewed and approved with reservations" state for a deterministic rule — which is uncomfortable the first time an organization adopts one, because it removes a kind of ambiguity that governance meetings have historically used to defer hard calls.
This has a second-order effect worth naming: it changes what a review meeting is for. When rule checking is automatic, the meeting stops being where violations get discovered — they were already flagged, before anyone sat down — and starts being where the interesting cases get argued: the legitimate exception, the rule that's technically failing for a defensible reason, the case where the rule itself needs revising because it no longer matches how the organization actually wants to operate. That's a better use of a governance board's time than manually re-deriving, diagram by diagram, facts a machine could have confirmed before the meeting started. It also means governance board time scales with the number of genuinely contestable cases, not with the size of the portfolio — which is the opposite of how manual review scales today.
There's a trust dimension too. "Did anyone review this" is a claim about the past that's hard to verify after the fact — was the review thorough, did the reviewer actually check the thing that mattered, or did it get rubber-stamped under deadline pressure. "Did it pass" is a claim anyone can re-verify at any time, by rerunning the same deterministic check against the current model. An auditor doesn't have to trust that a review happened correctly six months ago; they can rerun the rule today and get the same answer the governance process got, because it's the same check, not a different person's judgment applied after the fact. That reproducibility is, in practice, the difference between governance evidence and governance folklore.
Getting from policy document to working rule set
In practice, turning an existing principles document into a working rule set is less about writing rule syntax and more about triage. Every principle in the document falls into one of three buckets, and the honest categorization is most of the work:
- Directly enforceable as written, once the underlying tags and properties exist in the model. Ownership requirements, approved-pattern requirements, presence-of-metadata requirements. These convert almost mechanically — the example rules earlier in this article are all from this bucket.
- Enforceable after decomposition. Broad principles like "well documented" or "loosely coupled" that split into two or three narrower checks, some of which become rules and some of which become lint signals or dashboard metrics instead.
- Not enforceable in the model at all. Process requirements, review requirements, anything whose evidence lives in a ticketing system or a person's memory rather than in the architecture itself. These stay as documented process, honestly labeled as process rather than dressed up as an automated control.
The first pass through a real principles document is usually humbling — a large fraction of what looked like rules turns out to belong in bucket two or three. That's not a reason to abandon the exercise; it's the exercise doing its job. An organization that comes out the other side with fifteen genuinely enforceable rules, a clear lint checklist, and an honest list of process controls it isn't going to automate has a governance posture that's smaller on paper and considerably more real than the fifty-principle document it started with.
The underlying shift is a small one to describe and a significant one to live with: a rule is only doing governance work if it runs whether or not anyone remembers to ask it to. Everything else — however well-intentioned, however carefully worded, however unanimously agreed on in the meeting where it was adopted — is still waiting to become one.
See the model this article describes, working in a real editor.
Try the live demo