… so you can still understand and change them, when you can only work half an hour
Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it. — Brian Kernighan
![]()
PDF (drucken)
In the article Hyperfocus and balance, Arc Riley from PySoy talks about trying to get to the Hyperfocus state without endangering his health. Since I have similar needs, I am developing some strategies for that myself (though not for my health, but because my wife and children can't be expected to let me work 8h without any interruptions in my free time).
Different from Arc, I try to change my programming habits instead of changing myself to fit to the requirements of my habits.1
Easy times
Let's begin with Programming while you feel great.
The guideline I learned from writing PnP roleplaying games is to keep the number of things to know at 7 or less at each point (according to Miller, 1956;2, 3 though the current best guess of the limitation for average humans is only 4 objects!). For a function of code I would convert that as follows:
- You need to keep in mind the function you work in (location), and
- the task it should perform (purpose and effect), and
- the resources it uses (arguments or global values/class attributes).
Only 4 things left for the code of your function. (three if you use both class attributes/global values and function arguments. Two, if you have complex custom data-structures with peculiar names or access-methods which you have to understand for doing anything. One if you also have to remember the commands of an unfamiliar4, 5, 6 editor or VCS tool. See how fast this approaches zero even when starting with 7 things?)
Add an if-switch, for-loop or similar and you have only 3 things left.
You need those for what the function should actually do, so better put further complexities into subfunctions.
Also ensure that each of the things you work with is easy enough. If you get the things you use down to 7 by writing functions with 20 arguments, you don't win anything. Just the resources you could use in the function will blow your mind when you try to change the function a few months later. This goes for every part of your program: The number of functions, the number of function arguments, the number of variables, the lines of code per function and even the number of hierarchy levels you use to reduce the other things you need to keep in mind at any given time.
Hard times
But if you want to be able to hack that code while you feel dumb (compared to those streaks of genius when you can actually hold the whole structure of your program in your head and forsee every effect of a given change before actually doing it), you need to make sure that you don't have to take all 7 things into account.
Tune it down for the times when you feel dumb by starting with 5 things.7 After substracting one for the location, for the task and for the resources, you are left with only two things:
Two things for your function. Some Logic and calling stuff are 2 things.
If it is an if-switch, let it be just an if-switch calling other functions.8 Yes, it may feel much easier to do it directly here, when you are fully embedded in your code and feel great, but it will bite you when you are down. Which is exactly when you won't want to be bitten by your own code.
Loose coupling and tight cohesion
Programming is a constant battle against complexity. Stumble from the sweet spot of your program into any direction, and complexity raises its ugly head. But finding the sweet spot requires constant vigilance, as it shifts with the size and structure of your program and your development group.
To find a practical way of achieving this, Django's concept of loose coupling and tight cohesion (more detailed) helped me most, because it reduces the interdependencies.
The effects of any given change should be contained in the part of the code you work in - and in one type of code.
As web framework, Django seperates the templates, the URI definitions, the program code and the database access from each other. (see how these are already 4 categories, hitting the limit of our mind again?)
For a game on the other hand, you might want to seperate story, game logic, presentation (what you see on the screen) and input/user actions. Also people who write a scenario or level should only have to work in one type of code, neatly confined in one file or a small set of files which reside in the same place.
And for a scientific program, data input, task definition, processing and data output might be separated.
Remember that this separation does not only mean that you put those parts of the code into different files, but that they are loosely coupled:9
They only use lean and clearly defined interfaces and don't need to know much about each other.
Conclusions
This strategy does not only make your program easier to adapt (because the parts you need to change for implementing a given feature are smaller). If you apply it not only to the bigger structure, but to every part of the program, it's main advantage is that any part of the code can be understood without having to understand other parts.
And you can still understand and hack your code, when your child is sick, your wife is overworked, you slept 3 hours the night before - and can only work for half an hour straight, because it's evening and you don't want to be a creep (but this change has to be finished nontheless).
Note that finding a design which accomplishes this is far more complex than it sounds. If people can read your code and say "oh, that's easy. I can hack that" (and manage to do so), then you did it right.
Designing a simple structure to solve a complex task is far harder than designing a complex structure to solve that task.
And being able to hack your program while you feel dumb (and maybe even hold it in your head) is worth investing some of your genius-time10, 11 into your design (and repeating that whenever your code grows too hairy).
PS (7 years later): This only applies to the version of your code that stays in your codebase. During short-term experiments these rules do not apply, because there you still have the newly written code in your head. But take pains to clean it up before it takes on a life of its own. The last point in time for that is when you realize that you're no longer sure how it works (then you know that you already missed the point of refactoring, but you can at least save your colleagues and your future self from stumbling even worse than you do at that moment). That way you also always have some leeway in short-term complexity that you can use during future experimentation. Also don't make your code too simple: If you find that you're bored while coding or that you spend more time fighting the structures you built than solving the actual problems, you took these principles too far, because you're no longer getting full benefits from your brain. Well chosen local complexity reduces global complexity and the required work per change.