Module 2 ended with a promise: that the artifact reservalia/api:a3f9c21 waiting in ECR today would start travelling on its own to dev, staging and prod, and that Friday afternoon would go back to being, quite simply, a Friday afternoon. This lesson is the first step towards keeping that promise, and it does not consist of writing YAML — that comes in 03-02 — but of understanding exactly what separates a published artifact from a deployed one, and what has to be in place before a machine touches production without asking permission. We are going to revisit the distinction between Continuous Delivery and Continuous Deployment, now with a real pipeline in front of us; list the five prerequisites without which automating deployment only serves to fail faster; design Reservalia's environment chain by deciding what each one validates; and look squarely at approval gates, including the most popular of them all: the Friday freeze.

Contents

  1. From "the artifact waits in ECR" to "the artifact travels on its own"
  2. Continuous Delivery and Continuous Deployment: where the gate actually sits
  3. The five prerequisites of a healthy CD
  4. Reservalia's environment chain
  5. Approval gates: manual, automatic and deployment windows
  6. What changes in how the team divides responsibilities
  7. Reservalia's full commit → prod flow
  8. Common Mistakes and Tips
  9. Exercises
  10. Conclusion

  1. From "the artifact waits in ECR" to "the artifact travels on its own"

Let us take an honest inventory of where Reservalia stands at the start of module 3.

When Diego merges a pull request into main, the ci.yml workflow runs quality, test and build in parallel and, if all three pass, the publish job pushes the image reservalia/api:a3f9c21 to ECR. Four minutes after the merge there is an immutable, verified, traceable artifact. And there it stays.

What happens from that point on is still the Friday ritual from lesson 01-04: Diego opens a terminal, connects to the AWS console, hand-edits the ECS task definition to point at the new tag, runs the migrations from psql and then stares at the logs for three hours. The artifact is from 2026; the procedure is from 2012.

Seen as a diagram, Reservalia's pipeline today has one automated half and one human half:

flowchart LR
    subgraph AUTO["Automated — module 2"]
        A["commit"] --> B["ci.yml<br/>quality · test · build"] --> C["ECR<br/>reservalia/api:a3f9c21"]
    end
    subgraph MANUAL["Manual — the Friday ritual"]
        D["Diego opens the console"] --> E["edits the task definition"] --> F["psql: migrations"] --> G["watches the logs for 3 h"]
    end
    C -.->|"4-day gap"| D

That dotted arrow is the whole of module 3. And it is not just slowness: it is the part of the process that is written down nowhere, is not reviewed, is not tested and cannot be repeated the same way twice. The gap between the two halves has a technical name — deployment is not automated — but it also has a measurable consequence we already know about:

DORA metric Reservalia baseline Target Did module 2 improve it?
Deployment frequency 1.1 / week ≥ 5 / week No: it depends on Diego deploying
Lead time for changes 6.2 days < 4 h Partially: smaller PRs
Change failure rate 14% < 5% Yes: every change is verified before merging
Time to restore 68 min < 10 min No: going back is still manual

Two of the four metrics have not moved, and no amount of additional testing will move them. They are deployment metrics, and deployment is what we start now.

  1. Continuous Delivery and Continuous Deployment: where the gate actually sits

In lesson 01-01 we defined the three terms in the abstract. Now we can point to the exact spot in Reservalia's pipeline where the difference lives, which is far more useful.

Continuous Integration Continuous Delivery Continuous Deployment
What it guarantees That the integrated code works That any green commit could go to production today That every green commit does go to production
Where it ends Verified artifact Artifact deployed to staging, ready for prod Artifact in prod
Human gate Yes: somebody presses the prod button No: there is no button
Reservalia's status ✅ Achieved in module 2 🎯 Goal of this module 🎯 Goal for the end of the module

The usual confusion is to think that Continuous Delivery is "CD done halfway". It is not: it is 90% of the work. Everything hard — automating deployment, making the environments equivalent, being able to go back, having observability — has to be solved either way. The only thing left over for Continuous Deployment is removing an if condition from a workflow.

Put another way: if you are able to deploy to production by pressing a button that does not frighten you, you have already done the expensive part. Whether a person or a machine presses that button is then a business decision, not an engineering one, and it depends on things like the sector (a regulated fintech may need a human approval on record), the maturity of your observability or simply how much confidence the team has. Reservalia will reach Continuous Delivery in lesson 03-02 and will remove the gate at the end of the module, once rollback (03-05) and monitoring (03-06) are in place.

