Tackling Tech Debt: A Three Step Framework

Tackling complex design debt: A three-step framework

Main illustration: Haejin Park

Inconsistent product functionality, wildly differing UI components, and conflicting terminology – the slow creep of design debt can have far-reaching consequences for your business if left unchecked. Here’s a simple framework for managing it.

A few years ago, we wanted to improve the A/B testing functionality in some of our targeted messages. It was a trivial feature that we thought would be a cinch to build. But as the self-referential adage of Hofstadter’s Law goes, “It always takes longer than you expect, even when you take into account Hofstadter’s Law.”

So when this five-month-long project reached completion – at four times the amount of time we had initially estimated – we were left asking ourselves the question: what went wrong?

Moving fast makes you slow

There is a somewhat legendary startup mantra, initially popularized by Facebook, that says you must “move fast and break things” when trying to scale a product. The thinking behind the saying, which was one of Facebook’s original employee values, is that if you’re not breaking things, you’re not moving fast enough. It’s not a new concept, with IDEO founder David Kelley expressing a similar sentiment in one of his famous tenets of design thinking, “fail faster to succeed sooner.”

In our early days, like many other companies, we prioritized the speed of shipping over maintaining the consistency and interconnectedness of our system. We were a small startup, still trying to figure out what the future held for us, so it seemed reasonable to move fast, regardless of the risk of accruing design debt.

“Our in-app messages and email messaging enabled companies to reach customers inside the product in real time, so they could deliver the right message, at the right moment”

Initially, our sole focus was developing in-product messaging. Fuelled by our mission of “making internet business personal,” we wanted to make it easier for businesses to build personal relationships with their customers at scale. Our in-app messages and email messaging enabled companies to reach customers inside the product in real time, so they could deliver the right message, at the right moment.

Soon after, we decided to make those available not just for existing customers, but also for potential customers who visited a business’s website and product pages, to help these companies convert visitors to users.

But since that functionality solved a different problem and we were not clear about how it would evolve, it was viewed as an experiment, and built by a different team as part of a different product offering. This decision spawned many technical and design inconsistencies between similar messaging functionality that was implemented elsewhere in our products.

“If we had looked a little closer, we would have seen that we were accumulating two distinct types of design debt in our product”

As we were focused on moving fast, it may come as no surprise that this pattern was repeated when we launched our mobile push messages and a few years later, when we launched our customizable bots. From a distance, these were simply messages that were built in very different ways, for very good reasons. But if we had looked a little closer, we would have seen that we were accumulating two distinct types of design debt in our product: user-experience debt and functional debt. Let’s take a closer look at both of them now.

1. User experience debt

User experience (UX) debt inevitably leads to friction for customers, which leaves you open to having them poached by competitors. It’s the little things that add up over time to really irk a user, and inconsistencies across user interface (UI) components – like buttons, menus, or content – are all symptomatic of a larger issue at play.

For us, there were many points of friction in our in-product messaging: from the inconsistent UI experience of building various types of messages, to the conflicting workflows that contained the same steps as another messaging UI, but in a different order.

For example, when users would segment their audience for in-product messages to existing customers, they would do it through the UI below.

But when they were sending what was essentially the same in-product message to potential customers, the UI would look completely different. So our customers would have to learn the ins and outs of a brand new UI to do the same job – which unsurprisingly caused a lot of friction for our users.

Although differences like these could be considered trivial on their own, together they created a “death by a thousand cuts” effect, rendering the product difficult to understand and hard to use.

2. Functional debt

Alongside this issue, we were quietly building another type of debt: functional debt. This stemmed from the fact that different teams had built each of those message types in a unique way, which resulted in some features being deprioritized based on a particular team’s goals and objectives.

On the surface, there appeared to be illogical inconsistencies across messaging functionality, like scheduling, A/B testing, or goals. Once again, users were confused, and it didn’t take long for the requests to pile up.

And whenever we built a new feature, we’d literally rebuild it for each implementation so it was available for more than one type of message. This left us at a difficult crossroads: continue to duplicate the work and waste time to satisfy customer needs, or focus on a single message type but create more UX and functional debt.

The mindset of “moving fast and breaking things,” despite all of its initial short-term gains, had finally lost its magic, and had made us slow in the long run. We knew we had to change tack and find a way to align teams and functions as we scaled.

A three step framework for tackling the debt

Once we knew we had to tackle our debt, we gleaned insights from our previous experiences to produce a three-step framework for reducing it. Here’s how we did it:

1. Visualizing design debt

A part of the problem with solving design debt is the lack of alignment around it. Some people are focused on just their own part of the product, and lack a broader picture. Others have that broader picture, but lack a deep understanding of the details.

