Premature optimization is a quarter of all evil

We’ve all heard how premature optimization is bad, but it’s actually just one of four things to avoid doing prematurely. Here are the other three:

Premature generalization

Solving for specific cases is easier than solving for general cases, and accounting for options can add a lot of work. It’s also true that, once you build something, it often becomes apparent that you want something very different. If you piece together specific cases into a larger picture, you can often discover what you really want faster and with less wasted work.

Wait to generalize as late as possible. Odds are good that You Ain’t Going to Need It.

Premature expressiveness

Expressive, elegant code minimizes the number of pieces involved: the number of lines, the number of operations, the number of variables, the number of functions, the number of loops and branches, etc. Skilled programmers can recognize patterns and then compress the code. Often there are trade-offs, e.g. reducing the number of lines may require increasing the number of functions, variables, branches, and/or loops.

The problem is that finding patterns in code, then finding alternative ways of achieving the same results, and then weighing the trade-offs all take considerable time and attention. The danger, once again, is that you might be wasting a lot of time and effort if your requirements end up changing (which they very likely will). It’s often best to live with verbose, inexpressive code for some time until the requirements start settling.

Premature organization

Imagine dumping out the contents of your junk drawer and trying to logically organize everything. Do scissors go with pencils because they’re both tools? Do blank checks go with pills because they are both things you can use up? Maybe the objects should be grouped by size…or color…or weight…

It’s a fool’s game. We could debate how to organize a bunch of random objects until the end of time, and while some choices may be better than others, there are numerous ‘best’ solutions, each equally valid yet equally unsatisfying.

Most code in most projects is like a junk drawer. Beyond a very coarse-grained sorting, there is no useful structure to impose. Does this piece of code belong in that file or this one? In that module or this one? What should we name the files? The modules? How many files and modules do we really need? How should we group things together? Should I move this function down next to that one? Should X be encapsulated with Y?

Very often — most often — these questions do not have good answers. Most fine-grained organizational choices you make will seem arbitrary, not just to other people but even to your future self.

Don’t play the fool’s game. Tolerate disorder until it becomes a real problem and until clear improvements become evident. Should I move this code up or down in the file? Doesn’t matter. Should I put this in a new file? Doesn’t matter? In a new directory? Doesn’t matter. Should this go with that? None of this matters…until it does.

Wait. Eventually you’ll accrete enough new code to warrant some reorganization work. Until then, organizing as you go not only wastes time, it encourages excess structure: numerous files, most with fewer than a couple hundred lines of code, and numerous directories, most with a tiny handful of files. Such fractured structure helps no one: not the project maintainers nor newcomers reading the code.