Reservalia now deploys on its own, with reproducible environments, a deliberately chosen handover strategy and a rehearsed go-back button. And yet it still has a hole right in the middle: it finds out that production is unwell because a customer calls. The 3 minutes of detection promised by the table in the previous lesson do not exist yet, check-metrics.sh invokes thresholds nobody has defined, and the Friday freeze is still standing because nobody has an objective argument for lifting it. With no production signal, continuous deployment is simply deploying blind faster. This lesson closes the loop — and with it, the module: what to observe, how to turn those observations into objectives with consequences, when it is worth waking somebody up, and how the pipeline itself feeds on what happens in production.
Contents
- The open loop: why CD without monitoring is a fast bet
- The three pillars of observability
- The four golden signals applied to Reservalia's API
- Reservalia's minimal dashboard
- SLI, SLO and error budget
- The budget policy: goodbye to the Friday freeze
- Marking deployments on the dashboards
- Alerts: symptoms, not causes
- Feedback into the pipeline: the DORA metrics feed themselves
- Reservalia's dashboard after modules 2 and 3
- Common Mistakes and Tips
- Exercises
- Conclusion
- The open loop: why CD without monitoring is a fast bet
Everything built so far forms a chain that pushes changes into production faster and faster. What is missing is the return arrow.
flowchart LR
C["commit"] --> CI["ci.yml"] --> CD["cd.yml"] --> P["prod"]
P -->|"?"| S["Signal:<br/>does it work for the businesses"]
S -.->|"decides whether to keep deploying"| C
S -.->|"triggers rollback"| CD
S -.->|"feeds the DORA metrics"| M["Dashboard"]
Without that arrow three specific things happen, all of them visible at Reservalia today: the canary from 03-04 cannot decide whether to promote, because check-metrics.sh has nothing to compare against; the rollback from 03-05 arrives late, because detection depends on a customer calling; and the team keeps the Friday freeze because its only alternative to blind caution would be blind recklessness.
It is worth distinguishing two words that get used as synonyms. Monitoring is watching things you already know can fail: CPU, memory, the number of errors. Observability is the property of a system that lets you answer questions you had not anticipated: "why do bookings for businesses with split opening hours take four seconds only on Mondays?". The first is configured; the second is designed, by instrumenting the code so it emits enough context.
- The three pillars of observability
| Pillar | What it is | What question it answers | Cost | At Reservalia |
|---|---|---|---|---|
| Metrics | Numbers aggregated over time | Is there a problem? How big? | Low and constant | CloudWatch: latency, 5xx, healthy tasks |
| Logs | Discrete events with context | What exactly happened in this request? | High: it grows with traffic | CloudWatch Logs, structured JSON |
| Traces | A request's journey through the system | Where did the time go? | Medium, with sampling | OpenTelemetry (pending) |
The order matters and it is the order of an investigation: the metric warns you, the trace locates it, the log explains it. A team with only logs finds out late and searches blind; one with only metrics knows something is wrong but not why.
Reservalia makes a small investment that multiplies the value of its logs: emitting them as structured JSON with fixed fields.
// apps/api/src/log.ts — a log you can query, not just read
export function log(level: 'info' | 'error', message: string, extra: Record<string, unknown> = {}) {
console.log(JSON.stringify({
ts: new Date().toISOString(),
level,
message,
commit: process.env.COMMIT_SHA, // 1 · which version emitted this line
environment: process.env.ENVIRONMENT,
...extra, // 2 · businessId, routeId, durationMs…
}));
}- Including the commit on every line is what lets you, during an incident, filter errors by version and confirm in ten seconds whether they started with the 09:08 deployment. It is the same idea as the
/versionendpoint, applied to logs. - Structured fields rather than interpolated text.
log('error', 'booking failed', { businessId: 412, durationMs: 3140 })can be aggregated and filtered;console.log('Booking error for business 412')can only be read. The rule: never put a variable value inside the message text.
A cost warning that surprises many teams: logs are the pillar that grows most and bills most. Reservalia logs business operations at info (booking created, cancelled) and failures at error, but not one line per HTTP request: that is what metrics are for, and they cost a fraction.
- The four golden signals applied to Reservalia's API
The four golden signals are the minimal set that answers "is my service healthy?" for almost any request-driven system.
| Signal | What it measures | Concrete metric at Reservalia | Reference threshold |
|---|---|---|---|
| Latency | How long it takes to respond | p50, p95 and p99 of POST /api/bookings |
p95 < 400 ms |
| Traffic | How much demand there is | Requests/minute at the ALB; bookings created/hour | Context, not an alarm |
| Errors | What fraction fails | Proportion of 5xx responses over the total | < 0.1% |
| Saturation | How full the system is | CPU and memory of the ECS tasks; free connections in the RDS pool | CPU < 70%, pool < 80% |
Three points that separate a useful dashboard from a decorative one:
Latency is measured in percentiles, never as a mean. If 99 requests take 100 ms and one takes 10 seconds, the mean is 199 ms — a reassuring number that hides a user who has walked away. The p95 says "95% of users wait less than this", and the p99 is where the rare cases that end up in the support chat live. On top of that, the latency of errors must be measured separately: a service that starts returning 500s in two milliseconds improves its mean latency while it is falling over.
Traffic is almost never an alarm, but it is essential as context. A 5% error rate with 20,000 requests and with 40 requests are incidents of very different severity, and traffic dropping to zero may be the only signal that the front end is broken even though the API responds perfectly.
Saturation is the only signal that warns you beforehand. Latency, errors and traffic describe what is already happening; a connection pool at 85% says there will be an incident in twenty minutes. It is the signal that lets you act rather than react.
- Reservalia's minimal dashboard
Nuria creates a single dashboard, reservalia-api-prod, with the rule that it must fit on one screen and answer the question "is it healthy?" in ten seconds.
| Row | Widgets | Why it is there |
|---|---|---|
| 1 · Status | Requests/min · % of 5xx · global p95 · healthy / desired tasks | The traffic light: four numbers that summarise health |
| 2 · Critical path | p95 and errors of POST /api/bookings and of GET /slots |
The business is booking; the rest is secondary |
| 3 · Dependencies | RDS latency · free connections in the pool · SMS provider errors | Where the real cause usually is |
| 4 · Budget | Error budget consumed this month · version deployed per environment | The decision on whether we can keep deploying |
// apps/api/src/middleware/metrics.ts — one metric per request, without a heavy library
app.use((req, res, next) => {
const start = process.hrtime.bigint();
res.on('finish', () => {
const ms = Number(process.hrtime.bigint() - start) / 1e6;
emitMetric({
name: 'ApiLatency',
value: ms,
unit: 'Milliseconds',
dimensions: { // 1
route: req.route?.path ?? 'unknown',
method: req.method,
class: `${Math.floor(res.statusCode / 100)}xx`,
},
});
});
next();
});- Dimensions are the key and also the trap. They let you filter by route, method and response class, which is exactly what you need to tell whether the problem is general or confined to one endpoint. But each distinct combination of dimensions is a billable metric: using
req.pathinstead ofreq.route.pathwould create a metric for every/api/businesses/412/slots, that is, one per business, with a three-figure bill and unusable dashboards. Never use identifiers as a dimension.
- SLI, SLO and error budget
A dashboard tells you how the system is; it does not tell you whether that is acceptable. For that you need three linked concepts.
| Concept | What it is | Example at Reservalia |
|---|---|---|
| SLI (indicator) | A concrete measure of the user's experience | % of API requests that do not return 5xx |
| SLO (objective) | The value that indicator must reach in a window | ≥ 99.9% over 30 calendar days |
| SLA (agreement) | An SLO with contractual consequences | Not applicable: Reservalia signs no penalties |
| Error budget | What is left over up to 100%: the permitted failure | 0.1% of the month's requests |
Reservalia defines two SLOs, and only two. The temptation to define fifteen is strong and it is a mistake: an SLO nobody looks at disciplines nothing.
| SLO | SLI | Objective | Window | Budget |
|---|---|---|---|---|
| Availability | Requests without 5xx / total requests | 99.9% | 30 days | 0.1% ≈ 43 min of total outage |
| Booking latency | Requests to POST /api/bookings served in < 400 ms |
99.0% | 30 days | 1% of bookings |
Three design decisions deserve an explanation. It is measured from the user's point of view, not the server's: the SLI counts requests that reached the ALB, including those that failed because there was no healthy task; measuring only what the application managed to process is marking your own homework. The objective is not 100%, and this is the hardest part to accept: 100% is impossible (the network fails, AWS fails, the client fails) and undesirable, because the cost of each additional nine multiplies and that money comes out of building product. 99.9% over 30 days is 43 minutes of budget, which is exactly the material with which you buy the ability to deploy fast.
Because that is the central idea: the error budget is not an evil to be avoided, it is a resource to be spent. A team that ends the month with 100% of its budget intact has not been excellent: it has been too conservative, it has deployed less than it could and it has delivered less value than it could. A team that exhausts it on the 12th has a reliability problem. The goal is to reach the end of the month having spent almost all of it.
- The budget policy: goodbye to the Friday freeze
In lesson 03-01 we left the Friday freeze labelled as technical debt, with a retirement date and conditions. The error budget is the objective rule that replaces it, because it answers the same worry — "I do not want to break production at a bad moment" — with data instead of a calendar.
| Budget remaining | What can be done | Who decides |
|---|---|---|
| > 50% | Deploy as normal, any day of the week | The pipeline, with no intervention |
| 25 – 50% | Deployments continue, but reliability enters sprint planning | Marta |
| 10 – 25% | Only fixes, reliability changes and low-risk work | Marta + Nuria |
| < 10% | Feature freeze: the whole team on reliability until it recovers | Agreed in advance |
The three properties that make this work better than a freeze:
- It is objective. It does not depend on somebody feeling nervous today. The rule is agreed when nobody is in an incident, which is the only moment anyone thinks clearly.
- It is symmetrical. If reliability is good, the team gains freedom: you deploy at five o'clock on a Friday. The freeze only restricted; the budget also rewards.
- It aligns everybody. Product stops asking for "more features and also more stability" as if they were independent: the budget makes it explicit that they are the same currency.
Reservalia lifts the Friday freeze the day the three conditions written down in 03-01 are met — a rollback demonstrated in a timed drill, automatic detection of serious incidents, and an approval history that was filtering nothing — and replaces it with this table. In the first week without a freeze, Diego merges a change at 16:40 on a Friday; the canary promotes it at 17:10 and absolutely nothing happens. That non-event is the result of the entire module.
- Marking deployments on the dashboards
The question a team asks itself when facing a degradation is always the same: did this start with the last deployment? Answering it by looking at two tabs and comparing timestamps is slow and error-prone. The solution is cheap: annotate every deployment onto the graphs.
# cd.yml, after the prod smoke test
- name: Annotate the deployment on the dashboard
run: |
aws cloudwatch put-metric-data --namespace Reservalia/Deployments \
--metric-name Deployment --value 1 \
--dimensions Environment=prod,Commit=${{ steps.meta.outputs.sha }}With that metric overlaid as a vertical line on the latency and error graphs, the correlation is visible at a glance: if the curve bends right at the line, the suspect is identified; if it bent twenty minutes earlier, the deployment is innocent and you need to look at the dependencies. It is the same reasoning we applied to logs with the commit field, carried over to the graphs, and it is what makes the first question of an incident answerable in seconds rather than in ten minutes.
An honest caveat: correlation is not causation. A coincident deployment may be a coincidence — at 09:00 you deploy and the morning traffic peak begins. The annotation does not close the investigation; it starts it in the right place.
- Alerts: symptoms, not causes
An alert must meet three conditions: it is real (not noise), it is urgent (it cannot wait until tomorrow) and it is actionable (whoever receives it can do something). If any of them fails, it is not an alert: it is an email.
The fundamental distinction is between alerting on causes and alerting on symptoms.
| Cause-based alert | Symptom-based alert | |
|---|---|---|
| Example | "Task CPU at 91%" | "3% of bookings fail" |
| Problem | It may affect nobody; there are infinitely many possible causes | — |
| Advantage | — | It covers causes nobody anticipated, including the one that will fail tomorrow |
| When it is useful | As a prediction with headroom (disk at 85%) | Always: it is the reference |
Alerting on causes produces the worst of both worlds: lots of notifications that mean nothing to the user and, at the same time, gaps — the day the system fails for a reason nobody foresaw, there is no alert. Reservalia defines three alerts and not one more:
| Alert | Condition | Severity | Action |
|---|---|---|---|
Api5xxErrors |
> 1% of 5xx for 5 min in prod |
Wakes you | Check recent deployment, mitigate |
ApiBookingLatency |
p95 of POST /api/bookings > 1 s for 10 min |
Wakes you | Same: it is a user-facing symptom |
BudgetFastBurn |
10% of the monthly budget consumed in 1 h | Wakes you | An incident is in progress, even if the total percentage still looks fine |
ConnectionPoolHigh |
Free connections < 20% for 15 min | Ticket | Review during working hours |
ExpiredFlags |
There are expired release flags | Weekly ticket | Pay down debt (03-05) |
The third deserves a comment, because it is the most sophisticated. A monthly SLO has a problem: if the system goes down completely on the 2nd, the accumulated monthly percentage still looks fine for hours and nothing fires. The budget's burn rate measures how much is being spent right now, and detects in minutes an outage that the monthly percentage would take a day to reflect. It is the alert that turns an SLO into something operational rather than just a report.
On alert fatigue: it is the most common and the most expensive failure, because it does not manifest as an incident but as a slow loss of trust. A team receiving thirty notifications a day stops reading them — not out of indiscipline, but because it is humanly inevitable — and the day the important one arrives, it is lost among the rest. Three rules of hygiene: if an alert has required no action the last five times, delete it or turn it into a ticket; every alert that wakes someone links to a written procedure with the first three steps; and the number of alerts that wake people is reviewed monthly and must trend downwards.
The definitive test before creating an alert that will go off in the small hours: what would a person do at 3:40 on receiving it? If the answer is "look at it and go back to sleep", that alert must not exist.
- Feedback into the pipeline: the DORA metrics feed themselves
In lesson 01-05 we built the DORA dashboard with the deployments and incidents tables, filled in by hand. A dashboard that depends on somebody remembering to write a row is out of date within three weeks. Now that the pipeline knows about every deployment and the alerts know about every incident, both tables can fill themselves.
# cd.yml, last step of the prod job
- name: Record the deployment
if: always() # 1
run: |
psql "$METRICS_DATABASE_URL" <<SQL
INSERT INTO deployments
(environment, commit_sha, started_at, finished_at, success, run_id, first_commit_at)
VALUES
('prod', '${{ steps.meta.outputs.sha }}',
'${{ steps.meta.outputs.start }}', now(),
${{ job.status == 'success' }},
'${{ github.run_id }}', -- 2
'${{ steps.meta.outputs.first_commit_date }}') -- 3
ON CONFLICT (run_id, environment) DO NOTHING; -- 4
SQLif: always()also records failed deployments. Without them, the change failure rate would measure only successes and would always come out at 0%, which is the most common way of having a beautiful, useless metric.run_idlinks the row to the GitHub Actions run: from the dashboard you reach the logs in one click.first_commit_dateis what allows the real lead time to be computed, measured from when the code was written rather than from when the deployment started. It is obtained withgit log -1 --format=%cIon the oldest commit in the PR.ON CONFLICT DO NOTHINGmakes the insert idempotent, as 03-02 required: re-running the workflow does not duplicate the row or pollute the metrics.
Incidents are recorded the same way, from the alert and from rollback.yml itself in 03-05, which already took a mandatory reason precisely for this. With both tables up to date, the four metrics are queries:
-- Deployment frequency and change failure rate over the last 30 days
SELECT
count(*) FILTER (WHERE success) / 4.3 AS deployments_per_week,
round(100.0 * count(*) FILTER (WHERE NOT success) / count(*), 1) AS pct_failed,
round(avg(extract(epoch FROM finished_at - first_commit_at) / 3600)::numeric, 1) AS lead_time_hours
FROM deployments
WHERE environment = 'prod' AND finished_at > now() - interval '30 days';A methodological point about the change failure rate: it does not count the deployments that failed in the pipeline — those are good news, the system did its job — but those that reached production and had to be fixed or reverted. The practical way to measure it is to count deployments followed by a rollback or an incident in the next 24 hours, which is exactly what cross-referencing deployments with incidents allows.
- Reservalia's dashboard after modules 2 and 3
This is the result of two modules' work, measured with the same yardstick as the baseline:
| DORA metric | Baseline | Target | Now | What achieved it |
|---|---|---|---|---|
| Deployment frequency | 1.1 / week | ≥ 5 / week | 12 / week ✅ | Automatic cd.yml; no freeze |
| Lead time for changes | 6.2 days | < 4 h | 3.5 h ✅ | Small PRs, 4-min CI, 30-min deployment |
| Change failure rate | 14% | < 5% | 6.5% ⚠️ | Quality gate, canary and circuit breaker |
| Time to restore | 68 min | < 10 min | 9 min ✅ | Alerts, flags and rollback.yml |
Three of the four targets met, and the fourth halfway there. It is worth looking at why the change failure rate has stalled, because the answer is not in this module: the failures that remain are not deployment failures, they are content failures — a dependency that changed behaviour in a minor update, a schema migration that locked a table during working hours, a business case the tests did not cover. That is exactly what the next module tackles.
And there is one improvement no table captures. The Friday ritual from lesson 01-04 — three hours of SFTP, migrations by hand in psql, Diego watching logs — has disappeared. It has not been optimised: it has ceased to exist as a category of work. Marta, Diego and Nuria spend that time on something else, and that is the real benefit the four metrics only hint at.
What remains outstanding, and what shapes module 4:
- The pipeline is slow and it is putting on weight. CI takes 4 minutes today; with more tests it will be 15, and Diego will stop watching it. Cache, parallelism and selective execution are needed.
- Dependencies are not under control. Nobody knows how many transitive packages get into
reservalia/api, nor who maintains them, nor which exact version was used ina3f9c21. - The pipeline has permissions over production and has never been audited. Since 03-01 we have known it stopped being an internal tool and became a critical system, and it still has no security analysis, artifact signing or component inventory.
- Database migrations are still the fragile point. They are the only reason the rollback from 03-05 might not work.
Diego: "If CI takes longer than going for a coffee, I stop watching it. And now that the deployment is mine all the way to production, I watch it far more."
Common Mistakes and Tips
Mistake 1: measuring the mean instead of percentiles. It hides precisely the users having the worst time. Mistake 2: alerting on causes. It produces noise and, at the same time, gaps in the face of unanticipated failures.
Mistake 3: defining fifteen SLOs. An SLO nobody consults disciplines nothing; two that are looked at every week change the team's behaviour. Mistake 4: setting the objective at 100%, which makes the budget zero and turns any deployment into a violation.
Mistake 5: using identifiers as a metric dimension. A businessId as a dimension multiplies the cost by 340 and makes the dashboard illegible. Mistake 6: dashboards nobody looks at, built out of everything the tool offered instead of out of what answers a question.
Mistake 7: recording only successful deployments. The change failure rate will always look wonderful and will mean nothing.
Tip 1: define the SLOs with product, not just with engineering. The reliability objective is a business decision dressed up as a technical one. Tip 2: agree the budget policy in writing and in the cold, before the first incident that puts it to the test. Tip 3: review the alerts once a month and mercilessly delete the ones that have prompted no action.
Exercises
Exercise 1
Reservalia's dashboard shows a stable p95 of 180 ms all week, but support reports three businesses complaining about slowness when opening the schedule. The team replies that "the metrics are fine". Explain what may be happening and which three changes you would make to the instrumentation.
Exercise 2
It is the 18th of the month. The availability error budget is 78% consumed because of an RDS outage on the 6th. Product asks to deploy a large feature this week. Apply the policy from section 6, argue the decision and explain what you would do if product insists with a legitimate business reason.
Exercise 3
Design the alert that would detect, in under five minutes, a deployment that causes 100% of the bookings of a single large business to fail, while all the other businesses work normally. Discuss why Reservalia's three current alerts would probably not detect it.
Solutions
Solution 1. The most likely explanation is that the metrics are well measured and badly aggregated. A global p95 across all routes mixes thousands of cheap requests (/health, cached listings) with expensive ones: if 92% of the traffic responds in 30 ms, there is plenty of room for one specific endpoint to take several seconds without moving the global p95. On top of that, three businesses out of 340 are less than 1% of the traffic: by definition they live in the p99, invisible in the p95. And there is a third possibility: that the slowness is in the front end — resource downloads, rendering — and the API does not see it at all.
The three changes: (1) measure percentiles per route, not just globally, and bring the p95 and p99 of the critical-path routes onto the dashboard; (2) add the p99 next to the p95, because the problems support reports live there, and complement it with structured logs that record requests above a threshold (1 s, say) together with their businessId, which would let you discover in a minute that the three affected businesses are the ones with split opening hours and 400 weekly appointments; (3) instrument the client (Real User Monitoring) to measure what the browser experiences, which is the only thing the user perceives. The underlying lesson: an SLI that does not represent the user's experience is a number that reassures without informing.
Solution 2. With 78% consumed, 22% remains, so the policy places Reservalia in the 10-25% band: only fixes, reliability changes and low-risk work. The default decision is not to deploy the large feature this week, and the conversation with product should not be a negotiation of wills but the application of a previously agreed rule.
That said, there is an important and honest nuance: the budget was exhausted by an RDS outage unrelated to deployments, not by the team's changes. Blocking delivery because of an infrastructure incident punishes the wrong behaviour, and a policy perceived as unfair eventually gets ignored. Two legitimate ways out: review whether that outage should be excluded from the calculation (many teams define in advance which events are exceptional and how they are documented), or keep the restriction but accompany it with the work that resolves it — reliability work on RDS: Multi-AZ is already there, so what remains is reviewing retries, timeouts and graceful degradation.
If product insists with a legitimate reason (a dated commercial commitment), the right answer is not to skip the policy quietly, but to use the module's tools: deploy the code behind a switched-off feature flag, enable it for one business, then for 10% and only afterwards for everybody, with an immediate rollback available. That decouples the delivery date from the risk, which is precisely what all of this was built for. And the exception is recorded: who authorised it and why, so it can be reviewed in the retrospective.
Solution 3. The three current alerts would not detect it because of a dilution problem. A large business may account for 2% of total traffic; if all its bookings fail, the global error rate rises from 0.1% to 2.1%, which probably crosses the 1% threshold of Api5xxErrors… or probably does not, if the business is smaller or if the failure affects only part of its requests. With a business making up 0.5% of the traffic, the global signal is indistinguishable from noise, and the same goes for latency and for budget consumption.
The design that does detect it: an alert on the maximum error rate per business, not on the aggregate. In practice it is implemented by emitting an error metric grouped by a dimension of controlled cardinality — not businessId, which would be 340 metrics and violates the rule from section 4, but something like the business's size segment, or a counter of "distinct businesses with more than 50% failed requests in the last 5 minutes". That last formulation is the good one: a single number, cardinality one, that fires when any business is completely broken even though the aggregate looks impeccable. Condition: ≥ 1 business with more than 50% failures for 5 minutes, over a minimum of 20 requests to avoid false positives from low-traffic businesses.
An essential complement: when that alert fires, you need to be able to answer "which business?", and metrics do not give you that — the structured logs with businessId from section 2 do. It is the perfect illustration of the order of the three pillars: the metric warns, the log explains.
Conclusion
The loop is closed. Reservalia no longer deploys blind: it has metrics, structured logs and traces with distinct roles — the metric warns, the trace locates, the log explains; it watches the four golden signals on a dashboard that fits on one screen; it has translated "is it healthy?" into two SLOs with an error budget of 43 minutes a month that decides, through a table agreed in the cold, when to deploy freely and when to slow down; it marks every deployment on the graphs so the first question of an incident is answered in seconds; it alerts on symptoms and not on causes, with three alerts that wake people and no more; and it feeds its DORA metrics automatically from the pipeline itself, failed deployments included.
The Friday freeze has gone, and not out of bravery but the opposite: because there is now an objective rule saying when you can deploy and when you cannot, and because a deployment that is undone in four minutes and detected in two no longer deserves a ceremony. The numbers back it up: from 1.1 to 12 deployments a week, from 6.2 days to 3.5 hours of lead time, from 68 to 9 minutes of recovery. The change failure rate has stalled at 6.5% against a target of 5%, and that is the honest clue as to where the work goes next.
Because the failures that remain are no longer deployment failures. They come from a pipeline that is starting to put on weight and slow down, from dependencies that enter the artifact without anybody knowing how many there are or whose they are, from a system with permissions over production that has never had a security review, and from schema migrations that remain the only reason the go-back button might not work. Module 4, Advanced CI/CD Practices, tackles those four fronts, and it starts with the one that orders them all: its first lesson, CI/CD Pipelines, dissects the full anatomy of a pipeline — its stages, how they are orchestrated, what is parallelised, what can be skipped and how to design one that stays fast once the team has tripled in size.
CI/CD Course: Continuous Integration and Deployment
Module 1: Introduction to CI/CD
- Basic CI/CD Concepts
- Benefits of CI/CD
- Popular CI/CD Tools
- The Course Project: the Application We Are Going to Automate
- DORA Metrics: How Software Delivery Is Measured
Module 2: Continuous Integration (CI)
- Introduction to Continuous Integration
- Setting Up a CI Environment
- Build Automation
- Automated Testing
- Code Quality and Static Analysis
- Artifacts, Versioning and Promotion
- Integration with Version Control
Module 3: Continuous Deployment (CD)
- Introduction to Continuous Deployment
- Deployment Automation
- Infrastructure as Code and Reproducible Environments
- Deployment Strategies
- Feature Flags, Rollback and Failure Recovery
- Monitoring and Feedback
Module 4: Advanced CI/CD Practices
- CI/CD Pipelines
- Dependency Management
- Security in CI/CD
- Scalability and Performance
- Pipeline as Code: Templates, Reuse and Testing the Pipeline
- Databases in the Pipeline: Safe Migrations
Module 5: Implementing CI/CD in Real Projects
- Case Study: Web Project
- Case Study: Mobile Application
- Case Study: Microservices
- Case Study: Modernising a Legacy Project
Module 6: Tools and Technologies
- Jenkins
- GitLab CI/CD
- CircleCI
- Travis CI
- Docker and Kubernetes
- GitHub Actions in Depth
- Comparison and Criteria for Choosing a Tool
Module 7: Practical Exercises
- Exercise 1: Setting Up a Basic Pipeline
- Exercise 2: Integrating Automated Tests
- Exercise 3: Deploying to a Production Environment
- Exercise 4: Monitoring and Feedback
- Exercise 5: Hardening the Pipeline with Security and Secrets
- Final Project: A Complete End-to-End Pipeline
