Hard-earned hindsight.
If we rebuilt our pipelines today, we would not start with tools.
We would start with principles.
What We Would Do Differently
We would:
- standardize earlier
- automate more validation
- avoid manual gates by default
- version pipelines intentionally
- design for change, not perfection
Most importantly, we would assume growth.
Standardize earlier. We let teams build custom pipelines for too long. The cost of standardization increases with every unique pipeline. If we started over, we would establish standard templates from day one. Customization would be the exception, not the norm.
Automate more validation. Manual approval gates added latency without adding safety. We would invest in comprehensive automated checks instead. Security scans, contract tests, smoke tests—anything that can be validated mechanically should be. Save human judgment for truly ambiguous scenarios.
Avoid manual gates by default. Manual approval should require justification, not automation. The burden of proof should be on adding friction, not on removing it. Most deployments should flow automatically if all checks pass.
Version pipelines intentionally. Treat pipeline templates like APIs. Version them explicitly. Communicate breaking changes. Give teams time to migrate. We learned this the hard way after breaking dozens of pipelines with an unannounced change.
Design for change, not perfection. We spent too much time trying to build the “right” pipeline upfront. Requirements changed faster than we could design for them. We would focus on making pipelines easy to evolve instead of trying to anticipate every future need.
Assume growth. We built for our current team size and current deployment frequency. Both grew faster than expected. If we started over, we would design for 10x scale from the beginning. Not because we would reach it immediately, but because retrofitting scalability is painful.
What We Would Keep
We would keep:
- clear ownership
- boring defaults
- predictable behavior
- strong feedback loops
Those aged well.
Clear ownership. Having a dedicated team own the pipeline platform was one of our best decisions. It turned CI/CD from a shared responsibility (which means no responsibility) into a product with accountability.
Boring defaults. We resisted the urge to adopt every new tool and pattern. We chose boring, proven technologies. Azure Pipelines may not be exciting, but it is stable and well-documented. That reliability compounds over time.
Predictable behavior. Our pipelines are not the fastest or the most flexible. But they are consistent. The same input produces the same output. Failures are reproducible. That predictability builds trust.
Strong feedback loops. We invested heavily in fast feedback. Failed builds notify within seconds. Test results are visible immediately. Deployment status is clear. Engineers do not have to hunt for information. This responsiveness makes pipelines feel reliable even when they break.
The Biggest Lesson
CI/CD maturity is not about speed.
It is about confidence.
Fast pipelines that cannot be trusted slow teams down. Predictable pipelines enable progress.
We used to optimize for build time. Faster builds, faster deployments. We cached aggressively. We parallelized everything. We cut corners on validation to save seconds.
This backfired. Fast but flaky pipelines erode confidence. Engineers retry builds hoping for different results. They add manual verification steps. They deploy during low-traffic windows to minimize risk. All of that friction costs more time than the build time we saved.
When we shifted focus to reliability, deployment frequency increased. Not because pipelines got faster, but because engineers trusted them enough to deploy more often. Confidence is the real multiplier.
Final Thought
Pipelines grow up whether you plan for it or not.
Designing them as a platform early does not remove pain. It moves it to a place where it is manageable.
Related reading: