Designing for the Second User
Every piece of software has two users: the person who will use it now, and the person who will maintain it later. Often that second person is you, six months from now. Writing for them changes everything.
I have started thinking about every piece of software I write as having two users.
The first user is obvious. The person who will open the app, run the command, read the output. They are who you imagine when you write the code. Their needs are in front of you. You design for them because they are present, concrete, easy to picture.
The second user is harder to hold in mind. They are the person who will maintain this software next year. Who will try to understand why a particular decision was made. Who will change one thing and need to understand what else might move. Often, this second user is you, six months from now, with no memory of the original context.
Designing for the second user is not a different discipline from designing for the first. It is the same discipline, held to a higher standard. But it changes what you notice.
What the second user needs
The second user needs to understand the shape of the code at a glance. Not because you left comments explaining each step (those age badly), but because the structure itself is honest. Names that say what things are. Functions that do one thing. Files that live where you would expect to find them.
The second user also needs to understand the decisions. Not all of them. Not the ones that were obvious. The ones that look wrong at first and were made deliberately. A function that could be simpler but was left slightly more complex for a reason. A workaround for a platform quirk. A shape that looks like a smell but is load-bearing.
These are the only things worth explaining. Everything else should be legible from the code.
The trap of clever
The most common way I see code fail the second user is cleverness. A clever solution reduces ten lines to two, eliminates a loop, builds something elegant. It feels good to write. It often looks impressive. And then the second user, including future-me, spends forty-five minutes reconstructing the logic to make a change that should take five.
Cleverness transfers the cost from the writer to the reader. It is a debt paid by someone who did not consent to borrow it.
I am not saying avoid abstraction. Good abstraction reduces the surface area that the second user needs to understand. But the test for good abstraction is not “is this less code” but “does this make the intent clearer.” Those are different questions.
The six-month heuristic
When I am uncertain about a decision, I try to imagine myself opening this file six months from now. Not to refactor it or improve it. Just to make one specific change.
Would I understand what this does? Would the structure tell me where to look? Would I trust the shape of the code enough to make the change with confidence, or would I need to trace through multiple levels to feel safe?
If the answer is no, something needs to be simpler, not cleverer.
Second-user design compounds
There is a practical reason to do this beyond being kind to future-yourself. Codebases that respect the second user are faster to change. You can move faster because you trust the code. You can make larger changes because you understand the shape of the system. You spend less time archaeology and more time building.
The upfront cost of writing for the second user is small. A clearer name. A simpler structure. A brief comment where the intent is genuinely non-obvious. These cost seconds. The time they save compounds across every future change.
The first user benefits from features. The second user benefits from every decision you made about clarity. Both of them are you.