InterviewKata
Architecture styles · 16 of 16 · Putting it together

Choosing and combining styles

No real system is one style. This last page shows how Pedal's needs change from one city to thirty, how a weighted table of characteristics points to different styles at each stage, where such a table misleads, and how six styles end up combined in one system, each at the level where it pays for itself.

1. In one picture

A city isn't one kind of building. It has a stadium built for crowds, terraced houses built for families, a hospital built never to close, and a market built to change every week. Each was chosen for what it must do, and the roads between them follow their own rules. Nobody asks "what is the architecture of London?" and expects one answer.

Choosing a style is choosing which characteristics matter most (simplicity, independent releases, elasticity, fault isolation, ease of change, cost…) and accepting what the style costs elsewhere. The Software architecture track covers the method in depth: identifying characteristics (module 04), trade-off analysis (06), and choosing a style (17). This page shows it on Pedal, at two moments in its life.

Real systems combine styles at different levels: one style for how the system is deployed, another for how each deployable is organised inside, another for how parts talk. The skill is naming which style applies where, and why.

LevelQuestionStyles that answer it
Blast radiusHow much can one failure take down?Cell-based
DeploymentHow many things do we release, and who owns them?Monolith, service-based, microservices, serverless
CommunicationDo parts wait for each other?Client-server, broker, event-driven, peer-to-peer
Inside one deployableWhere do the rules live?Layered, hexagonal, clean, microkernel
Special pathsDoes one flow need its own shape?Pipes and filters for reports, space-based for surges

2. Pedal built this way

Pedal at launch is one team in one city: a modular monolith, hexagonal inside, is plenty. Three years later it runs 30 cities with eight teams. Here's the combination that fits that Pedal, level by level.

Notice what didn't happen: Pedal never became microservices everywhere. The monthly reports stayed a pipeline, the pricing service kept its plugins, and the docks' offline gossip only exists for the one situation that needs it. Each style is used where its cost buys something.

3. The rules

RuleWhy it existsBroken when…
Start from characteristics, not stylesThe style follows from what must be true, not from fashion."We're doing microservices" before anyone asked what must scale or release independently.
Name the style at each levelEveryone knows which rules apply where.Half the team thinks it's a monolith, the other half thinks it's services.
Spend complexity only where it paysExpensive styles go to the parts that need them.Space-based machinery for the admin dashboard.
Record the decision and its triggersThe next team knows why, and when to revisit.Nobody remembers why there are 40 services.

4. How it goes wrong

Copying a famous companyTheir forces (thousands of engineers, billions of requests) aren't yours.
One style for everythingForcing reports, surges and plugins all into the same shape.
Treating a scoring table as an answerIt's a conversation starter; the lab below shows where it misleads.
Never revisitingForces change as the product, team and traffic grow; the right style at launch is rarely right at year three.

5. The code

A small weighted decision table: our own 1–5 ratings of eight deployment-level styles against six characteristics, scored for three moments in Pedal's life. Change the weights and the ratings, and argue with them.

from __future__ import annotations

CHARS = ["simplicity", "independent releases", "elasticity", "fault isolation", "ease of change", "low cost"]

# Our own 1-5 ratings: a starting point to argue with, not a fact.
RATINGS = {
    "modular monolith": [5, 2, 2, 2, 3, 5],
    "microkernel":      [4, 3, 1, 2, 4, 4],
    "service-based":    [3, 3, 3, 3, 3, 3],
    "microservices":    [1, 5, 5, 4, 5, 1],
    "event-driven":     [2, 4, 4, 4, 4, 2],
    "serverless":       [3, 4, 5, 3, 3, 4],
    "space-based":      [1, 2, 5, 3, 2, 1],
    "cell-based":       [1, 3, 4, 5, 2, 1],
}


def rank(weights: list[int]) -> list[tuple[str, int]]:
    scores = {style: sum(w * r for w, r in zip(weights, rs)) for style, rs in RATINGS.items()}
    return sorted(scores.items(), key=lambda kv: (-kv[1], kv[0]))


