Introduction
Theory without practice is philosophy. Practice without theory is guesswork. Over 41 parts, you have built a comprehensive mental model of modern software delivery — from SDLC fundamentals through CI/CD pipelines, testing strategies, security practices, observability, and team dynamics. Now it is time to build.
This capstone presents 5 projects of increasing complexity. Each project integrates concepts from multiple parts of the series, requires hands-on tool usage, and produces a tangible artifact you can showcase in your portfolio. They are designed to be completed in order, each building on skills developed in the previous project.
| Project | Difficulty | Time Estimate | Key Series Parts |
|---|---|---|---|
| 1. Personal CI/CD Pipeline | Beginner | 1–2 days | Parts 9, 14, 15 |
| 2. Microservices Delivery Platform | Intermediate | 3–5 days | Parts 16, 20, 27 |
| 3. Supply Chain Security Audit | Intermediate | 2–3 days | Parts 13, 23, 24 |
| 4. DORA Metrics Dashboard | Advanced | 5–7 days | Parts 25, 26, 15 |
| 5. Full Delivery Lifecycle | Advanced | 2–4 weeks | Entire series |
Project 1: Personal CI/CD Pipeline (Beginner)
Build a complete CI/CD pipeline for a simple web application. This project covers the fundamentals: version control, automated testing, continuous integration, and automated deployment.
Requirements
- A Git repository on GitHub containing a simple web application (static site, React app, or Python Flask app)
- Automated unit tests (minimum 10 tests with 80%+ coverage)
- A GitHub Actions workflow that: lints code, runs tests, builds the application, and deploys to GitHub Pages
- Branch protection rules requiring CI to pass before merge
- A README with architecture diagram and setup instructions
flowchart LR
A["Developer\nPush to branch"] --> B["GitHub Actions\nTrigger"]
B --> C["Lint\n(ESLint/Flake8)"]
C --> D["Unit Tests\n(Jest/Pytest)"]
D --> E["Build\n(npm build/Docker)"]
E --> F{"Tests Pass?"}
F -->|Yes| G["Deploy to\nGitHub Pages"]
F -->|No| H["Notify Developer\n(PR Comment)"]
G --> I["Live Site\n✓ Deployed"]
Implementation Guide
# Step 1: Create project structure
mkdir my-cicd-project && cd my-cicd-project
git init
# Step 2: Initialize a simple Node.js application
npm init -y
npm install --save-dev jest eslint
# Step 3: Create source and test files
mkdir src tests
cat > src/calculator.js << 'EOF'
function add(a, b) { return a + b; }
function subtract(a, b) { return a - b; }
function multiply(a, b) { return a * b; }
function divide(a, b) {
if (b === 0) throw new Error('Division by zero');
return a / b;
}
module.exports = { add, subtract, multiply, divide };
EOF
# Step 4: Create test file
cat > tests/calculator.test.js << 'EOF'
const { add, subtract, multiply, divide } = require('../src/calculator');
test('adds 1 + 2 to equal 3', () => expect(add(1, 2)).toBe(3));
test('subtracts 5 - 3 to equal 2', () => expect(subtract(5, 3)).toBe(2));
test('multiplies 3 * 4 to equal 12', () => expect(multiply(3, 4)).toBe(12));
test('divides 10 / 2 to equal 5', () => expect(divide(10, 2)).toBe(5));
test('throws on division by zero', () => {
expect(() => divide(10, 0)).toThrow('Division by zero');
});
EOF
# .github/workflows/ci.yml — GitHub Actions Pipeline
name: CI/CD Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
lint-and-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: Lint
run: npx eslint src/ --ext .js
- name: Run tests with coverage
run: npx jest --coverage --coverageThreshold='{"global":{"branches":80,"functions":80,"lines":80}}'
deploy:
needs: lint-and-test
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
permissions:
pages: write
id-token: write
steps:
- uses: actions/checkout@v4
- name: Build
run: npm run build
- name: Deploy to GitHub Pages
uses: actions/deploy-pages@v4
Project 2: Microservices Delivery Platform (Intermediate)
Build a 3-service application with independent CI/CD pipelines, contract testing between services, and a blue-green deployment simulation. This project teaches you how to manage multiple deployable units with interdependencies.
Architecture
flowchart TD
subgraph "API Gateway"
GW["Nginx\nReverse Proxy\nPort 80"]
end
subgraph "Service A: Users"
A["User Service\n(Python Flask)\nPort 5001"]
ADB["PostgreSQL\n(Users DB)"]
end
subgraph "Service B: Orders"
B["Order Service\n(Node.js Express)\nPort 5002"]
BDB["PostgreSQL\n(Orders DB)"]
end
subgraph "Service C: Notifications"
C["Notification Service\n(Python FastAPI)\nPort 5003"]
CQ["Redis\n(Message Queue)"]
end
GW --> A
GW --> B
GW --> C
A --> ADB
B --> BDB
B --> A
B --> CQ
CQ --> C
Requirements
- 3 services in separate directories, each with its own Dockerfile and CI pipeline
- Docker Compose for local development (all services + databases + message queue)
- Contract tests between Service B (Orders) and Service A (Users) using Pact or schema validation
- Blue-green deployment simulation using Docker Compose profiles or Nginx upstream switching
- Health check endpoints on all services (
/health) - Integration test suite that validates cross-service interactions
Implementation Guide
# docker-compose.yml — Multi-service local environment
version: '3.8'
services:
gateway:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- user-service
- order-service
- notification-service
user-service:
build: ./services/users
ports:
- "5001:5001"
environment:
- DATABASE_URL=postgresql://postgres:postgres@users-db:5432/users
depends_on:
- users-db
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5001/health"]
interval: 10s
timeout: 5s
retries: 3
order-service:
build: ./services/orders
ports:
- "5002:5002"
environment:
- DATABASE_URL=postgresql://postgres:postgres@orders-db:5432/orders
- USER_SERVICE_URL=http://user-service:5001
- REDIS_URL=redis://redis:6379
depends_on:
- orders-db
- redis
- user-service
notification-service:
build: ./services/notifications
ports:
- "5003:5003"
environment:
- REDIS_URL=redis://redis:6379
users-db:
image: postgres:16-alpine
environment:
POSTGRES_DB: users
POSTGRES_PASSWORD: postgres
orders-db:
image: postgres:16-alpine
environment:
POSTGRES_DB: orders
POSTGRES_PASSWORD: postgres
redis:
image: redis:7-alpine
# Blue-Green Deployment Simulation
# Deploy new version alongside existing one
# Step 1: Build new version
docker build -t order-service:v2 ./services/orders
# Step 2: Start new version on different port (green)
docker run -d --name order-green -p 5012:5002 order-service:v2
# Step 3: Run smoke tests against green
curl -f http://localhost:5012/health || exit 1
# Step 4: Switch traffic (update Nginx upstream)
sed -i 's/order-service:5002/order-green:5002/' nginx.conf
docker exec gateway nginx -s reload
# Step 5: Verify and drain old (blue) instance
sleep 10
docker stop order-blue && docker rm order-blue
echo "Blue-green deployment complete!"
Project 3: Supply Chain Security Audit (Intermediate)
Audit an open-source project's software supply chain. Generate a Software Bill of Materials (SBOM), scan for vulnerabilities, check SLSA compliance, and sign artifacts. This project builds practical security skills that are increasingly required in enterprise environments.
Scope & Tools
| Task | Tool | Output |
|---|---|---|
| Generate SBOM | Syft | CycloneDX/SPDX JSON file |
| Vulnerability scanning | Trivy / Grype | Vulnerability report (JSON + table) |
| License compliance | Syft + custom script | License inventory with risk flags |
| Container scanning | Trivy | OS + library vulnerability report |
| Artifact signing | Cosign (Sigstore) | Signed container image with verification |
| SLSA assessment | slsa-verifier | SLSA level determination |
Implementation Guide
# Supply Chain Security Audit — Complete Workflow
# Step 1: Generate SBOM from a container image
syft packages alpine:3.19 -o cyclonedx-json > sbom.json
echo "SBOM generated: $(cat sbom.json | jq '.components | length') components found"
# Step 2: Scan SBOM for vulnerabilities
grype sbom:sbom.json --output table
grype sbom:sbom.json --output json > vulnerabilities.json
# Step 3: Scan container image directly with Trivy
trivy image --severity HIGH,CRITICAL --format json \
--output trivy-report.json alpine:3.19
# Step 4: Check license compliance
syft packages alpine:3.19 -o json | \
jq '[.artifacts[].licenses[]?.value] | group_by(.) | map({license: .[0], count: length}) | sort_by(-.count)' \
> license-inventory.json
# Step 5: Sign a container image with Cosign
cosign sign --key cosign.key myregistry/myapp:v1.0.0
# Step 6: Verify the signature
cosign verify --key cosign.pub myregistry/myapp:v1.0.0
Security Audit Report
Your final deliverable is a structured security audit report containing: (1) Executive summary with risk score, (2) SBOM inventory with component count and dependency depth, (3) Vulnerability findings ranked by CVSS score with remediation recommendations, (4) License compliance matrix with GPL/copyleft risk flags, (5) SLSA compliance assessment with current level and gaps to next level, (6) Signed artifact verification proof. Present this as a Markdown document with embedded JSON snippets.
Project 4: DORA Metrics Dashboard (Advanced)
Build a dashboard that measures all four DORA metrics from real GitHub or GitLab data. This project requires API integration, data processing, time-series calculations, and data visualisation.
The Four Metrics
- Deployment Frequency: How often does your team deploy to production?
- Lead Time for Changes: How long from commit to production?
- Change Failure Rate: What percentage of deployments cause an incident?
- Time to Restore Service: How quickly do you recover from failures?
Implementation Guide
# DORA Metrics Calculator — Core Logic
import requests
from datetime import datetime, timedelta
from typing import Dict, List
def calculate_deployment_frequency(
deployments: List[Dict], period_days: int = 30
) -> Dict:
"""Calculate deployment frequency over a period.
Args:
deployments: List of deployment events with 'timestamp' field
period_days: Number of days to analyze
Returns:
Dict with frequency metrics and DORA classification
"""
cutoff = datetime.now() - timedelta(days=period_days)
recent = [d for d in deployments
if datetime.fromisoformat(d['timestamp']) > cutoff]
total = len(recent)
per_day = total / period_days
per_week = per_day * 7
# DORA classification
if per_day >= 1:
classification = "Elite"
elif per_week >= 1:
classification = "High"
elif total >= 1: # At least once per month
classification = "Medium"
else:
classification = "Low"
return {
"total_deployments": total,
"per_day": round(per_day, 2),
"per_week": round(per_week, 2),
"classification": classification,
"period_days": period_days
}
def calculate_lead_time(
pull_requests: List[Dict]
) -> Dict:
"""Calculate lead time for changes (PR merge to deploy).
Args:
pull_requests: List of PRs with 'created_at' and 'deployed_at'
Returns:
Dict with lead time statistics and DORA classification
"""
lead_times = []
for pr in pull_requests:
if pr.get('deployed_at'):
created = datetime.fromisoformat(pr['created_at'])
deployed = datetime.fromisoformat(pr['deployed_at'])
hours = (deployed - created).total_seconds() / 3600
lead_times.append(hours)
if not lead_times:
return {"error": "No data available"}
median_hours = sorted(lead_times)[len(lead_times) // 2]
if median_hours < 24:
classification = "Elite"
elif median_hours < 168: # 1 week
classification = "High"
elif median_hours < 720: # 1 month
classification = "Medium"
else:
classification = "Low"
return {
"median_hours": round(median_hours, 1),
"mean_hours": round(sum(lead_times) / len(lead_times), 1),
"p95_hours": round(sorted(lead_times)[int(len(lead_times) * 0.95)], 1),
"sample_size": len(lead_times),
"classification": classification
}
# Example usage
sample_deploys = [
{"timestamp": "2026-05-14T10:00:00", "version": "v1.2.3"},
{"timestamp": "2026-05-13T14:30:00", "version": "v1.2.2"},
{"timestamp": "2026-05-12T09:15:00", "version": "v1.2.1"},
]
result = calculate_deployment_frequency(sample_deploys, period_days=7)
print(f"Deployment Frequency: {result['per_week']}/week")
print(f"DORA Classification: {result['classification']}")
Project 5: Full Delivery Lifecycle (Advanced)
The ultimate capstone: build an application end-to-end through the complete delivery lifecycle. This is a multi-week project, ideally done with a team of 2–4 people, covering every stage from requirements through production monitoring.
flowchart TD
A["Phase 1: Requirements\n& Architecture"] --> B["Phase 2: Implementation\n& Testing"]
B --> C["Phase 3: CI/CD Pipeline\n& Deployment"]
C --> D["Phase 4: Observability\n& Monitoring"]
D --> E["Phase 5: Incident Response\n& Improvement"]
E --> F["Phase 6: Documentation\n& Presentation"]
A ---|"ADRs, User Stories\nC4 Diagrams"| A
B ---|"TDD, Contract Tests\nCode Review"| B
C ---|"GitHub Actions\nDocker, K8s"| C
D ---|"Metrics, Logs, Traces\nSLOs, Alerts"| D
E ---|"Chaos Engineering\nPostmortems"| E
F ---|"README, Runbook\nDemo Video"| F
Project Phases
Phase 1: Requirements & Architecture (Days 1–3)
- Write 5–10 user stories with acceptance criteria
- Create Architecture Decision Records (ADRs) for key technology choices
- Draw C4 diagrams (Context, Container, Component levels)
- Define API contracts (OpenAPI spec)
- Define SLOs (Service Level Objectives) for your system
Phase 2: Implementation & Testing (Days 4–10)
- Implement using TDD — write tests first, then code
- Minimum 85% code coverage
- Contract tests between services (if multi-service)
- Code review process (even if solo — review your own PRs with a checklist)
- Security scanning integrated from day 1 (SAST with Semgrep)
Phase 3: CI/CD Pipeline & Deployment (Days 11–14)
- Multi-stage CI pipeline: lint → test → security scan → build → deploy
- Container-based deployment (Docker + orchestration)
- Blue-green or canary deployment strategy
- Automated rollback on health check failure
- Environment promotion: dev → staging → production
Phase 4: Observability & Monitoring (Days 15–18)
- Structured logging (JSON format, correlation IDs)
- Metrics collection (application and infrastructure)
- Distributed tracing (if multi-service)
- SLO-based alerting (alert on SLO burn rate, not individual metrics)
- Dashboard showing key health indicators
Phase 5: Incident Response & Improvement (Days 19–21)
- Simulate an incident (inject a failure, observe detection and response)
- Write a blameless postmortem for the simulated incident
- Implement at least one improvement from the postmortem
- Run a simple chaos experiment (kill a container, observe recovery)
Phase 6: Documentation & Presentation (Days 22–28)
- Comprehensive README with quick start instructions
- Runbook for common operational procedures
- Architecture diagram with deployment topology
- 5-minute demo video or recorded walkthrough
The 42-Part Integration Challenge
For maximum portfolio impact, annotate your Project 5 with references to which series part taught each technique you used. For example: "Branch protection (Part 9: Git Workflows)", "Contract testing (Part 20: Testing Strategies)", "SLO-based alerting (Part 26: Observability)". This demonstrates both technical skill AND systematic learning — a combination that stands out in interviews.
Evaluation Rubric
Use this rubric to self-assess each project. Each dimension is scored 1–5:
| Dimension | 1 (Minimal) | 3 (Competent) | 5 (Excellent) |
|---|---|---|---|
| Functionality | Partially works, manual steps required | Works end-to-end with minor issues | Robust, handles edge cases, zero manual steps |
| Automation | Some scripts, mostly manual | CI/CD pipeline, automated tests | Full automation including deployment, rollback, and monitoring |
| Security | No security considerations | Dependency scanning, basic SAST | SBOM, signed artifacts, SLSA compliance, security gates in CI |
| Documentation | Minimal README | Good README, architecture diagram | README, ADRs, runbook, API docs, demo video |
| Observability | Console logs only | Structured logs, basic metrics | Logs + metrics + traces, SLO dashboards, alerting |
Portfolio Presentation
These projects are most valuable when they are visible. Here is how to showcase them effectively:
GitHub Profile Optimisation
- Pin your best repositories: Pin Project 4 (DORA Dashboard) and Project 5 (Full Lifecycle) to your profile
- Write compelling READMEs: Start with a one-sentence summary, then architecture diagram, then "Quick Start" instructions that work in under 2 minutes
- Use topics/tags: Add tags like
ci-cd,devops,github-actions,docker,dora-metrics - Show green squares: Regular commits demonstrate sustained engagement
Architecture Diagrams
- Include Mermaid diagrams in your README (GitHub renders them natively)
- Create C4 diagrams for complex projects (Context, Container, Component)
- Add deployment topology diagrams showing infrastructure
Demo Recordings
- Record a 3–5 minute walkthrough of your CI/CD pipeline in action
- Show a deployment happening: push commit → CI runs → deploy → verify
- Demonstrate a failure scenario and automated recovery
- Tools: asciinema (terminal recording), Loom (screen + voice), or OBS Studio
# Record a terminal session with asciinema
# Install: brew install asciinema (macOS) or pip install asciinema
# Start recording
asciinema rec demo.cast --title "CI/CD Pipeline Demo"
# ... run your commands (push, wait for CI, deploy) ...
# Stop recording (Ctrl+D or 'exit')
# Upload to asciinema.org or embed in README
asciinema upload demo.cast
Recommended Reading
To continue your journey beyond this series, these books represent the definitive works in software delivery engineering:
| Book | Authors | Key Topics |
|---|---|---|
| Accelerate | Nicole Forsgren, Jez Humble, Gene Kim | DORA metrics, scientific evidence for DevOps practices, capabilities that drive performance |
| Continuous Delivery | Jez Humble, David Farley | Deployment pipelines, configuration management, release strategies, build automation |
| The Phoenix Project | Gene Kim, Kevin Behr, George Spafford | DevOps transformation narrative, Theory of Constraints applied to IT, the Three Ways |
| Team Topologies | Matthew Skelton, Manuel Pais | Four team types, interaction modes, Conway's Law, cognitive load, fast flow of change |
| Release It! (2nd ed.) | Michael Nygard | Production readiness, stability patterns, capacity planning, chaos engineering |
| Site Reliability Engineering | Betsy Beyer, Chris Jones, et al. (Google) | SLOs/SLIs/SLAs, error budgets, toil reduction, incident management, on-call practices |
Series Conclusion
You have completed the Software Engineering & Delivery Mastery series — 42 parts spanning the entire landscape of modern software delivery. Let us recap the journey:
- Parts 1–5: Mental models, SDLC, Agile methodologies, and process frameworks
- Parts 6–10: Version control, Git workflows, branching strategies, and code review
- Parts 11–15: CI/CD pipelines, build systems, artifact management, and deployment automation
- Parts 16–20: Containerisation, orchestration, testing strategies, and quality assurance
- Parts 21–25: Security, compliance, supply chain integrity, and DORA metrics
- Parts 26–30: Observability, incident management, SRE practices, and chaos engineering
- Parts 31–35: Infrastructure as Code, platform engineering, and developer experience
- Parts 36–39: Advanced testing, AI/ML delivery, and emerging practices
- Parts 40–42: LEAN thinking, team dynamics, and hands-on capstone projects
The most important thing you can do now is build. Start with Project 1. Iterate. Ship something imperfect and improve it. That is, after all, the essence of continuous delivery — and the essence of engineering itself.
Thank you for following this series. Now go deliver something extraordinary.