CI/CD pipeline architecture.
Two things decide whether people trust a pipeline: how fast it is, and whether a red build actually means something is broken.
The components
Every row below is read from the graph that produced the diagram above, so the two cannot disagree.
| Component | Tier | Why it is there |
|---|---|---|
| Developer | Client | Supporting component |
| GitHub | External | Supporting component |
| Webhook receiver | Application | Supporting component |
| Job scheduler | Application | Fans a pipeline into a job DAG |
| Runners | Application | Ephemeral, so no state leaks between builds |
| Test stage | Application | Supporting component |
| Security scan | Application | Supporting component |
| Deployer | Application | Progressive rollout, automatic rollback |
| Build cache | Data | The single biggest lever on build time |
| Image registry | Data | Supporting component |
| Artifact store | Data | Supporting component |
| Production cluster | Infrastructure | Supporting component |
Design decisions worth arguing about
A diagram shows what was chosen. It does not show what it cost, and that is usually the part that matters in a review or an interview.
Ephemeral runners, so builds cannot contaminate each other
Long-lived runners accumulate state: leftover dependencies, files from previous jobs, a cache that is subtly wrong. That state produces builds that pass on one runner and fail on another, which is the most expensive kind of flakiness because it looks like a code problem. Fresh containers eliminate it, at the cost of losing everything a warm machine had, which is exactly what makes an external cache mandatory rather than optional.
The cache is the main lever on build time
Once runners are ephemeral, most of a build is re-downloading and recompiling things that have not changed. A shared cache keyed on a lockfile hash usually cuts build time more than any amount of parallelism. The risk is correctness: a cache key that is too coarse serves stale artifacts and produces builds that succeed against dependencies you are not actually shipping, which is a genuinely dangerous failure because nothing looks wrong.
Fail fast, but only on signal
Ordering cheap checks before expensive ones gives faster feedback and cheaper failures. The tension is with flaky tests: a pipeline that fails randomly trains people to rerun without reading, and once that habit forms the pipeline has stopped being a safety mechanism regardless of its coverage. Quarantining flaky tests aggressively is worth more than adding new ones.
Progressive rollout so a bad deploy is bounded
Deploying everywhere at once means the blast radius of a bad release is everything. Rolling out to a fraction while watching error rates bounds it, and automatic rollback bounds the duration. The cost is that a deploy takes longer and you must run two versions simultaneously, which forces backward-compatible database migrations as a standing discipline rather than a special case.
How it changes with scale
Queue time, not build time, is what people experience as a slow pipeline, and it is a function of concurrency limits rather than of any individual job. Monorepos change the problem: without change detection, every commit builds everything, and the pipeline gets slower as the repository grows regardless of what was actually touched.
Where it breaks first
Cache poisoning. A corrupt or mis-keyed cache entry gets reused across builds, and the resulting failures look like code problems in unrelated places. Because the cache is invisible in the logs, this is usually diagnosed only after someone thinks to clear it, which is why cache keys should be conservative and manual invalidation should be a one-click operation.
Draw this yourself
Open the Diagram tab and describe the system. The agent emits a semantic graph rather than coordinates, so you can edit the components and the layout re-solves instead of drifting.
When the shape is right, Implement in code turns the canvas into a markdown specification, every component, every relationship and the notes, and starts a real turn in the Code tab with it.
Questions about this design
More templates
- URL Shortener System DesignThe canonical read-heavy system: roughly a hundred reads for every write, and a redirect that has to be fast enough that nobody notices it happened.
- Chat Application System DesignLong-lived connections change everything: the hard part is not storing messages, it is knowing which of your servers is holding the socket you need to write to.
- Payment System DesignThe only system on this list where being approximately right is indistinguishable from being wrong, and where the provider, not you, holds the truth.
- RAG Pipeline ArchitectureAlmost every RAG system that disappoints is failing at retrieval, not generation, and the architecture is what decides whether you can tell.