Digital and cloud-scale architecture stage summary
This stage takes the patterns from the applied stage and asks the operational question about each of them: how do you build it so it can be tested, run it so you can see inside it, change it without breaking the people who depend on it, and govern it without becoming the bottleneck. It covers hexagonal and clean architecture, the serverless and edge execution models, observability and site reliability engineering, deployment strategies, integration across team boundaries, architecture governance, and structured trade-off analysis.
One argument runs through all seven modules. Every decision in this stage is a trade, and the professional move is to make the trade explicit rather than to pretend one option is simply better. Dependency inversion buys testability and costs an interface. Serverless buys elasticity and costs control and steady-state money. Canary buys real user signal and costs the traffic volume needed to see it. Federated governance buys coherence and costs some team autonomy. A chaos experiment buys evidence and costs the preparation that makes it safe. The engineer who can name what was gained, what was accepted as worse, and the threshold at which the trade would change is the one whose architecture survives contact with production.
The sections follow the teaching order of the stage, two per module, so you can read straight through to rebuild the stage in your head or jump to the concept you need. Each section links back to its module for the full treatment.
What you carry out of this stage
- Draw the boundary of a service as ports and adapters, say which side owns each interface, and use that to run the whole domain test suite without starting a database
- Place a given rule in the correct ring of clean architecture, and spot the framework annotation on a domain entity that shows the dependency rule is being broken
- Decide between an edge function, a central serverless function and a long-lived server for a specific unit of work, naming the workload shape, the cold start exposure and the execution limits that drive the choice
- Take a latency complaint in a system of several services and say which observability signal answers it, why per-service metrics can all look healthy while the end-to-end path is slow, and what a trace waterfall would show
- Write an SLI, an SLO and the error budget it implies, convert the objective into allowed failure minutes, and explain why an objective set above the measured baseline governs nothing
- Choose between blue-green, canary and rolling deployment from the reversal cost of the change, and sequence a schema migration so that rollback stays possible
- Design a cross-service operation that survives retries and crashes, using a saga with compensating transactions, a transactional outbox and caller-generated idempotency keys, and say which consistency guarantee you have given up
- Set up governance that scales: what an architecture review board should and should not see, which rules belong in a fitness function that fails the build, and what TOGAF supplies as vocabulary rather than as compliance
- Design a first chaos experiment with a steady state, a numeric hypothesis, a minimal blast radius and a written abort condition, and name the prerequisites that must exist before it runs in production
- Record an architectural decision in ATAM terms by naming its sensitivity point, its trade-off point and its risk, in a form a colleague can audit a year later
A port is an interface the application owns, and that ownership is the whole pattern
Alistair Cockburn published hexagonal architecture in 2005 under the subtitle ports and adapters. The hexagon carries no special meaning beyond signalling that no side is privileged: there is no top for the user interface and no bottom for the database. Driving adapters initiate work against the application, and an HTTP controller, a command line tool and a test case are all driving adapters calling the same driving port. Driven adapters are the things the application calls out to: a relational repository, a message publisher, a text-message gateway.
The move that distinguishes the pattern from ordinary dependency injection is ownership of the interface. In a conventional layered application the data access layer defines the repository interface and the business logic imports it, so the dependency runs from the domain towards the infrastructure. In hexagonal architecture the application declares the repository interface in its own package, and the database adapter implements an interface it does not own. The arrow is reversed, and that is the structural difference, not the shape of the diagram.
Two consequences follow immediately. A test harness is architecturally the same kind of thing as the production controller, which is why test-first work is natural here rather than bolted on, and an in-memory implementation of a driven port is a real adapter rather than a mock. The check that tells you whether a codebase actually does this is blunt: try to run the entire domain test suite with no database and no broker running. If you cannot, some adapter detail has leaked through a port, and the leak is usually visible in the imports of a single file.
Hexagonal architecture: domain at the centre, ports out, adapters outside
Every arrow runs inward, adapters into ports and ports into the domain, and none runs outward, so the Postgres adapter can be replaced without editing a domain that knows only its ports.
Adapters depend on ports; ports depend on the domain; the domain depends on nothing outside itself. Source: Cockburn 2005 Hexagonal Architecture; Martin 2017 Clean Architecture; Vernon 2013.
Four rings, one rule: source code dependencies point inwards only
Robert Martin's clean architecture, set out in a 2012 article and expanded into the 2017 book, synthesises hexagonal architecture with Jeffrey Palermo's onion architecture from 2008 and adds a single overriding constraint. The rings run from the innermost outwards: enterprise business rules as entities, application business rules as use cases, interface adapters as controllers, presenters and gateways, and frameworks and drivers on the outside. Stability decreases as you move out, which is why the volatile things live where replacing them is cheap.
The dependency rule is absolute. Nothing in an inner ring imports anything from an outer ring. An entity never imports a controller; a use case never imports a database library or a framework annotation. Where a rule belongs follows from the rule itself. A constraint that holds regardless of how the application is invoked, such as an account balance not falling below its overdraft limit, is an entity invariant. Enforcing it only in a controller means it holds for one entry point, and enforcing it only as a database constraint means it holds only once the transaction reaches the database.
The reliable tell that a codebase claims this pattern without practising it is persistence or serialisation annotations decorating domain entities. Those are adapter concerns wearing domain clothing, and they are why the domain cannot be compiled or tested without the framework present. Removing them is usually a small change with a large consequence: the inner rings become independently compilable, and the tests that cover business rules stop needing infrastructure to run.
Clean Architecture: dependencies always point inward
The single arrow runs from frameworks and drivers on the outside to entities at the centre and never the other way, so the enterprise rules survive a change of database or web framework untouched.
Outer layers depend on inner; the entities at the centre know nothing about frameworks at the outside. Source: Martin 2017 Clean Architecture ch.22; Cockburn 2005.
Serverless is a billing and operations model, and it has hard edges
Functions as a service means the provider owns the servers, the operating system, the scaling and the patching, and you supply a function that runs in response to an event. Billing is per invocation and per unit of execution time, so idle capacity is not charged for. That is the entire economic argument, and it is a real one for work that arrives in bursts. Servers still exist; you simply cannot see, patch or tune them, which is the operational half of the same trade.
The execution model is stateless by necessity rather than by preference. Anything that must survive between invocations has to be externalised to a database, cache or object store, because that constraint is exactly what lets the provider route the next invocation to any available container. The hard edges follow from the same design. A single AWS Lambda execution is capped at fifteen minutes, so long-running transcoding or training work needs a different execution model or an orchestrated workflow. A cold start happens when no warm container is available, adding a fraction of a second for interpreted runtimes and rather more for a standard JVM; provisioned concurrency removes it by paying to keep containers warm. Vendor coupling lives in the event contracts, not in the deployment tool, so a framework that abstracts deployment does not abstract the lock-in.
The consequence for design is to look at workload shape before anything else. Per-invocation billing wins on spiky, irregular, event-driven work, where provisioning for the peak would mean paying for it all year. It loses at constant high throughput, where you pay a premium for elasticity you are not using and a long-lived instance is cheaper. Work out the crossover for your own memory setting and average duration rather than repeating a figure from a conference talk, and be honest that operational simplicity has value in its own right for a small team.
A managed service splits responsibility between provider and you
Every row splits into what the provider owns and what you still own, and the application row shows nothing on the provider side, so a managed platform moves the operational work without moving the accountability.
Managed services move responsibility; they do not remove it. Data and Application stay with you. Source: AWS Shared Responsibility 2023; NIST SP 800-145; CNCF Cloud Native Trail Map.
Edge functions buy distance and pay for it in what they can run
Edge computing runs code at points of presence close to the user instead of in one central region. The gain is physical: a request from Australia to a European region spends hundreds of milliseconds crossing the planet before any compute begins, while a function at a local point of presence answers within the local network. For work that happens on every single request, such as authentication checks, routing decisions and personalisation headers, that difference is felt directly by the user.
The cost is the runtime you get. Edge platforms execute in lightweight JavaScript isolates rather than full server processes, which means no file system or process modules, a very small CPU budget per request, and no direct database connections. Edge functions are stateless by design and call origin services when they need data. That makes them well suited to decisions and poorly suited to the work behind those decisions, and it is why an edge tier is usually a thin layer in front of something else rather than a replacement for it.
The largest latency win at the edge is often not compute at all but cache: content served from a content delivery network never reaches the origin, so it never pays for compute or for the round trip. The practical consequence is to choose the execution model per unit of work rather than per application. A single well-built system routinely uses all three: cache and edge functions for per-request decisions, central serverless for bursty event handling, and long-lived servers for the steady core.
Serverless and edge are different trades on latency and cold start
The only two axes are latency sensitivity and cold-start sensitivity, so an edge function on Cloudflare Workers and a bursty workload on default Lambda sit in opposite cells, and a choice made on price alone will land in the wrong one.
Edge wins when both latency and cold start matter; bursty serverless wins when neither does. Source: AWS Lambda 2023; Cloudflare Workers; Fastly Compute@Edge.
Metrics say something is wrong, traces say where, logs say what
Observability is the ability to infer the internal state of a system from its external outputs, and the working test is whether you can answer a question nobody anticipated without shipping new code to add logging. The three pillars divide the work. Logs are timestamped records of discrete events and answer what happened at a moment. Metrics are numeric measurements aggregated over time and answer how a service has been behaving. Traces are end-to-end records of one request as it crosses services and answer where in the path the time went.
They are a sequence rather than alternatives, and the reason is visible in any request that crosses several services. Each service can report a healthy error rate and a normal average latency while the end-to-end experience is terrible, because the wait is sitting inside one dependency that is slow rather than failing. A trace attaches an identifier at the first hop and propagates it, and each service contributes a span, so the waterfall shows the one span consuming the time. Correlating five separate log streams by timestamp to reach the same conclusion is the work that turns a two-minute diagnosis into a forty-minute one.
Structured logging is the join between the pillars. A log line written as a sentence cannot be queried; a record carrying fields for the identifiers that matter, including the trace identifier, can be filtered and correlated, and takes you from an error straight to the trace it belongs to. OpenTelemetry, the Cloud Native Computing Foundation project that resulted from merging OpenTracing and OpenCensus, is the vendor-neutral way to emit all of this: instrument once, and change the backend by changing an exporter rather than by re-instrumenting the estate. If localising a fault across services routinely takes tens of minutes, the missing capability is tracing, and more logging will not supply it.
Trace waterfall localises the fault to the longest span
Five spans are drawn to scale across a 1000 ms request and the slow DB query fills most of it, so tuning the other four spans away to nothing would still leave the request taking most of the time it takes now.
The waterfall tells you which span to investigate first; the gaps tell you what the system was waiting on. Source: OpenTelemetry trace spec 1.27; Beyer et al. 2018 SRE Workbook ch.16; Sigelman et al. 2010.
An objective is only real if the error budget can stop a release
Three terms sit in a hierarchy and are constantly confused. A service level indicator is the thing you measure, such as the proportion of requests that return success within a stated latency. A service level objective is the target for that indicator over a defined window. A service level agreement is a contractual commitment with a penalty attached, and it should always be looser than the objective you run to internally. The rate, errors and duration triple gives you indicators at every service entry point, which is where the measurement usually starts.
The error budget is the failure the objective permits, and it converts an argument into arithmetic. Over a thirty-day window, an objective of 99.9 percent allows 43.2 minutes of failure, 99.99 percent allows a little over four minutes, and 99.999 percent allows about 26 seconds. That arithmetic reframes incidents: a single event that fails 0.08 percent of the month's requests has consumed 80 percent of a 99.9 percent budget, and the remaining fifth of the month should be spent differently from the first four fifths.
Two consequences change what you would actually do. Set the objective just above the measured baseline and tighten it as reliability improves, because an objective the service has never met does not drive reliability, it drives arguments about the measurement. And give the budget real governance weight: if the budget is exhausted and risky deployments carry on regardless, the objective is a dashboard decoration rather than a control, and the reliability conversation goes back to being a matter of who argues hardest.
Three observability pillars feed three SRE practices
Three downward arrows turn each pillar into the practice beneath it, logs into an SLI, metrics into an SLO and traces into an error budget, leaving any telemetry with no arrow out of it as stored data rather than a decision.
Pillars are inputs; practices are decisions; without the arrows the data is dashboarded but not used. Source: Beyer et al. 2016 SRE; Beyer et al. 2018 SRE Workbook; Majors et al. 2022.
The strategy decides how quickly you can be wrong and still recover
Blue-green keeps two production environments, validates the idle one while the live one still serves traffic, then switches everything at the load balancer in one move. Rollback is a traffic redirect rather than a pipeline run, which makes it the fastest reversal available for application code. The price is double infrastructure through the transition, and the validation has to happen before the switch rather than after it, which is the part teams skip when they are in a hurry.
Canary sends a small share of traffic to the new version, holds it for an observation window, and promotes or reverses on the same indicators that define the objective. It produces real user signal at a small blast radius, and it works best with automated gates on error rate and tail latency rather than a person watching a chart. Its limitation is statistical: at one to five percent of traffic you need enough requests for a rare error rate to be measurable at all, so a low-traffic service needs a much longer window than a busy one. Rolling deployment replaces instances in batches with health checks between them, needs no extra infrastructure, and is the default in most orchestrators.
Rolling carries one non-negotiable precondition. During the rollout window the old and new versions serve traffic side by side, so every change must be backwards compatible in both directions. Renaming a field in a response part way through a rollout means some consumers receive the old shape and some the new one, and the failures are often silent rather than loud. The rule that follows is to match the strategy to the reversal cost: routine compatible changes roll, and changes that would be expensive or impossible to undo go blue-green or canary.
Deployment strategies sit on a 2x2 of user risk and operational cost
The map runs user risk down and operational cost across, prices every strategy twice, and separates a big-bang deploy that is cheap to run and expensive when it breaks from blue/green that buys low risk by paying for two environments.
Pick the strategy whose risk and cost you can absorb; canary is the balanced default. Source: Humble and Farley 2010; Beyer et al. 2018 SRE Workbook ch.16; DORA 2023.
Deployment and release are two decisions, and the schema is the one rollback cannot save
Feature flags separate shipping code from exposing behaviour. The new path goes to production disabled, and a flag decides whether it runs. That enables dark launching to internal users, progressive exposure to a growing share of customers, and a kill switch that takes effect as a configuration change rather than a pipeline run. After a change has already reached everyone, flipping a flag is the fastest reversal there is, which is precisely the moment a deployment strategy has run out of options.
Flags are also debt, and it accrues quietly. Every long-lived flag is a live branch in production code, and the combinations multiply faster than the tests do. The workable policy is two categories with different lifecycles: temporary release toggles that carry a removal deadline and are actually removed, and permanent operational toggles that have a named owner and a review. Teams that let the count drift into the dozens report that nobody can say with confidence what the system will do for a given user.
No deployment strategy protects the database. Blue and green share a schema, so a destructive migration makes rollback impossible even while application rollback remains instant, and this is where deployment strategies fail in practice rather than in theory. The safe sequence is expand then contract: apply a backwards-compatible schema change first, deploy code that tolerates both the old and new shapes, and remove the old shape in a later release once nothing reads it. Treat schema and code as separate releases every time, including the release that finally cleans up.
DORA four metrics form a delivery-stability feedback loop
Lead time and frequency measure delivery on the top row; change failure rate and MTTR measure stability below, joined by a dashed arrow, so shipping more often counts as an improvement only while the two stability metrics hold.
Track delivery and stability together; improving one alone is the warning sign that the loop is broken. Source: Forsgren et al. 2018 Accelerate ch.2; DORA 2023 State of DevOps.
Without a distributed transaction, correctness comes from sagas, outboxes and keys
When every service owns its own database there is no shared transaction coordinator, and any participant can fail independently at any point in a sequence. Two-phase commit was the traditional answer: a prepare round in which every participant confirms it can commit, then a commit round. It buys atomicity with distributed locks, and a participant that dies between the two rounds leaves the coordinator waiting or guessing. At throughput the locking costs more than the guarantee returns, which is why most distributed designs decline it.
A saga replaces one atomic transaction with a sequence of local transactions, each committing to a single database, linked by events or calls, with compensating transactions that undo the earlier steps when a later step fails. Choreography has each service react to events with no coordinator, which is simple and robust for short chains and becomes hard to reason about as the chain grows, because no single place holds the state of a job in flight. Orchestration puts the sequence and the compensation logic in one coordinator, which makes the flow visible and debuggable at the cost of a coupling point. Neither gives atomicity: intermediate states are visible to other readers, and the application has to tolerate that.
Two mechanisms carry the rest of the weight. The transactional outbox removes the dual-write problem by inserting the event row in the same database transaction as the business change, with a relay process publishing from the outbox and retrying until the broker confirms. That relay makes delivery at-least-once, so consumers must be idempotent by design rather than by luck. Idempotency keys do the same job on the request side: the caller generates one key for each logical operation and reuses it on every retry, and the server returns the stored result instead of repeating the effect. The blunt version of this lesson is that a retry policy without idempotency is a duplicate-charge policy that has not fired yet.
Five integration patterns, five coupling outcomes
Sync RPC, Async message, Publish-subscribe, Saga and Batch transfer are each read by their coupling outcome rather than their mechanism, so a periodic batch transfer is not the loose option it looks like: it trades coupling in time for coupling in cadence.
Pick the pattern by the coupling shape you need; async message is the default for inter-service integration. Source: Hohpe and Woolf 2003 EIP; Newman 2021 Building Microservices ch.4.
A published contract is a liability, and the migration has to be scheduled
Versioning exists so a contract can evolve without breaking consumers you do not control and cannot deploy. Adding an optional field is backwards compatible provided consumers are tolerant readers that ignore what they do not recognise, and that habit on the consumer side is what makes additive evolution possible at all. Removing a field, renaming one, changing a type or making an optional field required are breaking changes, and no amount of goodwill makes them safe to ship in place.
Versioning in the URL path is the most visible approach, but the large public APIs are a poor argument for it: GitHub versions through an X-GitHub-Api-Version header and Stripe through a date-based Stripe-Version header, both keeping the URL stable while the contract moves underneath. The mechanism matters less than the operational commitment behind it. Run at least two versions at once, and announce a deprecation with a migration window long enough for a consumer team to fit the work into a planning cycle rather than meet it as an incident, then withdraw the old version only once that window has run. Tell them in a way that does not depend on anyone reading a changelog, because the consumers you cannot deploy are exactly the ones who will not see it.
The reason to invest early is that integration effort grows faster than service count: five services have ten possible pairs and twenty services have one hundred and ninety. Idempotency, outboxes and contract discipline are cheap to establish while the count is small and expensive to retrofit across an existing mesh of live integrations. If a split is being considered, put the integration mechanics in place first, in the same way that governance is cheaper before fragmentation than after it.
Contract versioning rules: change, SemVer, evidence, consumer rule
A field can only be removed once the audit logs show that nobody is still reading it, and every change on the map carries the same three things: the SemVer step it forces, the evidence needed to ship it and the rule that protects consumers.
Every breaking change needs evidence and a consumer rule; without both the contract is no longer trustworthy. Source: Newman 2021 ch.4; Hohpe and Woolf 2003; SemVer 2.0.0.
Federated governance sets the guardrails and leaves the rest to the teams
Governance is what turns architecture from a recommendation into a constraint. It runs on a spectrum, and both ends fail in predictable ways. Centralised approval buys consistency and pays for it with a queue that slows every team down. Full decentralisation buys speed and pays for it with fragmentation, and the classic symptom is a dozen teams choosing four or more message brokers for individually sensible reasons, after which every cross-team integration needs its own client library, monitoring approach and operations runbook.
Federated governance sits between them: a small architecture function sets standards, approved choices and the non-negotiable constraints, and teams decide freely inside those guardrails. An architecture review board earns its place through scope rather than authority. It should see decisions that are expensive to reverse, that cross team boundaries, that change shared APIs or event schemas, or that touch security, compliance and data residency. It should not see refactoring, internal technology choices inside one team's context, or implementation detail that no other team can observe. A board that reviews everything becomes the bottleneck it was created to prevent.
TOGAF supplies the vocabulary at enterprise scale: four architecture domains covering business, data, application and technology, and the Architecture Development Method, a cyclical process running from a preliminary setup and an architecture vision, through the domain architectures, into opportunities and solutions, migration planning, implementation governance and change management, with requirements management running throughout. The 10th edition was published in 2022. The method is a reference cycle to tailor, not a waterfall to obey and not a standard to comply with. At smaller scale, take the domains, the governance concepts and the phase vocabulary, and run the full method only where cross-domain consistency has real business value.
TOGAF governance is a closed loop of four roles with different cadences
Four roles run at four cadences, from quarterly at the board to per incident in operations, and the dashed arrow returns incidents to the board, so a board sitting quarterly hears that a pattern failed only because operations reports it.
When any one role goes silent the loop breaks; the diagram makes the break visible. Source: TOGAF Standard 10th ed., Part V EA Capability and Governance, The Open Group 2022.
A rule nobody can bypass is a test in the pipeline, not a paragraph in a document
An architectural fitness function is an automated check that a system still satisfies an architectural characteristic, a term set out by Neal Ford, Rebecca Parsons and Patrick Kua in 2017. Its contribution is to move enforcement from a document and a periodic review to a check that runs on every commit and fails the build. A written standard depends on a reviewer remembering it under time pressure; a failing build does not depend on anyone's attention at all.
Two examples pay for themselves quickly. A dependency-direction check verifies that the domain never imports infrastructure, expressed as a rules library in some languages and as a short import-inspection script in others, which turns the dependency rule from the first two sections into something enforced rather than aspired to. A coupling check verifies that one service's database user has no access to another service's schema, which enforces data ownership at the point where it is normally violated quietly by a convenient query.
Fitness functions work best surrounded by lightweight instruments rather than heavy process. A technology radar sorted into adopt, trial, assess and hold, reviewed on a regular cadence, tells teams what is safe to reach for. Decision records reviewed in pull requests leave a searchable trail of why a choice was made, which makes the next decision better informed. Open architecture office hours catch problems early, because an engineer who would avoid booking a formal review will happily ask a question in a low-stakes session. The habit to adopt is to state each governance rule in the form that could fail a build, and to give the ones that genuinely cannot be automated a named owner and a review date instead.
Fitness functions turn governance into a closed loop
Define, automate, observe and review run as one cycle and the dashed arrow carries the review into the next iteration, so a rule wired into the pipeline is tightened or dropped on evidence rather than left unexamined.
Governance as code beats governance as document; every change earns a verdict on every iteration. Source: Ford, Parsons, Kua 2017 Building Evolutionary Architectures; Bass et al. 2021 ch.13.
A resilience claim you have not tested is a belief, not a property
Chaos engineering is the discipline of experimenting on a system in order to build confidence in its ability to withstand turbulent conditions in production. The word discipline is load-bearing: this is not breaking things at random. Netflix ran Chaos Monkey against production instances during working hours from 2011 and extended the idea to availability zone and region failure, on the explicit reasoning that a weakness is better discovered on a Tuesday afternoon with the team watching than at two in the morning during a real failure.
Every experiment has the same five parts. Define the steady state as a measurable description of normal, drawn from the same indicators that define your objectives. State a hypothesis specific enough to be wrong, with numbers for the expected fallback behaviour, the recovery time and the acceptable error and latency ceilings. Inject one failure with a defined scope and duration. Observe the indicators throughout. Conclude by comparing what happened with what you predicted. A rejected hypothesis is the more valuable outcome, because it has exposed a weakness under controlled conditions instead of during an unplanned outage.
The prerequisites are not optional extras. You need indicators and objectives with live monitoring to establish the steady state, distributed tracing to work out what is happening inside the system during the injection, an engineer on call for the window, a written abort condition whose threshold was decided before the experiment rather than during it, and stakeholders who know the experiment is running so that impact is not mistaken for an incident. Minimise blast radius: terminate one replica before you terminate a zone. Chaos on a system you cannot observe does not produce insight, it produces an incident with a nicer name.
A chaos experiment is a closed loop: hypothesis, scope, run, learn, repeat
The four phases run clockwise around a 2 by 2, from Hypothesis to Blast radius to Run to Learn, and the dashed arrow returns from Learn to the next hypothesis, so a fault injected without a named steady-state metric proves nothing about the system.
Without the loop closing back to the next hypothesis the run is just an outage rehearsal. Source: Rosenthal et al. 2020 Chaos Engineering; principlesofchaos.org.
Name the sensitivity point, the trade-off point and the risk, then write them down
The Architecture Trade-off Analysis Method, developed at the Software Engineering Institute, is first of all a risk identification method rather than a way of finding an optimal design. It gives three pieces of vocabulary worth using even when you never run the full workshop. A sensitivity point is a decision where a change moves one quality attribute sharply. A trade-off point is a decision where improving one quality attribute necessarily degrades another. A risk is a decision that may not survive the uncertainty in the requirements it was based on.
A worked example makes the three concrete. Calling a downstream service synchronously makes the caller's latency sensitive to the callee's response time, which is a sensitivity point: the caller is only ever as fast as its slowest dependency. Switching to asynchronous messaging improves the caller's latency and availability while giving up immediacy, since the downstream effect now lands seconds later, and that is a trade-off point rather than a free improvement. The risk is that asynchronous delivery needs retry handling and a dead-letter path, and that without them the message is dropped silently rather than loudly.
A full formal evaluation is proportionate for a small number of large decisions. For everyday ones, a decision record does the same job in three lines: what improves, what is accepted as worse, and the threshold at which the trade would need revisiting. That last line is the one most often missing and the one that matters most later, because it tells the next team what changed condition should reopen the question. An architecture with its trade-offs named is easier to maintain than one that was quietly optimised, because the next engineer can see which attribute was sacrificed on purpose.
A risk trade-off decision record has four named fields
Context, risk and trade-off record what was decided, and the Trigger row records what would make the team look again, so a record without it states a position that nothing in the future can dislodge.
Capture the trade-off explicitly; without the Trigger field the record cannot drive a future review. Source: Nygard 2011; ISO 31000:2018 Risk Management; Ford et al. 2017.
The traps this stage warns against
Claiming clean architecture while domain entities carry persistence and serialisation annotations, on the grounds that the folder structure is right.
Instead: The dependency rule is about imports, not folders. Run the domain test suite with no database, broker or framework container started. If it will not run, an adapter detail has crossed a port, and the annotation on the entity is usually where it crossed. Move persistence and serialisation concerns into adapters and make the check a fitness function so it cannot regress.
Adopting serverless for the operational relief, then discovering the bill at steady state.
Instead: Per-invocation billing is a bet on idle time. It pays on spiky, event-driven, irregular work and loses at constant high throughput, where a long-lived instance is cheaper for the same compute. Calculate the crossover for your own memory setting and average duration before committing, and price the fifteen-minute execution cap and the cold start exposure into the decision at the same time.
Setting a four or five nines objective as a statement of ambition when the service measures well below it.
Instead: Over thirty days, 99.99 percent allows a little over four minutes of failure and 99.999 percent allows about 26 seconds. From a baseline of a few percent failure, that budget is gone before the month starts, so the objective governs nothing and invites the measurement to be gamed. Set the objective just above the measured baseline, tighten it as reliability improves, and give the budget the authority to pause risky releases.
Treating blue-green as protection for a change that includes a schema migration.
Instead: Blue and green share the database, so instant application rollback does not survive a destructive migration. Expand then contract: ship the backwards-compatible schema change first, then code that reads and writes both shapes, then remove the old shape in a later release once nothing depends on it. Schema and code are separate releases, and the cleanup is a release of its own.
Adding an automatic retry policy to a cross-service call without an idempotency key, because the call looked safe to repeat.
Instead: A slow response is indistinguishable from a lost one, so retries against a healthy but slow service duplicate the effect. Generate one key per logical operation on the caller, reuse it for every retry of that operation, and have the server return the stored result for a key it has already seen. Apply the same reasoning to consumers, since an outbox relay delivers at-least-once by design.
Running a first chaos experiment against the whole component to make the test meaningful.
Instead: Minimise blast radius: one replica before one zone, and expand only after the smaller experiment has confirmed its hypothesis. Before anything runs in production you need indicators with live monitoring for the steady state, distributed tracing to diagnose during the injection, an engineer on call, a written abort threshold decided in advance, and stakeholders who know it is happening.
Standing up an architecture review board that reviews every technical decision so that nothing is missed.
Instead: A board that sees everything becomes the queue it was created to prevent, and teams route around it. Scope it to decisions that are expensive to reverse, that cross team boundaries, that change shared APIs or event schemas, or that touch security, compliance and data residency. Push the routine rules into fitness functions that fail the build, and leave internal choices inside a team's own context to the team.
Reading the TOGAF Architecture Development Method as a waterfall to complete, or as a standard to comply with.
Instead: The method is a cyclical reference process to tailor to the organisation, and its phases are a vocabulary as much as a sequence. Organisations running iterative programmes apply it in short cycles and re-enter at the vision phase when priorities change. At smaller scale, take the four domains, the governance concepts and the phase names, and keep the ceremony proportionate to the value of cross-domain consistency.
Core distinctions
- Driving port versus driven port: a driving port is the interface the application exposes to callers, and an HTTP controller, a command line tool and a test case are all adapters on that side; a driven port is the interface the application defines for the infrastructure it needs, and the database or broker adapter implements it
- Hexagonal and clean architecture versus layered architecture: the difference is ownership of the interface, not the shape of the diagram, because the application declares the driven port in its own package and the infrastructure implements an interface it does not own
- Serverless versus edge: central functions run for up to fifteen minutes on a general runtime with cold start exposure, while edge functions run in a lightweight isolate at a point of presence with a very small CPU budget and no direct database connection, so one is for work and the other is for decisions
- Indicator versus objective versus agreement: the indicator is what you measure, the objective is the internal target over a window with its implied error budget, and the agreement is the contractual commitment with a penalty, which should always be looser than the objective
- Metrics versus traces versus logs: metrics alert you that something is wrong, traces localise where in the request path the time or the failure sits, and logs tell you what happened at that point, which is why per-service metrics can look healthy while an end-to-end path is broken
- Blue-green versus canary versus rolling: blue-green reverses by redirecting traffic to an environment still running, canary limits exposure and promotes on measured indicators, and rolling replaces instances in batches and therefore requires backwards-compatible changes throughout the window
- Deployment versus release: deployment puts code in production, release exposes behaviour to users, and a feature flag separates them so that reversal after full rollout is a configuration change rather than a pipeline run
- Choreography versus orchestration: choreography has services react to events with no coordinator, which suits short chains, while orchestration puts the sequence and the compensation logic in one place, which is easier to observe and debug at the cost of a coupling point
- Saga versus two-phase commit: two-phase commit buys atomicity with distributed locks that fail badly when a participant dies mid-protocol, while a saga chains local transactions with compensations and gives eventual consistency in which intermediate states are visible
- At-least-once delivery versus idempotent processing: at-least-once is a property of the transport, which will redeliver after a crash, and idempotency is a property the receiver has to implement, usually with a stored result keyed by a caller-supplied identifier
- Centralised versus federated versus decentralised governance: centralised approval buys consistency and creates a queue, decentralisation buys speed and produces fragmentation, and federation sets standards and constraints centrally while leaving decisions inside the guardrails to teams
- Sensitivity point versus trade-off point versus risk: a sensitivity point is where one decision moves a quality attribute sharply, a trade-off point is where improving one attribute degrades another, and a risk is a decision that may not survive uncertainty in the requirements
- Testing versus chaos engineering: a test verifies that code does what it was written to do, while a chaos experiment verifies that the system behaves as predicted when the infrastructure around it fails, which is why it needs a steady state, a hypothesis and an abort condition rather than an assertion
That is stage 3 in one place. Ports and adapters with the application owning the interface, the dependency rule and the four rings, the serverless billing and operations trade with its fifteen-minute cap and cold starts, edge execution that buys distance and limits what can run, three observability signals that answer three different questions, objectives with error budgets that can actually stop a release, deployment strategies matched to reversal cost with schema handled as its own sequence, sagas with outboxes and idempotency keys in place of a distributed transaction, contracts versioned with a scheduled migration, federated governance backed by fitness functions rather than documents, and chaos experiments and trade-off vocabulary for testing and recording the decisions you have made. The stage scenario practice now puts that under pressure with realistic situations where two of these concerns pull in opposite directions, which is the form the exam takes and the form the job takes.
Sources and further reading
- Cockburn, A. Hexagonal Architecture (Ports and Adapters), 2005The primary source for driving and driven ports and for the claim that a test harness and a production controller are the same kind of adapter.
- Martin, R. C. Clean Architecture, 2017The four concentric rings and the dependency rule that source code dependencies point inwards only, synthesising hexagonal architecture and Palermo's onion architecture from 2008.
- AWS Lambda Developer GuideThe reference for the serverless execution model: per-invocation and per-duration billing, the stateless execution environment, the fifteen-minute execution limit, cold starts and provisioned concurrency.
- Cloudflare Workers documentationThe reference for the edge execution model: isolate-based runtimes, the restricted API surface, per-request CPU budgets and execution at points of presence rather than in one region.
- Beyer, B., Jones, C., Petoff, J. and Murphy, N. R. Site Reliability Engineering, 2016The source for indicators, objectives and error budgets as a governance mechanism, including the principle that an exhausted budget can stop a release.
- OpenTelemetry documentationThe vendor-neutral instrumentation standard behind the claim that changing observability backend should be an exporter change rather than a re-instrumentation project.
- Fowler, M. BlueGreenDeployment, 2010The original description of the pattern, including the point that the idle environment is validated before the switch and the guidance on sequencing database changes.
- Hodgson, P. Feature Toggles (aka Feature Flags), 2017The reference for toggle categories, their different lifecycles, and the management of toggle debt behind the two-category policy in this stage.
- Richardson, C. Saga pattern, microservices.ioThe canonical description of a saga as a sequence of local transactions with compensating transactions, in both the choreography and orchestration styles.
- Richardson, C. Transactional outbox pattern, microservices.ioThe source for writing the event record inside the same database transaction as the business change, and for the relay that publishes it with at-least-once delivery.
- The Open Group, TOGAF Standard, 10th EditionThe authoritative reference for the four architecture domains, the Architecture Development Method phases and the governance material used in this stage.
- Ford, N., Parsons, R. and Kua, P. Building Evolutionary Architectures, 2017The source of the fitness function as an automated, continuous and objective check on an architectural characteristic.
- Principles of Chaos EngineeringThe five principles used in this stage: hypothesis around steady state, vary real-world events, run in production, automate continuously, and minimise blast radius.
- Bass, L., Clements, P. and Kazman, R. Software Architecture in Practice, 4th editionThe reference for the Architecture Trade-off Analysis Method and its framing as risk identification, and for the vocabulary of sensitivity points, trade-off points and risks.