Automate your software delivery with Continuous Integration and Continuous Deployment pipelines
CI/CD is the backbone of modern DevOps practices. It automates the process of integrating code changes, testing them, and deploying to production - enabling teams to deliver software faster and more reliably.
Continuous Integration is the practice of automatically building and testing code every time a team member commits changes to version control. The goal is to detect integration issues early and fix them quickly.
Continuous Deployment extends CI by automatically deploying every change that passes all tests to production. Continuous Delivery is similar but requires manual approval before production deployment.
Code is always in a deployable state
Fully automated to production
Your Git workflow determines how code flows from development to production. Different workflows suit different team sizes and release cadences.
A structured workflow with multiple long-lived branches. Good for scheduled releases and maintaining multiple versions.
Branch Structure:
main - Production-ready code
develop - Integration branch for features
feature/* - New features (branch from develop)
release/* - Release preparation
hotfix/* - Emergency production fixes
Best for:
Teams with scheduled releases, multiple versions in production, or complex release processes
Everyone commits to a single branch (trunk/main) frequently. Short-lived feature branches merge daily. Enables continuous deployment.
Key Principles:
main branchBest for:
High-performing teams doing continuous deployment, SaaS products, teams wanting fast feedback
Simplified workflow with main branch and feature branches. Pull requests for code review. Deploy from main branch.
Workflow Steps:
1. Create feature branch from main
2. Make commits and push regularly
3. Open pull request for review
4. Discuss and review code
5. Deploy to staging for testing
6. Merge to main after approval
7. Deploy main to production
Best for:
Small to medium teams, web applications, teams using GitHub, continuous deployment workflows
GitHub Actions is a CI/CD platform integrated directly into GitHub. It uses YAML files to define workflows that run on specific events (push, pull request, schedule, etc.).
Workflow
Automated process defined in YAML file
Event
Trigger that starts a workflow (push, PR, etc.)
Job
Set of steps that run on the same runner
Step
Individual task (run command or action)
Create .github/workflows/ci.yml:
name: CI Pipeline
# Trigger on push to main or pull requests
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [18.x, 20.x]
steps:
# Checkout code
- uses: actions/checkout@v4
# Setup Node.js
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
# Install dependencies
- name: Install dependencies
run: npm ci
# Run linting
- name: Lint code
run: npm run lint
# Run tests
- name: Run tests
run: npm test
# Build application
- name: Build
run: npm run build
# Upload coverage
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
files: ./coverage/coverage-final.json
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Security audit
- name: Run security audit
run: npm audit --audit-level=moderate
# Dependency check
- name: Check for vulnerabilities
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}How you deploy to production matters. Different strategies offer different trade-offs between speed, safety, and complexity.
Gradually replace old version with new version across servers. Update a few instances at a time.
✅ Pros:
❌ Cons:
Run two identical environments (blue and green). Deploy to inactive environment, then switch traffic.
Process:
1. Blue environment serves production traffic
2. Deploy new version to green environment
3. Test green environment thoroughly
4. Switch router/load balancer to green
5. Keep blue as backup for quick rollback
✅ Pros:
❌ Cons:
Release to a small subset of users first, monitor, then gradually increase traffic to new version.
Process:
1. Deploy new version alongside old (e.g., 5% traffic)
2. Monitor metrics (errors, latency, business KPIs)
3. If healthy, increase to 25%, then 50%, then 100%
4. If issues detected, rollback immediately
✅ Pros:
❌ Cons:
Deploy code with features turned off. Enable features gradually via configuration without redeployment.
// Example with LaunchDarkly
import { useLDClient } from 'launchdarkly-react-client-sdk';
function NewFeature() {
const ldClient = useLDClient();
const showNewUI = ldClient?.variation('new-ui-redesign', false);
if (showNewUI) {
return <NewUIComponent />;
}
return <OldUIComponent />;
}✅ Pros:
❌ Cons:
CI/CD pipelines often need access to sensitive data (API keys, passwords, certificates). Proper secrets management is critical for security.
Never commit secrets to Git
Use .gitignore and secret scanning tools
Use environment variables
Store secrets in CI/CD platform's secret store
Rotate secrets regularly
Automate rotation and use short-lived credentials
Principle of least privilege
Give pipelines only the permissions they need
Audit access logs
Monitor who accesses secrets and when
name: Deploy to AWS
on:
push:
branches: [ main ]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: Deploy to S3
run: |
aws s3 sync ./build s3://my-bucket
- name: Invalidate CloudFront
run: |
aws cloudfront create-invalidation \
--distribution-id ${{ secrets.CLOUDFRONT_DIST_ID }} \
--paths "/*"You've learned how to automate software delivery with CI/CD pipelines:
Now that you can automate builds and deployments, let's learn about containerization and orchestration with Docker and Kubernetes.
Continue to Module 3: Container Orchestration →