Main illustration: Frederique Matti
The most productive engineers I’ve ever worked with aren’t the engineers who pull all-nighters or clock in 80 hour work weeks. Nor are they the engineers who can effortlessly craft an elegant five lines of x86 assembly to succinctly and efficiently solve a problem.
They are the engineers who always seem to be solving the right problem in the first place. If the mythical 10x engineer really does exist, it’s the engineer who is 10 times more likely to solve the right problem.
While we might always set out with the best of intentions to solve the right problem as engineers, there are many mistakes we can make that will send us in the wrong direction.
What solving the wrong problem looks like
Imagine you’re working for Silicon Valley’s newest, hottest unicorn start up. Your job is to build the best new to-do product since man first put pen to paper and the bullet point list emerged. The great visionary of the company, the VP of Product, walks up to you and tells you that it’s time. You’re going to build the killer new feature: notifications on your phone to remind you to tackle a task on your to-do list.
The idea comes to you straight away. “Let’s build a notification delivery framework in a Go microservice.” This seems like a solid idea, because you’re going to avoid the legacy cruft of this monolithic application that you’ve been working with for years, the one that’s been driving you nuts. And you’re going to perfectly encapsulate this simple task of sending notifications in its own little service. What’s more, you’re going to write that in a language that’s perfectly suited to this network-call-heavy workload. And you’re going to make it so that you can add notifications to this thing in the future.
On the surface this seems like a really good idea, right? Your startup ends up with a key piece of technology that will solve important problems for years to come.
But this statement terrifies me. It has a bunch of warning signs that you’re about to go off and completely waste a whole lot of time, effort and money solving the wrong problems. So, what does it mean to solve the right problem?
Software engineers aren’t paid to simply produce lines of code.
To borrow an idea voiced by Tyler Treat, Phil Haack and likely many others, software engineers aren’t paid to simply produce lines of code. If we take a step back, we’re really paid to build features, make things more efficient, make them scale bigger and bigger, and fix issues or prevent outages. The code is a byproduct of solving these problems.
But more than that, engineers are ultimately paid to contribute to the success of the organization we work for in very real world terms. Engineers are paid to solve problems that increase revenue, decrease costs, increase customer satisfaction, decrease churn. When your work is focused on moving these kinds of metrics, then you know you’re truly solving the right problem.
So, back to your perfect notifications solution. Are you solving the right problem? I’m going to go through three mistakes that I see here. Mistakes that I’ve seen engineers hit over the years when starting a new project. Mistakes that tell me that you are not solving the right problem.
Mistake 1: Technology-driven development
The first thing you’ve done is jump to a very technical solution – build a notification delivery framework in a Go microservice – very quickly. You’ve received a high-level feature request and you’ve immediately made a language and architecture choice without pausing for breath. By jumping to a particular technology, you have skipped over a bunch of really important questions:
- Do you actually need to solve the problem of making this service as efficient as possible? Or have you traded off a bunch of developer productivity to get performance gains from Go that you didn’t need?
- Is this the right place for a microservice? Will the amount of information you have to share between the main application and your new notification delivery service couple them so tightly that you’ll have added a bunch of complexity?
As engineers, we can become particularly enthusiastic about using one technology, because we want to learn it, get better at it, know it really well or have had great results with it in the past. If all you have is a hammer, at best you’re using an awkward tool for every screw you come across. At worst, you’ll seek out nails. You’ll seek out the problems that your tool of choice works best for. They probably won’t be the right problems to solve.
Instead, write down a description of your problem and why you’re solving it. Focus on the value solving this problem will bring to your organization, and avoid any references to solutions or technologies. Even if you’re 99% sure you know how you’ll solve the problem, try as hard as you can to not let that potential solution color how you describe it.
Later, when you’re designing your solution, you can test it against this description of the problem to make sure you’re not straying off and solving problems you don’t need to solve.
Mistake 2: The premature framework
Write the simplest piece of code that solves that problem.
The second mistake you’re making with your proposed solution is the massive bet you’re taking on the future. You might need to deliver all sorts of different notifications at some point, but what if the other notification types never become a concrete requirement? What if they’re just different enough that they don’t really fit in your framework? This is a massive and expensive form of premature abstraction.
As engineers, we want to build cool things. We want to build the more complex system – the one that solves bigger problems than we actually have (now or in the future).
In this example, there’s one problem you definitely have, the problem the VP of Product asked you to solve – deliver notifications for task reminders. Focus on solving that problem. Write the simplest piece of code that solves that problem. But follow good coding practices so that it’s possible to evolve your code in the future when you know more, rather than explicitly building in flexibility and extensibility in a bet on the future.
How can you avoid the premature framework? After you write down that description of your problem and why you’re solving it, get it reviewed. A peer who isn’t caught up in the excitement of the cool, new service you’re going to build will be more objective and able to point out the smaller problem that you should be focusing on. This means you’ll be setting out to actually solve the right problem.
Mistake 3: The sunk cost roadmap
The third and final mistake is the worst of all, but it’s not immediately apparent. It will emerge months down the line when you double down on solving the wrong problem.
You’ll have invested a lot of time into building a general purpose notification delivery framework, and it will be complex and expensive to run. Remember, it only has one use case, reminder notifications, but you’ll find yourself sitting down to dream up all of the other things that you could build with it. You’ll ignore the actual business problems and feature requests from real customers, and build a bunch of features that fit the solution you already have in place.
So, how can we avoid the sunk cost roadmap? This is where vigilance and, dare I say, some small amount of process comes in. Write down a description of your problem and why you’re solving it and get it reviewed every single time. If you build this habit, or you bake it into your process, you’ll flush out moments when you’re about to build the wrong thing – no matter how they get onto your roadmap.
At Intercom, we’ve been practicing this for a number of years with what we call an “Intermission”. It’s just our quirky name for a project brief. Before we get started on a new piece of software, it forces us to answer the question, “What problem are we solving, and why?”. While initially a tool our product managers used when thinking through new customer-facing features or products, we’ve found that it also works really well for backend engineering projects, because it keeps us focused on providing real value.
Focus on the problem – each and every time
So before breaking ground on any project, write down the problem you’re trying to solve and why you’re solving it. Get that reviewed before you make any decisions or write any code. And make sure you do it every time before you start building a new piece of software. Do this, and you’ll avoid the many pitfalls that can keep an engineer from solving the right problem.