# Vertical Slices vs Horizontal Slices in TDD: A Complete Guide to Test-Driven Development Workflows

> Understand the difference between vertical slices and horizontal slices in TDD. Learn why vertical slicing builds robust tests and better design, avoiding the pitfalls of horizontal slicing.

- Repository: [Matt Pocock/skills](https://github.com/mattpocock/skills)
- Tags: deep-dive
- Published: 2026-04-04

---

**Vertical slices in TDD require writing one test and the minimal implementation to pass it before moving to the next feature, while horizontal slices involve writing all tests first then all implementation—an anti-pattern that produces brittle tests and speculative design.**

Understanding the distinction between **vertical slices vs horizontal slices in TDD** determines whether your tests drive the design or merely validate speculative assumptions. The mattpocock/skills repository provides detailed guidance on these approaches in its TDD skill documentation, emphasizing how the organization of the RED-GREEN-REFACTOR loop impacts test durability. Mastering this concept prevents the common trap of writing tests that break during every refactoring cycle.

## Core Concepts: Vertical and Horizontal Slicing

The repository defines two fundamentally different ways to apply the RED-GREEN-REFACTOR loop in [`tdd/SKILL.md`](https://github.com/mattpocock/skills/blob/main/tdd/SKILL.md).

### Horizontal Slices (Anti-Pattern)

Horizontal slicing separates the testing and implementation phases into distinct bulk operations. As explicitly warned in [`tdd/SKILL.md`](https://github.com/mattpocock/skills/blob/main/tdd/SKILL.md) lines 18-22, this approach follows the pattern: "DO NOT write all tests first, then all implementation."

The workflow follows this sequence:

- RED: Write test1, test2, test3, test4, test5
- GREEN: Write impl1, impl2, impl3, impl4, impl5

This method encourages writing tests against imagined data structures and internal signatures before any concrete behavior exists. Because tests are decoupled from the immediate feedback of implementation, they often verify implementation details rather than observable behavior, making them fragile when internal structures change.

### Vertical Slices (Recommended)

Vertical slicing applies the RED-GREEN-REFACTOR loop incrementally for each individual behavior. According to [`tdd/SKILL.md`](https://github.com/mattpocock/skills/blob/main/tdd/SKILL.md) lines 31-40, the correct workflow alternates between test and implementation for each slice:

```

RIGHT (vertical):
  RED→GREEN: test1→impl1
  RED→GREEN: test2→impl2
  RED→GREEN: test3→impl3

```

Each test describes a concrete piece of observable behavior through the public API. The implementation emerges only to satisfy the specific test, ensuring that tests depend solely on external contracts rather than internal details.

## Key Differences Between Approaches

**Workflow Structure**
- **Horizontal Slice**: Write **all** tests first, then write **all** implementation code in one batch.
- **Vertical Slice**: Write **one** test, implement **just enough** code to satisfy that test, then repeat.

**Test Focus and Validity**
- **Horizontal Slice**: Tests verify imagined shapes—data structures, signatures, and internal state—before concrete user-facing behavior is established.
- **Vertical Slice**: Each test proves a specific observable behavior through the public API, driving the implementation design.

**Refactoring Resilience**
- **Horizontal Slice**: Tests become brittle when internal details change, and they may pass even when the system’s actual behavior is broken.
- **Vertical Slice**: Tests survive refactors because they only depend on the external contract, allowing internal structure changes without breaking the suite.

**Development Risk**
- **Horizontal Slice**: Large, speculative chunks of code written before confidence is built; high risk of dead-ends and rework.
- **Vertical Slice**: Small, focused increments that are continuously validated; easier to reason about and refactor safely.

## Code Examples: Shopping Cart Implementation

The following examples demonstrate the practical difference between these approaches in a simple shopping cart domain.

### Horizontal Slice Approach (Anti-Pattern)

In this pattern, all tests are drafted upfront, locking the developer into specific implementation details before writing any production code.

```python

# tests/horizontal_cart_test.py  (all tests written up‑front)

def test_add_item():
    ...

def test_remove_item():
    ...

def test_total_price():
    ...

def test_checkout_success():
    ...

def test_checkout_failure_when_empty():
    ...

# cart.py – implementation written after all tests exist

class Cart:
    def __init__(self):
        self.items = []          # implementation detail

        self.total = 0           # implementation detail

    def add(self, item):
        self.items.append(item)
        self.total += item.price

    def remove(self, item):
        self.items.remove(item)
        self.total -= item.price

    def checkout(self):
        if not self.items:
            raise ValueError("empty")
        return self.total

```

All tests were drafted before any code existed; the implementation grew to satisfy them, but the tests are tightly coupled to the internal `items` list and `total` field.

### Vertical Slice Approach (Recommended)

Each slice adds exactly one new capability, with the implementation growing only as needed.

**Slice 1: Add Item Behavior**

```python

# First slice – add‑item behaviour

def test_add_item():
    cart = Cart()
    cart.add(Item(name="Apple", price=1))
    assert cart.total() == 1          # test only public API

# cart.py – minimal code to pass the first test

class Cart:
    def __init__(self):
        self._total = 0

    def add(self, item):
        self._total += item.price

    def total(self):
        return self._total

```

**Slice 2: Remove Item Behavior**

```python

# Next slice – remove‑item behaviour

def test_remove_item():
    cart = Cart()
    apple = Item(name="Apple", price=1)
    cart.add(apple)
    cart.remove(apple)
    assert cart.total() == 0

# Extend Cart only with what the new test needs

class Cart(Cart):                     # (or modify existing class)

    def remove(self, item):
        self._total -= item.price

```

**Slice 3: Checkout Behavior**

```python

# Checkout behaviour slice

def test_checkout_success():
    cart = Cart()
    cart.add(Item(name="Apple", price=1))
    assert cart.checkout() == 1

def test_checkout_failure_when_empty():
    cart = Cart()
    with pytest.raises(ValueError):
        cart.checkout()

# Add checkout method – still minimal

class Cart(Cart):
    def checkout(self):
        if self._total == 0:
            raise ValueError("empty")
        return self._total

```

Each test drives the addition of exactly one new capability, keeping tests focused on the public contract rather than internal state.

## Repository Structure and Related Concepts

The vertical slice concept extends throughout the mattpocock/skills repository beyond basic TDD into project planning and issue management.

**Core TDD Documentation**
- [`tdd/SKILL.md`](https://github.com/mattpocock/skills/blob/main/tdd/SKILL.md): Contains the primary definitions of vertical vs. horizontal slicing (lines 18-40) and the RED-GREEN-REFACTOR loop guidance.
- [`tdd/tests.md`](https://github.com/mattpocock/skills/blob/main/tdd/tests.md): Provides concrete test examples illustrating vertical slice implementations.
- [`tdd/mocking.md`](https://github.com/mattpocock/skills/blob/main/tdd/mocking.md): Offers guidelines for mocking that support vertical slicing by avoiding premature abstraction.
- [`tdd/interface-design.md`](https://github.com/mattpocock/skills/blob/main/tdd/interface-design.md): Details how to design public APIs that facilitate vertical slice testing.

**Extended Applications**
- [`triage-issue/SKILL.md`](https://github.com/mattpocock/skills/blob/main/triage-issue/SKILL.md): References vertical slices as part of the Red-Green cycle in issue triage workflows.
- [`prd-to-plan/SKILL.md`](https://github.com/mattpocock/skills/blob/main/prd-to-plan/SKILL.md) and [`prd-to-issues/SKILL.md`](https://github.com/mattpocock/skills/blob/main/prd-to-issues/SKILL.md): Explain "tracer-bullet" vertical slices for breaking down Product Requirements Documents (PRDs) into end-to-end deliverable phases.

## Summary

- **Vertical slices** process one test and implementation pair at a time; **horizontal slices** batch all tests separately from all implementation.
- Horizontal slicing is explicitly marked as an anti-pattern in [`tdd/SKILL.md`](https://github.com/mattpocock/skills/blob/main/tdd/SKILL.md) because it creates brittle tests coupled to implementation details.
- Vertical slicing tests only public contracts, allowing internal refactoring without breaking the test suite.
- This approach provides immediate feedback, eliminates speculative code waste, and aligns development with demonstrable user value.
- The concept applies beyond unit testing to feature planning and PRD breakdowns throughout the mattpocock/skills repository.

## Frequently Asked Questions

### What is the main difference between vertical and horizontal slices in TDD?

Vertical slices alternate between writing a single failing test and implementing the minimal code required to pass it, repeating the RED-GREEN-REFACTOR loop for each behavior. Horizontal slices separate these activities by writing all tests for a feature upfront, then writing all implementation code in a subsequent phase.

### Why is horizontal slicing considered an anti-pattern?

Horizontal slicing encourages speculative design because tests are written against imagined data structures before concrete behavior emerges. According to [`tdd/SKILL.md`](https://github.com/mattpocock/skills/blob/main/tdd/SKILL.md), this leads to tests that may pass even when the system's actual behavior is broken, and they become fragile during refactoring because they depend on internal implementation details rather than public APIs.

### How does vertical slicing improve code quality?

Vertical slicing ensures every line of production code is justified by a specific failing test, eliminating waste and preventing over-engineering. Because tests are written against the public API and only enough code is written to make them pass, the resulting test suite survives internal refactoring and provides continuous validation of actual system behavior.

### Can vertical slicing be applied outside of unit testing?

Yes. The mattpocock/skills repository applies the vertical slice concept to project planning in [`prd-to-plan/SKILL.md`](https://github.com/mattpocock/skills/blob/main/prd-to-plan/SKILL.md) and [`prd-to-issues/SKILL.md`](https://github.com/mattpocock/skills/blob/main/prd-to-issues/SKILL.md), using "tracer-bullet" slices to break down large requirements into small, end-to-end deliverables. This approach ensures that each slice delivers demonstrable value before proceeding to the next feature.