Marta: "I would rather deploy twenty-line changes ten times a day than a two-thousand-line change once a month. Not because I am brave, but for exactly the opposite reason: because when a twenty-line change breaks, I understand it in a minute."

  1. The five prerequisites of a healthy CD

Automating deployment without these five elements does not speed up delivery: it speeds up the production of incidents. Each one answers a specific question.

1. A test suite you trust. The question is: if the pipeline is green, would you deploy without looking? If the honest answer is "it depends what changed", the suite is not yet an authorisation, it is an opinion. Reservalia has met this requirement since 02-04: unit tests over calculateSlots, integration tests against a real PostgreSQL and a quarantine policy for flaky tests. A single tolerated flaky test destroys this entire requirement, because it teaches the team to ignore red.

2. An immutable artifact. Is what I am about to put into production exactly what was validated? With reservalia/api:a3f9c21 and the "build once, promote the same digest" principle from 02-06, yes. If each environment rebuilt the image from source, the prod binary would be an artifact that was never tested, even if the commit were the same.

3. Equivalent environments. Will what works in staging work in prod? Only if they resemble each other in what matters: same PostgreSQL version (16), same Node version (20.11.0, the one in the base image), same network topology, same configuration variables even if the values differ. Equivalent does not mean identical — prod has more instances and real data — it means there are no differences that could change the behaviour of the software. This is the requirement that pushes us towards the Infrastructure as Code of lesson 03-03.

4. A reversible deployment. If it goes wrong, how long does it take me to put things back? This is the requirement most teams skip and the one that costs the most. Nuria said it back in module 1: "A deployment you cannot undo in five minutes is not a deployment, it is a bet." It is developed in 03-05.

5. Sufficient observability. If something breaks, do I find out before the customer does? Deploying automatically without production signals is deploying blind and waiting for the phone to ring. That is the content of 03-06, which closes the module.

Summarised in a table, with the concrete check that tells you whether they are genuinely met:

# Requirement Proof that you meet it Status at Reservalia Where it is worked on
1 Reliable test suite Nobody looks "just in case" when the pipeline is green ✅ Module 2 02-04
2 Immutable artifact The prod digest is the same one tested in staging ✅ Module 2 02-06
3 Equivalent environments All three environments come out of the same infrastructure code ❌ Created by hand 03-03
4 Reversible deployment A timed drill goes back in < 5 min ❌ Non-existent 03-05
5 Observability No incident was reported first by a customer ❌ Only scattered logs 03-06

The five form a system: failing at one degrades the other four. Without observability you do not know when to use rollback; without rollback, observability only lets you watch the fire live; and without equivalent environments the staging tests stop being an authorisation to move on to prod, so requirement 1 collapses even if the suite is impeccable.

Diego: "If CI takes longer than going for a coffee, I stop watching it. And if the deployment takes three days, I stop feeling it is mine."

  1. Reservalia's environment chain

An environment is not "a copy of the system": it is a place where you answer a question that cannot be answered any earlier. If an environment answers no new question, it is surplus and only adds latency to the lead time.

dev staging prod
Question it answers Does it start up and integrate properly with real AWS? Does it work with realistic data and volume? Does it work for the 340 businesses?
Data Synthetic, wiped every night Anonymised copy of prod, weekly Real
Database RDS db.t4g.micro RDS db.t4g.small RDS db.m6g.large, Multi-AZ
ECS instances 1 2 4
Who deploys Nobody: it is automatic Nobody: it is automatic Automatic after approval (today)
What triggers it Every green main Successful deployment to dev + smoke tests Success in staging + approval
Who watches it Diego, if something fails Marta validates it functionally Nuria watches the metrics
Approx. monthly cost €40 €120 €900
Can it be broken With no consequences With some annoyance Never silently

Three decisions in this design deserve an explanation, because they are the ones a team usually gets wrong when building its first environment chain:

There is no "QA" environment separate from staging. Adding an environment whose only purpose is for somebody to look at the same thing they could look at in staging lengthens the lead time without answering any new question. The rule is: one more environment, one more question.

The staging data is an anonymised copy, not a copy. Copying the production database with real customer names, phone numbers and email addresses into an environment with weaker access control is, besides bad practice, a legal problem. Reservalia's process replaces names, phone numbers and email addresses with generated values, and preserves volume and distribution: 340 businesses, ~9,000 appointments a month, the same proportion of cancelled appointments. That is what makes staging catch a performance problem that dev would never see.

