DecompositionGENERALSTAGE-SPECIFICTEAM-SPECIFICILLUSTRATIVE

Decomposition by Layer

Frontend, backend, database is a true statement about the solution and an empty one about the problem: it fits every application, so it distinguishes none. It is still the right split in a few specific situations — when the product is already known and one layer is the problem.

The moveWorked exampleNext questions

The situation, the reflex, and why it stalls

Every lesson starts where being stuck starts: someone has a problem, and the first move that comes to mind feels like progress.

The question

When someone splits the problem into frontend, backend and database, what have they actually decided — and when is that the right split after all?

The situation

A teammate has already divided the store into three tickets: "build the database schema", "build the API", "build the UI". They look reasonable. You are assigned the API, and on opening the ticket you realise you cannot tell which endpoints to write, because nobody has said what a customer does.

The reflex

Accept the split and start on your layer. The schema is designable from the entity list, the API from the schema, the UI from the API. Each person has a clear lane and none of them has to wait for the others to finish thinking.

Why it stalls

Every product question arrives in every lane at once, unanswered. Does a cart survive logout? The schema person guesses a session id, the API person guesses a user id, the UI person guesses local storage, and the three guesses meet at integration.

What the reflex produces — and fails to produce
  • Every product question arrives in every lane at once, unanswered. Does a cart survive logout? The schema person guesses a session id, the API person guesses a user id, the UI person guesses local storage, and the three guesses meet at integration.
  • No lane can demonstrate anything. The schema has tables and no rows; the API returns fixtures; the UI shows fixtures. The store works nowhere until every lane is finished, so the first end-to-end test happens at the end, where a failure is most expensive.
  • The layers become the organisation — a database team, a backend team — and every later feature has to be negotiated across three tickets instead of built as one.
ProblemUnderstandRequirementsConstraintsUnknownsDecompositionSmallest StepModelExperimentObserveDebugLearnIterate

The move

Precisely enough to apply it to a problem you have never seen — not a slogan.

  • Recognise the layer split for what it is: a decomposition of the *solution*, valid for any web application and therefore carrying no information about this one. It answers "what will the code be made of?" — a question that becomes useful only after "what will it do?" has been answered.
  • Use it where the product behaviour is already fixed and the problem genuinely lives in one layer: a page that renders slowly, a query that is the bottleneck, a migration from one database to another. There the capabilities are known and stable, and "which layer?" is the right first cut.
  • Use it, cautiously, when the team is organised by layer and cannot be reorganised for this work. Then do it with a twist: define each capability first, and let each layer's ticket say which capabilities it serves, so integration is planned rather than hoped for.
  • Everywhere else, split by capability first and let layers appear inside each capability, where they belong (Decomposition by Capability).

What the layer split does and does not say

The comparison makes the point sharper than prose: the same three words are a fine answer to one question and a non-answer to another.

The same three words, two questions
As an answer to "what does the store do?"
"Frontend, backend, database." True, and true of the chat app and the URL shortener too. Nothing in it says a customer can add to a cart or that an order must not be paid twice.
As an answer to "where is the product page slow?"
"Measure the browser, the API and the query separately." Now the split is a plan: three probes, and the layer with the time in it is the one to open next.

A split is informative only when the pieces differ in what they tell you about *this* problem; against the product question all three layers say the same thing, against the localisation question they say different things.

When to split by layer

The decision below is the honest version of "never split by layer": it names the cases where layer-first is right, and what each costs.

Should the first cut be by layer?

Behaviour undecided (greenfield feature)

when Never as the first cut. Split by capability; layers appear inside each.

cost Specialists work outside their layer for a while; the folder structure argues with the tree.

Behaviour fixed, fault localised (slow page, failing query)

when Yes: one probe per layer, then open the layer that holds the time or the error.

cost Needs instrumentation in every layer; a young system may have to add it first.

Behaviour fixed, whole layer changing (database migration, UI rewrite)

when Yes for the layer being replaced, but verify with capability tests that cross it.

cost The capability tests must exist first; without them the migration is verified by hope (Incremental Migration in Design).

Team organised by layer, cannot change

when Compromise: capability tickets, per-layer subtasks, and a walking skeleton in the first days.

cost Coordination overhead on every capability; the skeleton is unglamorous work nobody wants to own.

A layer split that is legitimate, with testable leaves

Here the layer split is applied where it belongs — a localisation problem in a working store — and even there every leaf carries an observation. The root is a symptom, not a product; that is what makes the split appropriate.

