What goes wrong when nobody owns the pipeline
In most young engineering teams the pipeline grows by accretion. One developer wrote the first workflow, another added a deploy step, a third pinned a version to fix a Friday outage. Two years later it is the most critical system nobody is responsible for. Builds take twenty minutes, one test fails every fifth run, a deploy token was created by someone who has left, and a release is blocked because a runner ran out of disk.
Pipeline ownership fixes this by giving it a named owner. It is a core piece of our Managed DevOps service, and it connects tightly to Kubernetes operations because the pipeline is how changes reach the cluster.
What good looks like
- Fast feedback. Pull request checks finish quickly enough that developers stay in flow.
- Reliable. A red build means a real problem. Flaky tests are quarantined and fixed, not retried until green.
- Reproducible. Tool versions are pinned, so the same commit builds the same artefact.
- Secure. No long-lived cloud keys in CI, scoped secrets, and branch protection on main.
- Reversible. Every deploy can be rolled back by one command or one button.
- Reusable. New repositories inherit the pipeline instead of copying and editing it.
For a wider survey of practices, see our guide to CI/CD pipeline best practices.
How it works in practice
Choose the tool you have
We work with GitHub Actions, GitLab CI and Jenkins. The right one is usually the one your source control already provides. Jenkins remains reasonable when you have heavy custom plugins or on-premises constraints, but its upkeep is real: controller upgrades, plugin compatibility and agent management. If that burden is the problem, our guide to moving Jenkins to GitHub Actions explains how we phase it.
Reusable pipelines
Copy-pasted YAML drifts. We centralise logic in reusable workflows (GitHub), includes or components (GitLab) or shared libraries (Jenkins). A service repository then calls the shared definition:
name: ci
on:
pull_request:
push:
branches: [main]
permissions:
id-token: write
contents: read
jobs:
build-test:
uses: your-org/platform-workflows/.github/workflows/service-ci.yml@v3
with:
language: node
image-name: checkout
secrets: inherit
Pinning the shared workflow to a version tag means we can change the platform pipeline and roll it out deliberately rather than breaking every repository at once.
Credentials without long-lived keys
Using OpenID Connect, a workflow exchanges a short-lived identity token for a scoped cloud role. There is no access key to leak or rotate. The role trust policy limits which repository and branch can assume it. This single change removes the most common CI security finding we see, and it fits naturally with the compliance and IAM reviews we run.
Deploys that can be undone
Build once, promote the same artefact through environments. Deploys run health checks and stop if they fail. For Kubernetes, we often pair the pipeline with a GitOps controller so that deployment is a Git change, as described in our GitOps service. Rollback is then a revert, which is easy to explain to auditors and easy to do at 3am.
Decisions and trade-offs
Hosted versus self-hosted runners. Hosted runners are zero maintenance but can be slow or costly for heavy builds. Self-hosted runners give control and sometimes lower cost, but you inherit patching and isolation. We start hosted and move specific jobs to self-hosted only when there is a measured reason.
Monorepo versus many repositories. Monorepos need path filtering and caching to avoid rebuilding everything. Many repositories need reusable templates. Either works; the mistake is not choosing deliberately.
Gates versus speed. Required reviews, security scans and approvals improve safety and add friction. We apply heavier gates to production and lighter ones to pull requests.
When not to buy this. If you have a single repository, a deploy a month and a developer who enjoys the pipeline, ownership may be more than you need. Consider a one-off CI/CD project instead.
Making builds faster
Slow pipelines are rarely slow for one reason. We work through the usual suspects in order of effort versus payoff.
- Cache dependencies. Package manager caches keyed on the lockfile avoid downloading the internet on every run.
- Cache Docker layers. Order the Dockerfile so rarely changing steps come first, and use a remote layer cache so a fresh runner does not rebuild from scratch.
- Parallelise tests. Split the suite by timing data rather than by file count so shards finish together.
- Skip what did not change. Path filters stop a documentation edit from running the full integration suite.
- Right-size runners. A build that is CPU bound on a small runner may be cheaper and faster on a larger one, because it finishes sooner.
- Fail fast. Run linting and unit tests before slow stages, so a typo does not consume fifteen minutes of compute.
We measure before and after, and we report honestly when a change did not help. Build speed matters beyond comfort: slow checks encourage large, infrequent pull requests, which are harder to review and riskier to ship.
Pipelines and the cluster
If you deploy to Kubernetes, the pipeline should hand off to the platform cleanly. Pipelines build and test the image and update a manifest or chart version; the cluster pulls the change. That separation means CI no longer needs administrator credentials for production, which shrinks the damage from a compromised pipeline. It is also how we keep the work aligned with cluster operations.
Common failure modes
- Flaky tests normalised. Teams add retries until a real regression slips through. We track flake rates and fix root causes.
- Floating versions. Using
latestfor actions or base images means builds break without a code change. We pin and use an update bot to bump deliberately. - Secrets in logs. Debug output echoing tokens. We scan and mask.
- Runner sprawl. Orphaned self-hosted runners with old credentials. We inventory and expire them.
- One giant pipeline. Everything runs on every change. We split by path and risk.
Delivery and handover
We begin with an audit and a baseline of build duration, failure rate and queue time. Stabilisation comes next, then standardisation and security. From then on, pipeline incidents go through the same incident process as production, and changes are reviewed in your repositories. Nothing is locked into our tooling: if you leave, you keep every workflow file.
How to tell it is working
Watch median pull request check time, build failure rate excluding genuine test failures, time from merge to production, and the number of manual deploy steps. If those trend in the right direction and developers stop mentioning the pipeline in retrospectives, it is doing its job.
To discuss your current setup, book a consultation.