December 08, 2023
Imagine that you just learned that you’ll need to build a new feature: custom profile pages for users.
The UX and product teams provided a high fidelity mockup of the desired UI:
It’s a big feature, so you’ll need to slices it into smaller pieces first. But what are the right slices?
This is the art of story slicing. The size and nature of your slices can mean the difference between a smooth, consistent rollout of valuable work and hot mess.
Consider two types of slices: horizontal and vertical.
This new feature will touch several architectural layers of the application:
A horizontal slicing approach breaks down the work according to the architectural layers, such that each slice of work is only concerned with a single layer.
My bet is that horizontal slices are intuitive from a developer’s point of view. Developers are naturally aware of the architectural layers and the nature of the work required by each layer. In my experience, most developers will default to slicing work horizontally and sticking to one layer at a time.
For example, the first slice might make all of the database-related changes. The second slice adds application logic. The third slice builds the JSON API layer, and so on. After all slices are shipped, the feature is complete and ready for users!
Each of these horizontal slices is limited to a single architectural layer.
Horizontal slices require less context switching, or jumping between different types of changes (database vs. application code vs. UI) in the same slice.
It also lets developers with special skills use their strengths, e.g., the team’s “frontend specialist” can focus on the UI layer.
If the interactions between the layers are very clear and well-understood, it’s possible to parallelize horizontal slices. For example, one pair tackles the database changes, while another pair writes the backend logic, and another works on the frontend UI, so that all horizontal slices are completed simultaneously.
Horizontal slicing assumes that the team really does understand how all of the layers will fit together. Ideally, when the database changes and the application code changes are complete, they will “just work.”
But, if the team is wrong and the slices don’t “just work” together, they won’t discover this until very late in the project, leading to unplanned “rework” and “bug fix” changes after the planned horizontal slices are shipped.
Example: When we were finally about to ship the UI piece, we realized that we actually needed a different database schema to support a critical part of the UI. Now we’re blocked by the database change, but that also means we’ll need to update the application code, too. So, we’ve added on another database slice and an application slice before we can finish the UI.
If teammates only pick up changes that fit their existing strengths, the rest of the team may miss out on an opportunity to build new skills.
Example: Most projects have some database slices, application slices, and UI slices. Pat always does the database cards, Charlie always writes the application code, and Sam sticks to the UI parts. Everyone tends to stay in their favorite areas and avoids learning about new things.
If horizontal slices are parallelized, developers working on different slices must frequently communicate to keep the layers “in sync” during development.
Example: We’ve distributed the slices across several dev pairs, but let’s check in daily to make sure that the database changes match the app changes and the API spec.
In the example feature above, the end user doesn’t receive any value until every horizontal slice is complete. From the perspective of the user, the feature is non-existent until the completion of all of the dev work. The user is unable to use any part of the feature and can’t provide any feedback.
Example: We put a lot of work in this sprint to complete the database schema needed to support user profiles. Nobody outside of the dev team cares about the database schema, so there’s nothing to demo today. But we swear it’s important!
A vertical slicing approach breaks down the work by functionality or value (to the user).
Vertical slices often combine multiple architectural layers to deliver a meaningful piece of functionality.
With each completed vertical slice, something useable/valuable should be delivered to the end user.
Vertical slicing is more intuitive to a customer or end user — the slices align with functionality that the user needs, not the technical layers behind it.
Each of these vertical slices delivers something useful to the end user, but every slice could require small, coordinated changes at every layer: database, application, API, and UI.
The first vertical slice requires the developers to integrate multiple layers. If there’s a bad assumption about how the layers would fit together, it will uncovered very early.
Also, because vertical slices can get demo-able features in front of users and stakeholders earlier, they also uncover bad assumptions about how the feature should work or look.
The end user (and product owners) can see tangible improvements in functionality with every vertical slice, rather than waiting for every horizontal slice to be completed. They can also give feedback earlier, because features are useable (or at least demo-able) earlier.
Developers need to work across multiple layers to complete every slice. A developer who prefers working on frontend UI might need to work on the database layer. This might not play to their strengths, but it gives them valuable exposure to different parts of the system and builds a broader skillset.
Defining a vertical slices forces the dev team to frame their work in terms of the value it delivers — using the language that stakeholders and users care about — rather than technical jargon.
Vertical slices combine multiple layers, so even small pieces of functionality can feel complex. Vertical slices often need to be very “thin” (adding very small increments of functionality) to avoid becoming overwhelming.
Example: A single vertical slice could require pull requests for multiple layers: database, application backend, and application front-end. To make this manageable, we’ll focus on a small portion of the user profile. That way the database change involves a single table, the application change is two functions, and the UI layers is a single component.
Different vertical slices often touch similar areas across multiple layers, making it harder to parallelize vertical slices without lot of conflicts.
Example: If we try to parallelize the vertical slices of the profile feature, the pairs will constantly be touching the same database tables and modules. Ultimately, it’s smoother to deliver each slice sequentially.
Vertical slices require more upfront thinking and planning. But they’ll help you discover risk earlier, deliver value consistently, and get feedback sooner.
Horizontal slices are intuitive to the dev team and feel faster to complete. But they delay risk, delay delivery, and require more communication overhead.
I think there’s a strong case for trying to slice as vertically as possible first, but be aware that this requires more work upfront.
Sometimes horizontal slices are unavoidable.
Consider work like refactoring and framework upgrades. They may be valuable in a way that a user doesn’t care about (e.g., maintenance, security, or performance), but doesn’t mean we shouldn’t do them.
I think developers sometimes avoid vertical slices because they feel “too big”, and we are naturally averse to large changes.
Finding a reasonably thin vertical slice sometimes required some creativity. If you search for articles on “story splitting,” you’ll find plenty of lists of dimensions on which you could try slicing.
The big idea is that size is slice size is proportional to complexity. So,
Examples: