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.
---
*Need help setting up CI/CD for your project? Contact us to discuss your automation needs.*
Enjoyed this article?
If you found this helpful, let's discuss how we can help with your next project.
Book a call