CI/CD Interview Questions: GitHub Actions & Pipeline Fundamentals
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 lint
Hierarchy:
-
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 trigger
Common 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-latest
lint ─┐
├→ 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 test
This 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: 18
Including specific combinations:
strategy:
matrix:
include:
- os: ubuntu-latest
node-version: 20
experimental: true
Secrets 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.sh
Repository 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-1
Caching 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 test
Built-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 install
Artifacts: 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.
Ready for more DevOps interview questions?
Our complete interview prep covers CI/CD, Docker, Kubernetes, and cloud infrastructure. Get the questions that actually come up in interviews.
Explore Interview Questions

Written by
EasyInterview Team
Based on 15+ years in tech and hundreds of technical interviews conducted at companies like BNY Mellon, UBS, and leading fintech firms.
Ready for More Interview Questions?
This is just one topic from our complete interview prep guide. Get access to 800+ questions across 13 technologies.