Why the Most Useful Software Is Built Backwards
I learned to write software in much the same way as other developers, by coding ideas that appealed to me.
I took my grand plans — the grander the better — broke them down into smaller pieces, then joined them together as code until I had something that worked. It felt amazing. I was hooked.
This approach works wonderfully as a creative process and it can be incredibly rewarding. I still do it like this on side projects, building software directly from my own ideas.
Sadly, it doesn’t seem to work so well as a way of building software for use by other people. Particularly not for people who will use it to do things we are not experts at ourselves. And that’s the kind of software we tend to be asked to build in our careers as software developers.
Beyond the relative simplicity of building software for ourselves, developing useful software for others turns out to be far harder.
BACKWARDS, FROM SMALL BEGINNINGS
The most useful software, particularly in complex domains, doesn’t tend to be built directly by decomposing our grand plans or abstractions and turning them into code.
It tends to be built backwards via an iterative merging, coalescing & refactoring of smaller results which add value and get used.
We need to head as directly as possible towards putting entire use cases in the hands of users. This may mean delivering mere fragments or slithers of our grander plan in each release as end-to-end use cases. Then we refactor it, grow and build upon it to add another use case, and another. Each time, taking it into regular use.
Our grand plans still have a place, but we need to combine them gradually into things that prove to be of value, revising them as we learn from what actually turns out to be successful. Because that might surprise us.
the grip of a tyre on a road or a wheel on a rail.
User traction — regular use of what we’ve built — can’t be added as an afterthought. It must be baked into the software we build and the reasons we build it.
Our search for traction can dictate, to a surprising extent, what we even end up building… which is why we must begin with it, track it doggedly and work backwards from the places we achieve it, using them as footholds for our next iteration.
At first glance, software that gets used regularly can look deceptively similar to software that doesn’t, but it might differ fundamentally in its structure or in how well it models the concepts and activities that users expect to be able to achieve with it, or in how it integrates into their workflow.
Beyond our personal projects, user traction and value are the real deliverables in software development. Everything else is less important. Who cares if we love the clean architecture, our choice of programming language or the neat way we did this and that… if no one uses it?
ASSUMPTIONS & EXPERIMENTS
In complex domains, even potential users — domain experts no less — find it hard to describe what might be useful to them. What they describe is usually only a proxy to the features that they actually need or will use.
Being somewhat illusive, traction must be found via a mixture of planning and experimentation. We can aim at where we assume it lies, planning our efforts in that direction, but we need to validate those assumptions by putting software into the hands of real users and getting them to use it regularly… for real work.
Then, as in any experiment, we need to take ourselves out of the equation and just observe what happens when they are left alone with our software. Holding their hands skews the results because we will ultimately not be there to help them. So let’s get out of their way now.
Many of our assumptions about traction are so obvious that we overlook them. This is why traction can seem illusive. We tend to back up too far, make a bunch of new assumptions and abstractions, or pull in too much of a broader plan then wonder why what we release next isn’t of much use. Traction, once gained, is also easily lost.
As engineers, we are often unwilling to test obvious-looking assumptions, particularly when they concern the behaviour of other humans (whom we believe we understand implicitly) rather than software (whose complexity and difficulties we tend to respect).
We should treat assumptions on a par with requirements and use cases; the things we tend to have on our radar. Assumptions should sit alongside them, and our testing of them should be as much of a priority.
Every written use case should be paired with the untested assumptions that lie within it.
User traction, if we have found it, only shows up in Production. Not in demos, not in prototypes.
Demoing to users at the end of a sprint is a useful step — but only a step — towards getting software into their hands for real use on a regular basis, so that should be our goal and where we measure success.
PLATFORMS, GRAND PLANS AND OTHER BIG THINGS
If we focus on traction and growing software iteratively from the places we achieve it, what becomes of bigger plans such as building general platforms or frameworks, which require a degree of planning, decomposition and what we might call forwards-facing engineering?
And what becomes of systems where even the simplest use case requires so many pieces that it will take months to reach end users?
It is still entirely possible to build these things and to seek traction. But we must remain aware as we build them that, until traction is proven, we are flying blind. We mustn’t kid ourselves that we are certain of our destination yet, and we must keep the untested assumption that what we are building will be used and useful in the forefront of our minds.
That way, even with grander plans and longer timelines, we will prioritise validating traction and perhaps adjusting course, at the earliest time that something cohesive — a first use case — can be released to users for real use.
BACKWARDS IS THE WAY FORWARDS
There is far too much unused software in the world. Valuing user traction above all else, and working backwards from it, seems to improve our chances of creating software that is genuinely useful.
Somewhat counterintuitively, backwards might be a sensible way forwards.