Milestones
The project is delivered in nine milestones. Each one adds a layer to the same system, in the order the classes teach it, so that you never build something you have not yet seen in class β and never leave everything to the last two weeks. Every milestone has acceptance criteria you can check yourself before the deadline, and a short check-in in class.
flowchart LR
m1["M1<br/>design doc<br/>estimates Β· SLOs"] --> m2["M2<br/>core path<br/>apigw Β· auth Β· 3 services Β· sql"]
m2 --> m3["M3<br/>scale<br/>cache Β· replica/pool"]
m3 --> m4["M4<br/>async & resilience<br/>kafka Β· outbox Β· DLQ<br/>timeouts Β· breaker Β· OTel"]
m4 --> m5["M5<br/>platform<br/>K8s Β· HPA Β· CI/CD Β· gate"]
m5 --> m6["M6<br/>load-test campaign"]
m6 --> m7["M7<br/>chaos game day"]
m7 --> m8["M8<br/>cost report"]
m8 --> m9["M9<br/>presentation"] Each milestone is graded on the tag m<n> pushed by the deadline (week shown in each section; dates on the planning page) and on the check-in. M1 and M6βM9 have their own pages with details and rubrics; M2βM5 share the rubric at the end of this page.
M1 β Design doc & estimates
Week 5 Β· 10% Β· tag m1. Agree on what you are building, how big it is and how you will prove it works.
- Design doc with every section, project additions, SLO table and capacity model.
- At least three ADRs, risk register, ownership table.
- Test target agreed with the instructor.
Details, template and rubric: design doc. Related: estimation and design doc exercises.
Check-in questions: Which SLO is the hardest to meet, and why? Which number in your estimates drives the biggest decision? Who owns what?
M2 β Core path running
Week 7 Β· 10% Β· tag m2. The main use case works end to end, locally, with one command.
Acceptance criteria
-
docker compose upfrom a clean clone brings every container to healthy in under 3 minutes (health checks incompose.yaml). - The main use case works end to end through the entry point (e.g. BoxOffice: list events β hold seats β confirm order), shown by a script or a k6 smoke test.
- Gateway (
apigw) routes to at least three services of your own, each with its own schema/database and migrations (Flyway or equivalent). - Authentication: login issues a JWT (or OIDC token); at least one route is protected and returns 401 without a token; at least one route is open.
- Rate limiting on at least one route at the gateway, shown by a burst that receives 429.
- k6 smoke test (β₯ 30 s, low rate) on the main use case passes with 0 errors, committed with its summary.
- OpenAPI available for every service.
Evidence in the docs: architecture diagram as built, how to run, smoke-test summary, a short screen recording or terminal log of the main use case.
Related: Lab 1 (first service), Lab 2 (k6), Lab 3 (edge), Lab 4 (gateway & identity).
Check-in questions: What happens if the auth service is down β which routes still work? Where is the rate limit enforced and with which algorithm? Show me one service's data: could another service write to it?
M3 β Scale & cache
Week 9 Β· 10% Β· tag m3. Make the hot read path cheap and prove it with numbers.
Acceptance criteria
- A cache (
cache, Redis) on the hottest read path, with TTL and invalidation on write; hit/miss metrics exposed. - A read replica with read/write routing or a connection pooler (PgBouncer) β chosen and justified by a measurement.
- At least two replicas of the hottest service behind a load balancer (Nginx or the gateway), with health checks.
- Before/after k6 test with the same script, data and resources: hit ratio β₯ 80% in steady state (or a justified lower value for your workload), p99 and knee before vs after.
- The critical write (e.g. seat hold) is consistent under concurrency: a k6 test with concurrent attempts on the same item shows no double booking/assignment (verification query committed).
- Capacity model of the design doc updated with measured per-instance capacity.
Evidence in the docs: before/after table, hit ratio, knee chart, consistency check output, design-doc changelog entry.
Related: Lab 5 (caching), Lab 6 (data at scale), cache & database and load-test report exercises.
Check-in questions: What would happen to the database if Redis were flushed right now? Show me the query that proves there is no double booking. What is your new bottleneck?
M4 β Async & resilience
Week 12 Β· 10% Β· tag m4. Move slow work off the request path, survive slow dependencies, and see what is happening.
Acceptance criteria
- A broker (
kafka,queueorpubsub) carries at least one business event between two services. - Transactional outbox on the producer or idempotent consumer (ideally both) β verified by a reconciliation query after a run where the consumer is killed: 0 lost, 0 duplicated side effects.
- Retries with backoff and a DLQ/DLT; a poison message ends there and processing continues.
- On the call to the slow/external dependency (
payment,thirdparty,emailorllmgw): timeout + retry with exponential backoff and jitter + circuit breaker (Resilience4j or equivalent), with a fallback or a clear error. - Demonstration with Toxiproxy (or equivalent): with 2 s of injected latency on the dependency, the entry point's p99 stays bounded and threads are not exhausted (numbers before/after).
- Observability: OpenTelemetry traces across at least three services (one trace screenshot crossing the broker), a RED dashboard (rate, errors, duration) per service, and one SLO burn-rate alert that fires in a test.
Evidence in the docs: event flow diagram, envelope schema, reconciliation output, Toxiproxy experiment table, dashboard and trace screenshots, alert definition.
Related: Lab 7 (async), Lab 8 (observability), Lab 9 (resilience & chaos), event-driven exercise; AI option: Lab 12.
Check-in questions: Kill the consumer now β what happens to the events, and how do you know none is lost? Why this timeout value? Open the dashboard: which service is the slowest and why?
M5 β Kubernetes + CI/CD
Week 14 Β· 10% Β· tag m5. Run on a platform and deliver every change automatically and safely.
Acceptance criteria
- Every service runs on Kubernetes (kind/minikube or a cloud cluster) from manifests in the repository (plain YAML, Kustomize or Helm), with requests/limits, startup/readiness/liveness probes, graceful shutdown and an Ingress (or Gateway API).
- An HPA on at least one service scales up under a k6 load and back down (replicas-over-time chart).
- Zero failed requests during a rolling update under steady load.
- CI (GitHub Actions) on every pull request: build, unit tests, container image, image scan (e.g. Trivy) failing on critical vulnerabilities, SBOM generated and stored as an artifact.
- Performance gate: a k6 smoke/short-load test with SLO thresholds runs in CI and blocks the merge when a threshold fails (show one red run).
- CD: merging to
maindeploys to the cluster (GitOps with Argo CD, or a deploy job), with a documented rollback. - Secrets are not in the repository (Kubernetes Secrets from CI secrets, or Sealed Secrets/External Secrets).
Optional A-level extra: add an AI step to the pipeline from Lab 13 β AI in CI/CD (e.g. an AI pull-request reviewer or AI-explained canary analysis), with a short evaluation of what it caught and what it got wrong.
Evidence in the docs: cluster diagram, manifests links, HPA chart, rolling-update k6 summary, links to a green and a red CI run, SBOM artifact, deployment and rollback procedure.
Related: Lab 10 (Kubernetes), Lab 11 (delivery), Kubernetes autoscaling exercise.
Check-in questions: Push a change that breaks a threshold β what happens? How do you roll back, and how long does it take? What limits your maxReplicas?
M6 β Load-test campaign
Week 16 Β· 15% Β· tag m6. Prove, with a rigorous k6 campaign, that the system meets the test-target SLOs β and find where it stops meeting them.
- Campaign plan, scripts, raw results and report.
- Test-target SLOs met for β₯ 10 minutes in 3 repetitions; knee and bottleneck identified; β₯ 2 improvement iterations.
Details, template and rubric: load-test campaign. Related: Class 15, Lab 2.
Check-in questions: At what load does the first SLO break, and which resource causes it? Which iteration helped most? How do you know the load generator was not the bottleneck?
M7 β Chaos game day
Week 17 Β· 10% Β· tag m7. Break the system on purpose, under load, in front of the class β and learn from it.
- At least three planned experiments plus one surprise fault injected by the instructor.
- Report with observations, SLO impact, time to detect/mitigate and action items.
Details, templates and rubric: game day. Related: Class 10, Lab 9.
Check-in questions: Which experiment surprised you? How did you detect the surprise fault β alert, dashboard or a user? Which action item matters most?
M8 β Cost report
Week 18 Β· 10% Β· tag m8. Put a price on the design target and on what you actually ran, and connect cost to your measurements.
- Monthly cost of the design target with a bill of materials, unit economics and sensitivity analysis.
- Final update of the design doc (changelog complete).
Details, template and rubric: cost report. Related: Class 11.
Check-in questions: What is the cost of one ticket sold (or message, ride, view, conversation)? What dominates the bill? What would you change to halve it?
M9 β Final presentation
Weeks 19β20 Β· 10% Β· tag final. Show the system working, the evidence that it meets its goals, and what you learned.
- 15-minute presentation + live demo + 10 minutes of questions; every member presents.
- 3-minute video and final documentation.
Details, template and rubric: presentation.
Rubric for M2βM5
Levels as defined in How the exercises are graded.
| Criterion | I | D | C | B | A |
|---|---|---|---|---|---|
| Acceptance criteria (mandatory) | Tag missing or system does not run | At least one criterion not met | All criteria met | All met with margin (e.g. better numbers than required) | All met plus a relevant extra (e.g. the M5 AI step, CDC relay, multi-instance tests) |
| Evidence and measurements | None | Claims without evidence | Every criterion has evidence in the docs | Numbers interpreted, before/after explained | Evidence reproducible by the reader with one command |
| Code and configuration quality | Broken | Fragile, hard-coded secrets or values | Clean, configuration externalised, migrations | Tests for the critical paths, consistent conventions | Code a new member could extend in a day |
| Documentation | Missing | Outdated or incomplete | Milestone section complete, design doc changelog updated | Clear diagrams and ADRs for the changes | Lessons learned and known limitations stated honestly |
| Check-in | Absent | Cannot demo or answer | Demo works, questions answered by the owners | Any member answers any question | Team anticipates the follow-up questions |
Check-ins
Each milestone has a 10-minute check-in in class during its week:
- Demo from a clean clone of the tag (2β3 minutes):
git clone, checkout the tag, one command, the use case working. No "it worked on my machine". - Evidence walk-through (2β3 minutes): the docs section of the milestone.
- Questions (4β5 minutes): the instructor picks any member for each question (examples are listed in each milestone). Owners may complement, but every member must be able to answer the basics.
A missed check-in without justification caps the milestone at D. Problems discovered during a check-in can be fixed until the deadline of the same week.