Skip to content

Feature slicing

Introduction#

The way that work is broken down into deliverable chunks has an enormous effect on the team’s effectiveness. When breaking down a feature, always try to split it into smaller features — that is, pieces that represent vertical slices of functionality rather than horizontal technology layers.

This approach ensures a focus on delivering user and business value, but while the concept is simple, the skill of consistently splitting work into vertical slices takes determination and experience to develop. All team members should actively contribute to story splitting, but the Tech Lead and Product Manager have a particular responsibility to set the pattern for this.

This page defines these terms, explains the benefits of this approach and provides advice on how to do this in practice.

See Walking skeleton for a description of how to use vertical slices to approach building a new product or feature.

Definition of a vertical slice#

To qualify as a true vertical slice, all of the following must be true.

A vertical slice is user-visible, user-valuable, and cuts through architectural layers.

The result must be user-visible. If the user can’t see a change in the system after a story is implemented, then we have failed to produce a meaningful change for them.

It must be user-valuable. The difference must be moving in a direction the user sees as valuable. If we fail to do this then we have misunderstood what the user wanted or they have changed their mind. Either way, we have learned something useful. Small slices allow us to learn from this kind of feedback and course-correct frequently.

It should cut through architectural layers. We favour implementing a tiny amount of user-visible functionality through the stack over functionality that is limited to only part of the stack. Until we have traversed the full stack, we are storing up unknowns that represent risk.

Vertical slice definition

Vertical slice definition. A vertical slice of functionality cuts through architectural layers.

Examples#

To illustrate the difference between vertical and horizontal slices, we consider some simple examples.

Example 1: Registration page

Imagine we are breaking down a large story for user registration:

As a new user, I can register for the site, so that I can save my cart and buy items.

If we break it down into task-like items, we end up with horizontal slices.

For example:

  • Design UI form
  • Build registration form with no API calls
  • Implement address lookup
  • Implement registration API
  • Add Users table to database schema
  • Write new user details to database

No user value is released until all of the horizontal slices are complete.

Instead, we could split it into smaller stories, each of which represents a vertical slice of functionality. This releases user value early and continuously.

  • New blank registration form
  • User can register with forename and surname
  • User can enter address manually during registration
  • User can enter address via postcode lookup during registration
  • User can set product preferences during registration
Example 2: Scheduling API

We start with a large story for a scheduling API:

As a job scheduler, I can have my jobs scheduled automatically, so that I do not have to do it manually.

This could be split horizontally as:

  • Map request parameters to domain objects.
  • Map domain objects to scheduling engine query object.
  • Send request to scheduling engine.
  • Parse response from scheduling engine.
  • Formulate API response.

But as with most horizontal slicing, we get no value until the last part is complete, and we cannot do any end-to-end testing. So we are deprived of feedback and valuable learning until the end. Better, we can split vertically as:

  • Schedule a single job (mapping request, sending to engine and parsing response, and responding to the requester).
    • Simplifications: one person, no skills matching.
  • Schedule first day of multiple jobs.
  • Schedule all jobs over multiple days.
    • Simplifications: allow as many days as required.
  • Allow scheduling period to be truncated.
  • Schedule jobs for multiple people (no restrictions).
    • Simplifications: no skills matching, all people have same home location, ignore engine request limits.
  • Match jobs to people’s skills.
  • Optimise for each person having different home location.
  • Page requests to scheduling engine to avoid exceeding limits.
Example 3: Flight search

We start with a large story for a flight search page:

As a holidaymaker, I can search for flights, so that I can find cheap and convenient options.

This could be split vertically as:

  • Blank Hello World page.
  • Origin and destination fields; results are simply echoed back when submitted.
  • Support searching for flights on one route: London to Edinburgh.
    • Simplifications: one person, direct flights only, single carrier, within country so single currency and no time differences, automatic seat allocation.
  • Support searching for flights between 5 major cities within the same country.
  • Support multiple passengers.

At this point, you may be able to go live with early adopters who only need the features already available. As soon as you reach this threshold, you can reprioritise the remaining feature slices based on feedback.

Maybe your target market is happy with the small selection of cities you support, but it’s important to them to be able to search all available carriers.

  • Support one additional carrier — one such slice for each available carrier.

They may not like automatic seat allocation:

  • Choose seats.

Or perhaps there is one main carrier your customers care about but they want more choice of cities:

  • Support 10 additional locations.

