Friday, March 9, 2018

How should You handle Technical Debt?

What is Technical Debt?

Technical debt is the difference between what was promised and what was actually delivered.

This includes any technical shortcuts made to meet delivery deadlines or to avoid training your development team.

Debt implies you pay interest on this debt until you reimburse it. If not interests shall continuously be added to the debt.

In other words either you pay back your debts and associated interests or you go bankrupt in the long term - because the value of your software diminishes over the years and at some point its value is lower than your debt -

Shipping first time code is like going into debt. A little debt speeds development so long as it is paid back promptly with a rewrite... The danger occurs when the debt is not repaid. Every minute spent on not-quite-right code counts as interest on that debt. Entire engineering organizations can be brought to a stand-still under the debt load of an unconsolidated implementation, object-oriented or otherwise.
— Ward Cunningham, 1992

Why An Agile Approach?

Agile bakes quality into the iterative development approach so the team can maintain a consistent level quality day after day. We ship only features up to expected quality level. Find that hard to believe? The trick is define and respect the definition of "done."

For traditional teams, "done" means good enough for quality assurance to begin working. The problem with that definition is that bugs creep in early in the release cycle–and continue to creep in. So by the time QA gets their hands on it, the product is saddled with layers upon layers of defects. Agile teams, however, define "done" as ready to release, which means developers don't move on to the next story or feature until their current item is practically in customers' hands. The developers are in charge and responsible for the quality.

To do it right, they use techniques like static quality checks, feature toggle, Git, automated testing - TDD and ATDD -, continuous integration and continuous delivery throughout the development cycle.

Avoiding technical debt is what allows development to be agile in the long run. The master branch of the code base should always be good to ship.

For many organizations this is a huge cultural change. With agile, the focus is away from schedules and towards high-quality, demonstrable software. The product owner is empowered to focus the team on the most valuable work, reducing the scope of the release instead of compromising on quality.

We cannot forget: the longer bugs linger, the more painful they are to fix.

Why do You Have Technical Debt?

If you’re working with legacy code, chances are you've inherited some technical debt. The following topics will help you tame existing debt, and enable your team to focus on the fun stuff like new feature development.

There’s a temptation on the development side to characterize architectural work as technical debt. It may or may not be, depending on the nature of the change (e.g., replacing a shortcut with the "real" solution vs. splitting a monolithic code base into microservices).

On the other side, product management often feels more urgency building new features than fixing bugs or slow performance. To avoid either side becoming jaded about the other party's opinion, everyone needs to understand the distinction between technical debt, desired architectural changes in the code base, and new features. Clear communication between development and product management is critical in prioritizing the backlog and evolving the code base.

Why Do You Cheat?

Fight the urge to compromise the definition of done by adding a separate testing task to the original user story. It's too easy to defer them and only invites technical debt. If testing isn’t done as part of the original story or bug fix, the original story or bug fix isn’t done.

Maintain a strict definition of done in your program and ensure it includes automated testing. Nothing saps the team's agility more than manual testing and a buggy code base.

How Can You Do it?

Software and systems built with high quality are easier to modify and adapt when an enterprise must rapidly respond to change. Many of the practices inspired by XP, along with a focus on frequent validation, create an emergent culture in which engineering and craftsmanship are key business enablers. These practices include:
  • Continuous Integration (CI) – Is the practice of merging the code from each developer’s workspace into a single main branch of code, multiple times per day. This lessens the risk of deferred integration issues and their impact on system quality and program predictability. Teams perform local integration at least daily. But to confirm that the work is progressing as intended, full system-level integration should be achieved at least one or two times per iteration. 
  • Test-First – Is a set of practices that encourage teams to think deeply about intended system behavior, before implementing the code. Test-first methods can be further divided into two categories: 
    • 1) Test-Driven Development (TDD), where developers write an automated unit test first, run the test to observe the failure, and then write the minimum code necessary to pass the test, and 
    • 2) Acceptance Test Driven Development (ATDD), where Story and Feature acceptance criteria are expressed as automated acceptance tests, which can be run continuously to ensure continued validation as the system evolves. 
  • Refactoring – This is a “disciplined technique for restructuring an existing body of code, altering its internal structure without changing its external behavior.” [2] Refactoring is a key enabler of emergent design and agility. To maintain system robustness, teams continuously refactor code in a series of small steps, providing a solid foundation for future development. 
  • Pair work – Agile teams can pair using a variety of techniques, many of which can be used in combination. Some teams use pair programming for all code development, as prescribed by XP. Others pair developers and testers on a story; each reviews the other’s work as the story moves to completion. Still, others pair when needed, with developers pairing for critical code segments, refactoring of legacy code, development of interface definition, and system-level integration challenges. Pair work is also a great foundation for refactoring, CI, test automation, and collective ownership. It also reduces or eliminates the need for post-implementation code reviews and rework. 
  • Collective ownership – This practice invites a shared understanding and responsibility for the solution by all team members. “Any developer can change any line of code to add functionality, fix bugs, improve designs, or refactor.” [3] This becomes critical for solutions that have large code bases, especially if only the original developer can make changes. Indeed, this has a similar effect to relying upon specialists, and delays will rule the day. Without collective ownership, there is no collective knowledge, resulting in a higher risk to maintain or enhance the solution. 
  • Agile Architecture – By balancing emergent design with intentional architecture, Agile architecture practices enable incremental value delivery. This avoids Big Design Upfront (BDUF) and the ‘start-stop-start’ nature of a phase-gated approach. Creating Architectural Runway is one of the primary tools for implementing Agile architecture. This runway consists of the code, components, and technical infrastructure necessary to support the implementation of prioritized, near-term features, without excessive redesign and delay.

What is the Minimum Step?

When someone discovers a bug in the software, take the time to add an automated test that demonstrates it. Once the bug is fixed, rerun the test to ensure it passes.

What Next?

Changing the team's philosophy - and that of the team's stakeholders - on how to manage technical debt isn't easy. Business sometimes cuts development time short in order to get to market sooner. With that in mind, let's recap some action items for taming technical debt:

Educate the product owner on the true cost of technical debt. Ensure story point values are accurate for future stories that require resolution of existing technical debt.

Modularize your architecture, and take a firm stance on technical debt in new components or libraries in the application. As the team and business see the agility in these new components, they will naturally want to extend those practices to other parts of the code.

Write automated tests! Nothing prevents bugs better than automated tests and continuous integration.

When a new bug is found, write a new test to reproduce it and then fix the issue. If that bug ever resurfaces, the automated test will catch it before customers do.

Remember, technical debt is a reality for all software teams. Nobody avoids it entirely–the main thing is to keep it from spiraling out of control. Always thrive for mastery.

No comments:

Post a Comment