Practice : Continuous Integration (CI)
Purpose and Strategic Importance
Continuous Integration (CI) is the discipline of integrating and testing code changes frequently, ideally with every commit. This practice is fundamental to accelerating delivery, maintaining technical quality, and improving developer confidence.
In environments without CI, integration is manual, slow, and risky - issues often surface late, and small problems snowball into major rework. CI eliminates these risks by catching issues early, making quality a continuous concern, not a final gate.
By embedding CI as a default practice, teams reduce integration friction, increase confidence in change, and lay the foundation for continuous delivery.
Description of the Practice
- Every change to the codebase triggers an automated workflow that builds, tests, and validates the change.
- The CI pipeline typically includes steps like linting, static analysis, unit tests, packaging, and security scanning.
- CI builds are expected to complete quickly and reliably - within 10–15 minutes for most services.
- Build failures are treated as team-level concerns, and a red pipeline blocks progress until resolved.
- CI status is visible to the team and integrated with version control and pull request workflows.
How to Practise It (Playbook)
1. Getting Started
- Choose a CI tool that aligns with your version control system (e.g. GitHub Actions, GitLab CI, Azure Pipelines).
- Create a basic CI config file in your repo (e.g.
.github/workflows/main.yml
) that:
- Installs dependencies
- Runs linters
- Executes unit tests
- Ensure CI runs on every commit and pull request, including branches and forks.
- Turn on branch protection to block merging unless all required CI checks pass.
- Document the CI pipeline structure in the README so all contributors understand what’s being tested.
2. Scaling and Maturing
- Add quality and security gates to the pipeline:
- Static analysis tools (e.g. SonarQube, ESLint)
- Security scanning (e.g. OWASP Dependency-Check, Snyk)
- Test coverage reporting
- Optimise for speed and reliability:
- Use parallel jobs for different stages (e.g. lint, test, build)
- Cache dependencies to reduce redundant work
- Fail fast - exit early if critical issues are detected
- Encapsulate pipeline logic in reusable templates if working across multiple services.
- Make the build visible:
- Post status updates to Slack or Teams
- Show build badges in the repo
- Create a dashboard for pipeline health and historical failures
- Audit your pipeline periodically to remove redundant steps and ensure alignment with evolving standards.
3. Team Behaviours to Encourage
- Treat a red build as a blocker - not an inconvenience.
- Swarm on pipeline failures - fix as a team, don’t leave it to one individual.
- Celebrate improvements to build speed and reliability.
- Include pipeline improvements in sprint planning and retrospectives.
- Rotate ownership of pipeline maintenance to grow collective knowledge.
4. Watch Out For…
- Flaky tests that occasionally fail and get ignored - these erode trust in CI and encourage bypassing the process.
- Slow pipelines that reduce feedback frequency and hinder developer flow.
- Overly complex CI logic that only a few engineers understand or can debug.
- Manual steps that block full automation, like approvals before testing or environment setup.
5. Signals of Success
- Teams consistently merge via green builds - CI is a precondition, not an afterthought.
- Build failures are treated as shared issues and resolved quickly (within hours, not days).
- CI config is visible, reviewed like any other code, and regularly improved.
- Developers receive feedback within 5–10 minutes of committing code.
- New contributors can understand and run the pipeline with minimal support.