Measuring progress instead of activity
Author: Valeriano Sandrucci
The arrival of TDD
Several years ago, TDD arrived: Test Driven Development. The underlying idea was simple: the number of errors that a compiler, even the best one, can find in software is tiny compared to the problems that emerge at runtime, when the system is actually being used.
The proposal behind TDD was apparently trivial: in addition to application code, write test code as well, meaning software that automatically runs other software to verify that it behaves as expected, and to catch bugs before they reach real users.
A divisive practice
It seemed like a sensible technical revolution, and in fact it was. But it polarized the developer community far more than many other practices: on one side, the supporters, convinced they had finally found a way to concretely improve reliability and maintainability; on the other, the detractors, who argued that tests, in practice, slow development down and add overhead.
Over time, the industry moved forward by proving the first group right: static analysis tools, quality metrics, and automated pipelines have grown. Today, test coverage around 80% is considered normal in many contexts, and in some areas it even represents a minimum threshold of acceptability.
And yet the polarization has never truly disappeared.
The shortcut of velocity
Periodically, the idea resurfaces that “all this quality” is a luxury, and that the real priority should be velocity: delivering faster, releasing sooner, increasing the team’s throughput.
This view too, taken in the abstract, is reasonable. The problem begins when activity is measured instead of progress.
In the name of velocity and agility, practices are often justified that are not very compatible with real agility and speed. The most common form is:
“Let’s just start, and then we’ll see.”
Starting without ignoring uncertainty
Now, let’s be clear: not every project is avionics or railway software, and not everything requires ultra-rigid processes or heavy formalism. In many cases, it is correct to start before every single detail has been clarified.
But “starting” does not mean ignoring uncertainty. On the contrary: it means being very aware of which uncertainty to accept and which uncertainty to reduce immediately.
The distinctions that help
The first useful distinction is understanding what is already stable and what is still ambiguous. Stable parts must be protected, while uncertain parts must be designed to evolve without breaking the rest of the system.
The second is identifying the reasons why something is unclear, because while uncertainty is sometimes inevitable, at other times it simply comes from a lack of analysis, design, or adequate expertise.
Let’s return to the example of testing: there are portions of code that are difficult to test, but that can become easy to test as soon as better design techniques are adopted. In this sense, testing measures not only the quality of the software, but also the quality of the architecture.
What we choose to measure
The third distinction, perhaps the most important one, concerns what we choose to measure.
It is possible that software without tests reaches production before well-tested software. But if we then consider the total time needed to obtain a system that truly works, including bug fixing, rework, regressions, production incidents, and evolutionary slowdowns, the apparent advantage often disappears.
The same applies to design, architectural analysis, technical debt reduction, or incremental requirements validation: all practices frequently accused of “slowing things down”. But that accusation holds only if we consider immediate activity instead of the overall result.
If instead we measure:
- the real budget needed to reach a stable system,
- the time from kickoff to actual availability for users,
- the software’s ability to evolve over time,
- the level of satisfaction among stakeholders and technical teams,
- the cost of rework and wrong decisions,
then something interesting emerges: bringing areas of uncertainty to the surface and addressing them explicitly does not slow the project down. On the contrary, it is often the fastest way to obtain a sustainable result.
True velocity is not the amount of activity produced in the short term, but the speed at which an organization reliably transforms an ambiguous problem into a working, maintainable, and useful system.
This is the point where software engineering stops being code production and becomes conscious risk management.
Author: Valeriano Sandrucci