Perhaps they really want to fly abroad:

  • Support flights that cross time zones.

And so on. The benefit of slicing finely like this is it opens up a wide range of options for the order in which incremental enhancements are made, and the benefit of going live early with a basic product is that you can learn which of these features are most valuable to implement first.

In each example, the first few stories lay simple foundations for the rest, and together form the start of a walking skeleton. Once these are complete, the other stories can be tackled in various orders, but until then they are blocked. To avoid the team waiting idle while those initial stories are implemented, they could swarm on them. They can do this by breaking them into even smaller tasks, this time along horizontal layers since they have gone as far as they can splitting vertically. These tasks can be implemented in parallel by multiple people, each working to agreed interfaces between the layers. Another way to parallelise work on even a single task in a small story is to pair, possibly using ping-pong pairing, where one person writes a failing test and the other writes the code to make it pass, before they swap roles.

Questions to ask#

Adopting the feature slicing mindset means interrogating every user story to see how it can be dissected into smaller slices of functionality. Here are some examples of the questions you might ask. These questions are illustrative and certainly not exhaustive.

  1. What’s the simplest thing we can start with? We’re looking for a functional vertical slice that barely does anything. This is often an empty form, API response or similar and is sometimes called a Hello World slice. Although the amount of user value produced is minuscule, it is not zero; and it produces a useful amount of learning value because it forces you to exercise several integration points.
  2. Can we start by allowing reads or gets but not writes? Retrieving and displaying information is often simpler than allowing it to be modified.
  3. Can we start with a simplified use case? For example, if we know we need to schedule multiple jobs we still may start by supporting only one job.
  4. Can we defer automation? For example, we may know that the ability to look up an address is considered essential by users but starting with manual address entry is still a useful functional increment.
  5. Can we defer authentication? Even if authentication will be essential for a releasable feature, a naive implementation that lacks authentication can represent a valid slice of functionality.
  6. Can we defer input validation? Of course, validation will need to be added before the feature is ready to be released to users, but even processing inputs without validation is a useful step forward and a valid slice.
  7. Can we display data unstyled? While we will want to make our UI look nice before releasing it, we can defer this and implement a slice that just shows the data in a raw form. The amount of rework this incurs is tiny. Where a design system is in place, using its standard components from the start usually costs no more than unstyled output, and is the better default.
  8. Can we defer performance optimisations? Sometimes we know that a naive implementation will work and move us usefully forward, even if we know it won’t be acceptable for a production release. A good example is deferring paging for API requests. However, at other times we cannot be confident that our approach will scale, and in these cases non-functional testing is essential to guide implementation and should not be separated into its own slice.

A note on testing

Functional testing should be done as part of implementing each slice, not separated into its own backlog item. The one common exception is UI automation for very thin slices, which can wait until several slices form a coherent journey (see Testing).

In contrast, it is often sensible to defer some non-functional testing, such as load, soak, and resilience testing, until a minimum useful feature set is present, which will likely consist of multiple slices. But do not defer it for too long. Non-functional testing should still be a regular activity throughout development. And some slices will introduce functionality that carries an obvious risk of performance or scalability issues. In these cases, testing is essential to validate the implementation approach and should not be separated out.

Benefits of slicing vertically#

We can clearly see the benefits of small vertical slices by comparing this approach with the other extreme of traditional waterfall delivery.

In waterfall delivery, no value is released until a big-bang go-live at the end. The resulting system may be what the user asked for, but almost certainly includes functionality that (they now realise) is of relatively low value to them and omits functionality that they have discovered is of high value to them. Because we have waited until the end to get meaningful user feedback — the kind that only comes through the user interacting with real working software — we and our users have missed the opportunity to feel out what the most valuable functionality is.

By breaking goals down into user stories that represent vertical slices of functionality, value is released in chunks, and rapid feedback is possible. Each time we deliver a piece of functionality, our users get to see how the features they asked for really work. This is a learning experience for us and our users, with assumptions being tested and refined. This iterative steering towards the next highest value features results in value being generated more quickly.

The focus on delivering working software early also forces us to tackle technical challenges that we may assume are easy but could turn out to be difficult. Again, assumptions are tested and refined, reducing risk.

All of these factors are multiplied as the slices of functionality become smaller. Small stories release the most value earliest and create the most value overall.

These differences can be visualised as cumulative value curves.

Cumulative value curves

