We have reached the end. TechCorp has gone from a monolith with five measurable problems (01-05) to six services operated with SLOs, deployed eleven times a day and secured in layers; we have told the migration (08-01), written the complete system (08-02) and deployed and operated it (08-03). This last lesson adds nothing new to the system: it distills what was learned so you can take it to your own context. First, TechCorp's lessons organized by dimension, each in the most honest form possible ("what we did, what went wrong, what we would do differently"). Then, the catalog of antipatterns we have seen and how to recognize them in time. Next, a consolidated list of best practices with the lesson where each one is explained, what TechCorp would do in its phase 2 and by what criterion, and a guide for the learner: how to decide, where to start, how to practice with the course environment and what to read next. And the close: the module-by-module journey and Marta's and Luis's final words.
Contents
- Lessons learned by dimension
- Catalog of antipatterns and how to detect them
- Consolidated list of best practices
- What TechCorp would do in phase 2
- Guide for the learner: applying the course to your context
- Recommended reading and resources
- Course wrap-up (in the conclusion)
- Lessons learned by dimension
Each table summarizes one dimension of the course. The "what we would do differently" column is the most useful one: it is what TechCorp would write in the project postmortem.
1.1 Organization and teams
| What we did | What went wrong | What we would do differently |
|---|---|---|
| Reorganize into four teams by business capability (Shopping Experience, Orders, Payments & Communications, Platform) before extracting anything; Conway's law applied on purpose (02-01, 08-01 phase 0). | The Platform team was a bottleneck for the first months: everyone asked it for manifests, queues and DB users. | Start the template, the reusable workflow and the runbook's "add a service" entry (08-03 §10) in phase 0, not in phase 2; Platform as a self-service provider from day one. |
| End-to-end ownership: each team deploys, operates and is on call for its own; on-call rotating across the four teams, only critical alerts with a runbook (06-05 §8). | At first on-call received cause-based alerts (high CPU, pod restarted) and burned out; and one service (discounts-service) was born without a clear owner. |
Symptom- and SLO-based alerts from the first service; the rule "no owner, no service" (CODEOWNERS and the Argo Application make it explicit). |
| Marta set clear constraints (do not stop the store, value per quarter, two DBs, one language) and measured results (08-01 §10). | The availability baseline did not exist: the "before" is an estimate. | Instrument the monolith with SLIs before the first phase; measuring is part of phase 0. |
1.2 Design
| What we did | What went wrong | What we would do differently |
|---|---|---|
Bounded contexts with strategic DDD; a different "product" and "customer" in each context; productTranslator ACL; no shared kernel (02-03). |
The first version of order.created carried Catalog's price without an ACL; a format change broke it in staging. |
An ACL at every boundary from the first contract, even if it looks like bureaucracy. |
One database per service, schemas and svc_* users as an intermediate step, opaque prefixed ids, customers_ref via events, migration without dual write (02-04). |
The dual write was attempted in the catalog (08-01 §11) and had to be thrown away. | Put "one write, in the owner; the copy, through events" in writing in phase 0. |
| Choreographed saga with an explicit state machine, compensations as business events, watchdog and reconciliation (02-05, 06-03). | INC-2031: the saga without symptom alerts or a well-designed DLQ got stuck for 40 minutes. | Watchdog, DLQ with deferred retry, late-saga alert and runbook before the phase 5 cutover, not after the incident. |
| Service size by business capability: six services, not sixty (02-01). | discounts-service (nanoservice) added latency and deployments without autonomy; it was reabsorbed. |
The question "does it have its own data and lifecycle and a team that wants it?" before creating a repository. |
Modular monolith first: returning the stock and payments writes to their owner inside the monolith (02-02, 08-01 phase 0). |
Nothing: it was the best invisible work of the project. | Do it even more: introduce the abstractions (ProductsRepository, StockReservation) for all six boundaries in phase 0. |
1.3 Communication
| What we did | What went wrong | What we would do differently |
|---|---|---|
Contracts first (OpenAPI, AsyncAPI), /v1/, RFC 7807 with code, 202 + ETag, Idempotency-Key (03-01, 03-06). |
The change from 201 to 202 on POST /orders surprised the front-end; notice came late. |
Customer-visible contract changes go on the roadmap with two sprints of notice and Deprecation from day one. |
| Events with a standard envelope, topic exchange, one queue per consumer with DLQ, at-least-once + idempotency (03-02, 02-05). | The Payments and Notifications binding to order.created was not in the initial design (08-02 §4-5): it appeared during implementation. |
Design the complete event map (producer, consumers, minimal payload) with event storming, and validate it with message pacts before the first queue. |
| Thin gateway: routing, JWT, rate limit, internal headers; no business logic (03-04, 07-01). | Someone proposed "composing the order detail in the gateway"; it was rejected in time. | Keep the written rule: composition lives in a BFF or in the service, never in the gateway. |
Versioning: URI for breaking changes, add without removing, Sunset, event upcasting (03-06). |
No contract breakage in production thanks to Pact; two in staging. | Nothing significant; perhaps adopt message pacts earlier. |
1.4 Implementation
| What we did | What went wrong | What we would do differently |
|---|---|---|
Template + versioned technical library (node-service-template, @techcorp/common-http); business code never shared (04-01, 02-02 §6). |
For two months ci.yml was copied three times with differences (08-01 §11); the outbox stayed in Orders until Inventory needed it. |
Luis's rule, literally: automate/extract before the second use, not the fourth. |
Configuration validated at startup with zod, fail-fast, no process.env outside config.js, secrets via Secret/ESO (04-03). |
A misspelled REMOTE_CATALOG in a ConfigMap took down a pod in staging… and config.js caught it at startup, which is the point. |
Nothing. |
| Test pyramid with injected doubles, Testcontainers, HTTP and message Pact, one E2E (04-05, 08-02 §9). | Message pacts arrived after INC-2031. | Pacts for events from the first event consumer (phase 2). |
Idempotency everywhere: processOnce, Idempotency-Key, UNIQUE (order_id), UNIQUE (order_id, type), upsert (02-05 §8, 08-02). |
Not a single duplicate charge or email in production. | Nothing; it is the practice that saved us the most times. |
1.5 Platform
| What we did | What went wrong | What we would do differently |
|---|---|---|
Containers: multi-stage, USER node, no latest, images by sha and semver, Trivy and cosign (05-01, 07-04). |
Nothing serious. | Digests in the prod overlays instead of tags, from the start. |
Kubernetes with Kustomize per service and environment, Helm for third parties, probes, securityContext, PDB, HPA/KEDA (05-02, 06-04, 07-04). |
Catalog's first HPA on CPU alone oscillated; the request metric was needed. | Autoscale on the metric that reflects real load (requests, queue length), CPU as a fallback. |
GitOps with Argo CD in prod, cd.yml in staging, can-i-deploy as the gate (05-03). |
A manual kubectl scale during a campaign reverted by selfHeal (08-03 §7). |
Educate: "production is changed by PR"; and the campaign overlay from the first Black Friday. |
Progressive deployments: rolling with maxUnavailable: 0, canary for Orders and Payments, blue-green for the gateway, expand/contract (05-04). |
The HTTP-weight canary does not detect slow consumers (08-03 ex. 2). | Canary vs stable panel with consumer metrics by version; Argo Rollouts when there is automated analysis. |
| No mesh in phase 1 (05-05). | Nothing: the decision held. | Nothing; revisit in phase 2 (section 4). |
1.6 Operations
| What we did | What went wrong | What we would do differently |
|---|---|---|
Observability: pino logs with redact, RED and business metrics, Loki, OpenTelemetry traces with traceparent through the outbox, correlation (06-01, 06-02). |
Cardinality took down Prometheus for 25 minutes (08-01 §11). | Bounded-label rule and sample_limit in the template from the first service. |
| Resilience: timeouts, retries with backoff only for transient errors, circuit breaker, bulkhead, retry queues with TTL and DLQ, watchdog and reconciliation (06-03). | Payments retried in a loop with prefetch(1) until INC-2031. |
The 06-03 patterns in common-http before the first consumer that talks to a third party. |
| SLOs and burn rate alerts, Alertmanager per team, versioned runbooks, severities, blameless postmortems, error budget policy (06-05). | SLOs arrived in module 6, when there were already five services in production. | An SLO and a symptom alert for each service when it is extracted, as part of its definition of "done". |
| Scaling on the right metric and k6 load tests before every campaign (06-04). | Nothing serious; the first Black Friday was the project's big success. | Nothing. |
1.7 Security
| What we did | What went wrong | What we would do differently |
|---|---|---|
Portable identity: Keycloak, JWT with customerId, verification at the gateway and in every service, owner rule (404), client credentials (07-01). |
Keycloak arrived in phase 5 federating the monolith; there were two awkward weeks of double sessions. | Keycloak in phase 0 in front of the monolith: identity is a prerequisite, like the gateway. |
Layers: TLS at the edge with cert-manager, RabbitMQ amqps with a user per service, DB verify-full, HMAC webhook; mTLS postponed (07-02). |
No breach; mTLS is still pending. | Nothing; the phase 2 mesh closes it. |
Code: strict zod, helmet, gitleaks, GDPR (personal data only in order.created, retention, customer.deleted), immutable audit log (07-03). |
The GDPR decision came late and forced recipients in Notifications (08-02 §5). |
Data minimization in events decided when designing the event map. |
Hardened platform: full securityContext, PSA restricted, ESO + Vault, OIDC in CI, deny-all NetworkPolicies, RBAC per ServiceAccount (07-04). |
PSA in audit, not enforce; ESO does not cover every service yet. |
Nothing new: they are the phase 2 tasks already listed in SECURITY.md. |
- Catalog of antipatterns and how to detect them
All of them have appeared in the course, almost all at TechCorp. The "how to detect it" column is a test you can apply to your system tomorrow.
| Antipattern | What it is | How to detect it | Where we saw it |
|---|---|---|---|
| Distributed monolith | Separate services that must be deployed together or share a schema | "Does a schema change in A force deploying B?"; "is there a mandatory deployment order?" (01-04 §7) | 01-04, 02-01 |
| Shared database | Two services read/write the same tables | Table × service matrix with more than one W, or foreign Rs without a contract (02-02 §3.3) | 02-02, 02-04 |
| Nanoservices / entity services | One service per table or per function | "Does it have its own data, lifecycle and team?"; chained synchronous calls for any use case | 02-01, discounts-service (08-01) |
| Fat gateway | Business logic, composition or transformations in the gateway | The gateway repository grows with every feature; PRs from business teams in it | 03-04 |
| Dual write | Writing to two stores (or DB + broker) without a shared transaction | Intermittent discrepancies in shadow; "lost" events with no explanation | 02-04 §6, 02-05 §7, 08-01 §11 |
| Chained synchrony | A calls B which calls C within the customer's request | p99 latency = the sum; one dependency down takes the whole flow down; traces with more than 3 synchronous hops | 02-05, 03-02, 06-02 |
| No idempotency | Retries that duplicate effects (two charges, two emails) | RabbitMQ redeliveries or network retries produce duplicate rows; no business UNIQUE |
02-05 §8, 08-02 |
| Alerting on causes | Pages for CPU, restarts, queues with 1 message | On-call receives alerts that require no action; alerts without a runbook | 06-05 §6 |
| High-cardinality metrics | Ids or untemplated routes as labels | Prometheus with growing memory; count({__name__=~".+"}) in the millions |
06-01 §11, 08-01 §11 |
| Secrets in the image or in git | Credentials in the Dockerfile, a versioned .env, a Secret in YAML "because it is base64" |
gitleaks; docker history shows the secret; kubectl get secret -o yaml in the repository |
05-01, 07-03 §7, 07-04 §4 |
| Shared models library | A @techcorp/models package everyone imports |
A change to Product forces redeploying six services |
01-04, 02-02 §6 |
| Extracting the core first | Starting with the service that depends on everyone | It is born with N calls to the monolith and no contracts | 02-02 §5 |
| Retrying everything, immediately | nack with requeue in a loop, retries on 4xx |
Queues jammed by one message; no x-attempts at all |
03-02, 06-03, INC-2031 |
latest and manual deployments |
Mutable images, kubectl apply from laptops |
Nobody knows which version is running; drift from git | 05-01 §5, 05-03 §6 |
| No baseline | Migrating without measuring the "before" | Impossible to prove improvement or detect regression | 08-01 §10 |
- Consolidated list of best practices
Twenty-five points, grouped, with the reference lesson. Use it as an architecture review or new-service checklist.
Decision and design
- Adopt microservices for measurable problems, not for fashion; build the four prerequisites on top of the monolith and modularize it first (01-04, 02-02, 08-01 phase 0).
- Cut by business capabilities and bounded contexts; validate with event storming and the table × area matrix (02-02, 02-03).
- One service = its data: its own database (or schema), opaque ids, no cross-service FKs, read replicas via events (02-04).
- Replace the distributed transaction with a saga with an explicit state machine, compensations as events and a watchdog (02-05, 06-03 §9).
- Extract in order of value/risk/dependencies with strangler fig behind a gateway and branch by abstraction with flags; never delete the old thing on cutover day (02-02, 08-01).
Communication
6. Contracts first (OpenAPI/AsyncAPI), RFC 7807 errors with code, 202 for asynchronous processes, ETag for polling (03-01).
7. Events with a standard envelope (eventId, type, version, occurredAt), topic exchange, one queue per consumer with deferred retry and DLQ (03-02, 06-03 §8).
8. Transactional outbox to publish; processOnce + business idempotency (UNIQUE) to consume; Idempotency-Key on POSTs (02-05 §7-8, 04-04, 08-02).
9. Thin gateway; composition in a BFF or a service; ACL at every boundary (03-04, 02-03).
10. Version: add without removing, /v2/ only for breaking changes, Deprecation/Sunset, event upcasting, HTTP and message pacts with can-i-deploy (03-06, 04-05, 05-03 §4).
Implementation
11. Service template + versioned technical library; business logic is not shared (04-01, 02-02 §6).
12. createApp without listen, dependency injection by parameter, layers pointing inward (04-02).
13. Configuration validated at startup, fail-fast, secrets outside the code and the image (04-03, 07-03 §7).
14. Pyramid: domain unit tests, component tests with doubles, integration with Testcontainers, contract with Pact, one E2E (04-05).
15. No external call inside a transaction; short transactions and idempotency-key lookup when in doubt (01-05, 08-02 §4).
Platform and deployment
16. Multi-stage images, non-root, tagged by sha and semver, scanned and signed; never latest (05-01, 07-04 §2).
17. Kustomize base + overlays per environment; Helm for third parties with versioned values; probes, securityContext, PDB, real requests (05-02, 06-04, 07-04).
18. GitOps: production changes by PR; Argo CD syncs; rollback = revert the commit (05-03 §6).
19. Progressive deployments per service (rolling, canary, blue-green) with expand/contract and event compatibility (05-04).
20. Reusable, versioned CI workflow; Luis's rule (05-03 §11).
Operations and security
21. Structured logs with redact, RED and business metrics with bounded labels, traces with context propagated over HTTP and through events (06-01, 06-02).
22. Timeouts always, retries only for transient errors with backoff, circuit breaker, bulkhead, DLQ with a runbook (06-03).
23. SLOs with an error budget, symptom and burn rate alerts, on-call with runbooks, blameless postmortems with actions (06-05).
24. Portable identity (OIDC/JWT) verified at the gateway and in every service; authorization by role, scope and owner; TLS everywhere; signed webhooks (07-01, 07-02).
25. Strict input validation, GDPR by design (minimization, retention, erasure), immutable audit log, secrets via operator, deny-all NetworkPolicies, minimal RBAC, and an honest to-do list (07-03, 07-04).
- What TechCorp would do in phase 2
None of this is urgent; each item has a criterion that says when to tackle it. It is the difference between a roadmap and a wish list.
| Initiative | What it brings | Criterion for tackling it | Lesson |
|---|---|---|---|
| Service mesh (Linkerd) with mTLS | Cryptographic identity between services, exact-weight internal canary, uniform retries/timeouts | When the Payments audit demands mTLS, or when there are more than ~12 services and the common-http patterns diverge |
05-05, 07-02 §7 |
| Selective event sourcing in Orders | Full order history, view rebuilding, natural audit trail | If the business asks "what exactly happened to this order" often, or if more than three distinct read views appear; never for the whole system | 02-05 §10 |
| gRPC Orders ↔ Inventory | Low-latency synchronous reservation for a "real-time stock" checkout | If the event-based saga stops being acceptable for the shopping experience (stock check in the cart), not before | 03-03 |
| GraphQL in the BFF for the web | A single endpoint for composite screens | When the web has as many composite screens as the app; bff-mobile already proves it |
03-03, 03-04 |
| TypeScript in the template | Typed contracts from OpenAPI/AsyncAPI, fewer shape errors | When the team masters it and the template ships it; migrate service by service, starting with common-http |
04-01 |
| Go for Inventory | Lower consumption and latency in the most "systems-like" service | Only if Inventory's cost or its p99 latency become a measured problem; the second language must be justified with data (Marta's rule) | 04-01 |
| Internal developer platform (IDP): Backstage or similar | Service catalog, one-click templates, owners, SLOs and runbooks in one place | When there are more than ~10 services or a second business domain; today platform/README.md is enough |
04-01, 08-03 |
| FinOps | Cost labels per team/service, monthly reports, budgets per SLO | Now: start with the table from 08-03 §11 labeled per service; automate when the bill exceeds ~€5,000/month | 08-03 §11 |
| Saga orchestration | A coordinator if the flow grows | Only if the signals from 02-05 §6 appear (more than 6 steps, conditional branches, frequent "which step is it at?") | 02-05 |
| Argo Rollouts / Flagger | Canary with automated metric analysis | When manual canaries exceed two a day | 05-04 |
Pending items in SECURITY.md |
cosign at admission, PSA enforce, ESO for everyone, mTLS |
Next quarter, in this order | 07-04 §10 |
- Guide for the learner: applying the course to your context
Deciding. Go back to the matrix from 01-04 and answer with data, not opinions:
| Question | If the answer is "yes" | If it is "no" |
|---|---|---|
| Do you have several teams by business capability getting in each other's way in the same code and the same tables? | Signal in favor | A small team performs better with a modular monolith |
| Are CI, containers, observability and end-to-end ownership already working? | You can start | Build that first on top of the monolith (phase 0 from 08-01) |
| Are there parts with very different load or availability profiles (TechCorp's catalog)? | There is a clear first candidate | Scale the monolith; it is cheaper |
| Is the domain stable and well understood? | The boundaries can be drawn | The boundaries will change; wait |
| Is delivery speed held back by deployment coupling (and not by something else)? | Microservices help | Fix the other thing (tests, processes, debt) |
Four or more "yes" with the prerequisites ready: extract incrementally, starting with the clearest reason. Fewer: modular monolith and a review in six months. And remember that the list in 01-04 has warning signs (a three-person team, "because Netflix does it", no automation) that override everything else.
Where to start (if you decide to go ahead): in this order, without skipping any: (1) the four teams —or however many you have— with clear ownership; (2) CI and an image of the monolith; (3) gateway in front and minimal observability; (4) table × area matrix and modular monolith; (5) first extraction by value/risk/dependencies with initial load, events, shadow and flag; (6) SLO and alert for the new service before calling it done; (7) template and library before the second service.
How to practice. The course environment is designed to be reproduced:
- Bring TechCorp up locally with the
compose.yamlfrom 08-03 §2, get a Keycloak token and followord-88213through Jaeger, Grafana and Loki as in 08-03 §5. Break things: stoppayments-serviceand watch the watchdog and the DLQ; publish a malformed event withpublishEvent.jsand reprocess it; run p-802 out of stock.
# Typical practice session (sibling repositories cloned: *-service, gateway, platform)
cd platform/local && docker compose up -d --wait # 19 containers; ~90 s
TOKEN=$(../scripts/token-keycloak.sh http://localhost:8180 techcorp e2e-tests ana.ruiz test-password)
docker compose stop payments-service # 1. break Payments and create an order: it stays in STOCK_RESERVED
curl -s -X POST http://localhost:8080/api/v1/orders -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
-H "Idempotency-Key: practice-$(date +%s)" -d '{"customerId":"c-1024","lines":[{"productId":"p-501","quantity":1}]}' | jq .id
# → wait 10 min and you will see order.cancelled (PAYMENT_TIMEOUT) from the watchdog; or start Payments first and you will see CONFIRMED
docker compose start payments-service
node ../../orders-service/scripts/publishEvent.js stock.reserved '{"orderId":"ord-nonexistent"}' # 2. event for an unknown order → log, not DLQ
node ../../orders-service/scripts/publishEvent.js payment.confirmed '{"orderId":"<an ord- in PENDING>"}' # 3. transition not allowed → transition_ignored in the log, status intact
# Jaeger http://localhost:16686 · Grafana http://localhost:3000 · RabbitMQ http://localhost:15672 (look at orders.saga.dlq after step 3 with a broken JSON)
docker compose down -v # clean database for the next session- Suggested extension exercises, from smaller to larger: coupons and promotions (01-03, 02-02 ex. 2, 08-01 §11): first as an Orders module, then decide with criteria whether it deserves a service; returns: new order status,
payment.refundedas a common branch,stock.restocked, email, audit; shipping: ashipping-servicethat consumesorder.confirmed, talks to a fictional carrier (ACL, signed webhook, retries) and publishesorder.shipped/order.deliveredfor Notifications and analytics to consume; v2 oforder.createdwith upcasting and message pacts; Argo Rollouts for the Orders canary with analysis ofslo:orders_error_ratio. - Repeat module 6 on your own extension: metrics, SLO, alert, runbook and a game day in which a colleague breaks something without warning.
- Recommended reading and resources
No URLs (they change); search by title and project. Books:
- Sam Newman, Building Microservices (2nd ed.) and Monolith to Microservices: the general reference and the strangler fig handbook TechCorp followed (modules 1, 2 and 8).
- Chris Richardson, Microservices Patterns: sagas, outbox, CQRS, API composition; the author's site microservices.io catalogs the patterns (02-05, 03-04).
- Eric Evans, Domain-Driven Design; Vaughn Vernon, Implementing Domain-Driven Design; Vlad Khononov, Learning Domain-Driven Design: bounded contexts, context map, aggregates (02-03).
- Martin Kleppmann, Designing Data-Intensive Applications: consistency, replication, messaging, the theoretical basis of 02-04 and 02-05.
- Gregor Hohpe and Bobby Woolf, Enterprise Integration Patterns: messaging, DLQ, idempotency (03-02, 06-03).
- Michael Nygard, Release It! (2nd ed.): timeouts, circuit breaker, bulkhead, stability (06-03).
- Betsy Beyer et al. (Google), Site Reliability Engineering and The Site Reliability Workbook: SLOs, burn rate, on-call, postmortems (06-05).
- Nicole Forsgren, Jez Humble and Gene Kim, Accelerate: the DORA metrics (05-03, 08-01 §10).
- Matthew Skelton and Manuel Pais, Team Topologies: stream-aligned teams and the platform team (02-01, 08-04 §1.1).
- Jez Humble and David Farley, Continuous Delivery; Marko Lukša, Kubernetes in Action: the basis of modules 5 and 8.
- Neal Ford, Mark Richards, Pramod Sadalage and Zhamak Dehghani, Software Architecture: The Hard Parts: granularity, data and sagas with explicit trade-offs.
Official documentation and projects worth keeping at hand: Node.js and Express; PostgreSQL and MongoDB; RabbitMQ (reliability, DLX and TTL guides); Docker and Compose; Kubernetes (concepts, Kustomize), Helm, kind, KEDA, Argo CD and Argo Rollouts, ingress-nginx, cert-manager, External Secrets Operator; GitHub Actions; Prometheus, Grafana, Loki, OpenTelemetry (JavaScript SDK) and Jaeger; Keycloak; Pact (including message pacts) and the Pact Broker; Trivy and Sigstore/cosign; OWASP API Security Top 10; The Twelve-Factor App; and the OpenAPI, AsyncAPI, RFC 7807 and W3C Trace Context specifications.
Common Mistakes and Tips
- Turning the best-practices list into dogma. Every item in section 3 has a lesson with its context and its trade-offs; applied without the context (for example, an outbox in a service that publishes nothing critical) it adds complexity without benefit. Read the lesson before imposing the practice.
- Copying TechCorp's final architecture instead of its method. Six services, RabbitMQ and Kubernetes are the answer to the five problems from 01-05 with 25 engineers; with three people and a changing domain, the right answer from the 01-04 matrix is a modular monolith.
- Confusing "phase 2" with "pending". Mesh, event sourcing, gRPC or Go have an activation criterion; tackling them before it is met is the "because Netflix does it" that 01-04 warned about.
- A postmortem without actions or actions without a date. The tables in section 1 are project postmortems; their value is in the "what we would do differently" column turned into tasks with an owner.
- Practicing only the happy path. The local environment is there to be broken: stop Payments, run out of stock, publish malformed events, rotate a secret. What you have not seen fail locally you will see fail in production.
- Tip: keep your own version of the tables in section 1 from the first day of your project, even if they are almost empty. The discipline of filling them in every quarter is worth more than any tool.
Exercises
Exercise 1: Your own matrix
Apply the matrix from section 5 to a system you know (the one at your job, a personal project or, if you have none, a hotel booking system with 4 engineers). Answer the five questions with concrete data, state which recommendation comes out and, if it is "extract", which would be the first service and what your phase 0 would do.
Exercise 2: Recognizing antipatterns
A team describes its system like this: "We have eight services on Kubernetes. They share one PostgreSQL database with separate schemas per service, but the reporting service does JOINs across schemas. We deploy all eight together every two weeks because a change to the common-models library forces updating all of them. The services call each other over HTTP in a chain and, when the customers one is slow, everything is slow. Each service retries failed calls three times in a row." Identify every antipattern from section 2 that appears, rank them by the damage they do and propose the first action for each, with the reference lesson.
Exercise 3: Prioritizing phase 2
Marta has budget for two initiatives from the table in section 4 next semester. With the data from the course (08-01 §10-11, 08-03 §11, SECURITY.md), choose two, justify them with each one's criterion and explain why you rule out at least three others that look attractive.
Solutions
Exercise 1. There is no single answer; the solution is the method. For the 4-engineer hotel: (1) one team, no one in the way → no; (2) probably no complete CI or observability → no; (3) bookings and billing have similar loads; perhaps the availability engine in high season → weak; (4) known domain → yes; (5) speed is held back by the lack of tests, not by deployment → no. Result: modular monolith with modules by capability (bookings, billing, availability, customers), CI, container, observability and SLIs starting now; review in 6-12 months. If your real system yields four "yes" with the prerequisites ready, the first service is the one with the clearest reason and the fewest dependencies (the "catalog" of your domain) and phase 0 is the list in section 5 ("where to start").
Exercise 2. By damage: (1) Shared database (the reporting service's JOINs cross schemas: any schema change breaks reports) → table × service matrix and a read DB for reports fed by events (02-02 §3.3, 02-04 §8); (2) Shared models library + distributed monolith (eight services deployed together because of common-models) → replace with per-service contracts and a technical-only versioned library; each service defines its DTOs (02-02 §6, 03-06); (3) Chained synchrony with immediate retries (everything is slow when customers is slow; three retries in a row multiply the load: retry storm) → timeouts with a budget, circuit breaker, retries with backoff only for transient errors, and replacing calls not needed in the request with events or read replicas (06-03, 02-04 §4, 03-02); (4) Joint deployment every two weeks (symptom, not cause) → disappears when 1-2 are solved; measure DORA to confirm it (05-03 §12). Not present (or unknown): fat gateway, secrets, cardinality; ask.
Exercise 3. A defensible answer: (a) FinOps — criterion "now": the bill is €2,760/month with identified levers of ~€800 (08-03 ex. 3); the initiative's cost is low (labels and a report); immediate return that also funds the rest. (b) Pending items in SECURITY.md (cosign at admission, PSA enforce, ESO for everyone) — criterion "next quarter"; known risk, bounded cost, and it unblocks the Payments audit which in turn will trigger the mesh. Ruled out: mesh/mTLS now — its criterion (audit or >12 services) is not met yet and it costs to operate (05-05); event sourcing — nobody has asked for the full history and the audit log from 07-03 covers what is sensitive; Go for Inventory — there is no measured cost or latency to justify it (Marta's rule); gRPC Orders↔Inventory — the saga meets its 60 s SLO with a p95 of 3 s; IDP — with 6 services and a README, it does not pay off. What matters is not the two chosen but that each rejection rests on the table's criterion and not on the appeal of the technology.
Conclusion
The journey, module by module. In module 1 we learned what a microservice is, what you gain and what you pay, how it compares with the monolith, when it makes sense to take the step, and we met TechCorp: a monolith with five problems and one function, createOrder, that did six things across five areas. In module 2 we designed: principles and coupling, decomposition with event storming and the table matrix, bounded contexts and the context map, one database per service and migration without dual write, and the choreographed saga with outbox and idempotency that replaced the transaction. In module 3 we made the services talk: REST contracts with 202, ETag and Idempotency-Key; RabbitMQ with its topology and its DLQs; gRPC and GraphQL as candidates; the gateway and the BFF; discovery and probes; versioning and pacts. In module 4 we wrote code: the template and the library, catalog-service, validated configuration, orders-service with its outbox and its consumers, and the test pyramid. In module 5 we packaged and deployed it: Docker and Compose, Kubernetes with Kustomize, CI/CD with GitHub Actions, Pact Broker and Argo CD, progressive deployments, and the honest evaluation of the mesh. In module 6 we made it operable: logs, metrics and traces; resilience and recovery; scaling; SLOs, alerts, on-call, runbooks and the INC-2031 postmortem. In module 7 we secured it in layers: Keycloak and JWT, TLS and signatures, validated code and GDPR, hardened platform. And in module 8 we brought it together: the migration told in order, the four missing services, the system deployed and operated, and these lessons.
Marta's words. "When I presented the plan in July 2025 they asked me why we didn't just rewrite the store in one go. Thirteen months later, the answer is in the numbers we never had to explain: not a single day without selling, eleven deployments a day, one serious incident that lasted forty minutes and taught us more than two years of Thursday nights. But what I value most isn't in the table: today every team knows what is theirs, measures it and improves it without asking permission. Microservices were the means; the end was an organization that could change the store every day without fear. If I had to sum the project up in one sentence for another CTO: don't start with the services, start with the homework; and don't delete anything on cutover day."
Luis's words. "I'll keep three things. First, that the best technical decision of the year was the least glamorous one: returning the stock and payments writes to their owner inside the monolith before moving a single line of server. Second, that almost everything that saved us in production was boring and was written before we needed it: the UNIQUE (order_id), the processOnce, the watchdog, the idempotency key toward the payment provider, the retry queue. And third, that the rule 'if it is done more than once per service, automate it before the second time' is easier to say than to follow —ci.yml got copied three times— and even so it is what lets a team of twenty-five people operate six services. To anyone just starting out: read createOrder from 01-05 again, now that you know what it became. The whole course is in the distance between those 80 lines and ord-88213 crossing four databases in three seconds."
For you. You have walked the same path as TechCorp, with its decisions and its stumbles. What you take with you is not a list of technologies —they will change— but a method: measure before deciding, do the homework before splitting, cut by capabilities and data, communicate through contracts and idempotent events, automate before the second use, deploy small and often, observe and agree on what "going well" means, secure in layers and learn in writing from every incident. Bring TechCorp up on your laptop, break it, extend it with coupons, returns or shipping, and then apply the method to your own system, starting with phase 0. Microservices are not the destination; they are a way for your organization to change its software with confidence, one order —and one deployment— at a time. Thank you for making it this far, and good luck with your own migration.
Microservices Course
Module 1: Introduction to Microservices
- Basic Concepts of Microservices
- Advantages and Disadvantages of Microservices
- Comparison with the Monolithic Architecture
- When to Adopt Microservices: Decision Criteria
- The Course Case Study: TechCorp's Online Store
Module 2: Microservice Design
- Microservice Design Principles
- Decomposing Monolithic Applications
- Defining Bounded Contexts
- Data Management: One Database per Service
- Distributed Consistency: Sagas, CQRS and Event Sourcing
Module 3: Communication between Microservices
- RESTful APIs
- Asynchronous Messaging
- Communication Protocols: gRPC, GraphQL
- API Gateway and Backend for Frontend
- Service Discovery and Load Balancing
- API Contracts and Versioning
Module 4: Implementing Microservices
- Choosing Technologies and Tools
- Building a Simple Microservice
- Configuration Management
- Hands-On Integration: Consuming APIs and Publishing Events
- Testing Microservices: Unit, Integration and Contract Tests
Module 5: Deployment and Orchestration
- Containers and Docker
- Orchestration with Kubernetes
- CI/CD for Microservices
- Deployment Strategies: Rolling, Blue-Green and Canary
- Service Mesh: Istio and Linkerd
Module 6: Monitoring and Maintenance
- Monitoring and Logging
- Distributed Tracing with OpenTelemetry
- Error Handling and Recovery
- Scalability and Performance
- SLOs, Alerts and Incident Management
Module 7: Security in Microservices
- Authentication and Authorization
- Communication Security
- Security Practices
- Container and Kubernetes Security
