CI/CD Course
CI/CD for Microservices
In this lesson
CI/CD for microservices is the discipline of building, testing, and deploying dozens — sometimes hundreds — of independently deployable services without requiring coordination between teams, shared release windows, or monolithic test suites that touch everything at once. The promise of microservices is independent deployability: one team ships their service on Tuesday, another ships theirs on Thursday, and neither waits for the other. Delivering on that promise requires a CI/CD architecture that is explicitly designed for it — because a microservices system with a shared deployment pipeline, a shared integration test environment, or mandatory cross-service coordination has the operational complexity of microservices without the delivery speed advantage.
Independent Deployability — The Core Requirement
Independent deployability means any service can be built, tested, and deployed without requiring changes to, coordination with, or shared test infrastructure from any other service. It is the defining property that makes microservices architecturally valuable — and the property most commonly destroyed by poor CI/CD design. A microservices architecture where deploying Service A requires Service B to be at a specific version, where integration tests require a shared environment running all twenty services, or where a release coordinator must approve a batch deployment has already lost the independence that justified the architecture in the first place.
Achieving genuine independent deployability requires three things at the pipeline level: each service has its own pipeline triggered by changes to its own code, contract tests replace shared integration environments for cross-service verification, and services are backwards-compatible by design so that new versions can be deployed without requiring simultaneous updates to consumers.
The Restaurant Chain Analogy
A restaurant chain does not close all its branches simultaneously to retrain all its staff on a new menu item. Each branch can introduce the new dish independently, at its own pace, without affecting the others. The head office provides standards — the recipe, the plating guide, the pricing — but each branch runs its own kitchen. Microservices CI/CD works the same way: a platform team provides the shared pipeline infrastructure (the recipe), each service team runs their own pipeline (their own kitchen), and deployments happen independently without central coordination (no simultaneous branch closure required).
Per-Service Pipeline Architecture
Each microservice owns its own pipeline, triggered by changes to its own code. In a polyrepo organisation, this is natural — each repository has its own workflow files. In a monorepo, path filtering (as covered in Lesson 21) ensures each service's pipeline only triggers when that service's code changes. Either way, the pipeline is scoped to the service: build, test, and deploy for this service only, on this commit, without any dependency on the state of other services.
Per-Service Pipeline — Monorepo with Path Filtering
# .github/workflows/payment-service.yml
# Only triggers when payment-service code changes
on:
push:
branches: [main]
paths:
- 'services/payment/**' # Only runs for payment-service changes
- '.github/workflows/payment-service.yml'
pull_request:
paths:
- 'services/payment/**'
jobs:
build-test-deploy:
uses: myorg/.github/.github/workflows/service-pipeline.yml@main # Reusable workflow
with:
service-name: payment
service-path: services/payment
image-name: ghcr.io/myorg/payment-service
secrets: inherit
What just happened?
The payment service pipeline only triggers when payment service code changes. It calls a centralised reusable workflow maintained by the platform team, passing only the service-specific parameters. Every other service in the monorepo has an identical structure — different paths, different service names, same underlying workflow. When the platform team improves the pipeline logic, every service benefits automatically.
Contract Testing at Scale — Replacing Shared Environments
The most common mistake in microservices CI/CD is building a shared integration environment where all services run together, and running cross-service integration tests against it before any service can deploy. This pattern destroys independent deployability completely: deploying Service A now requires Service B, C, and D to be healthy in the shared environment, and a failure in any one of them blocks every other service's deployment.
Contract testing — as introduced in Lesson 16 — replaces this shared environment with a set of bilateral agreements between services. Each consumer service defines what it expects from each provider service. Each provider service verifies in its own pipeline that it can satisfy those expectations. No shared environment is needed. Each service deploys when its own pipeline is green and its contracts are satisfied — not when every other service is also ready. Pact is the most widely adopted implementation, with a Pact Broker centralising the contract exchange between services.
Coordinated Releases — When Independence Has Limits
Not every change in a microservices system can be deployed independently. Breaking API changes, database schema changes that affect multiple services, and major feature launches that must appear atomic to users all require some form of coordination. The goal is not to eliminate coordination entirely — it is to make coordination exceptional rather than routine, and to handle it through patterns that preserve pipeline independence for the common case.
Patterns for Coordinated Changes Without Shared Pipelines
Shared Pipeline Infrastructure — The Platform Team's Role
With dozens of services each running their own pipeline, the maintenance burden of pipeline logic becomes significant without a centralisation strategy. As established in Lesson 21, reusable workflows are the mechanism that prevents pipeline logic from being duplicated across every service repository. In a mature microservices organisation, a platform team owns and maintains the centralised pipeline templates — the standard build workflow, the standard test workflow, the standard deploy workflow — and service teams consume them as versioned references.
Beyond workflow reuse, the platform team typically owns: the container base images that all services build from, the shared Helm chart library that all services deploy with, the contract testing broker that all service pipelines publish to, and the observability dashboards that show the health of every service's pipeline and deployment. This platform layer is the investment that makes independent deployability sustainable at scale — without it, each team reinvents the same pipeline infrastructure separately, and every security patch or performance improvement must be applied fifty times instead of once.
Warning: A Shared Integration Environment for All Services Destroys Independent Deployability
The most common way teams undermine their own microservices architecture is by creating a shared staging environment where all twenty services must be running and healthy before any one of them can deploy to production. This environment becomes the single most expensive maintenance burden in the organisation — it is perpetually broken because twenty services are always in various states of in-progress changes, every deployment of every service creates a new opportunity for the environment to become unhealthy, and debugging which service broke it consumes engineering time that should be spent on features. Replace shared integration environments with contract testing, and deploy each service to its own isolated staging environment scoped to that service alone.
Key Takeaways from This Lesson
Teacher's Note
If your team has a shared staging environment where all microservices must be healthy before any one can deploy, count how many hours per week are spent debugging that environment rather than shipping features — that number is the business case for contract testing.
Practice Questions
Answer in your own words — then check against the expected answer.
1. What is the property that defines a true microservices architecture from a CI/CD perspective — the ability to build, test, and deploy any service without requiring coordination with, or dependency on, the state of any other service?
2. What is the name of the centralised service — used alongside the Pact contract testing framework — that stores consumer-generated pact files and allows provider pipelines to retrieve and verify them, enabling contract testing across dozens of services without direct service-to-service coordination?
3. What API versioning pattern — also used for database migrations — allows a provider to make a breaking change by first adding the new version alongside the old (so consumers can migrate at their own pace), then removing the old version once all consumers have updated?
Lesson Quiz
1. A team with 15 microservices builds a shared staging environment where all 15 services must be running and passing integration tests before any one service can deploy to production. What fundamental CI/CD problem does this create?
2. A new checkout flow requires simultaneous changes to the frontend, payment service, and order service. The teams need the feature to appear atomic to users but want to preserve independent deployability. What pattern achieves both?
3. An organisation has 40 microservices, each with its own pipeline YAML that duplicates the same build and deploy logic. A security patch requires updating the Node.js version in every pipeline. What architecture change would make this a single update rather than 40 pull requests?
Up Next · Lesson 32
CI/CD for Monolithic Applications
Not every system is microservices — and monoliths have their own CI/CD challenges. Lesson 32 covers how to apply modern pipeline practices to large, single-deployment applications without the coordination overhead of distributed systems.