Where is the product page slow?
The product page takes too long to appear
  • Browserthe time might be rendering, not fetching
    • Time from response to painttestable The browser's timing shows the gap between the last byte and first paint; large means the layer to open is this one.
  • APIthe handler might be doing more than one query
    • Handler time minus query timetestable A log line per request shows total time and time in the database; the difference is the handler's own cost.
  • Databasethe query might be scanning
    • Query plan for the product lookuptestable The plan shows an index lookup, not a sequential scan, and the measured query time is a small share of the request.

The layer that holds the time is opened next; the others are closed. This is a layer split used as a search, which is the use it is good for.

How to do it

Most important first.

  • When handed a layer split, ask which capability each ticket is for. If the answer is "all of them", the tickets are the whole project three times.
  • Check whether the product behaviour is already decided and stable. If it is not, the layer split is premature regardless of who proposed it.
  • If the problem is "this one thing is slow or broken", a layer split is a diagnostic tool: which layer is the time in? Use it, with measurement (Measure Before You Optimize in Performance).
  • If the layer split must stay for organisational reasons, insist on one thin capability crossing every layer early — a walking skeleton — so integration happens in the first week, not the last (The Walking Skeleton).

Worked on a concrete problem

The move has to produce something. This is what it produced.

  • The three tickets, reread: "build the database schema" contains products, carts, orders, payments and inventory, and cannot decide whether a cart is a table without a product decision. "Build the API" cannot list endpoints without knowing whether checkout is one call or three. "Build the UI" cannot design the cart page without knowing whether a guest has a cart. Every ticket is blocked on the same unasked questions.
  • Where the layer split is right: after the store is live, "the product page takes too long to appear". The behaviour is fixed. Now "which layer?" is exactly the question — is the time in the browser rendering, in the API, or in the query? — and the split by layer is a measurement plan, one probe per layer.
  • Where it is tolerable: the team is a frontend pair and a backend pair who will not be reorganised. Then: define "browse, add to cart, checkout" first; give each pair a ticket per capability rather than one ticket per layer; build browse across both pairs in the first days so the seam between them is exercised before anything heavy sits on it.

How you know it worked

What now exists that did not before, and what question you can now ask.

  • You can say, for each layer ticket, which capability it is part of — or you have converted the layer tickets into capability tickets.
  • Something end to end works before any single layer is finished.
  • When the layer split is used, it is because the behaviour was already fixed and the problem was located in one layer.

The questions you can now ask

The field this whole domain exists for. After this lesson, these are the questions to put to an unfamiliar problem.

Next questions
  • ?Is this split telling me anything about *this* product, or would it be identical for any application?
  • ?Is the behaviour already decided and stable, so that the interesting question really is which layer?
  • ?If the team is organised by layer, what is the thinnest capability that crosses every layer, and can it be built first?

What can go wrong

How the move itself fails
  • Rejecting the layer split dogmatically where it was the right one: a performance investigation where "which layer is the time in?" was the only useful first question.
  • Accepting it because it matches the org chart, and then discovering the org chart at integration.
  • A hybrid that is neither: capability tickets whose acceptance criteria are still "the endpoint exists", so the layers came back through the definition of done.
What the move costs
  • Capability-first cuts across specialist skills; a database expert working on "add to cart" spends time on a button. Layer-first keeps specialists in their layer at the price of late integration.
  • The measurement use of the layer split — which layer is slow — needs instrumentation in every layer, which a young system may not have.
Misreads
  • "Layers are bad." Layers are how the solution is built; every capability contains them. The lesson is about which split comes *first*, not whether layers exist (Transport, Application, Domain, Infrastructure in Backend teaches the layers themselves).
  • "The layer split is fine if the schema is done carefully." The schema cannot be done carefully without the product decisions, and the product decisions come from capabilities. Care does not substitute for order.

Where this applies

Problem-solving advice is stated as universal far more often than it is. These labels say what each method is specific to — and where CONTESTED appears, the note gives the strongest form of the opposing view.

  • GENERALThat a layer split carries no product information holds for any product-shaped problem; what changes is whether the product behaviour is already fixed, which is what makes the layer split legitimate.
  • STAGE-SPECIFICGreenfield: the layer split is almost always premature because the behaviour is not decided. Existing system with a localised problem — slow page, failing query, migration — the layer split is the natural first cut because the behaviour is fixed and the question is where the fault lives.
  • TEAM-SPECIFICTeams organised by layer will keep proposing the layer split; the workable compromise is capability tickets with per-layer subtasks and a walking skeleton first, which a solo builder never needs.
  • ILLUSTRATIVEThe three tickets, the two pairs and the slow product page are invented to show where the split helps and where it hides the problem.

Where the depth lives

This domain asks the question and hands the answer off by name.