def show(stage: str, weights: list[int]) -> None:
    top = rank(weights)[:3]
    important = [c for c, w in zip(CHARS, weights) if w >= 4]
    print(f"{stage} (cares most about {', '.join(important)}): " + " > ".join(f"{s} {n}" for s, n in top))


if __name__ == "__main__":
    show("Pedal at launch, one team", [5, 1, 2, 1, 3, 5])
    show("Pedal in 30 cities, 8 teams", [2, 5, 4, 4, 4, 2])
    show("Festival surges matter most", [2, 2, 5, 3, 2, 2])
const CHARS = ["simplicity", "independent releases", "elasticity", "fault isolation", "ease of change", "low cost"];

// Our own 1-5 ratings: a starting point to argue with, not a fact.
const RATINGS: [string, number[]][] = [
  ["modular monolith", [5, 2, 2, 2, 3, 5]],
  ["microkernel", [4, 3, 1, 2, 4, 4]],
  ["service-based", [3, 3, 3, 3, 3, 3]],
  ["microservices", [1, 5, 5, 4, 5, 1]],
  ["event-driven", [2, 4, 4, 4, 4, 2]],
  ["serverless", [3, 4, 5, 3, 3, 4]],
  ["space-based", [1, 2, 5, 3, 2, 1]],
  ["cell-based", [1, 3, 4, 5, 2, 1]],
];

function rank(weights: number[]): [string, number][] {
  const scores: [string, number][] = RATINGS.map(([s, rs]) => [s, rs.reduce((t, r, i) => t + r * weights[i], 0)]);
  return scores.sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]));
}

function show(stage: string, weights: number[]): void {
  const top = rank(weights).slice(0, 3);
  const important = CHARS.filter((_, i) => weights[i] >= 4);
  console.log(`${stage} (cares most about ${important.join(", ")}): ` + top.map(([s, n]) => `${s} ${n}`).join(" > "));
}

show("Pedal at launch, one team", [5, 1, 2, 1, 3, 5]);
show("Pedal in 30 cities, 8 teams", [2, 5, 4, 4, 4, 2]);
show("Festival surges matter most", [2, 2, 5, 3, 2, 2]);
import java.util.ArrayList;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class Main {
    static final List<String> CHARS = List.of("simplicity", "independent releases", "elasticity", "fault isolation", "ease of change", "low cost");

    // Our own 1-5 ratings: a starting point to argue with, not a fact.
    static final Map<String, int[]> RATINGS = new LinkedHashMap<>();
    static {
        RATINGS.put("modular monolith", new int[]{5, 2, 2, 2, 3, 5});
        RATINGS.put("microkernel", new int[]{4, 3, 1, 2, 4, 4});
        RATINGS.put("service-based", new int[]{3, 3, 3, 3, 3, 3});
        RATINGS.put("microservices", new int[]{1, 5, 5, 4, 5, 1});
        RATINGS.put("event-driven", new int[]{2, 4, 4, 4, 4, 2});
        RATINGS.put("serverless", new int[]{3, 4, 5, 3, 3, 4});
        RATINGS.put("space-based", new int[]{1, 2, 5, 3, 2, 1});
        RATINGS.put("cell-based", new int[]{1, 3, 4, 5, 2, 1});
    }

    static List<Map.Entry<String, Integer>> rank(int[] weights) {
        List<Map.Entry<String, Integer>> scores = new ArrayList<>();
        RATINGS.forEach((s, rs) -> { int t = 0; for (int i = 0; i < rs.length; i++) t += rs[i] * weights[i]; scores.add(Map.entry(s, t)); });
        scores.sort(Comparator.comparing((Map.Entry<String, Integer> e) -> -e.getValue()).thenComparing(Map.Entry::getKey));
        return scores;
    }

    static void show(String stage, int[] weights) {
        List<String> important = new ArrayList<>();
        for (int i = 0; i < weights.length; i++) if (weights[i] >= 4) important.add(CHARS.get(i));
        String top = rank(weights).subList(0, 3).stream().map(e -> e.getKey() + " " + e.getValue()).collect(Collectors.joining(" > "));
        System.out.println(stage + " (cares most about " + String.join(", ", important) + "): " + top);
    }

    public static void main(String[] args) {
        show("Pedal at launch, one team", new int[]{5, 1, 2, 1, 3, 5});
        show("Pedal in 30 cities, 8 teams", new int[]{2, 5, 4, 4, 4, 2});
        show("Festival surges matter most", new int[]{2, 2, 5, 3, 2, 2});
    }
}

