CI/CD pipelines are central to modern software development. Whether you're applying for backend, full-stack, or DevOps roles, interviewers expect you to understand how code goes from commit to production.
This guide covers CI/CD fundamentals and practical GitHub Actions knowledge that comes up in interviews.
CI vs CD: The Foundation
Q: What's the difference between CI and CD?
This is often the opening question. Many candidates give vague answers.
Weak answer: "CI/CD is automated deployment."
Strong answer:
Continuous Integration (CI):
- Automatically build and test code when changes are pushed
- Catch integration issues early (merge conflicts, test failures)
- Every developer integrates frequently (at least daily)
- The build is the single source of truth
Continuous Delivery (CD):
- Code is always in a deployable state
- Automated pipeline to staging/pre-production
- Manual approval for production deployment
- "Could deploy at any time"
Continuous Deployment:
- Fully automated deployment to production
- Every passing commit goes live automatically
- Requires high test coverage and confidence
- "Do deploy every time"
Push → Build → Test → [Staging] → [Approval?] → Production
└─────── CI ──────┘ └────────── CD ─────────────┘
What interviewers want to hear: Understanding that CI is about integration quality, while CD is about release readiness. Know the difference between Delivery (manual gate) and Deployment (fully automated).
GitHub Actions: Core Concepts
Q: Explain GitHub Actions workflows, jobs, and steps.
# .github/workflows/ci.yml
name: CI Pipeline # Workflow name
on: # Triggers
push:
branches: [main]
pull_request:
branches: [main]
jobs: # Jobs run in parallel by default
test:
runs-on: ubuntu-latest # Runner environment
steps: # Steps run sequentially
- uses: actions/checkout@v4 # Action (reusable)
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci # Shell command
- run: npm test
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- run: npm run lintHierarchy:
- Workflow: YAML file in
.github/workflows/, triggered by events - Jobs: Independent units, run on separate runners, parallel by default
- Steps: Sequential tasks within a job, share the same runner
Key distinction: Jobs don't share filesystem by default. If job B needs artifacts from job A, you must upload/download them explicitly or use needs: for dependencies.
Workflow Triggers
Q: What events can trigger a GitHub Actions workflow?
on:
# Push/PR triggers
push:
branches: [main, develop]
paths:
- 'src/**' # Only trigger for src changes
- '!src/**/*.md' # Exclude markdown files
pull_request:
types: [opened, synchronize, reopened]
# Scheduled (cron)
schedule:
- cron: '0 0 * * *' # Daily at midnight UTC
# Manual trigger
workflow_dispatch:
inputs:
environment:
description: 'Deploy environment'
required: true
default: 'staging'
type: choice
options:
- staging
- production
# From other workflows
workflow_call: # Reusable workflow
# External events
repository_dispatch: # API triggerCommon interview follow-up: "How do you prevent running CI on documentation changes?"
on:
push:
paths-ignore:
- '**.md'
- 'docs/**'Job Dependencies and Parallelism
Q: How do you control the order jobs run in?
By default, jobs run in parallel. Use needs for dependencies:
jobs:
build:
runs-on: ubuntu-latest
steps:
- run: echo "Building..."
test:
needs: build # Waits for build to complete
runs-on: ubuntu-latest
steps:
- run: echo "Testing..."
deploy-staging:
needs: test
runs-on: ubuntu-latest
steps:
- run: echo "Deploying to staging..."
deploy-production:
needs: deploy-staging
runs-on: ubuntu-latest
environment: production # Requires approval
steps:
- run: echo "Deploying to production..."Visual:
build → test → deploy-staging → deploy-production
Parallel with dependency:
jobs:
lint:
runs-on: ubuntu-latest
# ...
test:
runs-on: ubuntu-latest
# ...
deploy:
needs: [lint, test] # Waits for BOTH
runs-on: ubuntu-latestlint ─┐
├→ deploy
test ─┘
Matrix Builds
Q: How do you test across multiple versions or platforms?
Matrix builds run the same job with different configurations:
jobs:
test:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node-version: [18, 20, 22]
fail-fast: false # Don't cancel others if one fails
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm testThis creates 9 jobs (3 OS × 3 Node versions).
Excluding combinations:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
node-version: [18, 20]
exclude:
- os: windows-latest
node-version: 18Including specific combinations:
strategy:
matrix:
include:
- os: ubuntu-latest
node-version: 20
experimental: trueSecrets and Environment Variables
Q: How do you handle secrets in CI/CD?
Never hardcode secrets. Use GitHub Secrets:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Deploy to production
env:
API_KEY: ${{ secrets.API_KEY }}
DATABASE_URL: ${{ secrets.DATABASE_URL }}
run: ./deploy.shRepository vs Environment secrets:
jobs:
deploy:
runs-on: ubuntu-latest
environment: production # Uses production-specific secrets
steps:
- run: echo "Deploying with ${{ secrets.PROD_API_KEY }}"Best practices:
- Least privilege: Only add secrets to environments that need them
- Rotation: Regularly rotate secrets
- Never log secrets: GitHub masks them, but be careful with base64/encoding
- Use OIDC: For cloud deployments, use OpenID Connect instead of long-lived credentials
# OIDC for AWS (no secret keys needed)
permissions:
id-token: write
contents: read
steps:
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789:role/github-actions
aws-region: us-east-1Caching Dependencies
Q: How do you speed up CI pipelines?
Caching avoids re-downloading dependencies:
steps:
- uses: actions/checkout@v4
- name: Cache node modules
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- run: npm ci
- run: npm testBuilt-in caching with setup actions:
- uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm' # Automatic caching!What to cache:
~/.npmornode_modules(Node.js)~/.cache/pip(Python)~/.m2/repository(Maven)~/.gradle/caches(Gradle)- Docker layers
Cache key strategy:
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
# Changes when lockfile changes, forcing fresh installArtifacts: Sharing Between Jobs
Q: How do you pass files between jobs?
Jobs run on different machines. Use artifacts:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm run build
- uses: actions/upload-artifact@v4
with:
name: build-output
path: dist/
retention-days: 7
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- uses: actions/download-artifact@v4
with:
name: build-output
path: dist/
- run: ./deploy.sh dist/Use cases:
- Build artifacts (compiled code, bundles)
- Test reports and coverage
- Logs for debugging failed runs
Deployment Strategies
Q: Explain blue-green vs canary deployments.
This is a common conceptual question.
Blue-Green Deployment:
┌─────────────┐ ┌─────────────┐
│ Blue │ │ Green │
│ (current) │ │ (new) │
└─────────────┘ └─────────────┘
↑
Load Balancer
- Blue is live, Green is idle
- Deploy new version to Green
- Test Green
- Switch load balancer to Green
- Blue becomes idle (instant rollback ready)
Pros: Instant rollback, full testing before switch Cons: Double infrastructure cost
Canary Deployment:
┌─────────────────────────────────┐
│ Load Balancer │
└───────────┬────────────┬────────┘
│ │
95%│ │5%
↓ ↓
┌───────────┐ ┌───────────┐
│ Current │ │ Canary │
│ Version │ │ (new) │
└───────────┘ └───────────┘
- Deploy new version to small subset
- Route 5% of traffic to canary
- Monitor metrics (errors, latency)
- Gradually increase (10%, 25%, 50%, 100%)
- Rollback if metrics degrade
Pros: Catches issues with minimal user impact Cons: Complex routing, longer rollout
Rolling Deployment: Update instances one at a time. Simpler but slower rollback.
Practical Workflow: Full CI/CD Pipeline
Q: Walk through a production CI/CD pipeline.
name: CI/CD Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
NODE_VERSION: '20'
jobs:
# ========== CI ==========
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- run: npm ci
- run: npm run lint
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- run: npm ci
- run: npm test -- --coverage
- uses: actions/upload-artifact@v4
with:
name: coverage-report
path: coverage/
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- run: npm ci
- run: npm run build
- uses: actions/upload-artifact@v4
with:
name: build
path: dist/
# ========== CD ==========
deploy-staging:
needs: [lint, test, build]
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
environment: staging
steps:
- uses: actions/download-artifact@v4
with:
name: build
path: dist/
- name: Deploy to Staging
run: ./scripts/deploy.sh staging
env:
DEPLOY_TOKEN: ${{ secrets.STAGING_DEPLOY_TOKEN }}
deploy-production:
needs: deploy-staging
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
environment: production # Manual approval required
steps:
- uses: actions/download-artifact@v4
with:
name: build
path: dist/
- name: Deploy to Production
run: ./scripts/deploy.sh production
env:
DEPLOY_TOKEN: ${{ secrets.PROD_DEPLOY_TOKEN }}Key patterns:
- Lint/test/build run in parallel (fast feedback)
- Deploy jobs wait for all CI jobs
- Staging deploys automatically
- Production requires manual approval via environment
Reusable Workflows
Q: How do you avoid duplicating workflow code?
# .github/workflows/reusable-deploy.yml
name: Reusable Deploy
on:
workflow_call:
inputs:
environment:
required: true
type: string
secrets:
deploy_token:
required: true
jobs:
deploy:
runs-on: ubuntu-latest
environment: ${{ inputs.environment }}
steps:
- uses: actions/checkout@v4
- run: ./deploy.sh
env:
DEPLOY_TOKEN: ${{ secrets.deploy_token }}# .github/workflows/main.yml
jobs:
deploy-staging:
uses: ./.github/workflows/reusable-deploy.yml
with:
environment: staging
secrets:
deploy_token: ${{ secrets.STAGING_TOKEN }}
deploy-production:
needs: deploy-staging
uses: ./.github/workflows/reusable-deploy.yml
with:
environment: production
secrets:
deploy_token: ${{ secrets.PROD_TOKEN }}Common Interview Questions
"A deployment failed. How do you roll back?"
- Immediate: Revert the commit and let CI/CD redeploy
- Blue-green: Switch load balancer back to previous environment
- Kubernetes:
kubectl rollout undo deployment/app - Keep previous artifacts: Redeploy known-good version
"How do you handle database migrations in CI/CD?"
- Run migrations before deploying new code
- Make migrations backward-compatible (add column, don't remove)
- Separate migration job with its own approval
- Consider blue-green for database changes
"How do you test infrastructure changes?"
- Terraform plan in PR, apply in main
- Use staging environment that mirrors production
- Infrastructure tests (Terratest, kitchen-terraform)
- Require approval for production infrastructure changes
Quick Reference
| Concept | Purpose |
|---|---|
| Workflow | YAML file defining automated process |
| Job | Independent unit on separate runner |
| Step | Sequential task within a job |
| Action | Reusable unit (uses: owner/repo@version) |
| Secret | Encrypted variable for sensitive data |
| Artifact | Files passed between jobs |
| Matrix | Run same job with different configs |
| Environment | Deployment target with secrets & approvals |
Related Articles
If you found this helpful, check out these related guides:
- Complete DevOps Engineer Interview Guide - comprehensive preparation guide for DevOps interviews
- Docker Interview Guide - Building images in CI pipelines
- Kubernetes Interview Guide - Deploying to K8s from CI/CD
- Linux Commands Interview Guide - Shell commands used in pipeline steps
- Git Rebase vs Merge Interview Guide - Version control workflows that trigger CI
What's Next?
GitHub Actions covers most CI/CD interview questions, but the concepts transfer to other tools. Once comfortable, explore:
- GitLab CI - Similar YAML syntax, different features
- Jenkins - More complex but highly customizable
- ArgoCD - GitOps for Kubernetes deployments
- Terraform Cloud - Infrastructure CI/CD
The developers who stand out can explain not just the "how" but the "why"—why we separate CI from CD, why caching matters, why deployment strategies exist.
