There’s a design philosophy I’ve evolved for quickly building small one-off tools. It’s a way to balance build effort against usefulness 1:
- don’t plan. do not plan
- start with a powerful platform, in as ‘stock’ a state as possible
- at first, write the absolute minimum of features
- say ‘no’ to nearly every feature that could be added
- keep your code loose - don’t worry about clean code
- stay under about 100 lines of total code
- aim for evolvability, not perfection
- do not be clever, under any circumstances
I’m calling this approach “Executable Sketches”.
It might seem trivial, but this combination of principles has some very useful properties.
There’s a few caveats:
First, this philosophy assumes you’ve achieved some mastery of your tools / craft. It doesn’t work as well if you’re figuring out your platform as you go.
Second, this isn’t a one-size-fits-all approach. It works best for small tools and programs used and maintained by exactly one person 2.
Third, you’ll be building what could be mistaken for ‘throwaway code’. An ‘executable sketch’ resembles ‘throwaway code’, but the former can be used to build tools that will last for years of continuous use.
The first point is important. Planning leads to over-engineering, YAGNI. Planning wastes time.
Planning assumes you understand the most important problems–which you won’t if it’s your first time writing a thing.
Build on a powerful, ‘stock’, platform
Pick something that’s easy to reach for, easy to setup, and only barely customized from ‘stock’ for your purposes.
(I often use stock Linux with Python with a few additional packages installed, scripted by Bash and GNU Make.)
It’s important that you’re very comfortable with this platform. Because keeping everything under 100 lines will take finesse and experience.
(I’ve written hundreds of tools with that Linux / Python / Bash / make platform.)
Start with the absolute minimum features
Try to go in order of most-to-least important. Iterate until you’ve got 80% of the functionality you need.
Just say ‘no’
Decline to add almost every feature you think of.
Thanks to the ‘build on stock platform’ rule, nearly every feature of your tool should be provided by the platform & libraries you use, not by your code.
In ‘Pareto-speak’, you’re doing the right 20% of the work to solve 80% of the problem. To put it another way - you’re not doing 80% of the work of a full solution with all the bells & whistles. Which should give you a sense of just how often you’ll say ‘no’ to features 3.
Cut until it hurts.
The only things to say ‘yes’ to are: the absolute bare minimum necessary features, plus a few small features to keep things interesting / fun.
Loose, messy code
You’re creating an ‘executable sketch’. It should feel like a sketch, with loose pencil strokes outlining a shape without careful delineation.
Just as cleaning up linework is wasted effort in a sketch, so is cleaning up your code (too much).
≤ 100 lines of code
I’ve seen plenty of real-world projects that meet every criteria I’ve listed here except keeping the source code very short.
Literally 5,000+ lines-of-code monstrosities of messy, sketchy code. The worst of all worlds. That sort of system is not evolvable, not easily comprehensible, just bad. Which entirely defeats the benefit of the ‘slightly more than nothing’ approach.
So keeping the codebase very, very small is the redeeming quality that makes my approach work in practice.
This isn’t a hard-and-fast rule - you may need, say, 500 lines of code for some specific tool. But the upper bound should be very, very low.
Evolvability, not perfection
Given the choice between ‘perfect’ code and easy-to-replace code, choose the latter.
It’s better that you can rip out large parts of the system without too much trouble.
Keeping the code short helps increase the odds that it is evolvable.
Do not be clever, under any circumstances
Your code should be so simple it’s almost stupid.
Straightforward is better than ???
There are productive, useful ways you can be clever, though. Anything that saves time but doesn’t complicate your code is a good kind of clever. For instance, using a automatic code formatting tool (if that’s your thing). But you should cut out anything that becomes an end to itself.
There are several benefits to the ‘slightly more than nothing’ approach.
Done properly, it barely needs any documentation. A short readme, a few comments sprinkled throughout, and a note at the top of each major source file.
Anything built with this approach should be easily understandable by someone moderately well-versed in your platform.
I like to think of this approach as “falling backwards into a bucket of success”. In that metaphor, the ≤ 100 LoC rule is the ‘bucket’, all the other rules are ‘gravity’. The bucket is full of ‘success’ because: the less code you write, the less you need to understand, maintain, document, comment, test, fix, etc.
I like the economic definition of the phrase ‘sweet spot’:
the optimum balance of costs and benefits
For most of the tools I write with this approach, the optimum balance is about 1-3 hours of work (cost), and weeks or months of usage without major modifications (benefits).
this article ‘eats its own dogfood’: it’s about 100 lines of text, written in haste. ↩
partly because involving more than one person means you’ll need documentation, which adds too much friction. And this thing you build will have bugs. In fact, some bugs are expected to never be fixed with this approach. ↩
some fine features I’ve said ‘no’ to with this approach:
- portability to multiple OS’s - it’s nice, but ‘no’
- detailed error messages - wouldn’t it be great, but ‘no’
- more and more command-line arguments & flags - heck ‘no’
- better error handling - ‘no’ sir