Solution Architecture Documents That Survive the First Code Review
Almost every solution architecture document has the same life story. Someone writes it during design, before a line of implementation code exists. It gets reviewed, gets a version number, gets approved in a meeting where everyone nods at the diagram. Then implementation starts, and within two or three sprints the document is describing a system that no longer exists — not because anyone lied in it, but because nobody's job was ever to keep it true.
This isn't a discipline problem you can fix by asking people to try harder. It's a structural problem: the document and the system it describes live in different places, updated by different processes, on different schedules, reviewed by different people. Fixing it means changing where the document lives, not writing sterner reminders to update it.
The document that was right for exactly one week
Picture the typical sequence. A solution architect spends two weeks producing a Word document or a slide deck: context diagram, component diagram, a sequence diagram for the critical flow, a paragraph on the chosen integration pattern, a section on non-functional requirements. It goes to an architecture review board. It gets approved — maybe with a few comments about naming, maybe a note that the retry strategy needs more detail. The document is now the record of what will be built.
Implementation starts. In week one, the team discovers the third-party payments provider doesn't support the synchronous call pattern the document assumed, so the integration becomes asynchronous with a webhook callback. In week three, code review flags that the "Notification Service" the document treats as one component is actually going to be two — one for transactional email, one for in-app alerts — because bundling them made the deployment story worse. In week five, someone adds a caching layer in front of the read-heavy lookup service because a load test came back ugly. None of these are architecture failures. They're exactly what implementation and code review are supposed to surface: things you couldn't know until you were building the real thing.
Each of these changes is real, is reasonable, and is invisible to the document. The document was approved as a gate before implementation, and there is no step in anyone's process called "go back and edit the Word doc" that has the same urgency as "ship the sprint." Updating the document is always correct in principle and never anyone's actual job in practice. It becomes a ticket in a backlog labelled "update architecture docs," and that ticket loses every prioritization fight against a customer-facing bug, forever, because nothing downstream depends on it being done — until, eventually, something does.
Why the drift stays invisible until it's expensive
The reason this failure mode is so persistent is that the cost of drift is deferred and diffuse. Nobody notices the document is wrong on the day it goes wrong, because nobody is checking the document against the system on any regular cadence. The cost shows up much later, and lands on someone who wasn't in the room when the drift happened:
- A new engineer onboards using the architecture document as their map, builds a mental model from it, and spends their first month quietly confused about why the real system doesn't match the diagram — usually without ever raising it, assuming they're missing something.
- A security review starts from the document's data flow diagram, misses the caching layer that was never added to it, and clears a review that didn't actually cover the thing that's holding a copy of customer data.
- An architect gets asked, eight months later, "does anything call the old billing service directly," answers from the document because it's the only artifact that claims to know, and is wrong — because three other teams have quietly started calling it since the document was written.
- A second solution architecture document, written for a related project, cites the first one as a source of truth for how a shared component works, and inherits the drift as an unstated assumption baked into a new design.
Every one of these costs would have been cheap to avoid at the moment the drift happened — a sentence added to a document, a diagram box moved. They become expensive precisely because they're discovered much later, by someone who had no reason to suspect the document was stale, and who now has to reconstruct the truth from the actual code, the actual infrastructure, and whoever remembers the meeting where the decision was made.
The fix is not a better document — it's a different location
The instinct most organizations reach for is process: mandate a quarterly documentation review, add "update the architecture doc" as a definition-of-done checkbox, appoint someone to own documentation hygiene. These help a little and fail for the same reason every time — they add a separate, low-priority obligation on top of a workflow that already has a higher-priority one. As long as updating the document is a distinct action from making the change, it will lose to shipping the change, because shipping the change is what anyone is actually accountable for.
The alternative is to stop treating the document as separate from the change in the first place. If the architecture description lives in the same repository as the code, stored in a form that diffs cleanly, and is reviewed in the same pull request that implements the change it describes, then updating it isn't a second obligation — it's part of doing the first one properly. A reviewer who wouldn't approve a PR that changes behavior without updating the relevant test won't approve one that adds a new external dependency without updating the model that's supposed to describe the system's dependencies, for exactly the same reason: it's part of the same change, reviewed by the same person, at the same time, under the same scrutiny.
This is the premise Mooodels is built around for architecture specifically: the architecture is a semantic model whose serialized form is deterministic and Git-friendly, so a solution architecture description can be a versioned artifact in the repository rather than a document that lives somewhere else entirely. Changing the model is a normal part of a pull request. Reviewing that change is a normal part of code review. And because the model is semantic rather than a picture, a reviewer can be shown what actually changed — which elements, which relationships, which properties — instead of being asked to eyeball a new export of a diagram against an old one.
What a real model diff buys you in review
Take the payments integration example from earlier. Before the change, the relevant slice of the model is small and unremarkable: an order service that handles the order lifecycle, a payments service standing in for the third-party integration, an order database, a SQL dependency from the order service to that database, an HTTPS dependency from the order service out to the payments service, and a landscape view that takes in all of it.
The implementation team discovers, during the sprint, that the payments provider requires an asynchronous webhook pattern rather than the synchronous call the design assumed, and that confirmation needs a small queue-backed worker in between. The PR that implements this carries the model change with it, and what the reviewer is shown is not a redrawn picture but a precise account of what moved:
| Change | Element or relationship | Detail |
|---|---|---|
| Added | PaymentsWorker (application) | Consumes payment confirmation webhooks |
| Added | PaymentEvents (queue) | New internal queue between provider and worker |
| Modified | OrderService → PaymentsService | Protocol changed from synchronous HTTPS to asynchronous HTTPS with webhook callback |
| Added | PaymentsService → PaymentEvents | Webhook delivery |
| Added | PaymentEvents → PaymentsWorker | Queue consumption |
| Added | PaymentsWorker → OrderDB | SQL |
Six changes, each one a fact about the system rather than a difference in pixels, each one attached to the commit that introduced it and to the code that made it true. Compare that with what the same change looks like in the Word-doc-and-diagram world: a screenshot of a sequence diagram gets swapped for a new screenshot of a different sequence diagram, and the only way to know what actually changed is to hold both images side by side and spot the difference by eye, if anyone thinks to do it at all. A diagram export has no diff. Two PNGs are either identical or they're not, and "not identical" tells you nothing about what moved. A model with a stable, ordered serialization diffs the way code diffs — change by change, with each addition, removal, and modification visible directly in the review, exactly where a reviewer is already looking.
The reason that account can be rendered at all is that the underlying change is expressed against identities rather than labels. The relationship that changed protocol is the same relationship it was before, carrying the same stable identifier, so the diff can say "modified" rather than "one thing disappeared and a similar-looking thing appeared." That sounds like a technicality until a reviewer is looking at a fifteen-item change and needs to know which items are genuinely new dependencies and which are existing ones that were merely retyped or renamed — a distinction that decides whether the change needs a security sign-off or not.
This is also where a deterministic, low-noise serialization stops being a nice-to-have and starts mattering directly. A format that reorders elements alphabetically on every save, or re-serializes properties in a different order depending on which client last touched the file, produces diffs that are mostly noise — three real changes buried in two hundred lines of cosmetic reordering, which is worse than no diff at all because it trains reviewers to stop reading them. A stable, ordered serialization means the diff a reviewer sees is the change that was actually made, and nothing else.
A diagram screenshot tells you less than you think
There's a deeper issue with diagram-as-picture review than "it doesn't diff." Even looking directly at a diagram, a human reviewer has to reconstruct meaning from geometry — this box is bigger, that arrow is now dashed, a new rectangle appeared near the bottom — and infer what that's supposed to represent. It's slow, it's easy to miss something subtle, and it depends entirely on the diagram actually having been redrawn accurately, which is the same discipline problem in miniature.
A model-based change doesn't ask a reviewer to infer anything. The PR diff above states, as a semantic fact rather than an inference from pixels, that OrderService's dependency on PaymentsService changed protocol, and that a new component and a new queue were introduced between them. If the reviewer's job includes checking that no new dependency crosses into a restricted zone without sign-off, or that every new integration declares a protocol, that's a fact they can check directly against the diff — the same way they'd check a code diff for a missing null check, not the way they'd squint at two screenshots.
This becomes concrete in exactly the scenario the title of this piece describes: a pull request that adds a new dependency on the payments service. In a diagram-first world, that fact might never make it into any document at all — nobody redraws the landscape diagram for a change that felt like "just adding a queue," and six months later nobody documented that the platform now has a live dependency on a third-party webhook. In a model-based PR, the new dependency is a line in the diff, attached to the same change that introduced it, reviewed by the same person who reviewed the code. It either gets committed alongside the code, or it doesn't — but it can't quietly not happen the way "update the doc later" quietly doesn't happen.
Rules that fail a PR the way a broken test does
Text-in-Git and PR review get you visibility. Deterministic rules get you enforcement, which is the part that survives a busy sprint when nobody has the bandwidth to read every diff carefully. A rule against the model is the same idea as a linter against code: a check that's exhaustive, repeatable, and doesn't depend on a human reviewer happening to notice something on a Friday afternoon.
Some rules that are trivial to state and genuinely useful to enforce automatically:
- Every application tagged
publicmust route through the approved API gateway — no direct external-facing dependency bypassing it. - Every relationship crossing from an internal zone to a third-party system must declare a protocol and an owner.
- No new dependency may be introduced on a component tagged
deprecated. - Every application must have exactly one owning team, and the tag must reference a team that exists in the model.
Wired into CI, a rule like the first one turns "did anyone check that the new payments webhook route respects our gateway policy" from a question a human reviewer might forget to ask into a check that runs on every PR and fails the build if the answer is no — the same shape as a unit test failing because a function now returns the wrong type. Nobody has to remember to look. The PR simply can't merge in a state that violates the rule, which is a much stronger guarantee than "the reviewer probably would have caught that."
This matters specifically for the architecture case because architecture violations are exactly the kind of thing that's easy to miss under review pressure — a new dependency added almost as an afterthought, three lines into a much bigger diff, that happens to cross a boundary nobody meant to cross. A human reviewer scanning a two-hundred-line PR for functional correctness is not reliably also going to notice that one of those lines quietly introduces a policy violation. A rule check doesn't get tired, doesn't get distracted by the rest of the diff, and doesn't need to be reminded it exists.
A worked walkthrough: the permit portal, six months in
It's worth tracing one scenario end to end, because the difference between the two approaches is easiest to see in sequence rather than in principle.
A team is building a citizen-facing permit portal. At design time, the solution architecture document — or, in the model-native version, the initial model committed alongside the first scaffolding PR — describes a straightforward shape: a citizen-facing portal application, a permit API behind it over HTTPS, a permit database behind that over SQL, and a landscape view that shows all three. Three elements, two relationships, one view. Nothing about it is controversial, and in both worlds it is an accurate description of what the team is about to build.
Under the Word-doc approach, this is the picture everyone remembers six months later, because it's the only one anyone drew. Under the Git-native approach, it's the state at commit one, and every PR since has been free to change it because changing it was never more expensive than changing the code.
By month two, load testing forces a caching layer in front of PermitAPI, added in the same PR as the caching code. By month four, a separate PR adds an async notification path when a permit status changes, because product asked for SMS updates a user can opt into — a new queue, a new worker, a new outbound dependency on an SMS gateway, with a rule check confirming the new external dependency declares an owner and a protocol before the PR is allowed to merge. By month six, an audit asks "what does this system depend on outside our network, and who signed off on each one" — a question that, in the model-native version, is answered by querying the model for external-zone relationships and reading off the owner tag on each, in minutes. In the Word-doc version, it's answered by re-interviewing three developers about what they remember building, because the document everyone has on file still shows the month-one shape.
Neither team did anything wrong technically. The portal works in both scenarios. The difference is entirely in whether the description of the system tracked the system, and that difference is exactly the gap between updating a document as a separate, deprioritizable task and updating a model as an inseparable part of the PR that changed the thing it describes.
Where this needs honesty, not oversell
None of this makes the discipline problem disappear entirely, and it's worth being direct about where it doesn't. Putting the architecture in Git makes updating it as easy as updating code, and puts it in front of a reviewer who's already looking at the related change — but a reviewer can still approve a PR without actually reading the model diff carefully, the same way a reviewer can rubber-stamp a code diff without reading it. The rule checks catch what's been written as a rule; anything not yet encoded as a rule still depends on a human noticing. And there's a category of high-level, narrative content — why a particular vendor was chosen over another, what tradeoffs were weighed in a decision three architects debated for a week — that a semantic model isn't the right place for and shouldn't try to hold; that belongs in a decision record referenced from the model, not squeezed into element properties. What changes isn't that documentation becomes automatically perfect. What changes is that keeping it current stops requiring anyone to do extra work outside the workflow they're already in, and stops depending on a separate backlog ticket that competes with every other priority and loses. A reviewer who's already reading a PR line by line for the code change is, at essentially no additional cost, also positioned to read the four-line model diff sitting next to it. That's a fundamentally cheaper place to put the check than a quarterly documentation audit that has to reconstruct, after the fact, everything that drifted since the last one.
Stakeholders who still want a document
None of this argues that a slide deck or a PDF is never useful. An executive steering committee is not going to review a pull request, and shouldn't have to. The point isn't that generated artifacts disappear — it's that they stop being the source of truth and become, correctly, a view generated from one. A landscape view rendered from the current state of the model, exported to a document for a steering committee, is accurate on the day it's generated because it's pulled from the same model the engineering team is actively updating — not a separately maintained copy that someone remembers to refresh before each meeting. Regenerating that view costs nothing extra once the model is current, which is exactly the property the Word-doc approach never had: a document that has to be manually kept in sync with a system that has already moved on.
This is the practical case for Mooodels sitting where it does: a browser-based, model-native tool where the architecture is a real semantic model with a deterministic serialized form, edited either visually on the canvas or by asking an AI assistant for a change it proposes as a reviewable patch — but never bypassing review, whether that review is a human reading a PR or a rule check gating one. That deterministic serialization is what makes "the architecture lives in Git, reviewed like the code" a literal, working sentence rather than an aspiration. The rule engine is what makes "a broken architecture decision fails the build" a real mechanism rather than a hope that someone reads carefully. Neither replaces the judgment of the architect who decides what the rules should be — they just make sure that judgment, once encoded, actually gets applied every time, on every change, instead of once, at approval, before the system had the chance to become something slightly different.
The failure this piece opened with — a document that was accurate for a week and quietly wrong for the following year — isn't a documentation problem so much as a location problem. Move the description of the system to where the system's changes already have to go through review, and the update stops being a separate task that loses every prioritization fight. It becomes part of the change itself, checked the same way, by the same people, at the same moment the system actually moves.
See the model this article describes, working in a real editor.
Try the live demo