How would you design a CI/CD pipeline for a microservices application?

Honeywell DevOps Engineer 3–5 Years CI/CD

Split the pipeline into two clear phases with different triggers and different guarantees. Continuous Integration runs on every push and pull request: install dependencies, lint, run unit and integration tests, build the artifact (a Docker image), scan it for vulnerabilities, and push it to a registry tagged with the commit SHA, never latest, so every deployed artifact is traceable to an exact commit. Continuous Deployment/Delivery picks up from there: it deploys that exact image to a lower environment automatically, runs smoke tests against it, and only promotes to production either automatically (continuous deployment) or with a manual approval gate (continuous delivery), depending on how much your org trusts the automated test suite.

Microservices-specific considerations

Each service gets its own independent pipeline, not one giant pipeline for the whole system, so teams can deploy independently without waiting on unrelated services. Use path-based triggers in a monorepo (only run service A’s pipeline when files under services/a/ change) or separate repos per service. Deployment order matters far less than most people assume if you enforce backward-compatible API contracts between services and use consumer-driven contract tests, that’s what actually lets you deploy services independently without a fragile “deploy everything in this exact order” runbook.

Best practice

Use a progressive rollout strategy in production, canary or blue-green, not a single all-at-once deploy, so a bad release only affects a small slice of traffic before automated health checks (error rate, latency) either promote it fully or trigger an automatic rollback. Keep the pipeline itself defined as code (Jenkinsfile, GitHub Actions YAML, GitLab CI YAML) and version-controlled alongside the service, not configured by hand in a UI.

Edge case interviewers probe for

Ask what happens to database migrations in this model. The answer: migrations must be backward-compatible with the currently running old version during a rolling deploy, since old and new code run simultaneously mid-rollout. That means additive-only changes go out first (add a new nullable column), the new code starts using it, and only after the rollout fully completes does a later migration remove the old column.

Common mistake

Treating “CI/CD pipeline” as one monolithic script that builds, tests, and deploys everything together with no clear promotion gates between environments, which makes it impossible to know exactly what artifact is running where, or to roll back one service without redeploying everything.

What the interviewer is checking

Whether you can reason about independent deployability, the actual point of microservices, rather than just listing CI/CD tool names.

Imagine a bakery with several independent stations: bread, cakes, and pastries. Continuous Integration is like every station checking their own work the moment they finish a batch, tasting it, checking it looks right, before it’s even allowed to go in the display case. Continuous Deployment is deciding whether that batch actually gets put out for customers, maybe it goes to a small back table first (a test environment) where a manager tastes it, and only then moves to the main counter (production).

Because each station works independently, the cake station doesn’t have to wait for the bread station to finish before putting out a new cake. That’s the whole point of having separate stations (services) instead of one person doing everything in one giant batch: a mistake in the pastry station doesn’t stop bread from going out.

A canary rollout is like putting the new cake recipe out for just the first ten customers instead of the whole display case. If they love it, you swap the whole case over. If several complain, you pull it immediately, only ten people ever tried the bad batch instead of everyone who visited that day.

Why interviewers ask this

Pipeline design reveals whether you actually understand what makes microservices valuable (independent, safe deploys) versus just knowing how to configure a CI tool.

What a strong answer signals

That you think about traceability (which exact commit is running), safety (progressive rollout, automatic rollback), and independence (services deploy without blocking each other).

Common follow-ups

  • How do you handle a database migration during a rolling deploy?
  • What’s the difference between blue-green and canary deployment?
  • How would you roll back a bad deploy in under a minute?

Advanced variation

“How would you test that a new version of service A doesn’t break service B before deploying it,” which expects consumer-driven contract testing (like Pact), not just “we have a staging environment.”

An industrial IoT platform running a dozen microservices used a shared GitHub Actions workflow template, but each service had its own pipeline instance triggered only by changes under its own directory. Every merge to main built a Docker image tagged with the commit SHA, ran a Trivy vulnerability scan (failing the build on critical CVEs), and deployed to staging automatically. Production deploys used a 10% canary for 15 minutes with automatic rollback if the error rate crossed a threshold, based on real metrics from the load balancer, before promoting to 100%. This caught a bad release from a downstream dependency upgrade within four minutes of the canary going live, well before it reached the majority of production traffic.

.github/workflows/deploy.yml
name: build-and-deploy
on:
  push:
    branches: [main]
    paths: ['services/payments/**']

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run tests
        run: npm ci && npm test
      - name: Build image tagged with commit SHA
        run: docker build -t registry/payments:${{ github.sha }} .
      - name: Scan image for vulnerabilities
        run: trivy image --exit-code 1 --severity CRITICAL registry/payments:${{ github.sha }}
      - name: Push image
        run: docker push registry/payments:${{ github.sha }}

  deploy-canary:
    needs: build
    runs-on: ubuntu-latest
    steps:
      - name: Deploy 10% canary
        run: ./scripts/deploy.sh payments ${{ github.sha }} --traffic=10
      - name: Watch error rate, auto-rollback on regression
        run: ./scripts/watch-and-promote.sh payments ${{ github.sha }}
Commit Test + Build Scan + Push Staging Canary (10%) Full rollout Auto rollback healthy metrics → promote error spike → rollback
  1. 1Tag every built image with the commit SHA, never latest, so any running artifact is traceable to exact source code.
  2. 2Give each microservice its own independent pipeline so teams can deploy without blocking on unrelated services.
  3. 3Use progressive rollouts (canary or blue-green) with automated health checks instead of all-at-once production deploys.
  4. 4Database migrations during rolling deploys must be backward-compatible, since old and new code run simultaneously.
  5. 5Consumer-driven contract tests, not strict deploy ordering, are what actually let services deploy independently and safely.