Laird Stewart
5/22/26

There's no free lunch in software

Scott Sumner wrote a good post earlier this month on the usefulness of tautologies which got me thinking about tautologies in software development. One which came to mind is

"To reduce intrinsic complexity of a program one must reduce the complexity of the problem it's solving"

I liken this to the no free lunch theorem (hence the title of this post) because when designing ML algorithms, you can only improve performance by limiting the set of problems under consideration (i.e., making assumptions about the problem at hand). Similarly, in software, you can only simplify code by making simplifying assumptions about the problem you're solving.

So what can we learn from this tautology? If you believe that software developers should strive for simplicity (as John Ousterhout argues in "A Philosophy of Software Design"), then you must also believe software developers should focus on making assumptions.

What are assumptions to a software engineer? Assertions, preconditions, and API contracts! We should therefore strive to write methods that looks like

/**
 * Assumes that bar() was called first.
*/
public void foo(Key key)
{
    Preconditions.checkArgument(key != null);
    Preconditions.checkArgument(cache.contains(key));
    Preconditions.checkState(isCacheInitialized());
    ...
}

with the understanding that it will simplify the final program. I've recently found myself writing more and more methods that look like this. When I started out, I would have looked at comments like

"this method assumes that it will only be invoked once"

or

"this only works assuming no more data will be added to this collection"

as "cop-outs" or lazy coding, when in reality they are important design decisions. Every simplification is the flip-side of an assumption, and it's better to document (or code!) assumptions directly than leave them implicit.