As I learn more about how to develop software and manage systems, I’m struck that there are many things we learn at some point – only to discover a pile of exceptions later.
A good example: one of the first things you learn as a coder, either self-taught or in an intro course in college, is to make every function small, readable, easily-testable, do as little as possible, and return a result predictably.
So now we know how to code. We just write a lot of small functions and
duct tape them together call them in a sensible order.
Of course, life’s not that simple.
Henry Neeman in Supercomputing in Plain English notes that in high-performance computing (HPC) applications, you want a function to do as much as possible so you can stuff it into a loop, which many compilers can optimize nicely:
How many of you love loops? Hate loops? Do not have an emotional reaction to loops? Okay I want you to love loops.
[In many programming contexts,] we write little routines on little pieces of data because it’s easier to debug and – after all – bugs are bad, right? We don’t want a lot of bugs.
The problem with that approach is then you don’t have enough for the compiler to chew on and the hardware to chew on, and so you get terrible performance.
So the way you want to write code to get good performance is literally the exact opposite of what we were taught when we were wee lads and lasses in our first programming course. You want to write big routines on big chunks of data.
It’s still probably best, in many cases, to develop small bits of code that are easily tested and iterated. (Most applications are not high-performance computing applications.) But it’s not always the right idea.
The takeaway for me is that domain knowledge is as important for building software as good intuition about coding best practices, precisely because “best practices” are downstream of the type of problem you’re trying to solve.