The Way of the Rubber Duck

Problem solving as a programmer

Michael Bundick
6 min readAug 20, 2019
Slow down, talk through your problem with a duck, or a person if you want to be weird about it.

If you google problem solving you will most likely find something like this:

  1. Define the problem
  2. Generate new ideas
  3. Evaluate and select solutions
  4. Implement and evaluate

While these steps aren’t terrible for general problems in your life, they hide a lot of what goes into problem solving and they also don’t apply very well to programming. So let’s take a look at programmatic problem solving — in this post we’ll talk through the steps, in the next one we’ll put it into practice with a programming problem.

The Eight Fold Path

It’s 2019, still didn’t hesitate to reference Waterboy

1. Believe in Yourself

This step may seem hokey but it has an outsized impact on your ability to solve difficult problems. If you don’t believe you have the tools to solve a problem you are likely to become discouraged, give up, and never solve the problem — a self fulfilling prophecy. If you believe a problem is beatable you are much more likely to put in the time and work to break it down and solve it.

2. Read the Problem for Understanding

Understanding what the problem is asking is critical for getting a correct solution. If you are asked to subtract b from a but instead do b-a you are going to arrive at a solution that is correct adjacent but still wrong. Luckily, there are questions you can ask to make sure you understand what is being asked of you and what the end result will be. The questions below are not an exhaustive list, and some of these questions may not apply to every situation, but by asking yourself clarifying questions you'll be able to begin understanding the problem in a way that is helpful for translating it to code.

  • What is the problem asking me to do? Simply explain the problem in your own words to yourself, another person, or a rubber duck. Restating the problem is going to help you begin digesting the challenge.
  • What is the expected input and output? What are the changeable values I’m giving to be transformed and what should I give back? Don’t just define your input/output vaguely, you should know details such as data type (int, float, bool, etc.), size, length, can it be negative, etc.
  • Can I break this into smaller problems? If the answer is yes, do it and use these sames steps on your smaller, easier problems. Once you solve the smaller easier problems, put it all back together.
  • Are there any restrictions on what I can do? Some problems come with restrictions, either arbitrary to test skill like in code challenges, or imposed by real world demands such as pre-existing code, customer requirements, speed, memory usage, etc. List them out so you don’t solve the wrong problem.
  • Have you seen similar problems? Why do we care if the problem looks familiar? Because you generally don’t get points for being clever. Don’t find a novel way to turn a screw when the screwdriver exist. In addition, even if a screwdriver is not an exact fit, it can point to a solution that does work. Sometimes just swapping out a bit (like the type of screwdriver head) is all you need to do to solve the problem.

3. Start with the Simplest Solution and Scale Up

Understanding a problem at scale can be daunting, you see so much happening that may not be entirely clear to you. If you approach some problems trying to solve for the end result right away, you end up overwhelmed. If you take that same problem and solve for the simplest possible scenario it will usually be trivial but it gives you a starting point to build on. Slowly adding complexity will illuminate patterns in the problem that will allow you to solve it with greater ease.

4. What Steps Did You Take to Solve the Problem in Step 3?

This step is an iterative process. We are going to list the steps, then try to simplify, rinse and repeat. This will take us to the limits of plain language so we will do one final step and write pseudocode.

Why am I pausing here for a moment? To point out the first four steps do most of our work and yet we haven’t written any code. That’s the secret, the core to programmatic problem solving, break down and solve the problem before touching the keyboard. It’s easy to get into the habit of solving by vomiting your first idea onto the screen, have it fail, and then try to solve through iterative trial and error. Don’t do that. Slow down, use pen and paper, think through the problem. Remember the Navy SEAL saying, “Slow is smooth, smooth is fast.”

You don’t have to move physically slow, just slow down and structure your thoughts

5. Translate Pseudocode to Code

We have finally arrived at the programming section of the problem solving technique. We have broken the problem down into tiny bits, we understand what we are giving and receiving, we know when things change, and we have small steps to work with. Only have to translate and we are done, right?

6. Debug, refactor, debug, refactor, debug…

Often times you may have a solution that works in your head but doesn’t work when translated. Or maybe it does but it results in code you would be embarrassed to show. This is where you fix that, read the error messages and bang out the kinks, and then make it pretty-ish. This is also a good time to mull over those restrictions from step 1 again. And remember, good code is DRY, so look for places you repeat or opportunities to make your code more universal, like putting it in a function.

7. Break It

We were so close to being done why are we breaking it?! Because someone else will, intentionally or unintentionally, it may even be yourself later on in the same coding session. So how do we break it? We look for edge cases, corner cases, and even things no sane person should be doing. Depending on the issues we identify and the application for the code, we may not find it useful to patch some of these issues but you should still be aware of them. Robust, reusable code will make everyone’s life easier.

Can’t pass on an opportunity to reference Rocky IV, the best Rocky movie

8. Feedback and Practice

This is the step you should most take to heart if you want to become a good programmer. There is no secret sauce to becoming good at something, it boils down to getting feedback and practicing.

Ask for input and critique, how could this have been written better? Too shy or no one around, self-critique and ask the internet — google your problem and see how other people did it. Side Note: DO NOT google an answer to copy and paste, you learn very little this way, especially if you don’t understand why the code works.

Practice is the other piece of the puzzle. Do code challenges on the regular. Choose problems that push you into unknown territory. Write code constantly. The more you do, the more you fail, the more you learn, the better you get.

One Final Note

Failure is common, it is part of the process. Sometimes you will take on a problem you just don’t grok. That is okay, work it honestly, try multiple solutions, and even if you end up with little to show for it you will likely have learned from it. Don’t give up quickly but do step away if you get stuck, because sometimes all you need is fresh eyes. If you do admit defeat, that’s fine but don’t stop there. Look at how other people solved the problem, understand each step of their process, look for how your approach differs. Knowing how you failed when someone else succeeded is overlooked as a teaching tool too often. Don’t take the L and walk away, learn and get a rematch later on.

Disclaimer: Failing at a coding problem will not make you Batman

--

--