Your Software as a Kitchen
Updated: Apr 27
A while ago I was consulting with a startup where both co-founders were non-technical. They brought me in something like 6 months after bootstrapping development and the codebase was already a complete mess, with significant levels of fear of change, heaps of unused code, and a general air of despair. While trying to explain the situation to one of the founders, I had the epiphany to ask him if he liked to cook, and when he said that he did, I described the problem in terms of working in the kitchen. This blog post intends to aid engineers in explaining the problems unique to ongoing software development to non-technical decision makers using this somewhat effective allegory.
The Problem With Metaphors
First, though, we have to talk a bit about existing metaphors for the issue at hand. The most commonly used is Technical Debt, describing a situation where pieces of software are intentionally (or not) left in a sub-optimal state due to time constraints. In this metaphor, when leaving a problem in the codebase, you're taking a loan, and this loan accumulates interest over time. The longer you wait to pay it back, the higher the interest component of the loan. This is a useful metaphor, but, as Tyler Jensen noted, it can be easily abused, and like other engineering terms such as Agile, suffer from Cargo Cultism to the point of losing sight of the important bit of a metaphor - making the original problem easier to understand. Soon, you see crusades aimed at 'obliterating all technical debt', which is of course a fool's errand because all technical debt was not created equal, and because an ugly piece of code is not necessarily a problem at all.
Another common metaphor talks about Software Entropy. This is a powerful metaphor indeed, but lacks in two major areas; it tends to lead to overly-technical discussions and, worse, it assumes that you're talking with someone who actually understands what entropy is to begin with. Its essence is that all systems tend to get more chaotic over time, unless effort is invested to counter that tendency. Many explanations of software entropy err, in my opinion, in assuming that causes of entropy are malignant and should be avoided, completely missing the point that entropy is a simple fact of existence.
But perhaps the biggest problem with metaphors is that they do not actually help converge the understanding of a group of people around precise definitions. Each of us might have her own interpretations of what is meant by any specific term, often resulting in mismatching expectations and general mayhem. For instance, one person might assume that technical debt is refers to 3rd party dependencies or infrastructure while another might refer to code. Of course, it could be argued that infrastructure and dependencies are code but this is besides the point. When different people use the same term to mean different things, their communication is hampered.
The Problem With Software
As in the case with Monads, you cannot really have good metaphors for software development, because software isn't like anything else we've had before as a civilization. It's not construction. It's not engineering. It may resemble gardening or rearing children, but we're now going to metaphors again, aren't we? My point is that software is a novel medium of creation, due to the simple fact that software changes over time. It is never done. And it is in these constant changes that the issue lies.
There are, in general, two reasons to change software: either it is to change its behavior, due to a bug or a change in requirements, or it is to change its structure, in order to make it easier to read, understand or change. The latter is usually called refactoring, but might also include changes to 3rd-party dependencies that do not (or should not) affect behavior, but which are not, strictly speaking, a part of your code base. But all changes have to be done against working software, whether it runs in a production environment, or only on a developer's machine.
After a change has been applied to the software, its structure (or design) might no longer perfectly model the real-world concern it aims to solve (assuming that it did before!). This mismatch in itself is not a problem. It only becomes a problem when a subsequent change needs to be applied to the same area, at which point a developer starts off by having a broken model of the world, a wrong set of assumptions, and - in short - extra work to perform in order to affect the desired change successfully. If working in short iterations, any such mismatch can be dealt with quite easily by performing a simple refactor.
But due to a myriad of reasons, this is rarely the case. It might be that the code was originally over-engineered with YAGNI indirections and generalizations; it might be that the previous developer had no confidence in their ability to perform the refactor; a strong engineering leadership might be lacking, allowing business people to state "refactor later" or to generally refer to refactoring as rework and as such unneeded. But whatever the cause, most software is not being continually refactored. So as time progresses, the amount of work required to affect changes to a software system tends to increase, and left untreated, this tendency will eventually halt development completely.
Not A Metaphor
Getting back to the original topic of this post, I will now attempt my own take at explaining this difficulty with software engineering using day-to-day experiences that most people can relate to. As a disclaimer, this is allegory (not a metaphor!) is not complete and should not be taken literally or reduced ad absurdum, but I do find it useful as a means to promote empathy to the problematic nature of software development in non-technical people.
Assume that you're a cook in charge of feeding a lot of people; maybe you're hosting a big family dinner, maybe you're working in a homeless shelter - it doesn't matter; you have one task: feed people. You have generous counter space (but not limitless), a wide kitchen sink, a set of pans and pots, groceries (let's say vegetables and chicken) and kitchenware (plates, cutlery, cups, etc). The task is composed of sub-tasks: you need to prepare the vegetables and meat - peel, slice, dice, etc; you need to cook the ingredients in pots or pans, in some specific order; and you need to transfer ready portions to plates and serve them to people.
There are, however, other, hidden sub-tasks within these sub-tasks; you have to dispose of leftovers from preparing your ingredients; you have to clean used dishes; you may have to store prepared but uncooked ingredients somewhere, but if the counter is full of vegetable peel and used dishes, you first have to move stuff to the sink. The sink, however, might be already full of piled dishes and pots, so you have to stop and wash some dishes. But if you already have something cooking on the stove, you might have to temporarily turn the it off to avoid it burning while you're washing the dishes. Maybe it's a dish that can't afford being halted in mid-cooking. So you move some piles of plates from the counter to the floor to make room for your prepared ingredients while you tend to other cooking dishes.... it goes on.
Working in a kitchen for an extended period of time in an effective manner requires developing an efficient methodology. It will usually evolve around pipelining food preparation, and using short breaks in cooking activity to perform "housekeeping" tasks such as cleaning your counter or washing dishes. A good balance of both "customer-facing" tasks (making food) and these housekeeping tasks is at the stem of a sustainable cooking experience. If you neglect housekeeping tasks for too long, your ability to perform customer-facing tasks gradually slows down until, at the extreme, it completely halts as you look around incredulously to find yourself stalemated by your own mess.
If we go back to software development, by "customer-facing" I refer to bugs, features, or any work that is assumed to have immediate business value, and by "housekeeping" I refer to refactoring or other tasks that are aimed at "paying the debt", "reducing entropy", "pruning the plants" or "cutting away the gangrene", that are seen as having no business value, but that do, in fact, have an indirect business value, insofar as to impede business value when they are neglected. A good balance of both types of tasks is at the stem of a sustainable software development culture.