I was recently reading about Erica Synths and their highly anticipated (I for example certainly wouldn’t mind having one for Christmas) Perkons hybrid digital/analogue drum machine that was announced at this year’s Superbooth and was planned to be available early Q4 of 2021. Perhaps fairly unsurprisingly as we are living in the middle of the component shortages era, they were also hit by a huge delay in getting access to an important chip so they are not able to release the product to market on time. In their official statement they say the following:
“Unfortunately our plans were affected by unforeseen circumstances coming from suppliers – we had ordered micro-controllers for Pērkons HD-01 early in 2021 with a promise to have them delivered in September, the delivery date kept pushed and pushed ahead up to the point of them becoming available only in late 2022. We are currently reworking the PCB to function with other parts that are available to us sooner.”
I fully trust that they will handle the situation successfully but as Erica Synths is a small boutique company, a redesign at such a late stage in the product development, modifying existing parts, restart testing the system from scratch, ordering new parts… Etc. can potentially mean a significant risk and a profit loss for them that is just impossible to plan for.
This story made me think of some of my own past experiences in projects that had to face difficult challenges and how we could more effectively handle inevitable situations like that.
Code refactoring – the necessary evil
Many years ago, working as part of an in-house team taking care of the flagship software product of the company, there was a management decision to switch up and “bring in” the development, changing the previously hybrid (but mostly external developers) setup. The basis of the decision was twofold, the product had a big chunk of legacy code that needed to be improved in order to be able to continue its development and build new features on it effectively and we also felt that this could give a lot more control moving forward.
The first issue is that it is a lot more difficult to communicate than a fancy new feature and also harder to measure your success. It also included a serious amount of code refactoring that opened up a can of worms that the development team wasn’t fully prepared for which led to some unexpected delays and some really annoying new bugs after the first major release. As this meant a big risk for our revenue stream, it caused a serious tension within the team which even spiraled up to the owners of the company.
In the long run, it proved to be the right decision and the step needed to be taken sooner than later but I quickly learned some valuable lessons.
What is refactoring?
Today’s extreme pressure and rush of digitalization puts development teams under some serious pressure to write code faster and add more and more functionality and do both in less time than ever before which inevitably results in “dirty code”. Code refactoring is used to clean up the code without changing its functionality. It is a process for creating an easier to maintain, more reliable, easier to expand upon code which has an overall better quality. This is a huge win but can be surprisingly difficult to communicate and charge for.
In my opinion the best approach is to make code refactoring a regular and planned task that occurs at least before adding any new major functionality. If you are interested and want to learn more about some of the most effective refactoring techniques, I would recommend this page.
The concept of ‘technical debt’
I am slowly galloping towards a concept often referred to as ‘technical debt’ or ‘code debt’ that was first introduced by software developer, Ward Cunningham, who in addition to being one of 17 authors of the Agile Manifesto, is also credited with inventing the wiki. He first used the technical debt metaphor to explain to non-technical stakeholders at WyCash why resources needed to be budgeted for refactoring.
Cunningham years later described how he initially came up with the technical debt metaphor:
“With borrowed money, you can do something sooner than you might otherwise, but then until you pay back that money you’ll be paying interest. I thought borrowing money was a good idea, I thought that rushing software out the door to get some experience with it was a good idea, but that of course, you would eventually go back and as you learned things about that software you would repay that loan by refactoring the program to reflect your experience as you acquired it.”
Development teams often take actions to expedite the delivery of a piece of functionality or a project sort of prioritizing speedy delivery over perfect code and as a consequence such code later needs to be refactored. However, it is important to understand that this is an intentional (even if a bit unconscious at times) decision so bad code doesn’t qualify as technical debt.
“A mess is not a technical debt” writes long-time software development consultant Uncle Bob In an impassioned post. “A mess is just a mess. Technical debt decisions are made based on real project constraints. They are risky, but they can be beneficial. The decision to make a mess is never rational. It’s always based on laziness and unprofessionalism and has no chance of paying off in the future. A mess is always a loss.”
The point I am trying to make is that technical debt is not a result of bad coding or someone doing a lazy or poor job, technical debt is the result of trying to reach project goals and can be extremely useful but it needs to be taken into account and the consequences need to be understood.
Feature Growth ≠ Value Growth
In my humble opinion, customer value is the single most important concept product managers need to keep in mind. It is your customer’s perception of the worth of your product which can mean multiple things. It can be the value for money it offers or the benefit it provides to your target market… etc. In theory, working on a new feature means having a good understanding of your user’s needs, a clear definition of what the feature should achieve and a focus on how best to deliver it through a stellar UX. If everything goes right, this should result in added customer value, right?
For early-stage products, this may actually be true but as the product grows, there’s a natural tendency to build up some “dead weight”. Adding new features one-by-one or changing existing ones step-by-step almost inevitably leads to decisions taken without really considering their impact on the overall product. This creates what Andrew Chen calls “Product Design debt” or Jared Spool calls “Experience Rot”. Customer value doesn’t grow at the same rate as benefits so our product’s overall value may be growing as we’re adding new features, but we’re also increasing its costs to the user (more complexity, more buttons to push..etc.) and being conscious of this trade-off is a really important and difficult task.
Somewhat counterintuitively, my mantra is “the idea of frequent delivery of working software” as all your customers really care about is the working software. This of course doesn’t mean that careful planning is not important, this doesn’t mean that you should not spend time with requirements documentation or design specifications. These are really valuable things as they start communication and help the team to better understand but they are not the value that your customer’s expect. If you had to kill a project 3 months in, would you rather have a detailed specification or an increment of potentially shippable code? My guess is that the potentially shippable code is always going to win.
It is easy to confuse valuable activities with real value delivered into your product.
Closing thoughts
Going back to the original question in the title about responsibility, I have to say that there is no straight answer as most of these important decisions have a shared responsibility. Whether you have a fully in-house team or work with external vendors, you need to be aware of the pros and cons before making an important decision so there is an important role of education and communication on both ends.
Technical teams should clearly communicate when you need to plan for something they just don’t see in their magic ball, the complexity of the task at hand and if taking shortcuts will have consequences later down the road. Likewise stakeholders from other teams should talk about business goals, customer needs and how your product will actually be used at the end of the day so developers can craft a better tool that represents real customer value.
Planning and expectation management go hand in hand and if you have a project where difficult decisions are lining up, please send us a message for a free discussion as we are more than happy to share our recent experiences.