dev is a deployed environment, not Diego's laptop. "It works on my machine" is not a validation. dev is the first place where the artifact runs on real AWS infrastructure, with IAM, with Secrets Manager and with an ALB in front. Many failures — a missing permission, a misspelled variable — show up exactly there and nowhere else.

Since the artifact is the same in all three, finding out where in the chain each version is comes down to asking each environment who it is. Thanks to the /version endpoint from 02-06, that is a three-line loop:

#!/usr/bin/env bash
# scripts/environment-status.sh — which SHA is in each environment right now?
for environment in dev staging prod; do
  # api-dev.reservalia.com, api-staging.reservalia.com, api.reservalia.com
  host="api.reservalia.com"
  [ "$environment" != "prod" ] && host="api-$environment.reservalia.com"

  sha=$(curl -sf --max-time 5 "https://$host/version" | jq -r '.commit')
  printf '%-8s %s\n' "$environment" "${sha:-NO RESPONSE}"
done

curl -sf silences the progress bar and returns an error if the HTTP status is not 2xx; --max-time 5 stops the script hanging if an environment does not respond; jq -r extracts the commit field without quotes. Run on any given Tuesday, Reservalia's output should look something like this:

dev      c1d4a55
staging  b7e2d10
prod     a3f9c21

In other words: the chain is moving and prod is two commits behind. That three-line snapshot is the cheapest instrument in existence for answering Nuria's question at 23:40 — "which version is deployed?" — and we will use it in every lesson of the module.

  1. Approval gates: manual, automatic and deployment windows

A gate is a condition a deployment must satisfy in order to move on to the next environment. There are three families, and they are not worth the same.

Type of gate Example at Reservalia Cost in lead time What it genuinely catches
Manual Marta approves the move to prod Minutes… or hours if she is in a meeting Problems of judgement: "we do not want to announce this yet"
Automatic by metric Error rate < 1% for 10 min in staging Seconds or minutes, with no blocking Technical problems, objectively and reproducibly
Deployment window "We do not deploy on Fridays" Up to 3 days of waiting Nothing. It only shifts the risk

The manual gate has one legitimate use and one illegitimate one. Legitimate: deciding when something is released for product reasons — a feature that has to coincide with a campaign, a change that requires warning the businesses. Illegitimate: "just in case". An approval whose reviewer has no information the pipeline does not already have is not a quality gate, it is a rubber stamp. It is easy to spot: if nobody has ever rejected an approval, the gate is not filtering anything.

And so we come to the Friday freeze, which almost every team has practised at some point. The reasoning is intuitive: if we deploy on Friday and it breaks, we ruin somebody's weekend. Let us look at what it actually produces:

  • Thursday's and Friday's work piles up, so on Monday a much bigger batch gets deployed. And we have known since 02-07 that the risk of a batch grows faster than its size.
  • Monday is therefore the most dangerous day of the week, precisely because of the freeze.
  • The team learns that deploying is dangerous, which reinforces the policy and closes the loop.

That is why the freeze is a symptom, not a solution: it is what a team does when it cannot go back in five minutes or find out about a failure in two. The cure is not to ban Fridays, it is to make Friday's deployment as boring as Tuesday's. Reservalia will keep the freeze for much of this module, and will lift it — deliberately and with data — once the rollback from 03-05 and the alerts from 03-06 are in place.

An important note: banning deployments on Fridays does not prevent incidents on Fridays. It prevents fixes on Fridays, because an urgent hotfix runs into the very same policy.

  1. What changes in how the team divides responsibilities

Automating deployment does not eliminate human work: it shifts it. It is worth making that explicit, because the cultural change is where most CD adoptions run aground.

Before (the Friday ritual) After (CD)
Who deploys Diego, manually The pipeline
Who decides that a deployment happens Diego, when he has a gap The PR merge
Nuria's role (SRE) Running deployments and putting out fires Building the platform that deploys, and the alerts
Marta's role (tech lead) Coordinating "release day" Defining SLOs, approving prod, looking after lead time
Diego's role (backend) Requesting a deployment and waiting His change gets there by itself: he is responsible all the way to production
Knowledge of the deployment In one person's head In a versioned, reviewable file

Two consequences worth accepting from the outset:

