Manual deployments are a thing of the past. In this guide, I’ll walk you through setting up a production-grade CI/CD pipeline that will have your code tested and deployed in minutes.
What We’re Building
By the end of this guide, you’ll have:
- Automated testing on every pull request
- Preview deployments for feature branches
- Automatic production deployments on merge to main
- Slack notifications for deployment status
Prerequisites
- A GitHub repository
- A hosting platform (we’ll use Vercel, but the concepts apply anywhere)
- Basic familiarity with YAML
Step 1: The Testing Pipeline
First, let’s ensure every PR is tested before it can be merged:
# .github/workflows/test.yml
name: Test
on:
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run linter
run: npm run lint
- name: Run type check
run: npm run type-check
- name: Run tests
run: npm test -- --coverage
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
files: ./coverage/lcov.info
Step 2: Preview Deployments
Preview deployments let reviewers see changes before merging:
# .github/workflows/preview.yml
name: Preview Deployment
on:
pull_request:
branches: [main]
jobs:
deploy-preview:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy to Vercel
uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
scope: ${{ secrets.VERCEL_ORG_ID }}
- name: Comment PR with preview URL
uses: actions/github-script@v7
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '🚀 Preview deployed to: ${{ steps.deploy.outputs.preview-url }}'
})
Step 3: Production Deployment
When code hits main, it goes straight to production:
# .github/workflows/deploy.yml
name: Production Deployment
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
env:
NODE_ENV: production
- name: Deploy to Vercel
uses: amondnet/vercel-action@v25
with:
vercel-token: ${{ secrets.VERCEL_TOKEN }}
vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
vercel-args: '--prod'
- name: Notify Slack
uses: 8398a7/action-slack@v3
with:
status: ${{ job.status }}
fields: repo,message,commit,author
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
if: always()
Best Practices
1. Use Caching Aggressively
Caching dependencies can cut your pipeline time in half:
- name: Cache dependencies
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
2. Run Jobs in Parallel
Independent jobs should run simultaneously:
jobs:
lint:
runs-on: ubuntu-latest
steps:
- run: npm run lint
test:
runs-on: ubuntu-latest
steps:
- run: npm test
build:
needs: [lint, test]
runs-on: ubuntu-latest
steps:
- run: npm run build
3. Use Environment Protection
For production deployments, add manual approval:
deploy:
environment:
name: production
url: https://yourapp.com
runs-on: ubuntu-latest
Common Pitfalls
- Not caching - Slow pipelines lead to ignored pipelines
- Too many jobs - Parallelization has diminishing returns
- Secrets in code - Always use GitHub Secrets
- No rollback plan - Always know how to revert
Monitoring Your Pipeline
Track these metrics:
| Metric | Good | Needs Work |
|---|---|---|
| Average pipeline time | < 5 min | > 10 min |
| Success rate | > 95% | < 90% |
| Time to rollback | < 5 min | > 15 min |
Conclusion
A well-designed CI/CD pipeline is like having a tireless QA engineer and deployment specialist working 24/7. Invest the time to set it up right, and it will pay dividends for years.
Want me to review your current pipeline? Get in touch.