The most challenging part of solving a problem is to define it. More accurately, to redefine it, so that it’s easy to solve. While sometimes a brute force approach is necessary, usually a problem exists because it hasn’t been thought about deeply enough. In particular, the few key variables causing 80% of the problem have not been identified yet.
The ability to solve problems systematically is a very rare skill in every profession, even highly trained ones like medicine. (What’s up, doc?)
Coming from a family of hypochondriacs, I’ve seen my share of doctors over the years. It never ceases to amaze me, that whenever I get second opinions on particular symptoms, each doctor usually gives a very different diagnosis. While the specialists all trained in roughly the same way, mostly by rote memorizing, they vary widely with respect to problem solving. Usually it takes a few visits to find a specialist which can reason through their diagnosis. Even more rare: a doctor who is willing to think about a medical problem as a problem solving generalist, regardless of whether they specialize in anything.
My grandmom had the most robust approach. She always visited 3 doctors. With 3 independent opinions, she had a high chance of getting at least one good answer. Each one usually told her something new or different, so the visit wasn’t a waste of time. It also minimized any individual bias of a specific doctor, or any assumptions which distorted the problem.
Software development is less forgiving of muddled thinking. Programming problem-solving strategies typically go through multiple iterations for serious problems. Here’s one that I use in some form:
- come up with as many options as possible
- looking for completely different assumptions about the problem
- use those as a springboard for related ideas
- come up with a test to verify a hypothesis about where the problem or bug lies
- once I get a test to confirm the problem, design a solution which addresses it
- implement the change
- prove that the change addresses the problem by re-running the same test
When coding, there is almost always a simple way to solve a technical problem. It requires that you understand both your problem, and how the current code is failing you.
Reducing the problem down to its simplest form, ideally where you can describe it in a sentence, helps you plan an angle of attack. Certainly when fixing bugs on pre-existing code, usually 95% of my time is spent diagnosing the problem. This is, of course, working on code without automated testing. Once I really know what the problem is, the fix is usually trivial.
If you define the problem correctly, you will have narrowed down potential causes to the critical essence of the problem, the 20% or less which is causing it. By narrowing your focus to only a few moving parts, it’s much easier to solve the problem. Sometimes this is even possible without code. Often it only requires a small change. Sometimes, you need to do a major overhaul, but if you know exactly what the problem is, you can confidently implement these changes.
In fact, finding this critical 20% is similar to prioritizing. What’s the main area I suspect? What class or method could be causing this? Once you have a guess or two about this, look for the critical 20% within the 20%, i.e. the top 4% of the problem’s causes. How can I test to confirm the exact location of the problem?