Unjustified Abstractions, Premature Abstractions
Reading on posts of Jonathan Blow and John Carmack, I think I managed to become still little better on programming. From now on I refrain from doing premature and unnecessary abstractions.
Premature abstractions are programming structures that have been introduced before they're justified. Often happens that justification never arrives. It's kind of gamble with your source code. Justified abstractions help you reach your goals but unjustified abstractions do the exact opposite:
- You invest time writing them, yet they never pay off
- They strain your program namespace, making it harder to name things properly.
- They increase the program size and complexity
- They hide the opportunities on good abstractions.
Every function definition should be justified. The justification isn't that the code is hidden elsewhere. You don't do abstractions to hide code. You do them to make the code simpler and more maintainable. Every call is an indirection which requires the reader to understand what the callee is doing. Every abstraction has a cost.
It is possible that prematurely done abstractions do not always end up unjustified, but that's an exception. Repeated gambles mean that the code gathers unnecessary constructs as time passes. It's the kind of game where you lose big by definition.
To prove that it correct to myself, I tried it few days ago. I wrote a stub for platformer game. I started it by implementing a tilemap that I can edit. I've made this kind of things before, but this time I avoided doing abstractions too early. The following patterns started to emerge:
- It is easier to recognise most frequently appearing code. And there's an obvious name for them.
- The interface to the tilemap is forming to match the code which is using it, without having to spend too much consideration towards it.
- There's less code, with clearer structure.
This advices against studying design patterns. If they're needed, they will emerge without having to figure them out. If they're not, it's a cardinal error to introduce them into your code prematurely.
To get most of abstractions you need to avoid introducing them yourself. You don't need to force. Let the code show the form it wants to take.