On Interface Complexity vs Implementation Complexity

There are two very different kinds of simplicity in software engineering: simple interfaces and simple implementations.

Simple interfaces are absolutely critical, because the budget for interface complexity is paid out of your head.

But simple implementations are far less important. The budget for implementation complexity is paid out of an ecosystem's capital investments, which can be unbounded and can grow steadily over time.

If two implementations have the exact same features, the simpler one is better. But that is rarely the comparison we are asked to make: in practice, capability and complexity grow together.

The truth is that implementation simplicity doesn't really exist anywhere in modern computing. When a developer argues that "vanilla Javascript" is simple, they are eliding the massive implementation complexity that makes it possible. The system is not simple at all, it's just a high-quality abstraction with reasonable interface complexity.

Even systems-level programmers working in C have a highly-abstracted relationship with the hardware. By the time a modern optimizing compiler has worked its magic, the processor has applied branch prediction and pipelining, and the actual machine instructions are translated into microcode, the connection between the programmer and the real machine has become very tenuous indeed. And that is a positive thing!

The contemporary Javascript ecosystem has far too many people who fetishize implementation simplicity, even to the detriment of overall interface simplicity.

fet·ish n. An object of unreasonably excessive attention or reverence.

Partly this is because they assume it's impossible to ship performant Javascript applications unless the code is very small -- an assumption that is increasingly outdated due to techniques like FastBoot and the rise of Service Worker and progressive web apps.

And partly it's because a lot of teams are still new to the idea of shipping ambitious applications, and think they can still get away with user experiences of yesteryear.

The result is a culture of "many tiny libraries" that gets the tradeoff backwards: by overemphasizing the implementation simplicity of each library, there is an explosion of total interface complexity for the programmer -- once you integrate a sufficient number of tiny libraries to deliver ambitious capabilities.