A Successful Project Has Technical Debt
09-10-2025
Technical debt does not mean chaos. Chaos reigns in the vast majority of software projects for many reasons: rush (real or induced), lack of knowledge and skills, lack of resources, dysfunctional teams, negligence, inadequate/dysfunctional corporate culture...
There is no perfect project; there are always areas that can be improved. In my experience, the problem arises when we give up and let chaos continue to reign and grow, or when we go to the other extreme, trying to make the project perfect, ideal.
My understanding of software craftsmanship inherently involves pragmatism, never losing sight of the fact that we develop solutions to specific problems and must seek the most viable alternatives in each context: more economical, with the shortest possible "time to market," more sustainable, durable over time, with the highest possible quality. It's a continuous balance between interests and principles. If we aim to build a perfect, textbook project from the perspective of best engineering practices, it's easy to fall into analysis paralysis. It's easy to spend too much money refining aspects that won't return the investment. The worst that can happen is that we take so long to bring the product to market and it becomes so expensive that the opportunity vanishes. I've seen this very often in projects I've worked on, either as an employee or on my own, with my own money (with my own product ventures). When we launched MavenCharts, our startup in 2010, we spent the little money we had developing a fabulous product with a lot of features that were never used because we ran out of funding and energy shortly after launching to production and seeing that no one used it. We could have validated much earlier with much less functionality.
Am I saying we should develop at full speed and forget about best practices? Negative. All the software we write must be sustainable, which implies it is covered by tests. That's the minimum. When you are proficient with tests and know the principles of sustainable code, it doesn't cost more to apply them. Where we can save is in premature optimizations and automations. We can also save on learning new technology, choosing well-known languages and frameworks (that's why I prefer the JVM or .Net stack). If we aim to launch the product while learning to use the new language we fancy, or that we've been told is the best, and the new trendy framework, we will spend too much money. We can't spend weeks preparing pipelines, defining masterful processes, tuning the latest frameworks on the market, preparing the system for massive scalability, for high availability... for things that aren't needed until after the product is in use and truly justified.
And this is the planned technical debt: all those elements that the software will need at some point if the project is successful, but that currently slow us down from going to production. The lean approach seeks continuous delivery without losing sight of the fact that what we build is solid and well-constructed, so we don't have to work on the same thing again. We seek a constant cadence of product delivery. If the ultimate goal were to make a Formula 1 car, we would deliver it in phases: first, we build a scooter, then a tricycle, then a cart... so that each delivery has some utility. This is very different from spending months building a perfect wheel for the Formula 1. With the scooter strategy, we can hit the track with our scooter early on, learning from what we encounter on the ground to build the next versions. With the perfect tire strategy, we can only look at the track dreaming of the day we have the whole car to ride on it. But maybe the business is about racing scooters!
The utility of digital products is so uncertain that until they are up and running and people validate them, we don't know if they really make sense. Even to solve old and well-known problems, there are always a thousand possible solutions. Optimizations, automations, and technological modernizations should come when we are having so much success with the product that to continue growing, it is essential to introduce them. However, if the product is not well built (for example, when it doesn't have automatic backup tests), we won't be able to introduce those essential improvements for the survival or evolution of the product. In each iteration or development cycle, we have to pay off old technical debt and add or plan new technical debt. We must make small improvements constantly, such as refactoring a little every day. Balancing different interests and needs at all times.
In the day-to-day of projects, there is always learning, especially from colleagues with more experience. Learning by doing is inevitable and desirable. However, projects must be joined knowing how to do it. Stakeholders/investors do not pay for people to train but to produce. That's why, at Lean Mind, we put so much emphasis on training and practice in the form of code katas, pet projects, and learning tasks where there are no deadlines or client money at stake. The company reserves time for training within working hours, so in projects, we apply what we've already proven works.