
The Patterns That Kill Productivity
Fast CI/CD pipelines aren't about faster computers. They're about smarter design. The organizations with the fastest pipelines avoid common anti-patterns that creep in over time.
Anti-Pattern 1: Sequential Everything
Build the frontend. Wait. Build the backend. Wait. Run unit tests. Wait. Run integration tests. Wait.
This is the most common anti-pattern. Each stage waits for the previous stage, even when there's no dependency.
The fix: Map dependencies explicitly. Frontend and backend builds have no dependency—run in parallel. Unit tests for different modules have no dependency—run in parallel. Only sequence where genuinely required.
A pipeline I reviewed recently went from 45 minutes to 12 minutes just by parallelizing independent stages.
Anti-Pattern 2: Testing Everything Every Time
A typo fix in documentation triggers the full test suite, integration tests, and security scans. This wastes CI resources and developer time.
The fix: Implement affected-based testing. Detect which files changed and run only relevant tests. Monorepo tools like Nx and Turborepo do this automatically. For polyrepo setups, path-based filtering in CI achieves similar results.
Caveat: Always run the full suite before production deployment. Optimization is for rapid feedback during development.
Anti-Pattern 3: No Caching Strategy
npm install downloads packages every build. Docker builds pull base images fresh. Go modules redownload dependencies. Each build starts from zero.
The fix: Cache aggressively. Package manager caches (npm, pip, go modules). Docker layer caching. Build artifact caching. Compiled dependency caching.
GitHub Actions, GitLab CI, and CircleCI all provide caching primitives. Use them.
Anti-Pattern 4: Fat Base Images
Development convenience leads to bloated images. "Let's add that tool in case we need it." Soon you have 2GB images that take 5 minutes to pull.
The fix: Multi-stage builds. Use full development images for building. Copy only artifacts to slim runtime images. Your runtime image should contain only what's needed to run, not what's needed to build.
A typical Node.js app can go from 1GB+ to under 100MB with proper multi-stage builds.
Anti-Pattern 5: Flaky Tests Without Accountability
Tests that pass sometimes and fail sometimes. Everyone knows which tests are flaky. Nobody fixes them. Instead, they're retried automatically until they pass.
The fix: Track flaky tests. When a test is retried, log it. Report flakiness rates weekly. Assign ownership for fixing. Tests above a flakiness threshold get quarantined—they don't block builds but must be fixed within a sprint.
Flaky tests erode trust in the entire pipeline. Fix them.
Anti-Pattern 6: Manual Steps in the Pipeline
"After CI passes, someone manually triggers deployment." "Approvers need to click a button in Jenkins." Manual gates create bottlenecks.
The fix: Automate everything except production deployment approval. Even then, make approval one click—not "review this list of changes, then click approve." The review happens in the PR. The pipeline execution should be automatic given approval.
Measuring Pipeline Health
You can't improve what you don't measure. Track these metrics:
Lead time: commit to production. This is your true velocity. Pipeline duration: start to finish. Where time goes. Success rate: percentage passing first try. Reliability indicator. Flaky test rate: tests requiring retry. Trust erosion indicator.
Review these monthly. Set improvement targets. Pipeline optimization is continuous.