No Boilerplate
This AM I stumbled on this, that I wrote back in 2015:
I’ve been programming for almost three (3) decades now and I’m in love with it as ever. But where before I was younger and had more patience for brutality of repetition, if not in code, then in code structure, today I really hate repeating myself. Hence this site where I’ll be creating and curating things that save me code and typing, but not in some kind of “generalized library” sort of way, but really saving it by allowing me to work on a higher level of abstraction.
Here are the rules of engagement for no-boilerplate projects:
- No code generation - instead we “run” higher-level abstracts and allow for behavior customization with hooks for lower-level code if needed.
- No backward compatibility - instead, if there are any braking changes, we convert old higher-level abstracts to new higher-level abstracts (either on fly or permanently)
Looking back at this, it has aged very, very well:
- My biggest gripe with LLMs is that they are code generators. It is up to us to recognize that, structure their output accordingly, strongly drive toward eliminating repetition. Hence, what I want is not LLM generating code for me but LLM compiling prompts for me. I have never put it quite that way before but I wrote about it here and here
- What I used to call “hooks”, today I call “escape hatches”: a way to escape out of the higher level abstraction into the lower, underlying abstraction. I am a strong proponent of deliberately leaving these points in the platforms even if they are often abused.
- I will point out that the principle above is in direct contrast to the way I approach building the actual working code (vs platforms for others to build): I purposefully remove the degrees of freedom from the code until the only thing that it can say is the thing that it is saying (maybe analogous to David Deutsch’s hard-to-vary explanations). Software is… well, so soft that is too easily malleable and the biggest challenge to correctness and the velocity is too many degrees of freedom. So within the confines of a single component, I strive for the highest cohesion possible but then allow for components to be either bypassed or replaced as needed (e.g. a daisy chain of HTTP proxies is a classical example: they all conform to the same protocol - HTTP - and hence you can choose where in the chain you are adding/removing a proxy)
- Today I am even more bearish on backward compatibility shims in the code and I more strongly prefer expressing the old behavior in the terms of new behavior. Backward compatibility of behavior is essential, especially when it’s not a dependency leaf and other agents (like customers!) depend on it, but un-abstracted (primitive if you will) shims in the code are a sign of a decaying process of the software design and building.
- Contrast a bunch of
if
statements sprinkled around with abstracted backward compatibility like keeping old interface but implementing it in terms of new code or explicitly recognizing in the type system that there are older versions of the code.
Last modified on 2025-04-22