Leer en español · Lire en français · Auf Deutsch lesen
Section 0.8 of 0.8 · Beginner · 20 min read
Requirements, version control, testing, delivery and the journey of one request
Video lesson
5 min · narrated, with captionsListen only (audio)
Read the transcript
Building software is a team sport with a careful routine. Needs are written down, every change is recorded and checked, and automatic tests act as a safety net. At the end of this lesson, we follow one tap of a Place order button through every part of a food delivery system, bringing the whole chapter together.
Software starts with a need. Some requirements say what a system must do, such as, a customer can pay by card. Others say how well, such as, checkout completes within two seconds. Many teams write needs as user stories. For example: as a hungry customer, I want to reorder a meal I've had before, so that I can eat sooner. Acceptance criteria then say exactly when the story is done.
Many people change the same code every day, so teams use version control, almost always a tool called Git. It records every change, who made it and why. Each saved change is a commit, here on the main line. To work on something new, a developer creates a branch: a separate line of work. Here, two commits add a tip button and its tests, without disturbing anyone else. When it's ready, the developer opens a pull request. Automated checks run, and a colleague reviews the change. Review catches mistakes, but it also spreads knowledge, so at least two people understand every change. Once approved, the branch is merged back into main.
Every change risks breaking something that used to work. Automated tests are programs that check other programs, and they come in layers. At the base are unit tests. Each checks one small piece, such as a function, and runs in milliseconds, so a team can have many. In the middle, integration tests check that parts work together, such as the order code with a real database. At the top are a few end-to-end tests, which drive the whole system as a user would. They're realistic, but slow and sometimes flaky. The aim is to catch most problems with fast, focused tests.
The automation that carries a change to customers is called a pipeline. With continuous integration, every change is merged often and automatically built and tested. A failure goes straight back to the developer, within minutes. Next comes staging: a copy of the system as close to production as possible, where the release is rehearsed. Production is the live system that real customers depend on. With continuous delivery, a person approves each release at the press of a button. With continuous deployment, every passing change goes live automatically.
Teams also choose how to organise the work. In a sequential approach, often called waterfall, each phase finishes before the next begins. It suits fixed requirements, but users see nothing until late. Scrum, the most widely used agile framework, works in short cycles called sprints, of a month or less, each ending with a review. Kanban has no fixed cycles. Work flows across a visible board, and the team limits how much is in progress at once. And DevOps is a culture that brings building and running software together: you build it, you run it.
Now, the whole chapter in a single moment. A customer taps Place order in a food delivery app. The app turns the basket into JSON. First, DNS turns the company's name into an IP address. The app sends an HTTPS request. A load balancer in the company's cloud region passes it to one of several copies of the order service, running in containers. The order service calls the payment provider's API, and waits. The customer mustn't see confirmed until the payment is approved, so this call is synchronous. Then, in one transaction, the order and its items are saved to the database. Either all of it is saved, or none of it. Finally, an order placed message goes on a queue, and the app shows confirmed. The notification service picks it up when ready, and a push notification reaches the phone. If that service is briefly down, nothing visible breaks.
Ask what if, at each step, and the architectural questions appear. What if the payment call times out? Did the charge happen? A unique key sent with the order lets the service safely ask again, without charging twice. What if saving the order fails after the payment succeeded? Payment and database are separate systems, so the design needs a way to refund or retry. And what if dinner-time traffic triples? More copies of the order service are added, and the database becomes the likely bottleneck. None of these questions is about writing a function. They're about how the parts are arranged.
Someone had to decide all of that: what's synchronous, where the data lives, what runs where. Deciding how the parts are arranged is software architecture. That completes Chapter zero. Try the optional chapter test, then continue to Chapter one: what software architecture is.
What you need to know
- Requirements describe what a system must do and how well; user stories (As a..., I want..., so that...) with acceptance criteria are a common lightweight way to capture functional needs.
- Git records every change as commits; branches isolate work in progress, and pull requests bring code review and automated checks before a merge into main.
- The testing pyramid recommends many fast unit tests, fewer integration tests and few slow end-to-end tests.
- Changes move through development, test and staging environments before production; the closer staging is to production, the more problems it catches.
- Continuous integration verifies every change automatically; continuous delivery keeps software always releasable; continuous deployment releases every passing change automatically.
- Waterfall, Scrum and Kanban organise work differently; DevOps is a culture of shared responsibility for building and running software.
- A single request in a real system crosses DNS, HTTPS, a load balancer, services, a database transaction, third-party APIs and queues, and deciding how those parts are arranged is software architecture.
From an idea to a requirement
Software starts with a need: a restaurant wants to take orders online, a hospital wants patients to book appointments themselves. Turning that need into something a team can build is the work of requirements. Some requirements describe what the system must do ("a customer can pay by card"); others describe how well it must do it ("checkout completes within two seconds for 95% of customers"). Chapter 2 shows that the second kind, the quality attributes, drive most architectural decisions.
Many teams capture functional needs as user stories: short descriptions written from the user's point of view. Kent Beck introduced them in Extreme Programming in the late 1990s, and a team at the London company Connextra devised the template most people now use in 2001:
As a hungry customer,
I want to reorder a meal I have ordered before,
so that I can eat sooner without searching the menu again.
Acceptance criteria
- My last 10 orders appear on the home screen.
- Tapping "Reorder" fills the basket with the same items.
- Items that are no longer available are flagged, not silently dropped.A story is deliberately small and incomplete. Ron Jeffries described it as a card, a conversation and a confirmation: the card is a reminder, the details are worked out in conversation between the people who want the feature and the people building it, and the acceptance criteria confirm when it is done. Practitioners disagree about how much should be written down in advance. Teams in regulated industries such as medical devices and aviation must keep formal, traceable requirements; product teams often prefer lighter stories refined just before work starts. Both are legitimate in their context.
Version control with Git
Source code is the team's most valuable shared document, and many people change it every day. Version control records every change, who made it and why, lets people work in parallel without overwriting each other, and allows any earlier state to be restored. Almost every team now uses Git, created by Linus Torvalds in 2005 for the development of the Linux kernel, usually together with a hosting service such as GitHub, GitLab or Bitbucket.
A few ideas cover most daily use. A commit is a saved snapshot of a set of changes, with a message explaining it ("Add tip button to checkout"). A branch is a separate line of work: a developer creates a branch from main, makes commits there without disturbing anyone, and when finished merges the branch back, combining the two histories. If two people changed the same lines, Git reports a merge conflict for a human to resolve.
On hosting services, merging usually goes through a pull request (GitLab calls it a merge request): a web page showing the proposed change, where automated checks run and colleagues carry out code review, reading the change, asking questions and suggesting improvements before approving it. Review catches defects, but its larger benefits are spreading knowledge (at least two people understand every change) and keeping the design consistent. Architects often take part in reviews of changes that affect structure or interfaces.
git switch -c feature/tip-button # create a branch and move onto it
# ... edit files ...
git add checkout.py test_checkout.py
git commit -m "Add tip button to checkout"
git push -u origin feature/tip-button # upload; then open a pull request
# after review and green checks, the branch is merged into mainTeams differ on branching. Some keep long-lived branches per feature or release; others practise trunk-based development, merging small changes into main at least daily and hiding unfinished features behind switches called feature flags. Research by the DORA programme associates short-lived branches with better delivery performance, but the right choice also depends on how a team releases.
Testing: the pyramid
Every change risks breaking something that used to work, which is called a regression. Automated tests are programs that check other programs, so they can be run on every change in minutes instead of by people over days. They come in layers. A unit test checks one small piece, such as a function, in isolation, and runs in milliseconds. An integration test checks that parts work together: the order code with a real database, or the shop with a payment provider's test environment. An end-to-end test drives the whole system as a user would, for example tapping through the app from menu to order confirmation.
Mike Cohn's testing pyramid, published in his 2009 book Succeeding with Agile, suggests the proportions: many unit tests, fewer integration tests, and few end-to-end tests. The higher layers are more realistic but slower, more expensive to maintain and more prone to flaky failures that come and go for reasons unrelated to the change. Some practitioners argue for a different shape, such as more integration tests for systems that are mostly glue between services; the principle all agree on is to catch most problems with fast, reliable, focused tests.
def delivery_fee(basket_total, distance_km):
if basket_total >= 25:
return 0.0 # free delivery on larger orders
return round(1.50 + 0.40 * distance_km, 2)
def test_free_delivery_over_threshold():
assert delivery_fee(30.00, 5) == 0.0
def test_fee_grows_with_distance():
assert delivery_fee(10.00, 5) == 3.50Environments: development to production
Code passes through several environments on its way to customers, each a separate copy of the system with its own servers, data and settings. The aim is to find problems where they are cheap and harmless, before they reach production, the live environment real users depend on.
| Environment | Who uses it | Data | Purpose |
|---|---|---|---|
| Development | One developer, on a laptop or a personal cloud space | Small, made-up | Write and try code quickly. |
| Test (or integration) | The team and the automated pipeline | Made-up or anonymised | Run automated and exploratory tests on the combined changes. |
| Staging (or pre-production) | Testers, product owners, sometimes selected customers | Production-like in shape and volume, never raw personal data without safeguards | Rehearse the release on a copy as close to production as possible. |
| Production | Real customers | Real | Run the business. Changes arrive only through the pipeline. |
The more environments differ from production, the more problems slip through: a test database with a hundred rows will not reveal the missing index that makes a query take forty seconds against ten million. Keeping environments alike is easier when they are built from the same container images and the same scripted configuration, a theme of infrastructure as code.
Continuous integration, delivery and deployment
Continuous integration (CI) is the practice of merging everyone's work into the shared main line frequently, at least daily, with an automated build and test run checking every change. It grew out of Extreme Programming in the 1990s. Its point is that integration problems are found within minutes, while the change is fresh in the author's mind, instead of in a painful "integration phase" weeks later. A team practising CI treats a failing build as an emergency: fixing it comes before any new work.
Continuous delivery (CD) extends CI so that the software is always releasable: every change that passes the pipeline could be put into production at the press of a button, and releasing becomes a routine business decision rather than a risky event. The approach was set out by Jez Humble and David Farley in their 2010 book Continuous Delivery. Continuous deployment goes one step further and releases every passing change automatically, with no human approval; teams that do this may deploy many times a day.
The automation that carries a change from commit to production is a pipeline. Because each step is automated and identical every time, releases stop depending on one person remembering a checklist at midnight.
Checklist: before a change reaches production
What a mature team's process checks, mostly automatically, for every change.
- The change is linked to a story or issueAnyone can later find out why it was made.
- It is smallSmall changes are easier to review, test, deploy and, if necessary, undo.
- A colleague has reviewed itThrough a pull request, with questions answered and comments resolved.
- Automated tests pass at every levelUnit and integration tests in CI, and the end-to-end checks that cover the affected journey.
- It ran in stagingThe same build, configuration mechanism and deployment steps as production.
- There is a way backA rollback or a feature flag can switch it off quickly if something goes wrong.
- Someone will notice if it breaksMonitoring and alerts cover what the change touches (Chapter 9, observability).
Ways of working: waterfall, Scrum and Kanban
Teams also choose how to organise the work itself. In a sequential approach, often called waterfall, the project moves through phases in order: requirements, design, implementation, testing, deployment. Each phase finishes before the next begins. The name is usually traced to Winston Royce's 1970 paper, although Royce did not use the word and in fact warned that a single pass through the phases was risky. Sequential plans still suit work where requirements are fixed and change is very costly, such as some regulated or hardware-dependent projects. Their weakness for most software is that users see nothing until late, so misunderstandings surface when they are most expensive to fix.
Agile approaches, named in the Agile Manifesto of 2001, deliver working software in short cycles and adjust plans as they learn. Scrum is the most widely used framework: a small team works in fixed cycles called sprints, of one month or less, each starting with planning and ending with a review of the work and a retrospective on how the team worked. The 2020 Scrum Guide defines three accountabilities: the Product Owner (deciding what is most valuable), the Scrum Master (helping the team use Scrum effectively) and the Developers (doing the work). Kanban, adapted for software from Toyota's production system, has no fixed cycles: work items flow across a visible board (to do, doing, review, done), and the team limits how many items may be in progress at once so that work is finished before more is started.
None of these is right for every team, and many organisations mix them: Scrum for product teams, Kanban for support and operations, a more sequential plan around a fixed regulatory deadline. Calling a process "agile" does not make it so; what matters is whether working software reaches real users often enough to learn from them.
DevOps: one team's responsibility from code to customer
Traditionally, developers wrote software and a separate operations team ran it, with conflicting incentives: developers were rewarded for change, operations for stability. DevOps is the movement, named at the first DevOpsDays conference in Ghent in 2009, that brings the two together. It is primarily a culture of shared responsibility ("you build it, you run it"), supported by practices such as CI/CD, infrastructure as code, monitoring and blameless review of incidents. It is not a job title or a tool, although both are sold under the name. The DORA research programme is best known for measuring delivery performance with four key metrics: how often a team deploys, how long a change takes to reach production, how often changes fail, and how quickly service is restored after a failure.
Worked example: the journey of one tap
Everything in this chapter comes together in a single moment. A customer has filled a basket in a food-delivery app and taps Place order. Within a second or two the screen says "Order confirmed", and twenty minutes later the phone buzzes: "Your rider is on the way". Here is what happens, step by step, with the concept behind each step and where it was introduced.
Step by step
- 0. The app builds the requestThe app is software on the phone (Section 0.1). It gathers the basket from its in-memory data structures (Section 0.2) and turns them into a JSON body: restaurant, items, delivery address, and a unique order key generated on the phone.
- 1. DNSThe app needs the address of
api.fooddelivery.example. The phone asks a DNS resolver, which returns an IP address, often from a cache (Section 0.4). - 2. HTTPSThe app opens a TCP connection and a TLS session, checking the server's certificate, then sends
POST /v1/orderswith an access token proving which customer is signed in (Sections 0.4 and 0.6). - 3. Load balancerThe request reaches a load balancer in the company's cloud region. It decrypts HTTPS and forwards the request to one of several healthy copies of the order service, running in containers across availability zones (Section 0.7).
- 4. API and paymentThe order service is reached through its REST API. Its code, organised into modules (Section 0.3), validates the basket, then calls the payment provider's web API to authorise the card, waiting synchronously with a timeout, because the customer must not be told "confirmed" until the payment is approved (Section 0.6). It sends the order key as an idempotency key, so that if the call is retried the card is charged only once.
- 5. Database transactionIn one ACID transaction the service inserts the order and its items and records the payment reference. Either all of it is saved or none of it is; once committed it survives a crash (Section 0.5). An index on the customer's ID will make their order history fast to load.
- 6. QueueThe service puts an
OrderPlacedmessage on a message queue and immediately replies201 Created. The app shows "Order confirmed". Everything else happens asynchronously (Section 0.6). - 7. Other services reactThe restaurant's tablet service, the rider-dispatch service and the notification service each read the message when ready. If one is briefly down, its messages wait in the queue.
- 8. Push notificationThe notification service reacts to
OrderPlaced, and later to a similar message when a rider is assigned, by calling the push service run by Apple or Google, another third-party API, which delivers the notification to the customer's phone. - 9. And behind it allEvery piece of code on this path was written from a user story, reviewed in a pull request, tested at several levels and released through a CI/CD pipeline, via staging, to production (this section).
Worked example
Worked example: what the journey teaches
Ask "what if?" at each step and the architectural questions appear.
| What if the payment call times out? | Did the charge happen or not? The idempotency key lets the order service safely ask again without charging twice. |
|---|---|
| What if the database commit fails after payment? | Payment and database are separate systems, so no single ACID transaction covers both. The design needs a way to refund or retry, a problem Chapter 6 solves with sagas. |
| What if the notification service is down? | Nothing visible breaks: orders are still taken, and messages wait in the queue. That is the reason for making this step asynchronous. |
| What if dinner-time traffic triples? | The load balancer spreads requests over more copies of the order service, added by autoscaling; the database is now the likely bottleneck. |
| What if a release breaks checkout? | Tests in the pipeline should catch it; if not, monitoring raises an alert and the team rolls back to the previous version. |
None of these questions is about how to write a function. They are about how the parts are arranged and what each part promises the others.
Why this matters for architecture
The journey of one tap uses a dozen components, three organisations and both synchronous and asynchronous communication, and it still had to be designed: someone decided that payment is synchronous and notifications are not, that orders live in a relational database, that the service runs in containers across two zones, that the payment provider is isolated behind one module. Deciding how these parts are arranged, which qualities each arrangement achieves and what it costs, and recording why, is software architecture. The way software is built matters to architecture too: an architecture that cannot be tested in pieces, or released without coordinating ten teams, slows every change. Chapter 1 begins with what software architecture is.
Check your understanding
What is the difference between continuous delivery and continuous deployment?
With continuous delivery every passing change can be released at the press of a button, but a person decides when. With continuous deployment every passing change is released automatically.
Why does the testing pyramid put unit tests at the base?
They are fast, cheap and reliable, so a team can run thousands on every change and get feedback in minutes. Slower, more fragile end-to-end tests are kept for the few journeys that need them.
In the journey of one tap, why is the payment call synchronous but the notification asynchronous?
The app must not say "confirmed" until the payment is approved, so it waits for that answer. The notification can be sent a little later, so it goes through a queue and its failure cannot block ordering.
What are the two main benefits of a pull request beyond catching bugs?
It spreads knowledge, because at least one other person understands each change, and it keeps the code's design consistent, because reviewers can question structural choices.
Sources and further reading
Have a question about this section? ArchitectBot, the course’s AI study assistant, answers from the course first.
Ask: “How does the way my team tests and deploys code affect the architecture we should choose?”