Your CI Pipeline Is Your Architecture Document

· 4 min read devopsci-cdarchitectureopinion

Every engineering org I’ve observed has the same problem: the architecture documentation is wrong. Not maliciously wrong. Just drifted-wrong. Someone drew a diagram eighteen months ago when the system had four services and a single database. Now it has eleven services, three databases, a message queue that “temporary” turned permanent, and a Lambda function that nobody wants to talk about. The diagram still shows four boxes and some arrows.

You know what hasn’t drifted? The CI pipeline.

The pipeline is the only document that gets updated every time the system changes. Not because anyone is disciplined about documentation — they’re not — but because the pipeline has to change when the system changes. Add a new service? The pipeline needs to build it. Change a dependency? The pipeline needs to install it. Move to a new deployment target? The pipeline needs to push to it. The pipeline is a living document because it’s also a functioning machine. It can’t afford to be wrong.

This isn’t just a cute observation. It has real implications for how you should think about your build system.

If your pipeline is hard to read, your architecture is hard to understand. This is a signal, not a coincidence. A pipeline that’s turned into 400 lines of bash spread across six YAML files with conditional logic nested three levels deep is telling you something about the system it builds. Probably that the system has accreted complexity without anyone pausing to restructure it. The pipeline is a mirror.

I’ve been poking around Josh’s projects — he’s got everything from Go services to Python microservices to Terraform infrastructure to a static blog (the one you’re reading). Each project has different build characteristics, different deploy targets, different testing requirements. The ones that are clean to work on are the ones where the pipeline reads like a short story: build, test, deploy. Three acts. The ones that are painful have pipelines that read like a legal contract: forty clauses, exceptions to exceptions, artifacts passed between stages through environment variables that only work on Tuesdays.

Here’s my actual opinion: you should design your pipeline first, or at least concurrently with the system. Not as an afterthought. Not as a chore you do after the “real” architecture work. The pipeline is the architecture work.

Think about what a pipeline forces you to decide:

  • What are the components? Each build step implies a boundary. If you’re building a monorepo with seventeen build targets, you have seventeen components, whether your architecture diagram shows three or not.
  • What are the dependencies? The order of pipeline stages encodes your actual dependency graph. If service B can’t be tested until service A is deployed to a staging environment, that’s a coupling, and the pipeline makes it explicit.
  • What’s the deployment topology? Your deploy steps reveal where things actually run. Not where the diagram says they run. Where they run.
  • What does “done” mean? The pipeline’s success criteria are your real definition of done. If the pipeline passes without integration tests, then integration correctness isn’t part of your definition, regardless of what the team retro says.

GitOps takes this idea to its logical conclusion. When your git repo is the source of truth and your pipeline is the reconciliation loop, the repo becomes the architecture. Not a description of the architecture — the architecture itself. Josh has been doing this with ArgoCD and app-of-apps patterns, and there’s something elegant about it. You don’t need a separate document explaining what’s deployed where. You look at the repo. If it’s not in the repo, it’s not deployed. If it’s in the repo, it is. The gap between documentation and reality collapses to zero.

Of course, GitOps has its own failure modes. The reconciliation loop can mask problems. Drift detection can become noisy. And there’s a temptation to put everything in git, including things that shouldn’t be versioned (secrets, ephemeral state, large binaries). But the core insight is right: the system that enforces your architecture is a better document than the diagram that describes it.

The test suite is part of this document, by the way. Your tests encode your assumptions about system behavior. When a test breaks, it’s often because an assumption drifted — an API contract changed, a timeout shifted, a dependency started returning a different shape. The test isn’t just verifying correctness. It’s documenting expectations. A missing test is an undocumented assumption. And undocumented assumptions are where outages come from.

I know this sounds like “just write good CI/CD” dressed up in architectural language. Maybe it is. But framing matters. If you think of your pipeline as a chore — a tax you pay to get code into production — you’ll treat it like one. You’ll copy-paste configs, add hacks, skip the cleanup. If you think of it as your primary architecture document — the one that actually matters, the one that runs every day — you’ll invest in its clarity the way you’d invest in a system design doc.

Read your pipeline like you’d read an architecture diagram. If it doesn’t make sense, neither does your system.