Skip to main content

Vertical Slices Over Horizontal Layers

What This Concept Is

A vertical slice is a unit of work that crosses every layer your system has but only addresses one small piece of user-visible behavior. A horizontal layer, by contrast, is a unit of work that finishes one layer across many behaviors before moving on.

Given the capstone's layers (API -> service -> repository -> database plus tests, migrations, and deploy config), the difference is:

  • Vertical: ship POST /tasks end-to-end, including its service method, its repository method, its migration, its test, and its deploy -- then move to the next feature.
  • Horizontal: design all the API endpoints, then all the services, then all the repositories, then all the migrations, then wire them together.

Vertical slicing is the default for modern delivery because it keeps a working system working. It also maps naturally onto trunk-based development, small PRs, and Fowler's keystone interface pattern: ship the one path that the next feature can be hung from.

A well-cut slice is independently shippable. That is the acid test. If cutting feature A into slices produces pieces that can be deployed in any order without leaving the system broken, the slicing is right. If one slice requires three others before it makes sense, you have not sliced; you have sequenced.

Why It Matters Here (In the Capstone)

Horizontal layering is seductive in a capstone because it feels organized. In practice it has three failure modes:

  • integration risk stacks up at the end, when debugging is hardest and deadlines are closest;
  • no feature is shippable until the entire stack is complete, so early demos are impossible;
  • mistakes in layer N are not found until layer N+2, because there is nothing exercising them.

Vertical slicing forces you to feel friction from every layer on every feature. That friction is information, not punishment. It tells you where the walking-skeleton baseline is weak and where your design has not met reality yet.

This module depends on the walking skeleton being already in place. Once the seams are proven, each new vertical slice adds a small piece of user-visible behavior through those seams, and each slice's value is measurable as something a user or test can observe.

Concrete Example(s) -- from a real capstone

Take the capstone REST service again. Two ways to build the first real feature, "create and list tasks":

Horizontal approach (discouraged):

  • Week 1: design every endpoint (/tasks, /users, /projects)
  • Week 2: implement every service class
  • Week 3: implement every repository
  • Week 4: run migrations, wire everything, fix what does not compile, then finally test

Vertical approach (encouraged):

  • Day 1: ship POST /tasks that writes one row and returns 201, plus one integration test
  • Day 2: ship GET /tasks that lists the rows, plus one test
  • Day 3: ship GET /tasks/{id}, plus one test
  • Day 4: ship an error case for POST /tasks with invalid input
  • Only then: move to users, or to projects, or to auth

After day 1 you already have a deployable system that does real work for one user story. After day 2 you have a demoable flow. If the deadline moves in, you have a shippable subset at every point -- a property that horizontal delivery cannot offer until the very end.

Common Confusion / Misconceptions

The first misconception is that vertical slicing means "skip design." It does not. The architecture and layering decisions (from Module 1) are exactly what make vertical slices cheap. A slice is small because the seams are in the right place.

The second is that a vertical slice must be trivial. A slice can be as narrow or as wide as is sensible, as long as it crosses every layer and produces observable behavior. "Add server-side validation to POST /tasks" is a legitimate slice.

The third is confusing vertical slicing with "one big pull request per feature." Slices are units of work, not units of merge. You can slice a feature into three commits or three PRs; the key is that each commit leaves the system in a shippable state.

The fourth is the "fake vertical" -- the slice crosses layers on paper, but some layers are stubs (the persistence layer writes to a dict in memory, the external call is mocked). That is still horizontal: the risky layer is not real yet.

How To Use It (In Your Capstone)

For every planned chunk of work, ask and answer these in order:

  1. Can I cut this into pieces that each cross every layer?
  2. Does each piece produce something a user (or a test) can observe?
  3. After each piece, is the main branch still deployable?
  4. Which piece do I ship first, and what does it prove?
  5. If I cannot ship this today, which piece is the next smallest one that still crosses every layer?
  6. Is the slice behind a feature flag (keystone interface) so the merge is safe even if the UI is incomplete?
  7. Did I update one test at each level exercised by the slice?

If a chunk cannot be sliced vertically, treat that as a design signal. Usually the layers are too coupled, the seams are in the wrong place, or the feature should be smaller.

A slice is "right-sized" when one person can finish it in a session (2-4 hours), it crosses every architectural layer the final feature will cross, it leaves main deployable, and it produces one observable outcome. If a slice is larger than a session, cut it. Common cut lines: split read from write, split happy path from validation, split persistence from presentation, split collaborators (auth first, feature behind auth).

Anti-Patterns to Recognize

  • Fake vertical. Persistence or external calls stubbed; the slice looks vertical but the risky seam is not real.
  • Refactor slice. An internal restructuring billed as a slice with no observable output. File as refactor work.
  • Shadow layer. Works in dev but not in staging because a deploy seam was skipped.
  • Too-narrow slice. So trivial it does not exercise the seams (constant-string endpoint).

See also (integrative)

External references:

Check Yourself

  1. Why does a horizontal approach concentrate risk at the end of the schedule?
  2. What is the minimum a vertical slice must produce, and what observable artefact proves it produced it?
  3. What does it mean if a feature resists being sliced -- is that a feature problem, a design problem, or both?
  4. How do you tell the difference between a real slice and a "fake vertical"?
  5. How does a feature flag or keystone interface let you ship slices before their consumers are ready?

Mini Drill or Application (Capstone-scoped)

  1. Take your capstone backlog and pick the first feature after the walking skeleton. In 20 minutes, cut it into at least three vertical slices, and write the user-observable behavior, layers touched, and one-sentence test for each slice.
  2. Commit the slice-plan to library/raw/slice-plan-<feature>.md in your repo so reviewers can see the sequencing.
  3. Ship slice one today. Prove with a staging deploy and a screenshot or response payload.
  4. Put the next two slices behind a feature flag (keystone interface style) and merge them before their UI is complete.
  5. At the end of the week, retrospect: did any slice expand beyond its budget? If yes, what seam forced the growth, and does it belong in the debt ledger (Concept 15)?

Source Backbone

Capstone implementation applies earlier code-quality, testing, and refactoring material. These books are the source backbone for that practice.