You're reading "CI/CD Is a Dirty Word", written on January 28, 2026.
CI/CD Is a Dirty Word
Most developers don’t like CI. At best, they tolerate it.
It’s that thing you have to wait on and keep half an eye on, just in case the changes you were already emotionally done with decide to boomerang back and ruin your afternoon. Will it be green so you can move on to the next thing? Or will it randomly be red and eat the rest of your day? And how far can you trust the result anyway?
CI is like your car or your fridge. You expect it to work. You don’t start your day genuflecting in gratitude before your own refrigerator. It only really enters your emotional life when it breaks.
Ask any developer whether CI is important and they’ll say yes. Like flossing is important. You just gotta do it.
But unless you built a CI setup on your own, chances are to you it’s just a pile of scripts, configs, and mysterious failures. If you’ve ever heard the expression “a face only a mother could love” — yeah, that was referring to CI pipelines.
Most devs just want to write code and have it show up in production working exactly like it did on their laptop. Instantly. I get it. I was that developer. I am that developer every time I try to ship something.
Unfortunately, wisdom says the “just ship it” dream doesn’t work past a certain size. At least not unless you move mountains behind the scenes to make it possible.
So here we are — tolerating the pipelines in our lives.
The Developer Experience of “CI/CD”
Through the eyes of a regular IC, CI/CD is just a font of friction. It takes forever. It fails randomly through no fault of your changes. It blares a horn so everybody can see you messed up — even if you didn’t. And frankly, who can figure all of this out anyway? You have stuff to do! So you push “retry” and pray. After the third failure, you sigh, roll up your sleeves, and start looking through the test logs. Oh, this looks like Jim’s service. HEY JIM!
So developers optimize for survival. They batch changes to “avoid too many builds.” They hesitate to refactor because “the pipeline will explode.” They treat green builds as luck and red builds as punishment. CI/CD becomes something they appease, not something they rely on — a system of rituals that might, if they’re lucky, let their code through.
That emotional posture — defensive, skeptical, detached — is incredibly common. And it’s rational, given what most pipelines actually are.
How Pipelines Quietly Become Terrible
Bad CI/CD systems rarely start out that way. They grow into it, one reasonable decision at a time.
At first, you add a few sensible steps. Run the tests. Build the artifact. Maybe deploy to a test environment. Then something slips through, so you add another check. A new test suite. A security scan. A longer integration job. Nobody wants to argue against more safety, so the pipeline only ever grows.
Speed, meanwhile, is rarely treated as a requirement. Feedback time stretches little by little until long waits feel normal. Pushing to CI becomes something you do at the end of your day, not something that supports tight iteration.
Test strategy drifts in the same direction. Instead of fast, isolated tests, more confidence gets pushed into slow, fragile end-to-end flows. They’re easier to add than to design properly, and once they exist, they’re very hard to remove — even when many failures have nothing to do with the change under test.
As the system evolves, the architecture starts shaping the pipeline whether anyone plans for it or not. Tightly coupled services mean large integration jobs. Shared state means careful sequencing and cleanup. Heavy dependencies and slow startup times turn simple checks into multi-minute processes. The pipeline has to model the real behavior of the system, so it becomes more complex, more stateful, and harder to reason about.
In that sense, the pipeline becomes a mirror. If the codebase is hard to isolate, tests will be slow and flaky. If components can’t be built or versioned independently, builds will be heavy and tightly coordinated. If environments aren’t truly reproducible, the pipeline ends up depending on hidden state and historical accidents.
Architects play a role here too, often unintentionally. Every new service boundary, async flow, and cross-system integration multiplies the surface area CI/CD has to build, test, version, and coordinate. The pipeline doesn’t create that complexity. It just exposes it.
None of these decisions are outrageous on their own. Together, they produce the slow, fragile, hard-to-trust CI/CD that developers learn to work around instead of rely on.
Why We Keep Making It Worse
None of this happens because developers are careless or architects are villains. It happens because the incentives are completely skewed toward reducing friction today, even if it creates more friction later.
If skipping a test, adding a shortcut, or pushing work into a slower integration stage makes your current task easier, that’s a visible win. The future cost — longer pipelines, flakier runs, harder debugging — is abstract, delayed, and shared with everyone else. So it’s easy to justify. You’re not breaking the system. You’re just trying to get your work done.
Long-lived feature branches are a perfect example. Working in isolation feels safer. You get fewer pipeline runs, fewer integration surprises, fewer interruptions while you’re in flow. The eventual merge is going to be ugly, sure — but that’s Future You’s problem. Or your teammate’s. Or “we’ll deal with it when the time comes.”
What’s missing is that the benefits of good CI/CD are mostly invisible when it works. You don’t see the bugs that never made it to production. You don’t see the integration issues that were caught when they were still small. You don’t feel the disasters that didn’t happen because changes stayed small and constantly integrated.
The visible experience of CI/CD is waiting, red builds, and annoying failures. The invisible experience is stability, recoverability, and the ability to change the system without fear. Humans are very bad at valuing invisible benefits, especially under delivery pressure.
So we trade tomorrow’s reliability for today’s convenience, one small decision at a time. And then we wonder why the pipeline feels like an enemy instead of a safety net.
If You Want a Boring Pipeline, Build a Boring System
There is a world where CI/CD feels like a working fridge. You don’t think about it. You don’t plan your day around it. It just quietly does its job.
Tuning the pipeline won’t do it. You get there by designing the system so the pipeline has an easy job in the first place.
If parts of your system can’t be built, tested, and run in isolation, CI/CD will be slow and fragile. If services depend on shared state, startup order, or “the way we usually deploy things,” your pipeline will turn into a pile of special cases. If something barely works on a developer laptop, it has no chance of being reliable in automated builds and real environments.
Integration environments expose existing flakiness. They are where complexity comes to collect its bill.
So when CI/CD is slow, brittle, or unpredictable, the answer usually isn’t another retry, another timeout, or another stage. It’s changing the system. Breaking dependencies. Simplifying flows. Removing hidden state. Making things smaller and more predictable. That work is architectural, and it’s never as fun as adding features. It’s also the only work that makes the pipeline stop fighting you.
When that work actually happens, builds get faster because there’s less to build. Tests get more reliable because there’s less shared chaos. Deploys get dull because there are fewer surprises. CI/CD fades into the background, because you stopped feeding it a system that’s hard to reason about.
That’s the deal. If you want a pipeline that feels invisible, you have to build a system with as few moving parts as you actually need. Otherwise, CI/CD will keep being the place where reality shows up to argue with your architecture.