Some teams play planning poker to carefully estimate and track their velocity. Others don’t have such a formal process, but can feel on a gut level when the team starts to lose speed. Most teams do slow down. The terrible irony is that adding more developers to the team to offset diminishing velocity has diminishing returns. So, how can we get back to the sweet spot of delivering value fast?
The beginning of a project feels so optimistic, and fast. There are lots of foundational decisions, and then you are off to the races. Within a day you can see something on a staging server. A day later, authentication is baked in. By the end of the week, the product future starts to unfold with design looming into coded reality.
While previous projects have decayed into technical debt, this time it feels like you will get it right; you can fully understand your needs and build the right stuff. The possibility of this kind of amazing delivery continuing forever seems easy and achievable.
The thing that can be hard to recognize is that our early work on the project is aided by many great frameworks and libraries that amplify every effort. If we are honest, we have borrowed most of our stellar velocity.
Borrowed and actual velocity at product launch.
The more user interface and product that we borrow with those libraries the faster we go, but with a cost. Libraries are meant to address general needs, and products thrive on addressing very specific needs and flows. The vision that we have borrowed from our libraries starts to drift from our real needs. The greater the borrowing, the faster we descend into technical and product debt.
In the launch phase, teams need to go to market quickly, and it would be foolish to handcraft your own framework. Having a clickable wireframe that has been tested with users, means the developers won’t write useless code. Often though, the product and business team don’t understand or value product design sprints, and aren’t able to cut scope. These kinds of products will stagger under their debt before we can know whether they are viable.
The thing that teams should keep in mind is that these early stage practices of heavy borrowing are only sustainable during this early development, twelve weeks or less. Much of the borrowing will need to be paid back, and postponing that work after the pain point only leads to a compounding slow down.
Now that your product has been validated, your process for developing the product has to change.
Many of the libraries that we depend on don’t serve us well. Because we have gotten so much from them in that early launch, we can hold on to them dearly. Teams will write acres of code trying to force their libraries to bend to their drifting will.
Listen to your pain, and beware of sunk cost fallacy. You can be tricked into investing more time in propping up libraries than it takes for you to write all new code that works the way you need.
Costly libraries typically have a lot of user interface, or opinionated ways of working. It is pretty easy to notice your own user interface patterns and aggregate them into a micro-framework. Don’t rush to this work. Notice patterns when they repeat three or more times, and clean them up then.
Even in code that you have built to do exactly what your product needs, change is constant. Your team will drift from the ideas that were once perfect. For that reason, it is important to write lightweight code that changes easily, and change it often.
Be a good camper and leave your code in a better place with every commit. Small refactors and cleanups will help the team discover patterns and designs that can be reused. Even extracting those patterns should be fast and easy.
Invest 10% of every feature, but not more, on this kind of clean up. That way you will never be forced to do a major retrofit, or rebuild.
In the launch phase of a product, team members wear every hat. Changing hats from product to design to development many times over while working on a feature is fun, but costly. Development happens slower when we have to stop and figure out what something should look like or how it should behave.
Product people are amazing orchestrators. With their focus on delivering the highest impact and value, they can calculate what features and bugs a team should work on first, last, and never. They get into the weeds about how things should behave, who should get to do what. When they race ahead and do that work with the help of design and user research, they de-risk and reduce the cost of development.
Following product and design, we build just what is needed. That means lower technical debt. Consistent products have more consistent code patterns.
Diminishing velocities after launch when a product continues to borrow product and user interfaces instead of investing.
Teams don’t always recognize they have transitioned from launch stage to growth stage. Even teams that know they should be doing things differently can’t figure out how to make that change. Alarmingly often, great products decay into a rescue project.
Teams come up with drastic and unrealistic ideas in these dark times. Sometimes they want to rebuild from scratch. Sometimes they want to break their behemoth ball-of-mud into microservices. A team that can’t refactor that kind of technical disaster won’t be able to rebuild it as a whole or in tiny parts. A code base in this state has to slow down and take stock.
Technical debt of this scale does not happen in a vacuum. It is driven by practices outside the development team. Features that are under-specified or shoved into the queue without validation, pile up as unmaintainable code. Business leaders pushing for unrealistic releases can lead to a proliferation of hard to track bugs. Evaluating the whole company’s process of building software is foundational to getting back to reliable feature delivery.
While it is counter-intuitive, the best approach to massive technical debt is to do a product evaluation so that the team can trim down the feature bloat. A retrospective design sprint that investigates the product’s highest value can help an overwhelmed team focus their efforts.
An idea launches itself into software and then grows up to be valued and used. There are different and important practices for these two phases that can prevent the product from becoming a rescue project.
- Keep it small
- Borrow, but borrow wisely
- Validate everything with design and product practices
- Pay back product that has been borrowed
- Get rigorous about design and product validating features
- Keep trimming and renewing the code