The author of the change becomes the owner of that change in production. This is the famous you build it, you run it. When the deployment takes three days and somebody else does it, it is easy to wash your hands of it. When your commit is in production forty minutes after the merge, the feedback is yours and it is immediate. This does not mean Diego becomes the on-call SRE; it means he sees the metrics for his change and gets involved when something goes sideways.

The pipeline becomes production. If deployment is automated, the workflow has permissions over the infrastructure and a vulnerability in the pipeline is a vulnerability in production. That change of status — from "internal tool" to "critical system" — is the reason lesson 04-03 exists, devoted to pipeline security.

  1. Reservalia's full commit → prod flow

This is where the module is heading: the diagram as it will look once lesson 03-06 is finished. You do not yet know how to implement most of it, and that is fine; it serves as a map for placing each lesson.

flowchart TD
    C["Diego merges the PR<br/>commit a3f9c21"] --> CI["ci.yml: quality · test · build<br/>~4 min"]
    CI --> PUB["publish<br/>ECR reservalia/api:a3f9c21"]
    PUB --> CD["cd.yml is triggered"]
    CD --> DEV["Deployment to dev<br/>automatic · 03-02"]
    DEV --> SM1["Smoke test /version<br/>does it report a3f9c21"]
    SM1 -- fails --> RB1["Automatic rollback<br/>03-05"]
    SM1 -- ok --> STG["Deployment to staging<br/>automatic · 03-02"]
    STG --> SM2["Smoke + E2E tests<br/>+ metrics for 10 min"]
    SM2 -- ok --> GATE{"Prod gate"}
    GATE -- "Marta approves" --> PROD["Deployment to prod<br/>canary 10% · 03-04"]
    PROD --> MON["Metrics and SLOs<br/>03-06"]
    MON -- "high error rate" --> RB2["Automatic rollback<br/>03-05"]
    MON -- "stable for 15 min" --> FULL["Promotion to 100%"]
    FULL --> REG["Record in the deployments table<br/>DORA metrics · 03-06"]

Walking it from top to bottom gives you the module's table of contents: 03-02 builds the deployment boxes for dev and staging; 03-03 guarantees that those three environment boxes come out of the same code; 03-04 turns the prod deployment into a controlled canary; 03-05 draws the two rollback arrows; and 03-06 closes the loop with the metrics that decide whether the canary advances or retreats.

And there is one property of the diagram worth underlining: the artifact a3f9c21 appears only once, in the first step. Everything else is movements of that same digest. That is the principle from 02-06 taken to its conclusion.

Common Mistakes and Tips

Mistake 1: automating deployment before you can go back. This is the reverse of the sensible order. If you deploy five times a day and do not know how to revert, you have multiplied your exposure to risk by five without touching your ability to respond. Rollback first, frequency afterwards.

Mistake 2: calling an environment staging when it looks nothing like production. A staging with SQLite, 30 test records and a single instance answers no useful question; it only generates a false sense of validation and the classic "but it worked in staging".

Mistake 3: accumulating environments. I have seen chains like devtestqauatpreprodprod. Each link adds days of lead time and, almost always, none of them answers a question the previous one does not.

Mistake 4: using a literal copy of the production database in staging. Real personal data in an environment with weaker access control, more people holding permissions and less auditing.

Mistake 5: confusing "we have manual approval" with "we have control". If the approver has no additional information and no criteria for rejecting, the gate only adds latency.

Tip 1: write down which question each environment answers and put it in the README.md. It is the best filter against environment proliferation.

Tip 2: measure how long an artifact spends waiting at each gate. It is usually the largest component of lead time, and it is invisible until you measure it.

Tip 3: treat the Friday freeze as technical debt, with a retirement date and concrete conditions for lifting it, not as a permanent policy.

Exercises

Exercise 1

A team deploys to production once every three weeks, with approval from a committee. Its CI is good: reliable tests, immutable artifacts in a registry and main always green. It wants to move to Continuous Delivery. Assess the five prerequisites and say which one to tackle first and why.

Exercise 2

Reservalia is considering adding a preprod environment between staging and prod, identical to prod but without real traffic. Argue for and against, and reach a decision, saying which piece of data you would consult in order to decide.

Exercise 3

The CTO proposes: "Since we are automating anyway, let us also remove the prod approval: pure Continuous Deployment from tomorrow." You agree with the destination, but not with the date. Write three measurable conditions that should be met before removing that gate.

