Mostly I agree with what the author is saying. But there is a clear distinction between the simplest “system” and do the simplest thing.
The simplest thing to do is almost always the easiest, but knowing what is easiest thing to do is a lot trickier —- see Javascript frameworks.
But I think I disagree with the author’s second axiom:
“2. Simple systems are less internally-connected.”
Creating interfaces is more complex than not. Even if it leads to a cleaner design because of interface boundaries. At the least, creating those boundaries adds complexity, and I don’t mean “more effort”. I mean it in the sense that creating functions is more complex than calling “goto”. And it took decades to invent the mechanism needed to call functions —- which is probably the next most simple thing.
However, using call stacks and named pointers and memory separation (functions) leads to vastly improved simplicity of the system as the system as a while grows in complexity.
So in fact, using your own in-memory rate limiter may be a simpler implementation than using Redis, but it it also violates the second principle (using clear interfaces leads to simpler systems.)
And it turns the author’s first premise — Gunicorn is simpler than Puma. Because Puma does the equivalent of building their own rate limiter — managing its own memory and using threads instead of processes.
And Gunicorn does the equivalent of using Redis — externalizing the complexity.
What Gunicorn did was simpler to implement (because it relies on an existing isolated architecture - Unix processes and files) but means it has a greater complexity (if you take into account that it needs that whole system to work.
However that system is a brilliant set of reductions in complexity itself, but it runs up against limitations and performance at some point.
Puma takes on itself more complexity to make administering the server less complex and more performant under load. Also, because it is, in a sense, reinventing the wheel, it lacks the distillation of simplicity that is Unix.
So, less internally connected systems are easier to expand and maintain and interface boundaries lead to less complex systems as a whole, but are not, in themselves less complex.
Limitations in the system that cause performed problems (like Unix processes and function calls) are not necessarily “more simple than can possibly work” —- but the implementations of those abstractions are not perfect and could be improved.
Sometimes it’s not clear where to push the complexity, and sometimes it’s not clear what the right abstraction level is; but mostly it’s about making due with the existing architecture you have, and not having the time or resources to fix it. Until the complexity at your level reaches a point that it’s worth adding complexity at a higher level due to being unable to add the right amount of complexity at a lower level.