John Roest

The Code That Knew What It Wanted

Thu Jul 17 2025

The Code That Knew What It Wanted

I did not always write tests first. In fact, I used to think it was a waste of time. Why write tests for code that does not exist yet? Why not just write the thing, and test it after? That is what I found to be strange. But it actually helped me become a better developer.

Three Simple Rules

Let me be clear. TDD is not about testing. It is about design. You follow three rules. No more. No less.

  1. You are not allowed to write any production code unless it is to make a failing test pass.
  2. You are not allowed to write any more of a test than is sufficient to fail.
  3. You are not allowed to write any more production code than is sufficient to pass the failing test.

It sounds rigid. Mechanical even. But that is where the freedom lies.

A Story in Red, Green, and Refactor

Let us say you want to build a simple inventory system. Not a full-blown ERP. Just enough to track items and their quantities.

Cycle 1: Add Item to Inventory

We begin with a failing test.

@Test
void addsNewItemToInventory() {
    Inventory inventory = new Inventory();
    inventory.addItem("keyboard", 10);
    assertEquals(10, inventory.getQuantity("keyboard"));
}

This does not compile. We create the Inventory class.

public class Inventory {
    public void addItem(String name, int quantity) {
    }

    public int getQuantity(String name) {
        return 0;
    }
}

The test runs and fails. We implement just enough to pass.

public class Inventory {
    private Map<String, Integer> items = new HashMap<>();

    public void addItem(String name, int quantity) {
        items.put(name, quantity);
    }

    public int getQuantity(String name) {
        return items.getOrDefault(name, 0);
    }
}

Green. Now we refactor: nothing to clean up yet. We move on.

Cycle 2: Add Quantity to Existing Item

Now a new behavior. Add to existing items.

@Test
void addsQuantityToExistingItem() {
    Inventory inventory = new Inventory();
    inventory.addItem("mouse", 5);
    inventory.addItem("mouse", 3);
    assertEquals(8, inventory.getQuantity("mouse"));
}

Red. Good. We change the implementation.

public void addItem(String name, int quantity) {
    int existing = items.getOrDefault(name, 0);
    items.put(name, existing + quantity);
}

Green. And elegant. That is the point.

Cycle 3: Removing Items

Now the tricky part. We want to remove items from the inventory.

@Test
void removesQuantityFromItem() {
    Inventory inventory = new Inventory();
    inventory.addItem("monitor", 10);
    inventory.removeItem("monitor", 4);
    assertEquals(6, inventory.getQuantity("monitor"));
}

Red again. We implement.

public void removeItem(String name, int quantity) {
    int existing = items.getOrDefault(name, 0);
    items.put(name, existing - quantity);
}

Green. But wait. What if quantity goes negative?

Cycle 4: Prevent Negative Quantity

@Test
void doesNotAllowNegativeQuantity() {
    Inventory inventory = new Inventory();
    inventory.addItem("cable", 2);
    inventory.removeItem("cable", 5);
    assertEquals(0, inventory.getQuantity("cable"));
}

The test fails. We fix it.

public void removeItem(String name, int quantity) {
    int existing = items.getOrDefault(name, 0);
    int newQuantity = Math.max(0, existing - quantity);
    items.put(name, newQuantity);
}

Now it is green.

We are no longer guessing. We are responding to the code. Listening to what it needs. Giving it what it wants.

Why It Matters

People think TDD is about being careful. It is not. It is about being fast. You write less code, not more. You stop guessing. You stop coding by coincidence. You stop being afraid to change things.

The tests tell you what to do next. They hold your hand. They guard your back.

When I write code without tests first, I feel like a kid in the dark. When I write tests first, I feel like an engineer.

Real Pain, Real Discipline

TDD is not always easy. Sometimes you hate it. You want to break the rules. Just for this one thing. Just a quick fix. No test.

Do not.

The moment you stop writing tests first, you stop doing TDD. You are lying to yourself. And the code knows.

I once tried to refactor a system without tests. It was like trying to change the engine of a plane while flying it. You cannot do that. You crash.

When the Tests Come First, the Bugs Come Last

With TDD, you do not just end up with code. You end up with confidence. You know it works because you watched it grow.

Like a craftsman shaping wood, one test at a time, one feature at a time.

You do not just build software. You grow it.

One Last Thing

You do not have to believe me. Try it. Pick a feature. Write the test first. Watch the red. Make it green. Refactor. Then do it again.

Soon the tests will be writing your code for you.

And you will wonder why you ever did it any other way.