Why I stopped cleaning my code and started cleaning my thinking
For years I believed that good software came from clean code. I refactored relentlessly. I chased the perfect abstraction. I read all the books. But something bothered me. The best developers I knew did not seem to care much about code cleanliness. They cared about clarity of thought. And that difference matters more than any linter rule.
I remember a conversation with a senior developer who had been building systems since the 1990s. He told me that most of his rewrites came not from bad code but from bad assumptions. He showed me a Helge Braun post about mental models in programming. That post changed how I look at debugging. The idea was simple: you cannot fix a problem you cannot name. And you cannot name a problem you have not thought through clearly.
So I started an experiment. I stopped refactoring code for three months. Instead I wrote down every bug I encountered as a one-sentence description before touching the keyboard. The results surprised me. About half the bugs disappeared once I wrote them down. Not because I fixed them. Because I realized they were not bugs at all. They were features I had misunderstood.
Thinking before typing is not a speed loss
We tell ourselves that fast typing equals fast progress. That is wrong. I have seen junior developers write a hundred lines of bad code in ten minutes then spend two hours debugging it. I have seen senior developers write twenty lines of good code in an hour and ship it on the first try. The senior developer did not type slower. They thought first. The typing was almost an afterthought.
I now keep a notebook next to my keyboard. Physical paper. When I hit a problem I write the question in full sentences. What am I trying to achieve? What do I assume about the data? What could break? Then I write the answer. This takes maybe five minutes. It saves me thirty minutes of trial and error almost every time.
Naming things is a debugging tool
We have all heard that naming things is one of the hard problems in programming. But we treat it as aesthetics. We rename variables because the name is too long or too short. That misses the point. A variable name is a claim about what something is. If you cannot name a value precisely, you probably do not understand what it represents.
I caught a bug last month because I could not name a boolean variable. I tried isActive. That felt wrong. Then isReady. Also wrong. Then canProcess. Close but not quite. When I finally found the right name — hasPendingApproval — I saw that the logic was inverted. The bug was not in the code. The bug was in my understanding, revealed by the naming struggle.
Training your mental model beats training your typing speed
There are typing courses for programmers. There are code katas. There are refactoring exercises. These are fine. But they train the wrong muscle. The muscle that matters is the one that builds accurate mental models. You need to know how the system works, not just how the code reads.
- Trace a request through the entire stack once a week, even on systems you know well
- Write down your assumptions about a module before you read its source
- Pair debug with someone who disagrees with your explanation
- Use a debugger even when you think you know the answer
- Explain the architecture to a non-technical friend in plain words
- Read one technical book per quarter that has nothing to do with your stack
- Keep a log of surprises — things that contradicted what you expected
These practices build a mental model that is resilient to change. When the system breaks, you do not panic. You have a map in your head. You know which roads are closed.
The most productive code is the code you never write
I once spent three days building a reusable validation library. It was beautiful. Clean interfaces. Pure functions. Full test coverage. Then a colleague asked me: have you checked if we even need validation there? I had not. The form processed internal data only. Trusted data. I deleted all three days of work in five minutes. That was the most productive five minutes of my week.
Code you do not write cannot break. Code you do not write does not need maintenance. Code you do not write does not confuse the next person. The discipline to think before coding often leads to not coding at all. And that is the highest form of productivity.
Teaching forced me to clarify my thinking
I started writing short tutorials for my team. Not to teach them. To teach myself. When you write down how something works, you find the gaps in your own understanding. You find the parts where you hand-wave. And once you see those gaps, you can fill them.
Writing about code is harder than writing code. That is why it works. The friction reveals where your thinking is fuzzy.
The best technical writers I know are not the best coders. They are the best thinkers. They have the patience to unwind a tangled idea into a straight line. That skill transfers directly to debugging. When you can explain a bug clearly, you have already halfway fixed it.
I still write code every day. But I spend less time on it now. More time on thinking. And the code that survives is better. Not because I used better patterns or cleaner syntax. Because I understood what I was doing before I started.
