Imagine you are building a software platform and you have two paths. Down one path you can take on yourself to add every feature you can ever think of. You will have total control over the features and also total responsibility for each of its defects (and there will be defects). The testing matrix of the features grows obviously with every such features and it’s all on you to test it and make sure it’s all compatible with one another. On the upside, you get predictable execution, easier optimizations, no mess anywhere in your platform - the control is total. This is the declarative approach - nothing that hasn’t been accounted in advance can ever be expressed in such a platform.
Down the other path, instead of having to think of and then implement and then test, etc. all your features, you allow your customers to run their own code, using your APIs, within your infrastructure. If this is inducing sweat - good - it should. Sandboxing is hard even when you are using tools specifically built for sandboxing. Because it’s not only the security aspect (patching, always patching) but the control aspects of limiting time and memory, giving everybody their fair share of resources, more stateful (harder) scaling, and so on. This is the imperative approach - you give tools and others use their ingenuity to combine them into what they need. And at the end of the day you achieved something that declarative cannot ever do: you gave your customers the tools to unblock themselves in the face of your lack of ingenuity or resources.
I recently wrote about unblocking others and oneself. But I missed talking about this third constituency that you can unblock: your customers. If anything, this is the purpose of every product - allowing to do something cheaper, easier, or at all. But in this specific case I am talking about allowing customers to do things in your product which you never predicted they will need to do. Think about it - in the declarative approach you have to think of everything that could be done and then provide it yourself. That already there represents a huge decrease in the funnel of solutions that can be expressed in your platform - you are just not imaginative enough, on one is, to imagine all the problems that your customers want to solve today and in the future.
I have done both kinds: declarative and imperative. And I consider not launching imperative early enough at Moovweb probably the biggest miss of my career. It was already implemented and working and we decided not to launch it entirely due to loss-of-focus reasons. But I didn’t think much of it at the time. I didn’t see, at all, how it is going to influence the rest of the Moovweb/Layer0 platform and how it sent us for years down a certain, very different, and ultimately worse path. Success in software products is very path dependent because we build abstractions on top of other abstractions. And in case of platforms, your users are also building their abstractions on top of your abstractions. And declarative approach makes you the bottleneck of the possible abstractions and thus blocks your customers (mostly at the end potential customers) from forward momentum.
So when in doubt, do the imperative approach first. It is far easier to later add declarative bits and pieces for the most common use and optimize those. But without the imperative “escape hatch” the infinitely long tail of things you never thought of can never be satisfied.
Last modified on 2025-01-31