So after we realized the need to change the status quo, our first step was to map out all of the inconsistencies between the different message types. From here, we could create a single unified flow for all messaging.

The initial prototype was rough – we built it on a hack day. At this point, we prioritized improving the nuts and bolts of the user flow over the aesthetics. But it made the idea tangible and showed that, in theory, everything could be built on top of a single system. This created much-needed alignment on the team, so we could push to get the project added to the roadmap.

2. The importance of prioritizing and rationalizing designs

But as soon as we started talking about prioritizing this work, we met some expected pushback. When weighting a rationalization of existing features against building something new that customers are asking for, the former would almost always be deprioritized. A word of advice on this: to make sure you don’t run into a similar wall, it’s important to build a strong customer and business case on why tackling debt is so important. This usually isn’t easy, but it will help you get the result you want.

Customers are rarely asking you to fix inconsistencies. So instead you should examine indirect indicators: what are your activation rates, or how confusing is your product to people? For example, our product had twice the amount of associated customer conversations tagged with #customer-confusion, compared to any other part of Intercom.

“We had to spend countless hours reimplementing the same features in a slightly different way, leading to a drastic increase in the time to ship”

You can also explore internal indicators. For example, what are the main things your engineers spend time on? In our case, we had to spend countless hours reimplementing the same features in a slightly different way, leading to a drastic increase in the time to ship.

Finally, are there any initiatives that might be related to tackling that debt that the company has already committed to? If so, you should try to connect them together.

Back then, we were developing a new message type, called Product Tours, and in order to avoid repeating our mistake of building a completely custom message type in a non-reusable way, we pushed for this rationalization to be done.

Faced with tangible evidence of this backlog, everyone could see how important it was and knew that it needed to be tackled ASAP.

3. How to approach design debt

If you already have alignment across teams but are unsure of where to start, here are two methods for tackling your design debt:

1. You can take a bottom-up approach. Let’s say our system consists of these five objects. In the bottom-up approach, you’d first focus on just these two objects, ignoring the rest.

After creating a unified system for them, you’d move on to rationalize it against the next object and so on, eventually arriving at a unified future-proof system. It’s a simple, manageable approach. You solve one problem at a time and for the time being, you ignore the rest. But be warned, such an approach can lead to blindness, as you are only looking at a narrow part of the problem, resulting in the need to re-evaluate previous decisions.

2. Alternatively, you can take a top-down approach. This involves rationalizing various objects in tandem to create a unified system.

This approach gives you a bird’s-eye view so you can make more future-proof decisions, but it isn’t without its pitfalls. Due to the sheer size of the problem, you may find it’s easy to get lost in the weeds, which can lead to ambiguities and roadblocks.

Now, as for which approach is better, I think the short answer depends on your product vision.

Basically, how confident are you in the future evolution of all the things you are rationalizing? Do you know which use cases they would need to support and which capabilities they’d need to have in a year’s time? Do you know if there are any other similar objects being built in the near future?

If all of that is clear, it seems logical to pick a top-down approach, allowing you to rationalize everything in one big swoop. Otherwise, starting bottom-up would make much more sense.

Yet most projects are more complex than the ones I’ve described above, as the clarity of your product vision may vary widely for each part of your product.

For example, looking at all the outbound objects in Intercom’s system, we were quite confident about how some of them would overlap with each other and evolve in the future, while for others we were not so sure. To avoid painting ourselves into a corner, we took a holistic look at everything to identify patterns among them.

As we were doing that, we ended up cutting off some of the objects from our rationalization since our vision for them was too fuzzy. Others were fully considered, but were discarded once they reached a point where things started becoming blurry. Finally, we narrowed it down to a list of objects that we ended up rationalizing at a very high level of fidelity.

And this combined approach, of starting top-down and switching to bottom-up, allowed us to have the best of both worlds – a holistic understanding of how the system should work without getting lost in the complexity of the system.

Embrace debt

Through that process we were able to come up with a unified future-proofed system to support all of our message types. After releasing the changes a few years ago, we saw a 33% decrease in conversations tagged with product confusion, an improved user experience for activation, and a dramatic acceleration in our ability to build new functionality.

Which brings me back to my initial statement: “Moving fast makes you slow.”

The truth is, I don’t think it’s actually a problem. Incurring debt is not something to be avoided at all costs. It’s a natural result of operating in a highly ambiguous and fast-paced environment. “Moving fast and breaking things” when your product vision is weak is much better than over-optimizing for a future that may never come.

But once you’ve strengthened your product vision, it’s time to take a pause, look at everything that you’ve shipped, and create a scalable system to support future growth; one that supports moving fast without the penalties of design debt.