Skip to content

What is software architecture?

Every non-trivial codebase has an architecture, whether or not anyone wrote it down. It’s not the diagram in a wiki page — it’s the set of decisions that are expensive to reverse: how the system is split into parts, and which parts are allowed to depend on which other parts. Move a function between two files and you’ve made a local change. Decide that the billing module can call into auth but auth can never call into billing, and you’ve made an architectural one — it constrains every line of code written after it.

Two different things go by the name “architecture,” and mixing them up causes most of the confusion:

  • Prescriptive architecture — the intended design. What the team agreed on, or what a diagram, an ADR, or a “layers” doc says should be true. It lives in documents, or in people’s heads.
  • Descriptive architecture — the as-built structure. What the code actually does, extracted from the imports, calls, and inheritance that exist right now, whether or not anyone approved them.

The diagram below makes this concrete with a simplified browser: intended, one-way process boundaries on the left, and the same boundaries as-built on the right, after two extra edges crept in over time.

Chromium-style multi-process browser: intended one-way process boundaries on the left, vs. as-built edges that grew back over time on the right

Intended (declared) As-built (actual)

Browser process Renderer process GPU process Browser process Renderer process GPU process declared actual undeclared, grew over time

Simplified — illustrative, not a complete architecture. Chromium’s multi-process split (browser/renderer/GPU) is real; the two amber “grew over time” edges are a hypothetical example of drift, not a claim about Chromium’s actual internals.

These two start out close, or even identical, on day one of a project. Then they drift apart. A deadline arrives and someone imports across a boundary that was never supposed to be crossed. Six months later a new hire reads the architecture doc, believes it, and builds on top of a structure that no longer exists. The doc isn’t lying on purpose — it’s just describing a system that used to be there. This gap between prescriptive and descriptive architecture is one of the most common, least visible sources of confusion in a growing codebase, and it only widens without something actively checking one against the other.

Nobody can hold the full dependency graph of a real codebase in their head past a few thousand lines, so “what’s the architecture, actually?” turns into an archaeology project: grep for imports, trace call chains, ask around. That’s slow, and it’s already stale by the time you finish, because someone merged a PR while you were reading.

The rest of this section works through the pieces that make up that as-built picture — components, dependencies, recovery algorithms, smells, metrics, and drift — one at a time.

In arcade-agent: this is exactly the gap it targets — it recovers the descriptive, as-built architecture straight from your code, instead of trusting whatever the docs still claim. See Getting started for the actual tool chain.