Continuous Integration
Practices for frequently integrating code changes with automated validation
Continuous Integration (CI) is the practice of frequently merging code changes into a shared repository, where each integration is verified by automated builds and tests. CI enables teams to detect problems early and deliver software more rapidly.
What is Continuous Integration?
Continuous Integration promotes frequent team integrations and automatic builds. By integrating the system more frequently, integration issues are identified earlier when they are easier to fix, and the overall integration effort is reduced via continuous feedback.
CI Principles
Commit Frequently
- Integrate changes at least daily
- Keep commits small and focused
- Avoid long-lived feature branches
- Merge to main branch often
Maintain a Single Source of Truth
- All code in version control
- All configuration in version control
- Build scripts checked in
- Infrastructure as code
Automate the Build
# Example: GitHub Actions CI workflow
name: CI
on: [push, pull_request]
jobs:
build:
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
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: build
path: dist/Make the Build Self-Testing
test:
runs-on: ubuntu-latest
needs: build
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 unit tests
run: npm test -- --coverage
- name: Upload coverage
uses: codecov/codecov-action@v3Keep the Build Fast
| Build Stage | Target Time |
|---|---|
| Checkout & Setup | < 1 min |
| Install Dependencies | < 2 min |
| Compile/Build | < 3 min |
| Unit Tests | < 3 min |
| Total | < 10 min |
CI Pipeline Stages
Stage 1: Build
build:
stage: build
script:
- npm ci
- npm run build
- npm run build:docs
artifacts:
paths:
- dist/
- docs/
expire_in: 1 dayStage 2: Lint & Format
lint:
stage: validate
script:
- npm run lint
- npm run format:check
- npm run typecheckStage 3: Unit Tests
unit-test:
stage: test
script:
- npm test -- --coverage --ci
coverage: '/All files[^|]*\|[^|]*\s+([\d\.]+)/'
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
junit: junit.xmlStage 4: Security Scans
security-scan:
stage: security
parallel:
matrix:
- SCAN_TYPE: [sast, sca, secrets]
script:
- |
case $SCAN_TYPE in
sast) semgrep --config auto . ;;
sca) npm audit --audit-level=high ;;
secrets) gitleaks detect --source . ;;
esacStage 5: Integration Tests
integration-test:
stage: test
services:
- postgres:15
- redis:7
variables:
DATABASE_URL: postgres://postgres:postgres@postgres:5432/test
REDIS_URL: redis://redis:6379
script:
- npm run test:integrationBranch Strategies
Trunk-Based Development
main ────●────●────●────●────●────●────●────●────▶
\ / \ /
● ●──────●
feature short-lived
(1 day) feature (2-3 days)Characteristics:
- Single main branch
- Short-lived feature branches (< 2 days)
- Feature flags for incomplete work
- Continuous deployment from main
GitHub Flow
main ────●────●────────●────●────●────▶
\ /
●────●───●
feature branch
(with PR review)Characteristics:
- Main branch always deployable
- Feature branches for all changes
- Pull request for code review
- Deploy after merge to main
GitFlow (for regulated environments)
main ●─────────────────●─────────────────●────▶
\ / /
develop ●────●────●───●────●────●────●──●────▶
\ / \ /
● ●─────●
feature featureCharacteristics:
- Separate develop and main branches
- Release branches for stabilization
- Better for scheduled releases
- More overhead, more control
Build Artifact Management
Versioning Strategy
# Semantic versioning with build metadata
VERSION=1.2.3+build.${CI_PIPELINE_ID}
# Or with git SHA
VERSION=1.2.3+${GIT_SHORT_SHA}Artifact Storage
# Upload build artifacts
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: app-${{ github.sha }}
path: |
dist/
package.json
retention-days: 30Container Images
# Build and push Docker image
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
myapp:${{ github.sha }}
myapp:latest
cache-from: type=gha
cache-to: type=gha,mode=maxCI Best Practices
Code Quality Gates
quality-gate:
stage: validate
script:
- npm run lint
- npm run test -- --coverage
rules:
- if: $CI_PIPELINE_SOURCE == "merge_request_event"
allow_failure: falseParallel Execution
test:
parallel:
matrix:
- NODE_VERSION: ['18', '20', '22']
OS: ['ubuntu-latest', 'windows-latest']
runs-on: ${{ matrix.OS }}
steps:
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.NODE_VERSION }}
- run: npm testCaching Dependencies
- name: Cache node modules
uses: actions/cache@v4
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-Fail Fast
jobs:
lint:
runs-on: ubuntu-latest
steps:
- run: npm run lint
# Fail immediately, don't wait for other jobs
test:
needs: lint # Only run if lint passes
runs-on: ubuntu-latest
steps:
- run: npm testCI Notifications
Slack Notification
- name: Notify Slack
if: failure()
uses: slackapi/slack-github-action@v1
with:
payload: |
{
"text": "CI Failed: ${{ github.repository }}",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Build Failed*\n${{ github.event.head_commit.message }}"
}
}
]
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}Status Checks
# Require status checks before merge
# In repository settings:
# - Require status checks to pass
# - Require branches to be up to date
# - Select required checks: lint, test, securityMetrics & Monitoring
Build Metrics to Track
| Metric | Target | Action if Exceeded |
|---|---|---|
| Build Duration | < 10 min | Optimize or parallelize |
| Build Success Rate | > 95% | Investigate failures |
| Test Coverage | > 80% | Add tests |
| Time to Fix Broken Build | < 10 min | Prioritize fixes |
Dashboard Example
┌─────────────────────────────────────────────────────────────────┐
│ CI DASHBOARD │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Build Status: ✅ Passing Last Build: 5 min ago │
│ │
│ Build Duration Trend (last 7 days) │
│ ┌────────────────────────────────────────┐ │
│ │ ▄ │ 12 min │
│ │ ▄█▄ ▄ │ 10 min │
│ │ ▄███▄ ▄█▄ ▄▄ ▄ ▄▄ │ 8 min │
│ │ ▄█████▄▄███▄ ▄██▄ ▄█▄ ▄██▄ │ 6 min │
│ └────────────────────────────────────────┘ │
│ Mon Tue Wed Thu Fri Sat Sun │
│ │
│ Success Rate: 96.5% Coverage: 84.2% Avg Time: 7.3 min │
│ │
└─────────────────────────────────────────────────────────────────┘Troubleshooting
Common Issues
| Issue | Cause | Solution |
|---|---|---|
| Flaky tests | Non-deterministic behavior | Isolate tests, fix race conditions |
| Slow builds | Large dependencies | Cache dependencies, parallelize |
| Merge conflicts | Long-lived branches | Merge more frequently |
| Environment issues | Inconsistent setup | Use containers, lock versions |
Debugging CI Failures
# Add debug output
- name: Debug
if: failure()
run: |
echo "Node version: $(node -v)"
echo "NPM version: $(npm -v)"
cat package.json
ls -la node_modules/ | head -20
# Enable step debug logging
# Set secret: ACTIONS_STEP_DEBUG = trueRelated Resources
- CI/CD Overview
- Continuous Delivery
- Automated Testing
- Code Reviews
- Microsoft Playbook: Continuous Integration
Compliance
This section fulfills ISO 13485 requirements for software validation (4.1.5), control of production (7.5.1), and monitoring (8.2.4), and ISO 27001 requirements for secure development lifecycle (A.8.25), change management (A.8.32), and security testing (A.8.29).
How is this guide?
Last updated on