In recent years, I've come to realize that in software development, many teams often focus on too many increments. Yet they would really benefit from breaking down their problems into smaller pieces to simplify their hypotheses and validate them more quickly. Why should they do this? Because what's expensive is "waste", i.e. time spent doing useless things.
My motto was inspired by Mike Hill, who uses the expression MMMSS (many more much smaller steps). What it means is that you can never cut out enough of your work.If you want to create value faster, do many more much smaller steps and validate faster that you're doing the right thing. And there are many ways to do this.
In this article, I explore various techniques, methodologies and tools that can help or even encourage developers to work in smaller increments and optimize their delivery flow.
In my role as engineering manager at Nexapp, I'm constantly asking developers: can we cut out more? Can we eliminate the "nice-to-have"? What is absolutely necessary to demonstrate progress? I ask these kinds of questions because there's a tendency to want to start coding quickly, and you think, while I'm at it, I'll do this task at the same time. But it's like renovating a house, it ends up costing a lot of money!
So what are the advantages of working in small increments? First of all, it allows uncertainties to be eliminated quickly and demonstrates that the solution is viable. It also allows developers to focus on highly targeted elements and eliminate cognitive overload. The result is often code that's better divided, more cohesive, with less coupling and easier to test...
Breaking the work down into smaller chunks also facilitates communication within the team, and allows for a better understanding of objectives. We can validate that the code does what we want it to do more easily, and that the customer's needs are well met, because we're able to put a result in their hands more quickly.
This approach consists of breaking the work down into vertical slices, thus creating a "walking skeleton" or first slice of functionality that shows it's feasible and relevant. In this way, many uncertainties are quickly eliminated and we are able to demonstrate that our solution is viable.
Then, we can build on that increment, on that basis, making small increments that add value as we go along. What this allows us to do is cut the work so small, with very small and targeted elements, that we can stop as soon as the customer considers that his need has been met. This is what we call "good enough". Another effect sometimes appreciated by certain teams is that we can share the work better. Everyone works on the little additional functionality that builds on the main part that has been developed and validated by the group beforehand.
Overall, our aim is to simplify the work of developers by clarifying and simplifying our hypotheses, enabling them to write tests confirming them, and to quickly put the result of this experimentation in the hands of users to validate that we're doing the right thing and the minimum necessary to meet their needs.
A good way of slicing up stories is to use the SPIDR method. This method suggests using different axes to divide up a task. So, we suggest trying to see if we can divide the work according to the different scenarios (Paths), according to the components of the user interface (Interface), according to the type or sample of data (Datas) or even according to the business rules (Rules). You can start by subdividing along one axis, then resubdivide along another, and so on. The order in which you subdivide doesn't really matter. What's important is to see if it's possible to further subdivide a task.
For example, we might consider that adding the button to go to the next page is one feature, and going to the previous page is another (Interface). Or that payment by credit card is different from payment by gift card (Path). Or that we'll start by managing only adult customers, then later senior customers, then teenagers, then children (Datas).
The following illustration summarizes the method:
TDD is a development technique that involves writing tests before developing code. The aim is to guide the design or architecture of the solution, using tests as a guide. This approach allows us to shorten the feedback cycle, minimize the amount of increment added and concentrate on more specific functionalities. It's very simple: we write a test, pass it, then improve the design. Breaking it down into smaller tests helps us to achieve greater cohesion and less coupling in the code, as well as quickly validating whether our solution validates our hypothesis (the test). As we go along, we improve our code, our design and correct our trajectory.
Once our code is written and our tests turn from red to green, we've made our solution a reality. It's no longer a hypothesis, but an established fact. Our design and architecture are therefore not based on hypotheses, but on facts, since it's during the refactoring phase that we build our architecture. Building on facts is often more effective than relying solely on hypotheses.
However, this requires technical excellence. TDD (Test-Driven Development) itself is not magic; it requires excellent refactoring skills and the ability to build an evolutionary architecture. Developers who practice TDD adopt an evolutionary design approach. Each test contributes to the solution, and after each test, we re-evaluate the architecture if necessary. With a solid technical grasp, developers are able to adapt quickly to changes and continually improve their code.
Continuous Delivery is a process that aims to rapidly deliver small increments of code into production. This approach makes it possible to quickly validate whether the code works in a real environment, and avoids major integration phases and conflicts between different developers.
At the start of my career, we worked in waterfall mode and tried to plan everything in advance. We would estimate projects over a period of six months to a year, trying to cover all requirements and architecture from the outset. After several months, or even a year, we would discover the limitations of our imaginary solution and the invalidity of some of our assumptions. As a result, we discovered too late that our solution had to be reoriented, that there would be cost overruns, and that we wouldn't be able to meet our deadlines. Finally, in the last moments before the deadline, we would try to pool all the code that had been done, because we had supposedly planned everything correctly. When the time came for integration, many bugs and conflicts appeared. So there was a stabilization phase, precisely to enable the global solution to function. Unfortunately, many companies still have this waterfall mentality, disguised under the mask of false agility.
The extreme programming philosophy, which came along to correct this and is at the origin of REAL agility, opts for more experimental development. The aim is to try and go faster in a more incremental way, not trying to predict everything in advance. We experiment with small hypotheses that we transform into facts as we go along. So, instead of trying to predict everything, let's go with what we know and then evolve with that, we'll learn from our mistakes, we'll learn from what we've tried to do and we'll adjust along the way. We want to develop continuously, get continuous feedback and adjust continuously. By experimenting as early as possible, you can also validate the result and measure its impact. To know if we have developed the best solution.
Of course, in order to achieve continuous delivery, we need to have techniques for slicing up the work and work in small increments. For me, continuous delivery is a mindset. Yes, it's a technique, but it's first and foremost a mindset that we continually need to experiment.
The advice I'd give to young developers, and then even to more experienced ones, is to continually seek to cut your work into smaller pieces. Just when you think you've achieved the right balance or size, ask yourself: is there still room for more cutting? And the answer is often yes. You'll find out when there's nothing left to cut and it's the smallest "valuable piece". Sometimes, we'll do two pieces at the same time. But separating them, having the vision that they're two different things, gives us the luxury of being able to do them asynchronously.
Unfortunately, we often stop at a certain size and say to ourselves: I don't want to rework the code twice, I'll have to change too many elements. On the other hand, by keeping the work blocks too big, we have many reasons to fail and many reasons to do things that, in the end, shouldn't have been done. You're more likely to make mistakes, or to code unnecessarily. There's no need to strive for perfection or to cover every possible case, because some cases will never happen in reality. It's better to wait and be sure that it's the right thing to do, limit yourself to the essentials, then fine-tune with the nice-to-haves.
I think it's best not to aim for the perfect solution the first time, but rather to discover the perfect solution over time. It's better to accept imperfections in exchange for feedback. Because in reality, the perfect solution will never exist in software development. Software, as it says, is malleable, so it must evolve over time. The context in which a product is developed will continually evolve, whether as a result of technological advances, increasing system complexity, new needs or new markets. To be able to adapt to these changes, it's essential to work in small increments, bringing the analysis, realization and validation phases closer together.
In short, it's crucial for developers to work in small increments to optimize their software development process. Using methodologies and tools such as work breakdown, TDD and Continuous Delivery can help to work more efficiently and validate progress quickly. Developers also need to focus on technical excellence and be ready to adapt to the changing context in which they work.
I've presented a few techniques, philosophies and tools for moving towards MMMSS (Many More Much Smaller Steps), but there are many more, such as micro-commits, the mikado method and refactoring. These are topics for a future article! Here are a few references for further reading: