The Engineering Method: A Step-by-Step Process for Solving Challenging Problems

Getting stuck before you even begin to work on an engineering problem is more common than you think. Use this method to help you break a problem down, find a path toward a solution, and avoid mistakes.

Formation is a career accelerator for software engineers founded by former Meta and Nextdoor software engineers who have one goal: to help mid-level and senior engineers from all backgrounds land life-changing roles.

Have you ever experienced the feeling of panic when presented with a problem you’ve never seen before, you’re on a deadline, and all eyes are on you? Maybe it’s your manager asking you to work on something urgent and mission-critical, or in an interview for your dream job and you’re across the table from the one person standing in your way of getting the offer. Most of us have felt this moment of panic at some point or another.

In my experience as a tech lead and engineering manager at companies like Microsoft and Meta (then Facebook), the people who are most successful in these situations aren’t always the ones who are the smartest or have the most experience. Rather, it’s the people who used effective strategies for solving problems they hadn’t seen before. The junior engineers who do this well build experience and grow quickly. The higher-level engineers who have honed these skills are the key players on their teams and are often the best equipped to mentor the junior engineers.

But how do people consistently make progress when working on something new and challenging? What do you do when you really have no idea where to start? We created The Engineering Method, a simple set of steps that can help you break a problem down, find a path toward a solution, and avoid mistakes. Here at Formation, we apply the method to software engineering problems, but the same steps can be applied to many different fields.

Step 1: Thoroughly understand the problem


A. Ask clarifying questions

Good questions often fall into one of two categories. They’re either questions that:

  • Gather requirements to precisely understand what the goal is or isn’t
  • Search for subtle edge cases or exceptions to the rules of the problems

B. Come up with your own “happy case” examples

A “happy case” refers to a normal input that isn’t trying to test the edge cases of the question. Happy path and “golden path” are also terms used to describe this. What does the feature, algorithm, or system need to do in the normal cases? What do these normal cases look like?

At this stage, you are using the information gathered to and testing your understanding. You want to be able to generate 2-3 more examples that illustrate the requirements. These examples are going to help you find the patterns that will lead you towards possible approaches.

C. Come up with edge cases

Once you’ve come up with some “happy cases” and understand the problem, test the limits. Try not to go straight to the easy ones like, “what if it’s null”. This is useful for the implementation but is unlikely to inform the algorithm. Instead, try to think about logical edge cases that are more unique to that specific problem.

Some edge cases are easier to deal with in some designs versus others. Listing out these edge cases now will help you choose between multiple implementation options later.

For an algorithmic problem, here are some of the most common categories of edge cases:

  • Negative numbers
  • Empty cases (Empty array, string)
  • Out of bounds
  • Cycles in lists
  • In problems with arrays or matrices, often the literal edges of the data have interesting quirks
💡
Looking to land your next role? Join Formation to get unlimited personalized practice, mentorship, and mock interviews.

Step 2: Identify and explore possible solutions

Always try to identify multiple solutions. Weigh the pros and cons of the different solutions and then select one to try. Sometimes, an attempt doesn’t end up working out, but the data gathered from these unsuccessful ideas often informs better ideas.

For example, let’s say you’re attempting to solve something with depth-first search. While testing the idea on an example, you might discover that it doesn’t work. The way it doesn’t work might lead you to discover that breadth-first search does work. Keeping your work on these unsuccessful ideas might help inform your eventual correct solution.

A. Identify a simple solution


Try the simple thing first. If no obvious solution comes to you right away, work through a list of major solution archetypes for the type of problem.

  • Algorithmic problem: stack, queue, BFS, DFS, dynamic programming, etc.
  • Systems: map/reduce, key/value store, work queue

B. Work through some example cases manually


Take the sample input or ideas from the exploration phase and try to solve it manually. As you solve it, try to see if you can generalize your decisions. If you can't, try making small variations to your input or using input ideas from Step 1 to see if you can identify the patterns.

C. Work out the expected time and space complexity of each idea


Runtime and space complexity is one important part of comparing solutions. Often, but not always, we want to implement the solution with optimal complexity.

Step 3: Choose a solution


A big part of engineering is making decisions and choosing between different implementation options. Proactively point out the advantages/disadvantages of your ideas. If there is more than one solution on the table, you should consider which makes the most sense build.

Often, there is no one best answer, so think about trade-offs and discuss the reasoning behind your decision with your team. Consider time/space complexity, but remember this is only one consideration. You should also consider difficulty of implementation. Remember, simple solutions are often best, especially in a time crunch.

Step 4: Make a plan


You’ve decided on a basic approach, an algorithm, or a high-level system design. It’s time to build it now, right? Not quite. Even for simple problems, a little bit of planning can go a long way. For something small, it might be as simple as splitting out some helper functions. Oftentimes, just starting at the top can lead to confusion and bugs. Starting with small, testable building blocks is often better.

Step 5: Build it


At this point, with a clear understanding of the solution and an implementation plan, the actual coding will go much more smoothly. As problems become more difficult, it becomes more and more important to explicitly split out these steps. Doing so will avoid costly mistakes and, while it feels slow, it will often be much faster in the long run.

For problems that seem simple enough that you can effectively jump into coding quickly, remember that you’ve not actually skipped the preceding steps. You just did them ahead of time in your past work or practice and are utilizing that past experience.

Step 6: Test it


You've written a solution. You're done. Right? NO. As an engineer, you are always responsible for the results and quality of your work. As you uncover potential issues, you will be moving back and forth between 4 and 5. The good news in this step is that you should have already done most of the work! Some test cases should have come from Steps 1 and 2.

A. Test the happy cases


Start with a standard happy case. For example, for sorting, you might test a standard unsorted array: [3, 5, 4, 1, 2].

Pick a few important test cases you have identified earlier in Steps 2 and 3.

B. Test the edge cases

You should be picking from edge cases that you already identified earlier. Once you’ve written the code, you may also discover some implementation-level edge cases. A good way to make sure that you’ve covered all edge cases is to ensure that every line of code is executed at least once! This is also the time to handle truly malformed input.

Wrapping Up

As engineers, our job is to bring new ideas to life. This often means doing something new, something different than we’ve done before, or maybe even building something that has never existed before. This means we can expect to encounter problems we haven’t seen previously. Experience can build some intuition that can seep things up, but the people who solve new problems the best are the ones who can make progress even when experience and intuition can’t help.

Feeling panic set in? Take a deep breath, then follow the Engineering Method.

Looking for structure in your job hunt? Apply below to close your most urgent interviewing skill gaps and interview with confidence.