Solutions

Solution 1. The team already meets requirements 1 and 2 (reliable tests, immutable artifact). Requirements 3, 4 and 5 remain to be assessed. The first one to tackle is 4, the reversible deployment, for two reasons. The first is about risk: moving from one deployment every three weeks to several a week multiplies your exposure, and without the ability to revert, every incident lasts as long as the diagnosis takes. The second is cultural and more important: the approval committee exists because deploying is frightening, and the fear comes from an error being irreversible. Demonstrating that any deployment can be undone in five minutes is what makes the committee's existence negotiable; without that, any proposal to go faster will run — rightly — into the risk argument.

The reasonable order after that would be: 5, observability (without a signal you do not know when to revert, so rollback is left at the mercy of somebody noticing), and then 3, equivalent environments, which is the most expensive and slowest because it means Infrastructure as Code.

Solution 2. In favour: a preprod identical to prod allows infrastructure changes and load tests to be validated without risk, and it is common in sectors with regulatory requirements. Against: it costs practically the same as prod (around €900/month for 340 paying businesses, hard to justify), it adds a link to the lead time and, above all, it answers no new question if staging already has anonymised data with realistic volume. The added risk is that an environment without real traffic does not catch concurrency or load problems either, which is precisely what would be asked of it.

Decision: do not add it. The data that confirms this is an analysis of the production incidents of the last six months: how many of them would have been caught by a preprod and not by staging? If the answer is zero or one, the environment does not pay for itself. If several failures did turn out to be tied to infrastructure differences, the right answer would still probably be to fix staging — make it more like prod through IaC, lesson 03-03 — rather than adding a sixth environment.

Solution 3. Three measurable, verifiable conditions:

  1. Demonstrated rollback: the go-back workflow exists, has been tested in a real drill and returns prod to the previous version in under 5 minutes, measured rather than estimated.
  2. Automatic detection: alerts exist on symptoms (error rate and p95 latency of the booking endpoint) that fire in under 2 minutes, and the record of the last three months shows that no high-severity incident was detected first by a customer.
  3. An approval history with no rejections: over at least 30 consecutive deployments, the manual approval has not rejected or modified a single one. If it never filters, it adds no information and its cost in lead time is pure waste. If it has rejected some, you need to understand why before removing it: the pipeline is probably not checking something it should be.

You can add a fourth, transitional condition: apply Continuous Deployment first to the least critical service, or only to changes that do not touch the payments domain, and widen the scope once the track record supports it.

Conclusion

This lesson has established the framework for the module. The artifact reservalia/api:a3f9c21 has been ready since module 2; what is missing is the path that takes it to the 340 businesses, and that path takes the shape of an environment chain — dev to check that it starts up on real AWS, staging to check that it holds up under realistic data and volume, prod for the customers — with gates between them.

The ideas worth taking away: Continuous Delivery is 90% of the work and Continuous Deployment is removing one condition from a workflow, so the real goal is a button that does not frighten you; a healthy CD needs five prerequisites — reliable tests, an immutable artifact, equivalent environments, a reversible deployment and observability — which form a system where failing at one degrades the rest; each environment must answer a question that cannot be answered any earlier, or it is surplus; manual gates are for product decisions and automatic metric-based ones for technical decisions; and the Friday freeze is the symptom of a team that cannot go back quickly, not a quality policy.

The division of roles has changed too: Nuria builds the platform instead of running deployments, Marta looks after the objectives instead of coordinating releases, and Diego owns his change all the way to production. And the pipeline, which will hold permissions over the infrastructure, stops being an internal tool and becomes a critical system.

The next lesson, Deployment Automation, comes down from theory to the file: we will write .github/workflows/cd.yml line by line, see how it is triggered from the publish job, how it authenticates against AWS with OIDC without storing keys, how the ECS service is updated with the exact digest, where each environment's configuration lives — because the artifact is the same and what changes is the configuration — and how a smoke test against /version confirms that what is running really is what we thought we had deployed.

CI/CD Course: Continuous Integration and Deployment

Module 1: Introduction to CI/CD

Module 2: Continuous Integration (CI)

Module 3: Continuous Deployment (CD)

Module 4: Advanced CI/CD Practices

Module 5: Implementing CI/CD in Real Projects

Module 6: Tools and Technologies

Module 7: Practical Exercises

Module 8: Additional Resources

© Copyright 2026. All rights reserved