Output (the same in all three languages)

Pedal at launch, one team (cares most about simplicity, low cost): modular monolith 67 > serverless 61 > microkernel 59
Pedal in 30 cities, 8 teams (cares most about independent releases, elasticity, fault isolation, ease of change): microservices 85 > serverless 78 > event-driven 76
Festival surges matter most (cares most about elasticity): serverless 62 > microservices 61 > event-driven 56

6. Try it

Weigh the characteristics

Set how much each characteristic matters to you, or start from a preset. The bars rank eight deployment-level styles using our own 1–5 ratings (the same as the code above).

The ratings are our judgement for a typical team, and you should argue with them. A table like this compares broad properties; it can't see details such as "hot shared data" or "must work offline", which is exactly where the conversation has to go next.

7. What it costs, and when to pick another style

What you get

  • Each part gets the shape it needs: no forcing reports, surges and plugins into one mould.
  • Complexity where it pays: simple parts stay simple.
  • Evolvable: change the style at one level without redoing the others.

What you pay

  • More concepts: the team must understand several styles and where each applies.
  • Boundaries between styles need care: events in, calls out, translation at the edges.
  • Documentation matters: without a picture and decision records, nobody knows which rules apply where.
If…Consider instead
A new product, one teamModular monolith, hexagonal inside
Many teams that must ship independentlyMicroservices with events between
Small team, spiky and low trafficServerless
Many customers needing different behaviourMicrokernel
A failure must never reach everyoneCell-based around the rest

8. Signs you've outgrown it

9. Remember it and say it

Memory hook: "Pick for the forces, mix by level"Choose each style for the characteristics it serves, and use different styles at different levels of the same system.
How this hook works: what “Pick for the forces, mix by level” means

What the slogan means, phrase by phrase

“Pick for the forces”
Start from what must be true: releases, scaling, isolation, cost.
“mix by level”
Deployment, communication and inside-the-code styles are separate choices.

Why this picture fits: A city: a stadium for crowds, houses for families, a hospital that never closes, a market that changes weekly.

The story → the concept

The cityPedal at 30 cities
DistrictsCells by region
BuildingsRides, docks and billing services
The roadsEvents between services
Rooms inside a buildingHexagonal code inside each service

Watch it on an example

  1. At launch: a modular monolith scored highest for one team.
  2. At 30 cities and 8 teams: microservices with events, inside cells.
  3. Reports stayed a pipeline; pricing kept its plugins.

The trap it saves you from

Choosing a style because a famous company uses it.

It still works when…

For your own system: name the style at each level and the characteristic it serves, in one sentence each.

In an interview or design review

10. Check yourself

What should a style choice start from?

Styles are bundles of trade-offs; pick the bundle that fits.

Can one system be microservices, event-driven and hexagonal at once?

Deployment, communication and code organisation are separate choices.

A scoring table puts serverless first for festival surges. What should you ask next?

Hot shared data points to space-based; the table compares only broad properties.

When should a style decision be revisited?

Record the triggers with the decision.

11. Go deeper

ResourceWhy
Architecture 17: Choosing and combining stylesThe architect's method in depth.
Architecture 06: Trade-off analysisWeighing characteristics properly, beyond a simple table.
Architecture 27: Architecture decisions and ADRsRecording the choice and when to revisit it.
The comparison tableEvery style building Pedal, side by side.