Rough Work/the guardrails

The Third User

Every component or module you build has two users. The first is the End User — the person clicking buttons or viewing data. The second is the Developer — the one consuming the public API to build the app.

The danger begins when you create a third: the Test.

This happens when a test reaches inside a component to check private state, or verifies that a specific internal helper was called. The temptation is understandable — private state is easy to inspect, and it feels thorough. But instead of testing what the code does, you're testing how the code does it.

// Third User: Tests implementation details
it('adds item to cart', () => {
  const cart = new Cart();
  cart.add(new Item('Apple', 1.00));
  expect(cart._items.length).toBe(1); // reaching into private state
  expect(cart._recalculateTotals).toHaveBeenCalled(); // verifying internals
});

// Testing the boundary: Tests observable behavior
it('adds item to cart', () => {
  const cart = new Cart();
  cart.add(new Item('Apple', 1.00));
  expect(cart.total()).toBe(1.00); // what the user actually cares about
});

The first test breaks the moment you rename _items or inline _recalculateTotals — even if the cart still works perfectly for both real users. You've made refactoring expensive without making the software safer.

Test the boundary instead: what the End User sees or what the public API returns. Observable behavior is stable in a way that implementation details never are — it's the contract your real users depend on, and a test anchored to it survives the refactoring it's supposed to enable. That's when a test suite becomes something you trust.

to navigate