Skip to content

CI/CD Interview Questions

CI/CD Interview Questions & Answers (1–50)

Section titled “CI/CD Interview Questions & Answers (1–50)”

Q1: What is CI/CD and why is it important?

Section titled “Q1: What is CI/CD and why is it important?”

Answer:

TermWhat it meansDeployment trigger
CI (Continuous Integration)Auto build + test on every commitNever (just tests)
CD (Continuous Delivery)Auto deliver to staging; human approves prodManual approval
CD (Continuous Deployment)Auto deploy to production if tests passFully automatic

CI/CD Pipeline flow:

Developer commits
[Source Control]
[CI Pipeline]
├── Build (compile, package)
├── Unit Tests
├── Integration Tests
├── Security Scan (SAST, dependency check)
├── Build Docker image
└── Push to registry
[CD Pipeline]
├── Deploy to Staging
├── Run E2E / smoke tests
├── [Manual approval gate] ← Continuous Delivery stops here
└── Deploy to Production ← Continuous Deployment continues here
[Monitor] Alerts, dashboards, rollback if needed

Benefits:

  • Faster feedback: bug found in minutes not weeks
  • Smaller releases = lower risk per deployment
  • Repeatable: same process every time, no snowflake deployments
  • Audit trail: every change is tracked

DORA Metrics (measure CI/CD maturity):

1. Deployment Frequency → Elite: multiple/day | Low: monthly
2. Lead Time for Changes → Elite: < 1 hour | Low: > 6 months
3. Change Failure Rate → Elite: 0-15% | Low: 46-60%
4. MTTR (Mean Time to Recovery) → Elite: < 1 hour | Low: > 6 months

Interview tip: Always mention DORA metrics when asked about CI/CD maturity. Interviewers love when you know how to measure CI/CD success, not just implement it.


Q2: What is the difference between Continuous Delivery and Continuous Deployment?

Section titled “Q2: What is the difference between Continuous Delivery and Continuous Deployment?”

Answer:

AspectContinuous DeliveryContinuous Deployment
Deployment to prodManual approval requiredFully automated
GateHuman decisionOnly automated tests
RiskLower (human safety net)Higher (fully automated)
SpeedSlightly slowerFastest possible
Best forRegulated industries (finance, healthcare)High-velocity SaaS teams
ExampleEngineer clicks “Deploy” buttonMerging PR triggers prod deploy

Example workflow difference:

Continuous Delivery:
commit → test → staging deploy → [HUMAN APPROVES] → prod deploy
Continuous Deployment:
commit → test → staging deploy → [AUTO after 30min soak] → prod deploy

Interview tip: Most companies practice Continuous Delivery, not Deployment. Even Netflix and Google use approval gates for their most critical services. Continuous Deployment works best with feature flags so you can deploy dark code and enable it later.