Cumulative value curves. Small vertical slices release the most value earliest and create the most value overall.

The value shown in this graph is a combination of learning value, which accumulates as we better understand user needs, technical feasibility and any dependencies, and user value, created through the addition of useful features to the system.

Learning value and user value

Learning value and user value.

At the start, learning value accumulates rapidly, but direct user value does so somewhat slower. Over time, this balance shifts: once the biggest unknowns are understood we are able to make more rapid progress on delivering features that users need. The rate of value generation eventually slows, enabling a choice to be made between continuing to iterate on this system or switching to other work if that is of higher priority. The relentless focus on delivering what is most valuable in each increment buys us the option to stop early, knowing that most of the value has already been delivered.

The Agile Manifesto and its Twelve Principles are still highly relevant today and capture the above succinctly.

Agile Manifesto (selected excerpt)

Working software over comprehensive documentation.

Customer collaboration over contract negotiation.

Responding to change over following a plan.

INVEST#

The vertical slicing approach tallies with the Valuable and Small aspects of the INVEST model.

INVEST
  • (I)ndependent — the story can be implemented without needing other stories to be completed first.
  • (N)egotiable — the details aren’t fixed up front; they’re refined through conversation.
  • (V)aluable — the story delivers clear value to users in a way that works for the business.
  • (E)stimable — the team has enough information to roughly gauge the effort required.
  • (S)mall — ideally 2 days or less end to end.
  • (T)estable — there are clear criteria to verify the story is done and working as expected.

Although it is implicit in that model that “valuable” implies “user-visible,” this is often misunderstood, hence the focus on this point here. This handbook also takes a stricter view of small than the original model, aiming for items of 5 story points or less, which for most teams means 1–2 days of work.

The foundation stories in the examples deliberately trade away some independence, because later slices depend on them. That is a price worth paying while the walking skeleton is being built, and slices become far more independent once it is in place.

In practice#

A great way to develop vertical slicing skills is to play Elephant Carpaccio, a game developed by Henrik Kniberg and Alistair Cockburn.

Tips
  • Be vigilant for user stories that fail the simple test of creating a change that is user-visible, user-valuable, and cuts through all relevant architecture layers.
  • Remember that Epics, Features, and user stories should always be vertical slices — never settle for horizontal layers disguised as stories.
  • A slice is valuable even if it only moves the system a small step towards an outcome that users will find valuable. It doesn’t need to represent a complete, user-ready feature — just progress in the right direction.
  • Progressively splitting items into finer vertical slices releases more value earlier, provides more frequent feedback opportunities, and reduces overall project risk.
  • Delivering in vertical slices doesn’t mean you have to expose features to users in the live system until they’re ready. Feature flags are one mechanism to allow partially complete features to be deployed to production without becoming visible to users.
  • As well as user stories, Tasks and even individual commits should ideally also be vertical slices, but this isn’t always feasible.
  • Not all work can be usefully expressed as user stories. For example, refactoring an area of the codebase may be valuable but it’s not useful to force it into user story form. However, with careful attention, most work can be viewed in terms of user benefit, and doing so forces a focus on value that can be highly informative.

Key points#

  • Split features into vertical slices that are user-visible, user-valuable, and cut through the architectural layers, not into horizontal technology layers.
  • Small slices release value earlier, generate feedback sooner, and expose technical risk while it is cheap to address.
  • A slice only needs to move the system a step towards something users value. It doesn’t need to be a complete, releasable feature.
  • Interrogate every item for a smaller first step, such as implementing reads before writes, a simplified case, manual before automated, or deferring validation, styling, or optimisation.
  • The first few slices lay foundations for the rest and form the start of a walking skeleton. Swarm on them rather than waiting.
  • Functional testing is part of each slice. Some non-functional testing can wait for a minimum useful feature set, but not for long.
  • Feature flags let slices reach production before they are ready for users.

Mistakes to avoid

  • Horizontal layers disguised as stories. “Build the API” or “add the database table” delivers no user value.
  • Slices nobody can see. If the user can’t observe a change, it isn’t a vertical slice, however useful the work.
  • Separate testing items. Functional testing belongs in the slice it validates.
  • Holding slices back until the whole feature is finished. Slices that never reach production don’t generate much learning.
  • Deferring non-functional testing too long, or at all when a slice carries obvious performance or scalability risk.
  • Leaving slicing to one person. Everyone contributes, with the Tech Lead and Product Manager setting the pattern.