A symbol of the fact that the simplest solution is often the most effective.
Brian Kernighan summed it up in “The Elements of Programming Style” (Kernighan & Plauger, 1978): Debugging is twice as hard as writing code. So if we programme as cleverly as possible, by definition we are not clever enough to debug our own code. It is one of those statements that make us smile briefly. And then we realise we have just been caught out.
Because let us be honest: We have all written code that seemed brilliant at the time of writing. An elegant abstraction here, a clever pattern there. And then we come back six months later and no longer understand our own brilliance. Or worse: someone else has to maintain this code and has no chance of reconstructing the intention behind our decisions.
This problem becomes dramatically worse when AI agents come into play. In my article “Faster than Understanding”, I described how an AI agent implemented a non-trivial software metric in 15 minutes and I subsequently spent hours checking whether the implementation was correct. The code looked clean. The tests were green. But looking right and being right are not the same thing. More code does not mean more value. Especially not if I do not know whether the code does what it is supposed to do.
Code is not an asset
In our industry, there is a persistent misconception: that more code means more value. That a growing codebase is a sign of progress. That the number of lines we write per day is a measure of our productivity.
The opposite is true. Edsger Dijkstra knew this as early as 1975: In his essay “How do we tell truths that might hurt?”, he argued that we should record lines of code not as “lines produced” but as “lines spent”: as expenditure, not as revenue. Conventional wisdom records them on the wrong side.
Every line of code is a line that may contain a bug. Every line of code is a line that may contain a security issue. Every line of code is a line that I must take into account in future changes and that may become a problem in the long term.
Code is a liability, not an asset.
The hidden costs
What applies to manually written code applies all the more to automatically generated code. The AI agent from my example produced code, and to this day I cannot say with certainty whether it does what it is supposed to do. It is the most expensive kind of code: code whose raison d’être nobody wants to question any more, because the risk of making a change has become incalculable.
Sir Tony Hoare described this tension in his Turing Award lecture “The Emperor's Old Clothes”: We can design software so simply that there are obviously no flaws. Or we can make it so complicated that there are no obvious flaws. AI-generated code tends towards the second option. Our task is to insist on the first.
Not every problem needs code
In the same essay, Edsger Dijkstra also reminded us that simplicity is a prerequisite for reliability.
Before I write a single line of code, I should ask myself: Does this problem even need to be solved with software? Is there a process, a convention, a conversation that renders the need for new code superfluous? And if code is unavoidable: what is the simplest solution that works?
Code that I do not have does not cause any problems.
This applies regardless of whether a human wrote the code or a machine generated it. Whether I need 15 minutes or three days for an implementation is irrelevant if the correct answer would have been not to solve the problem with code in the first place.
Writing for humans
In 1999, Martin Fowler wrote in “Refactoring: Improving the Design of Existing Code” that any beginner can write code that a computer understands. Good programmers, however, write code that humans understand. Harold Abelson and Gerald Sussman put it even more fundamentally in “Structure and Interpretation of Computer Programs”: Programs should be written for humans and only incidentally for machines.
Code that I can understand causes no problems. The code that causes problems is the code that nobody understands any more, that nobody dares to change, and that nobody wants to delete because it is unclear what would happen then.
John F. Woods offered the best advice on this in a 1991 post on the comp.lang.c++ newsgroup: Always program as if the person who ends up having to maintain your code is a violent psychopath who knows where you live. Behind the humour lies a serious truth. Code must be self-explanatory. Not through comments, not through external documentation, but through its structure, its naming conventions, its simplicity.
Perfection through omission
Antoine de Saint-Exupéry, a writer and not a programmer, put it this way in “Terre des Hommes” in 1939: Perfection is not achieved when there is nothing left to add, but when there is nothing left to take away.
That is the standard we should apply to our software. Not: What else can I add? But: What else can I leave out?
Code I do not have does not cause problems. Code I can understand and test does not cause problems. And for everything in between, I need tests: for the code I have, but do not know whether I still need it or whether it does what it is supposed to. Tests that give me the confidence to change or delete code. Tests that document what the code is supposed to do. Tests that tell me when something is broken.
Simplicity is discipline
Writing simple code is harder than writing complicated code. It requires us to have truly understood a problem before we solve it. It requires us to have the courage to say no: No to an abstraction that nobody needs. No to a feature that might be useful at some point. No to a clever solution when a simple one will do. And yes, even no to 15 minutes of AI-generated code if we are unable to judge its correctness.
Simplicity is not a weakness. It is discipline. And it is the best insurance we can take out for the future of our software.