Setting Up CI/CD Pipelines: From Code to Production
Setting Up CI/CD Pipelines: From Code to Production
A well-configured CI/CD pipeline automates testing, building, and deployment, reducing errors and speeding up delivery. Here's how to set up a production-ready CI/CD pipeline for your application.
What is CI/CD?
CI (Continuous Integration): Automatically test and build code when changes are pushed.
CD (Continuous Deployment): Automatically deploy code that passes tests to production.
Benefits:
- Catch bugs early
- Deploy faster
- Reduce manual errors
- Improve code quality
- Enable frequent releases
Pipeline Stages
A typical pipeline includes:
1. Lint & Format: Check code style
2. Test: Run unit and integration tests
3. Build: Create production artifacts
4. Deploy: Deploy to staging/production
GitHub Actions Example
Here's a complete GitHub Actions workflow for a Next.js application:
YAML
name: CI/CD Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
env:
NODE_VERSION: '20'
jobs:
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: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run linter
run: npm run lint
- name: Run tests
run: npm test
env:
CI: true
- name: Build application
run: npm run build
env:
NEXT_PUBLIC_API_URL: ${{ secrets.NEXT_PUBLIC_API_URL }}
deploy-staging:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/develop'
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build application
run: npm run build
env:
NEXT_PUBLIC_API_URL: ${{ secrets.STAGING_API_URL }}
- name: Deploy to staging
uses: deployment-action
with:
environment: staging
# Add your deployment steps here
deploy-production:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
environment: production
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build application
run: npm run build
env:
NEXT_PUBLIC_API_URL: ${{ secrets.PRODUCTION_API_URL }}
- name: Deploy to production
uses: deployment-action
with:
environment: production
# Add your deployment steps here
Environment-Specific Deployments
Staging Environment
Deploy from develop branch to staging:
YAML
deploy-staging:
if: github.ref == 'refs/heads/develop'
environment: staging
Production Environment
Deploy from main branch to production:
YAML
deploy-production:
if: github.ref == 'refs/heads/main'
environment: production
Docker-Based Pipeline
For containerized applications:
YAML
jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- 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:
context: .
push: true
tags: myapp:latest,myapp:${{ github.sha }}
cache-from: type=registry,ref=myapp:latest
cache-to: type=inline
Testing Strategies
Unit Tests
Run on every commit:
YAML
- name: Run unit tests
run: npm test -- --coverage
Integration Tests
Run on pull requests:
YAML
- name: Run integration tests
run: npm run test:integration
env:
DATABASE_URL: ${{ secrets.TEST_DATABASE_URL }}
E2E Tests
Run before production deployment:
YAML
- name: Run E2E tests
run: npm run test:e2e
env:
BASE_URL: ${{ secrets.STAGING_URL }}
Security Scanning
Add security checks:
YAML
- name: Run security audit
run: npm audit --audit-level=moderate
- name: Scan for vulnerabilities
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
Deployment Strategies
Blue-Green Deployment
Deploy new version alongside old, switch traffic:
YAML
- name: Deploy blue environment
run: |
docker-compose -f docker-compose.blue.yml up -d
- name: Health check
run: |
curl -f http://blue.example.com/health || exit 1
- name: Switch traffic
run: |
# Switch load balancer to blue
Rolling Deployment
Gradually replace old instances:
YAML
- name: Rolling deployment
run: |
kubectl set image deployment/myapp myapp=myapp:${{ github.sha }}
kubectl rollout status deployment/myapp
Notifications
Notify team on failures:
YAML
- name: Notify on failure
if: failure()
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
text: 'Deployment failed!'
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
Best Practices
1. Use Secrets
Never hardcode secrets:
YAML
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
2. Cache Dependencies
Speed up builds with caching:
YAML
- uses: actions/setup-node@v4
with:
cache: 'npm'
3. Parallel Jobs
Run independent jobs in parallel:
YAML
jobs:
test:
# ...
lint:
# ...
build:
needs: [test, lint]
4. Fail Fast
Stop pipeline on first failure:
YAML
jobs:
test:
continue-on-error: false
5. Use Matrix Builds
Test on multiple versions:
YAML
strategy:
matrix:
node-version: [18, 20, 22]
Monitoring Deployments
Track deployment metrics:
- Deployment frequency
- Lead time
- Mean time to recovery (MTTR)
- Change failure rate
Common Pitfalls
1. Not testing in CI: Tests only run locally
2. Deploying on every commit: No staging environment
3. No rollback plan: Can't revert bad deployments
4. Ignoring failures: Pipeline continues on errors
5. Hardcoded secrets: Security risk
Tools
- GitHub Actions: CI/CD for GitHub repos
- GitLab CI: Built-in CI/CD for GitLab
- Jenkins: Self-hosted CI/CD
- CircleCI: Cloud-based CI/CD
- Vercel/Netlify: Automatic deployments for frontend
Conclusion
A well-configured CI/CD pipeline is essential for modern development. Start simple, add complexity as needed, and always prioritize reliability over speed.
Remember: The goal is to deploy with confidence, not to deploy as fast as possible.
Enjoyed this article?
If you found this helpful, let's discuss how we can help with your next project.
Book a call