Answer: GitHub Actions is an event-driven automation platform. Workflows are defined in .github/workflows/*.yml.

.github/workflows/ci.yml
name: CI Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
env:
IMAGE_NAME: myapp
jobs:
test:
name: Test
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
- name: Upload coverage
uses: codecov/codecov-action@v3
build:
name: Build & Push Docker Image
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and push
uses: docker/build-push-action@v5
with:
push: true
tags: |
${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME }}:latest
${{ secrets.DOCKER_USERNAME }}/${{ env.IMAGE_NAME }}:${{ github.sha }}

Q4: How do you use GitHub Actions secrets and environments?

Section titled “Q4: How do you use GitHub Actions secrets and environments?”

Answer:

Secrets hierarchy (where they can be set):

Organization Secrets → shared across repos
Repository Secrets → specific to one repo
Environment Secrets → only available in specific environment
# Using secrets in workflow
jobs:
deploy:
runs-on: ubuntu-latest
environment: production # Links to GitHub Environment (can require approval)
steps:
- name: Deploy to production
env:
API_KEY: ${{ secrets.PROD_API_KEY }} # From environment secrets
DB_URL: ${{ secrets.DATABASE_URL }} # From repo secrets
REGION: us-east-1 # Non-secret, just env var
run: ./scripts/deploy.sh
- name: Notify Slack
if: always() # Run even if deploy fails
uses: slackapi/slack-github-action@v1
with:
payload: '{"text": "Deploy ${{ job.status }}: ${{ github.ref }}"}'
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
Terminal window
# Set secrets via CLI
gh secret set DOCKER_PASSWORD --body "mypassword"
gh secret set DOCKER_PASSWORD < ./password.txt # from file (more secure)
gh secret list # list all secret names
# Environment-specific secrets
# GitHub UI: Settings → Environments → production → Add secret
# These secrets ONLY become available when environment: production is set in workflow
# Required reviewers for environment (approval gate)
# Settings → Environments → production → Required reviewers → add team/user

Using OIDC instead of stored secrets (most secure):

permissions:
id-token: write # Needed for OIDC
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
# No AWS_SECRET_ACCESS_KEY needed! OIDC token used instead

Interview tip: Use OIDC (OpenID Connect) instead of storing cloud credentials as secrets. With OIDC, GitHub gets a short-lived token from AWS/Azure/GCP directly — no long-lived credentials to rotate or leak.


Q5: How do you implement matrix builds in GitHub Actions?

Section titled “Q5: How do you implement matrix builds in GitHub Actions?”

Answer: Matrix builds run the same job with different combinations of variables in parallel — great for cross-platform and multi-version testing.

jobs:
test:
name: Test (Node ${{ matrix.node }} on ${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false # Continue other matrix jobs even if one fails
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
node: [16, 18, 20]
exclude:
- os: windows-latest
node: 16 # Skip this combination
include:
- os: ubuntu-latest
node: 20
experimental: true # Custom variable for this combination
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- run: npm ci && npm test
- name: Experimental only step
if: matrix.experimental == true
run: npm run test:experimental

Matrix outputs:

Matrix creates 3 × 3 = 9 jobs (minus 1 excluded = 8 jobs), running in parallel:
┌────────────────────────────────────────┐
│ ubuntu+node16 ubuntu+node18 ubuntu+node20 │
│ windows+node18 windows+node20 │
│ macos+node16 macos+node18 macos+node20 │
└────────────────────────────────────────┘

Interview tip: Use fail-fast: false for test matrices so you see ALL failures across versions, not just the first one. Use fail-fast: true (default) when early failure matters (e.g., build matrix).


Q6: How do you cache dependencies in GitHub Actions?

Section titled “Q6: How do you cache dependencies in GitHub Actions?”

Answer: Caching avoids re-downloading dependencies on every run — can save 2-5 minutes per job.

How cache works:

First run: MISS → install deps → save to cache
Next run: HIT → restore from cache → skip install
Cache key changes: MISS → reinstall → save new cache
# Node.js (npm)
- name: Cache npm packages
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
# ↑ cache bust when lockfile changes
restore-keys: |
${{ runner.os }}-npm- # fallback: use any npm cache for this OS
# Python (pip)
- uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements*.txt') }}
restore-keys: |
${{ runner.os }}-pip-
# Go
- uses: actions/cache@v4
with:
path: ~/go/pkg/mod
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
# Docker layer caching (most impactful for image builds)
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and push with cache
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: myapp:latest
cache-from: type=gha # Restore from GitHub Actions cache
cache-to: type=gha,mode=max # Save all layers to cache

Measuring cache effectiveness:

Terminal window
# In workflow, check if cache was hit
- name: Cache npm
id: cache-npm
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
- name: Install deps (skip if cache hit)
if: steps.cache-npm.outputs.cache-hit != 'true'
run: npm ci

Interview tip: Cache key strategy is critical. Use hashFiles() on lockfiles — NOT on package.json. package-lock.json changes only when dependencies actually change, giving better cache hit rates.


Q7: What is a Jenkinsfile and how does it work?

Section titled “Q7: What is a Jenkinsfile and how does it work?”

Answer: A Jenkinsfile defines a Jenkins pipeline as code using Groovy DSL.

// Declarative Pipeline
pipeline {
agent any
environment {
DOCKER_IMAGE = 'myapp'
DOCKER_TAG = "${env.BUILD_NUMBER}"
REGISTRY = 'registry.example.com'
}
stages {
stage('Checkout') {
steps {
git branch: 'main', url: 'https://github.com/org/repo.git'
}
}
stage('Test') {
steps {
sh 'npm ci'
sh 'npm test'
}
post {
always {
junit 'test-results/*.xml'
}
}
}
stage('Build Docker Image') {
steps {
script {
docker.build("${REGISTRY}/${DOCKER_IMAGE}:${DOCKER_TAG}")
}
}
}
stage('Push Image') {
steps {
withCredentials([usernamePassword(
credentialsId: 'registry-creds',
usernameVariable: 'USERNAME',
passwordVariable: 'PASSWORD'
)]) {
sh "docker login -u $USERNAME -p $PASSWORD ${REGISTRY}"
sh "docker push ${REGISTRY}/${DOCKER_IMAGE}:${DOCKER_TAG}"
}
}
}
stage('Deploy to Staging') {
steps {
sh """
kubectl set image deployment/myapp \
myapp=${REGISTRY}/${DOCKER_IMAGE}:${DOCKER_TAG} \
-n staging
"""
}
}
stage('Deploy to Production') {
when {
branch 'main'
}
input {
message "Deploy to production?"
ok "Yes, deploy!"
}
steps {
sh """
kubectl set image deployment/myapp \
myapp=${REGISTRY}/${DOCKER_IMAGE}:${DOCKER_TAG} \
-n production
"""
}
}
}
post {
failure {
slackSend channel: '#deploys',
message: "Build FAILED: ${env.JOB_NAME} ${env.BUILD_NUMBER}"
}
success {
slackSend channel: '#deploys',
message: "Build SUCCESS: ${env.JOB_NAME} ${env.BUILD_NUMBER}"
}
}
}

Answer:

// Specify agent for the whole pipeline
pipeline {
agent {
label 'docker-agent' // Node with 'docker-agent' label
}
}
// Different agents per stage
pipeline {
agent none
stages {
stage('Build') {
agent { label 'linux' }
steps { sh 'make build' }
}
stage('Test Windows') {
agent { label 'windows' }
steps { bat 'test.bat' }
}
}
}
// Docker agent
pipeline {
agent {
docker {
image 'node:18-alpine'
args '-v /tmp:/tmp'
}
}
}

Q9: How do you use Jenkins shared libraries?

Section titled “Q9: How do you use Jenkins shared libraries?”

Answer:

// vars/deployApp.groovy (shared library function)
def call(String environment, String image) {
sh """
kubectl set image deployment/app \
app=${image} -n ${environment}
kubectl rollout status deployment/app -n ${environment}
"""
}
// Jenkinsfile using shared library
@Library('my-shared-library') _
pipeline {
agent any
stages {
stage('Deploy') {
steps {
deployApp('staging', "myapp:${BUILD_NUMBER}")
}
}
}
}

Answer:

.gitlab-ci.yml
stages:
- test
- build
- deploy
variables:
DOCKER_DRIVER: overlay2
IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
test:
stage: test
image: node:18
cache:
key: "$CI_COMMIT_REF_SLUG"
paths:
- node_modules/
script:
- npm ci
- npm test
coverage: '/Lines\s*:\s*(\d+\.?\d*)%/'
artifacts:
reports:
junit: test-results.xml
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
build:
stage: build
image: docker:24
services:
- docker:24-dind
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
- docker build -t $IMAGE_TAG .
- docker push $IMAGE_TAG
only:
- main
- develop
deploy-staging:
stage: deploy
script:
- kubectl set image deployment/app app=$IMAGE_TAG -n staging
environment:
name: staging
url: https://staging.myapp.com
only:
- develop
deploy-production:
stage: deploy
script:
- kubectl set image deployment/app app=$IMAGE_TAG -n production
environment:
name: production
url: https://myapp.com
when: manual # Require manual trigger
only:
- main

Q11: What is GitOps and how does ArgoCD implement it?

Section titled “Q11: What is GitOps and how does ArgoCD implement it?”

Answer: GitOps: Git is the single source of truth for infrastructure and application configuration. Changes happen via Git commits/PRs.

ArgoCD continuously watches a Git repo and syncs cluster state.

Terminal window
# Install ArgoCD
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
# Get admin password
kubectl get secret argocd-initial-admin-secret -n argocd \
-o jsonpath="{.data.password}" | base64 -d
# Port forward UI
kubectl port-forward svc/argocd-server -n argocd 8080:443
# ArgoCD Application
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: myapp
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/org/k8s-configs
targetRevision: main
path: apps/myapp/overlays/production
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true # Delete resources removed from Git
selfHeal: true # Fix manual changes
syncOptions:
- CreateNamespace=true
Terminal window
# Sync manually
argocd app sync myapp
# Check app status
argocd app get myapp
# Roll back
argocd app rollback myapp --revision=5

Q12: What is Kustomize and how does it work with CI/CD?

Section titled “Q12: What is Kustomize and how does it work with CI/CD?”

Answer:

k8s-configs/
├── base/
│ ├── deployment.yaml
│ ├── service.yaml
│ └── kustomization.yaml
└── overlays/
├── staging/
│ ├── kustomization.yaml # Patches for staging
│ └── replica-patch.yaml
└── production/
├── kustomization.yaml
└── replica-patch.yaml
overlays/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: production
resources:
- ../../base
images:
- name: myapp
newTag: "1.5.0" # Update via CI: kustomize edit set image
patches:
- path: replica-patch.yaml
Terminal window
# Update image tag in CI pipeline
cd k8s-configs/overlays/production
kustomize edit set image myapp=myapp:$NEW_TAG
git commit -am "chore: update image to $NEW_TAG"
git push
# ArgoCD detects change and syncs

Q13: What are the different deployment strategies?

Section titled “Q13: What are the different deployment strategies?”

Answer:

1. Rolling Update (Default in K8s)

strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 25%
maxSurge: 25%

✅ Zero downtime | ❌ Mixed versions briefly

2. Blue-Green Deployment

Terminal window
# Switch traffic by changing service selector
kubectl patch service myapp-service \
-p '{"spec":{"selector":{"version":"green"}}}'

✅ Instant rollback | ❌ Double infrastructure cost

3. Canary Deployment

# 90% traffic to stable, 10% to canary
# Using Argo Rollouts
spec:
strategy:
canary:
steps:
- setWeight: 10 # 10% traffic
- pause: {duration: 5m}
- setWeight: 50 # 50% traffic
- pause: {duration: 10m}
- setWeight: 100 # Full rollout

✅ Test with real traffic | ❌ Complex setup

4. Recreate

strategy:
type: Recreate

✅ Simple | ❌ Downtime during deployment


Q14: How do you implement feature flags in CI/CD?

Section titled “Q14: How do you implement feature flags in CI/CD?”

Answer:

// Application code uses feature flag
const featureFlags = require('./flags');
if (featureFlags.isEnabled('new-checkout-flow', userId)) {
return newCheckout();
} else {
return oldCheckout();
}
# Environment-based flags
env:
- name: FEATURE_NEW_CHECKOUT
value: "true" # Toggle per environment
# External flag services: LaunchDarkly, Unleash, Flagsmith

Q15: How do you structure testing in a CI pipeline?

Section titled “Q15: How do you structure testing in a CI pipeline?”

Answer:

# GitHub Actions — Test pyramid approach
jobs:
unit-tests:
runs-on: ubuntu-latest
steps:
- run: npm run test:unit
- uses: actions/upload-artifact@v3
with:
name: unit-results
path: coverage/
integration-tests:
needs: unit-tests
services:
postgres:
image: postgres:15
env:
POSTGRES_PASSWORD: test
steps:
- run: npm run test:integration
e2e-tests:
needs: integration-tests
steps:
- name: Run Cypress
uses: cypress-io/github-action@v6
with:
start: npm start
wait-on: 'http://localhost:3000'
security-scan:
steps:
- uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
performance-test:
steps:
- run: |
k6 run --vus 50 --duration 30s load-test.js

Q16: What is SAST and DAST in a DevSecOps pipeline?

Section titled “Q16: What is SAST and DAST in a DevSecOps pipeline?”

Answer:

TypeWhenTools
SAST (Static)During build — analyze source codeSonarQube, Semgrep, CodeQL
DAST (Dynamic)Against running appOWASP ZAP, Burp Suite
SCA (Dependencies)Build timeSnyk, OWASP Dependency Check
Container ScanImage buildTrivy, Grype, Clair
# GitHub Actions: SAST with CodeQL
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: javascript, python
- name: Run CodeQL Analysis
uses: github/codeql-action/analyze@v2
# Container scanning
- name: Scan image with Trivy
uses: aquasecurity/trivy-action@master
with:
image-ref: myapp:${{ github.sha }}
exit-code: '1'
severity: 'CRITICAL,HIGH'

Answer:

# 1. Parallel jobs
jobs:
test-unit:
runs-on: ubuntu-latest
steps: [...]
test-e2e:
runs-on: ubuntu-latest
steps: [...]
# Both run simultaneously!
lint:
runs-on: ubuntu-latest
steps: [...]
# 2. Fail-fast — stop all on first failure
strategy:
fail-fast: true
# 3. Path-based triggers
on:
push:
paths:
- 'src/**'
- '!docs/**' # Skip if only docs changed
# 4. Conditional jobs
deploy:
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
# 5. Reuse Docker layers via cache
- uses: docker/build-push-action@v5
with:
cache-from: type=gha
cache-to: type=gha,mode=max

Q18: How do you implement infrastructure as code in CI/CD?

Section titled “Q18: How do you implement infrastructure as code in CI/CD?”

Answer:

# Terraform in CI/CD (GitHub Actions)
- name: Terraform Init
working-directory: ./infrastructure
run: terraform init
- name: Terraform Plan
run: terraform plan -out=tfplan
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: Comment plan on PR
uses: actions/github-script@v6
with:
script: |
const plan = fs.readFileSync('tfplan.txt', 'utf8')
github.rest.issues.createComment({
issue_number: context.issue.number,
body: `\`\`\`terraform\n${plan}\n\`\`\``
})
- name: Terraform Apply
if: github.ref == 'refs/heads/main'
run: terraform apply -auto-approve tfplan

Q19: How do you handle database migrations in CI/CD?

Section titled “Q19: How do you handle database migrations in CI/CD?”

Answer:

# Strategy: Run migrations before new code is live
# Option 1: K8s Init Container
initContainers:
- name: db-migrate
image: myapp:$NEW_TAG
command: ["python", "manage.py", "migrate"]
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: db-secret
key: url
# Option 2: Kubernetes Job before deployment
jobs:
migrate:
runs-on: ubuntu-latest
steps:
- name: Run migration
run: |
kubectl apply -f migration-job.yaml
kubectl wait --for=condition=complete job/db-migration --timeout=5m
deploy:
needs: migrate
runs-on: ubuntu-latest
steps:
- name: Deploy application
run: kubectl apply -f deployment.yaml

Q20: What is trunk-based development and how does it affect CI/CD?

Section titled “Q20: What is trunk-based development and how does it affect CI/CD?”

Answer:

Trunk-Based Development:
- All developers commit to 'main' (trunk) at least daily
- Feature branches are short-lived (< 2 days)
- Feature flags used to hide incomplete features
- CI triggers on every commit
vs. GitFlow:
- Long-lived branches (feature/*, release/*)
- PRs to develop → merge to main
- More complex, slower feedback
# Trunk-based CI — run on every commit to main
on:
push:
branches: [main]
jobs:
build-test-deploy:
steps:
- run: npm ci
- run: npm test
- run: npm run build
- run: ./deploy.sh staging # Automatic staging deployment
- run: ./deploy.sh prod --canary 10% # 10% canary to prod

Back to DevOps Q&A Index


Q21: What is the difference between CI and CD?

Section titled “Q21: What is the difference between CI and CD?”

Answer (Simple):

  • CI (Continuous Integration) = Automatically build and test every commit. Catch bugs early.
  • CD (Continuous Delivery) = Every passing commit is deployable. Deploy button is always ready.
  • CD (Continuous Deployment) = Every passing commit AUTO-deploys to production. No human needed.
Developer pushes code
[CI Pipeline]
- Build code
- Run unit tests
- Run integration tests
- Security scan
- Build Docker image
[CD Pipeline]
- Deploy to staging (automatic)
- Run smoke tests
- Deploy to production (manual approval = Delivery)
OR
(automatic = Deployment)

Q22: How do you prevent secrets from being exposed in CI/CD logs?

Section titled “Q22: How do you prevent secrets from being exposed in CI/CD logs?”

Answer:

# GitHub Actions — use secrets, never hardcode
# BAD ❌
env:
API_KEY: "sk-prod-abc123secretkey"
# GOOD ✅ — Use GitHub Secrets
env:
API_KEY: ${{ secrets.PROD_API_KEY }}
# Mask custom values in logs
- name: Mask token
run: echo "::add-mask::${{ steps.get-token.outputs.token }}"
# Jenkins — use credentials binding
withCredentials([string(credentialsId: 'api-key', variable: 'API_KEY')]) {
sh 'curl -H "Authorization: $API_KEY" https://api.example.com'
}
# Jenkins automatically masks the value in logs
# GitLab — masked variables
# Settings → CI/CD → Variables → check "Masked"

Q23: What is the difference between a CI runner and an agent?

Section titled “Q23: What is the difference between a CI runner and an agent?”

Answer:

PlatformTermDescription
GitHub ActionsRunnerVM/container that executes workflow jobs
JenkinsAgentNode that executes pipeline stages
GitLab CIRunnerProcess registered to execute jobs
Azure DevOpsAgentService that runs pipeline jobs
# GitHub Actions — GitHub-hosted runner (free)
runs-on: ubuntu-latest # GitHub manages this
# GitHub Actions — Self-hosted runner (your own server)
runs-on: [self-hosted, linux, gpu] # Your server
# Jenkins — define agent
pipeline {
agent {
label 'linux-agent' # Named Jenkins agent
}
}
# GitLab — register runner
gitlab-runner register \
--url https://gitlab.com \
--token <token> \
--executor docker \
--docker-image node:18

Q24: What is semantic versioning in CI/CD?

Section titled “Q24: What is semantic versioning in CI/CD?”

Answer: MAJOR.MINOR.PATCH — e.g., 2.1.3

Version PartWhen to bumpExample
MAJORBreaking changeAPI removed, incompatible
MINORNew feature, backward compatibleNew endpoint added
PATCHBug fix, backward compatibleFixed null pointer exception
Terminal window
# Auto-generate version in CI from git tags
git tag v1.2.3
git push --tags
# In CI pipeline:
VERSION=$(git describe --tags --abbrev=0) # v1.2.3
FULL_VERSION=$(git describe --tags) # v1.2.3-5-gabcd123
# Conventional Commits → auto bump version
# feat: add login button → MINOR bump
# fix: correct null check → PATCH bump
# feat!: change auth API → MAJOR bump (breaking)
# Tools: semantic-release, standard-version
npx semantic-release # Auto-tags + changelog generation

Q25: How do you implement multi-environment CI/CD (dev/staging/prod)?

Section titled “Q25: How do you implement multi-environment CI/CD (dev/staging/prod)?”

Answer:

# GitHub Actions — environment-based deployment
name: Deploy
on:
push:
branches: [main, develop]
jobs:
deploy-staging:
if: github.ref == 'refs/heads/develop'
environment:
name: staging
url: https://staging.myapp.com
runs-on: ubuntu-latest
steps:
- name: Deploy to staging
run: |
kubectl config use-context staging-cluster
kubectl apply -f k8s/overlays/staging/
deploy-production:
if: github.ref == 'refs/heads/main'
environment:
name: production # Requires approval in GitHub settings
url: https://myapp.com
needs: [test]
runs-on: ubuntu-latest
steps:
- name: Deploy to production
run: |
kubectl config use-context prod-cluster
kubectl apply -f k8s/overlays/production/

Answer: Pipeline as Code means CI/CD pipeline is defined in a file stored in the repository (version controlled), not configured through a UI.

# GitHub Actions (.github/workflows/ci.yml)
# GitLab CI (.gitlab-ci.yml)
# Jenkins (Jenkinsfile)
# Azure Pipelines (azure-pipelines.yml)
# CircleCI (.circleci/config.yml)
# Benefits:
# ✅ Version controlled (git history)
# ✅ Code reviewed (PR reviews)
# ✅ Reproducible
# ✅ Self-documenting
# Example — complete pipeline as code
name: Full Pipeline
on: [push]
jobs:
pipeline:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm run lint
- run: npm test
- run: docker build -t myapp:${{ github.sha }} .
- run: trivy image myapp:${{ github.sha }}
- run: docker push myapp:${{ github.sha }}
- run: kubectl set image deployment/myapp app=myapp:${{ github.sha }}

Q27: How do you implement approval gates in CI/CD?

Section titled “Q27: How do you implement approval gates in CI/CD?”

Answer:

# GitHub Actions — environment protection rules
# 1. Go to Settings → Environments → production
# 2. Add required reviewers
# 3. Workflow will pause and notify reviewers
jobs:
deploy-prod:
environment: production # Pauses here for approval
steps:
- run: kubectl apply -f production/
# Jenkins — input step
stage('Deploy to Production') {
input {
message "Deploy to production?"
ok "Yes, deploy!"
submitter "devops-lead,cto" # Only these users can approve
}
steps {
sh './deploy-prod.sh'
}
}
# GitLab — when: manual
deploy-production:
stage: deploy
script:
- ./deploy.sh production
when: manual # Requires clicking "Play" in GitLab UI
only:
- main

Q28: What is artifact management in CI/CD?

Section titled “Q28: What is artifact management in CI/CD?”

Answer: Artifacts are the build outputs (binaries, Docker images, test reports) passed between pipeline stages.

# GitHub Actions — upload/download artifacts between jobs
jobs:
build:
runs-on: ubuntu-latest
steps:
- run: npm run build
- name: Upload build artifacts
uses: actions/upload-artifact@v3
with:
name: build-output
path: ./dist/
retention-days: 7
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- name: Download build artifacts
uses: actions/download-artifact@v3
with:
name: build-output
path: ./dist/
- run: aws s3 sync ./dist/ s3://my-bucket/
# GitLab — artifacts
build:
script: npm run build
artifacts:
paths:
- dist/
expire_in: 1 week
reports:
junit: test-results.xml

Q29: How do you implement rollback in CI/CD?

Section titled “Q29: How do you implement rollback in CI/CD?”

Answer:

Terminal window
# Strategy 1: Re-run previous pipeline
# In GitHub Actions: Actions → Find last good run → Re-run
# Strategy 2: Git revert
git revert HEAD # Reverts last commit, creates new commit
git push # Triggers new pipeline with old code
# Strategy 3: Kubernetes rollback
kubectl rollout undo deployment/myapp
kubectl rollout undo deployment/myapp --to-revision=3
kubectl rollout history deployment/myapp # View history
# Strategy 4: ArgoCD rollback
argocd app rollback myapp --revision=5
argocd app history myapp # See previous versions
# Strategy 5: Helm rollback
helm rollback myapp 3 # Rollback to release 3
helm history myapp # View helm release history
# Strategy 6: Feature flag (instant, no redeployment)
# Toggle flag off in LaunchDarkly/Unleash
# No code deployment needed!

Q30: What is a monorepo and how does CI/CD work with it?

Section titled “Q30: What is a monorepo and how does CI/CD work with it?”

Answer: Monorepo = All projects in one repository.

# Monorepo structure
myorg/
├── services/
│ ├── api/
│ ├── worker/
│ └── auth/
├── packages/
│ ├── ui-components/
│ └── utils/
└── infrastructure/
# GitHub Actions — only run for changed service
jobs:
changes:
runs-on: ubuntu-latest
outputs:
api: ${{ steps.filter.outputs.api }}
worker: ${{ steps.filter.outputs.worker }}
steps:
- uses: dorny/paths-filter@v2
id: filter
with:
filters: |
api:
- 'services/api/**'
worker:
- 'services/worker/**'
deploy-api:
needs: changes
if: ${{ needs.changes.outputs.api == 'true' }}
runs-on: ubuntu-latest
steps:
- run: cd services/api && ./deploy.sh
deploy-worker:
needs: changes
if: ${{ needs.changes.outputs.worker == 'true' }}
runs-on: ubuntu-latest
steps:
- run: cd services/worker && ./deploy.sh

Q31: What is continuous testing and shift-left testing?

Section titled “Q31: What is continuous testing and shift-left testing?”

Answer:

Shift-Left = Move testing earlier in the development cycle (to the “left” on the timeline).

Traditional (Shift-Right):
Dev → Build → QA Testing → Security Review → Deploy
↑ Problems found late, expensive to fix
Shift-Left:
Dev → [Unit tests] → [Integration tests] → [Security scan] → Build → Deploy
↑ Problems found early (in dev phase), cheap to fix
# Test pyramid in CI
jobs:
# Level 1: Unit tests (fast, many)
unit-tests:
runs-on: ubuntu-latest
timeout-minutes: 5
steps:
- run: npm run test:unit -- --coverage
# Level 2: Integration tests (medium)
integration-tests:
needs: unit-tests
timeout-minutes: 15
services:
postgres:
image: postgres:15
steps:
- run: npm run test:integration
# Level 3: E2E tests (slow, few)
e2e-tests:
needs: integration-tests
timeout-minutes: 30
steps:
- run: npx playwright test
# Security shift-left
sast-scan:
runs-on: ubuntu-latest
steps:
- uses: github/codeql-action/analyze@v2

Q32: What is blue-green deployment in CI/CD?

Section titled “Q32: What is blue-green deployment in CI/CD?”

Answer:

Current State:
Load Balancer → Blue (v1.0, LIVE, 100% traffic)
Green (v2.0, IDLE, 0% traffic)
Deploy new version:
Step 1: Deploy v2.0 to Green, test it
Step 2: Switch load balancer: Blue=0%, Green=100%
Step 3: Keep Blue running (instant rollback if needed)
Step 4: After confidence, decommission Blue
Benefits:
✅ Zero downtime
✅ Instant rollback (just switch back)
✅ Test in production-like environment before switching
# GitHub Actions — Blue-Green with ECS
- name: Deploy to Green
run: |
aws ecs update-service \
--cluster production \
--service myapp-green \
--task-definition myapp:${{ env.NEW_REVISION }}
aws ecs wait services-stable --cluster production --services myapp-green
- name: Run smoke tests against Green
run: curl -f https://green.myapp.com/health
- name: Switch traffic to Green
run: |
aws elbv2 modify-listener \
--listener-arn $LISTENER_ARN \
--default-actions Type=forward,TargetGroupArn=$GREEN_TG_ARN

Q33: What is a canary release and how does it differ from blue-green?

Section titled “Q33: What is a canary release and how does it differ from blue-green?”

Answer:

FeatureBlue-GreenCanary
Traffic split0% or 100%Gradual (5% → 25% → 100%)
RiskAll users switch at onceSmall % exposed first
RollbackInstantGradual or instant
Cost2x infrastructureMinimal extra
Test with real traffic
# GitHub Actions — Canary with weighted routing
- name: Deploy canary (10% traffic)
run: |
kubectl apply -f canary-deployment.yaml
# Service routes: 90% stable, 10% canary
# Monitor error rates, latency for 30 mins
- name: Monitor metrics
run: |
ERROR_RATE=$(curl -s prometheus/api/v1/query?query=error_rate | jq .data.result[0].value[1])
if (( $(echo "$ERROR_RATE > 0.01" | bc) )); then
echo "Error rate too high: $ERROR_RATE"
kubectl delete -f canary-deployment.yaml
exit 1
fi
- name: Promote to 100%
if: success()
run: |
kubectl set image deployment/myapp-stable app=myapp:$NEW_TAG
kubectl delete -f canary-deployment.yaml

Q34: How do you implement compliance checks in CI/CD?

Section titled “Q34: How do you implement compliance checks in CI/CD?”

Answer:

# DevSecOps pipeline with compliance checks
jobs:
compliance:
runs-on: ubuntu-latest
steps:
# 1. License compliance
- name: Check licenses
uses: pivotal/licensed@v3
with:
command: status
# 2. SAST — Static code analysis
- name: SonarQube scan
uses: SonarSource/sonarcloud-github-action@master
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
with:
args: -Dsonar.qualitygate.wait=true # Fail if quality gate fails
# 3. Dependency vulnerabilities
- name: Check dependencies
run: |
npm audit --audit-level=high
# or: pip-audit, safety check, bundler-audit
# 4. Container compliance
- name: CIS benchmark check
uses: docker://aquasec/kube-bench:latest
with:
args: --benchmark cis-1.6
# 5. Infrastructure compliance
- name: Checkov IaC scan
uses: bridgecrewio/checkov-action@master
with:
directory: infrastructure/
framework: terraform

Q35: What is a pipeline timeout and why is it important?

Section titled “Q35: What is a pipeline timeout and why is it important?”

Answer:

# Without timeouts, a stuck job runs forever and blocks other jobs
# GitHub Actions — set timeouts
jobs:
build:
timeout-minutes: 30 # Job-level timeout
steps:
- name: Run tests
timeout-minutes: 15 # Step-level timeout
run: npm test
- name: Deploy
timeout-minutes: 10
run: ./deploy.sh
# Jenkins — timeout
stage('Tests') {
timeout(time: 20, unit: 'MINUTES') {
sh 'npm test'
}
}
# Common timeout scenarios:
# - Tests hang (infinite loop, deadlock)
# - Deployment stuck (waiting for approval)
# - External service not responding
# - Resource contention (no runners available)

Q36: What is GitOps and how is it different from traditional CI/CD?

Section titled “Q36: What is GitOps and how is it different from traditional CI/CD?”

Answer:

AspectTraditional CI/CDGitOps
TriggerCode push → deployGit commit → reconcile
Source of truthCI/CD pipeline configGit repository
DeploymentPipeline pushes to clusterOperator pulls from Git
SecurityPipeline has cluster accessCluster pulls (safer)
RollbackRe-run pipelinegit revert
ToolsJenkins, GitHub ActionsArgoCD, Flux
# GitOps flow with ArgoCD:
# 1. Dev pushes code → CI builds image → CI updates image tag in K8s config repo
# 2. ArgoCD detects config change in Git
# 3. ArgoCD syncs cluster to match Git state
# In CI pipeline (updates Git config repo):
- name: Update image tag in GitOps repo
run: |
git clone https://github.com/org/k8s-configs
cd k8s-configs
kustomize edit set image myapp=myapp:${{ github.sha }}
git commit -am "chore: update myapp to ${{ github.sha }}"
git push
# ArgoCD then auto-syncs the cluster

Q37: How do you handle flaky tests in CI/CD?

Section titled “Q37: How do you handle flaky tests in CI/CD?”

Answer:

# Strategy 1: Retry failed tests
- name: Run tests with retry
uses: nick-fields/retry@v2
with:
timeout_minutes: 10
max_attempts: 3
command: npm test
# Strategy 2: Quarantine flaky tests
# Mark flaky test with special tag
# jest --testPathPattern="stable" (skip flaky)
# Run flaky tests separately on a schedule, not blocking
# Strategy 3: Identify and report flaky tests
- name: Run tests and track flakiness
run: |
for i in 1 2 3; do npm test; done | tee test-results.txt
# Check if same test fails inconsistently
grep -E "FAIL|PASS" test-results.txt | sort | uniq -c
# Strategy 4: Fix root causes
# - Use deterministic test data
# - Mock external APIs
# - Use fake timers for time-dependent tests
# - Fix race conditions
# - Use proper test isolation (cleanup DB after each test)

Q38: What is a release pipeline vs a build pipeline?

Section titled “Q38: What is a release pipeline vs a build pipeline?”

Answer:

Build Pipeline (triggered per commit):
Code → Test → Build → Package → Artifact Registry
Fast, runs many times per day
Purpose: Validate code quality
Release Pipeline (triggered per release):
Artifact → Deploy to Staging → Test → Approve → Deploy to Production
Slower, runs less frequently
Purpose: Safely deliver to production
Example:
PR merged to main
[Build Pipeline]
- Run tests
- Build Docker image
- Push to ECR as myapp:abc123
Image is ready but NOT deployed yet
[Release Pipeline] (triggered manually or on tag)
- Pull myapp:abc123
- Deploy to staging
- Run E2E tests
- Manual approval
- Deploy to production

Q39: What is DRY (Don’t Repeat Yourself) in CI/CD?

Section titled “Q39: What is DRY (Don’t Repeat Yourself) in CI/CD?”

Answer:

.github/workflows/deploy-template.yml
# GitHub Actions — reusable workflows
name: Deploy Template
on:
workflow_call:
inputs:
environment:
required: true
type: string
image-tag:
required: true
type: string
jobs:
deploy:
environment: ${{ inputs.environment }}
runs-on: ubuntu-latest
steps:
- name: Deploy
run: |
kubectl set image deployment/myapp \
app=myapp:${{ inputs.image-tag }} \
-n ${{ inputs.environment }}
---
# Use the reusable workflow
name: CI/CD
jobs:
deploy-staging:
uses: ./.github/workflows/deploy-template.yml
with:
environment: staging
image-tag: ${{ github.sha }}
deploy-production:
needs: deploy-staging
uses: ./.github/workflows/deploy-template.yml
with:
environment: production
image-tag: ${{ github.sha }}

Q40: How do you monitor CI/CD pipeline health?

Section titled “Q40: How do you monitor CI/CD pipeline health?”

Answer:

Terminal window
# Key CI/CD metrics to track:
# - DORA metrics (DevOps Research and Assessment)
# 1. Deployment Frequency
# How often do you deploy? (daily, weekly, monthly)
# Elite: Multiple times/day
# Low: Less than once/month
# 2. Lead Time for Changes
# Code commit → Production deployment time
# Elite: < 1 hour
# Low: > 6 months
# 3. Mean Time to Recovery (MTTR)
# How fast do you recover from failure?
# Elite: < 1 hour
# 4. Change Failure Rate
# % of deployments causing production failure
# Elite: < 5%
# GitHub Actions — track build times
- name: Record metrics
run: |
START=$(cat /tmp/start_time)
END=$(date +%s)
DURATION=$((END - START))
echo "Pipeline duration: ${DURATION}s"
# Send to monitoring
curl -X POST https://metrics.internal/ci \
-d "pipeline=$GITHUB_WORKFLOW&duration=$DURATION&status=success"
# Tools for pipeline monitoring:
# - Grafana + Prometheus (self-hosted)
# - Datadog CI Visibility
# - GitHub Insights
# - Jenkins Analytics

Q41: What is a staging environment and how should it differ from production?

Section titled “Q41: What is a staging environment and how should it differ from production?”

Answer:

Staging should mirror Production as closely as possible:
Production: Staging:
- 3 replicas - 1-2 replicas (cost savings OK)
- Production DB - Copy of production DB (with PII masked)
- Real cloud resources - Same cloud region/services
- Real external APIs - Sandbox/test versions of external APIs
- Production secrets - Separate set of secrets
- SSL certificate - Staging SSL certificate
- CDN enabled - CDN optional
- Real traffic - Synthetic/test traffic
Common staging mistakes:
❌ Staging uses SQLite, production uses PostgreSQL
❌ Staging has different env vars set to "test"
❌ Staging skips external service calls
❌ Staging runs single container vs production's 3 replicas
→ These make staging tests meaningless!

Q42: How do you implement ChatOps for deployments?

Section titled “Q42: How do you implement ChatOps for deployments?”

Answer:

# ChatOps = Trigger deployments from Slack/Teams commands
# "Hey bot, deploy myapp:v1.5 to staging"
# GitHub Actions — triggered by Slack slash command
on:
repository_dispatch:
types: [slack-deploy]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Deploy
run: |
ENV=${{ github.event.client_payload.environment }}
TAG=${{ github.event.client_payload.image_tag }}
kubectl set image deployment/myapp app=myapp:$TAG -n $ENV
# Slack bot → GitHub webhook → Trigger workflow
# Response: Post deployment status back to Slack channel
# Tools:
# - Slack + GitHub Actions
# - PagerDuty + Slack for incident response
# - Rundeck for operational tasks
# - Hubot + custom scripts

Q43: What are self-hosted vs cloud-hosted CI runners?

Section titled “Q43: What are self-hosted vs cloud-hosted CI runners?”

Answer:

FeatureCloud-Hosted (GitHub-managed)Self-Hosted
CostMinutes-based billingInfrastructure cost
Maintenance❌ Zero✅ You manage
SecurityEphemeral, isolatedYour responsibility
PerformanceStandard specsCustom hardware (GPU etc.)
Network accessPublic internetYour VPC/network
Docker support✅ Built-in✅ If configured
Best forMost use casesGPU jobs, air-gapped, cost savings
# GitHub Actions — self-hosted runner
runs-on: self-hosted
# Register self-hosted runner
# Settings → Actions → Runners → New self-hosted runner
# Download runner script, configure, and start
./config.sh --url https://github.com/org/repo \
--token <TOKEN> \
--name my-server \
--labels linux,gpu,high-memory
./run.sh # Start listening for jobs

Q44: How do you handle database migrations safely in CI/CD?

Section titled “Q44: How do you handle database migrations safely in CI/CD?”

Answer:

# Pattern: Run migrations BEFORE deploying new code
# Why: New code may expect new schema; old code must still work with new schema
jobs:
# Step 1: Migrate DB first
migrate-database:
runs-on: ubuntu-latest
environment: production
steps:
- name: Run migrations
run: |
# Run as K8s Job
cat <<EOF | kubectl apply -f -
apiVersion: batch/v1
kind: Job
metadata:
name: migrate-${{ github.sha }}
namespace: production
spec:
template:
spec:
containers:
- name: migrate
image: myapp:${{ github.sha }}
command: ["python", "manage.py", "migrate", "--no-input"]
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: db-secret
key: url
restartPolicy: Never
EOF
kubectl wait --for=condition=complete \
job/migrate-${{ github.sha }} \
-n production \
--timeout=5m
# Step 2: Only deploy after successful migration
deploy-app:
needs: migrate-database
runs-on: ubuntu-latest
steps:
- run: kubectl apply -f k8s/production/

Q45: What is the difference between push-based and pull-based deployment?

Section titled “Q45: What is the difference between push-based and pull-based deployment?”

Answer:

ModelDescriptionToolsSecurity
Push-basedCI pipeline pushes changes to targetJenkins, GitHub ActionsCI needs cluster credentials
Pull-basedOperator in cluster pulls from GitArgoCD, FluxNo external credentials needed
Push-based:
GitHub Actions → (has kubectl credentials) → Deploy to K8s cluster
Risk: CI system has production access (wider blast radius)
Pull-based (GitOps):
GitHub Actions → Update config in Git repo
ArgoCD (inside cluster) → Detects change → Applies to cluster
Safer: Cluster connects out, not inbound
# Push-based (GitHub Actions)
- name: Deploy
env:
KUBECONFIG: ${{ secrets.KUBECONFIG }}
run: kubectl apply -f k8s/
# Pull-based (ArgoCD)
# ArgoCD application auto-syncs when Git changes
# No cluster credentials needed in CI

Q46: How do you version APIs in a CI/CD pipeline?

Section titled “Q46: How do you version APIs in a CI/CD pipeline?”

Answer:

# API versioning strategies:
# 1. URL versioning: /api/v1/users, /api/v2/users
# 2. Header versioning: Accept: application/vnd.myapi.v2+json
# 3. Query param: /api/users?version=2
# In CI/CD — deploy multiple API versions simultaneously
# Blue deployment = v1 API
# Green deployment = v2 API
# Both run simultaneously during transition period
jobs:
deploy-api-v2:
runs-on: ubuntu-latest
steps:
- name: Deploy v2 API (alongside v1)
run: |
kubectl apply -f k8s/api-v2/deployment.yaml
kubectl apply -f k8s/api-v2/service.yaml
# Update Ingress to route /api/v2/* to new service
kubectl apply -f k8s/ingress-v2.yaml
- name: Test v2 API
run: |
curl -f https://api.myapp.com/api/v2/health
- name: Update documentation
run: |
# Auto-generate API docs from code
npx swagger-cli validate api-spec.yaml
aws s3 cp api-spec.yaml s3://docs-bucket/v2/

Q47: What is a Chaos Engineering experiment in CI/CD?

Section titled “Q47: What is a Chaos Engineering experiment in CI/CD?”

Answer: Chaos Engineering = Intentionally inject failures to test system resilience.

# Run chaos tests as part of staging pipeline
jobs:
chaos-tests:
needs: deploy-staging
runs-on: ubuntu-latest
steps:
- name: Install chaos tools
run: |
pip install chaostoolkit
pip install chaostoolkit-kubernetes
- name: Run chaos experiment — kill random pod
run: |
chaos run experiments/kill-pod.json
- name: Verify service is still up
run: |
sleep 30 # Wait for recovery
curl -f https://staging.myapp.com/health
# If this fails, system doesn't self-heal!
experiments/kill-pod.json
{
"title": "Kill random API pod",
"steady-state-hypothesis": {
"title": "Service responds to requests",
"probes": [{
"name": "service-healthy",
"type": "probe",
"provider": {
"type": "http",
"url": "https://staging.myapp.com/health",
"timeout": 10
}
}]
},
"method": [{
"name": "kill-api-pod",
"type": "action",
"provider": {
"type": "python",
"module": "chaosk8s.pod.actions",
"func": "terminate_pods",
"arguments": {
"label_selector": "app=api",
"ns": "staging"
}
}
}]
}

Q48: How do you implement dependency updates automation?

Section titled “Q48: How do you implement dependency updates automation?”

Answer:

.github/dependabot.yml
# Dependabot — auto-create PRs for dependency updates
version: 2
updates:
- package-ecosystem: npm
directory: "/"
schedule:
interval: weekly # weekly or daily
labels:
- dependencies
open-pull-requests-limit: 10
groups:
minor-updates:
patterns: ["*"]
update-types: ["minor", "patch"]
- package-ecosystem: docker
directory: "/"
schedule:
interval: weekly
commit-message:
prefix: "chore(docker)"
- package-ecosystem: github-actions
directory: "/"
schedule:
interval: monthly
# Auto-merge safe dependency updates
on:
pull_request:
types: [opened, synchronize]
jobs:
auto-merge-deps:
if: github.actor == 'dependabot[bot]'
runs-on: ubuntu-latest
steps:
- name: Check if minor/patch update
id: metadata
uses: dependabot/fetch-metadata@v1
- name: Auto-merge if minor/patch
if: steps.metadata.outputs.update-type != 'version-update:semver-major'
run: gh pr merge --auto --squash "$PR_URL"
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

Q49: How do you implement progressive delivery?

Section titled “Q49: How do you implement progressive delivery?”

Answer: Progressive Delivery = Controlled, gradual rollout with automated analysis.

# Flagger — automatic canary analysis and promotion
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: myapp
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: myapp
progressDeadlineSeconds: 120
service:
port: 80
analysis:
interval: 1m # Check metrics every minute
threshold: 5 # Max failed checks before rollback
maxWeight: 50 # Max canary traffic %
stepWeight: 10 # Increase by 10% each interval
metrics:
- name: request-success-rate
thresholdRange:
min: 99 # Must be > 99% success rate
interval: 1m
- name: request-duration
thresholdRange:
max: 500 # Must be < 500ms p99 latency
interval: 1m
webhooks:
- name: load-test
url: http://flagger-loadtester.test/
timeout: 5s
metadata:
cmd: "hey -z 1m -q 10 -c 2 http://myapp-canary/"

Q50: What are the DORA metrics and how do you measure them?

Section titled “Q50: What are the DORA metrics and how do you measure them?”

Answer: DORA (DevOps Research and Assessment) metrics are the 4 key indicators of DevOps performance.

MetricMeasuresEliteLow
Deployment FrequencyHow often you deployMultiple/day< 1/month
Lead Time for ChangesCommit → Production< 1 hour> 6 months
Change Failure Rate% failing deployments< 5%> 46%
MTTRTime to recover from failure< 1 hour> 1 week
Terminal window
# Measure Deployment Frequency
# Count deployments per day from CI/CD tool
# Measure Lead Time
git log --format="%H %ci" HEAD | head -1 # Get commit time
# Compare with deployment time in pipeline logs
# Measure Change Failure Rate
# (Failed deployments / Total deployments) × 100
# Track incidents that were deployment-related
# Measure MTTR
# Time from incident created → incident resolved
# Track in PagerDuty, OpsGenie, or similar
# Tools that auto-calculate DORA metrics:
# - Faros (open-source)
# - LinearB
# - Cortex
# - DORA Team's Four Keys project (GitHub)

Back to DevOps Q&A Index