Embracing Structured Problem Solving

William Mills
Launch School
Published in
6 min readJul 10, 2019

Learning how to be a good programmer is learning how to solve lots of small problems and understanding how they all fit together into a stable, coherent and functional whole.

Structure is important

One truth that has been really hard for me to learn is that coming to solutions is only a small part of problem solving. If you skip over understanding the problem then you’re skipping over the most important step.

It’s really easy to be given a problem, ‘feel’ like you can see a solution in your mind and then ‘run off’ towards getting there. With simple and easy exercises you can often do this without penalty, but as the difficulty of the challenge ramps up, the pitfalls of this approach become more apparent.

Part of progressing through Launch School is solving hundreds of small problems exercises and dozens of far bigger challenges and projects. We’re often provided the luxury of being provided test cases as part of the problem description, but that’s a dangerous crutch to get used to leaning on. It’s really important as a part of understanding the problem to extend these test cases by writing your own *before* you even start thinking about data structures or algorithms, let alone actual code.

Launch School emphasizes the PEDAC approach to problem solving:

  1. Process (or understand) the problem
  2. Examples/Test-Cases
  3. Data Structure
  4. Algorithm
  5. Code

It’s no coincidence which order these come in, even if it can initially feel tedious to go through each step. The payoff of embracing this process and spending time in the first steps is that you can expose errors in your understanding before wasting time and effort going down a path that isn’t fruitful. Also, while you’re under pressure in an interview it’s really great to have a map so that by the time you get to writing your code you’ve already outlined a plan that you (and your interviewer) can reference as a guide.

You can miss things by rushing through them

There are always multiple paths towards a programmatic solution, and realizing when you’re at a logical junction requires the patience to refrain from leaping at your first idea. By simmering at understanding the problem and personally writing your own test-cases to test that understanding, you can often reveal alternate approaches towards a solution that you might have otherwise missed.

In watching other people code it’s strangely common to see people early in their process write pseudo test-cases, but in a way that those cases aren’t really useful for testing. I understand that it’s a sort of brainstorming short-hand while exploring the problem, but you should always write your test cases in a way that will be useful to you later on because you will always need to verify that your code is functioning as expected. There’s no sense in doing the same thing twice. In coming up with test cases, you should have test cases not just for the happy path, but also for inputs that are zero, one, few, many, boundary conditions and probably some bad input. Will negative numbers, null, undefined, float etc… throw your code sideways? Ask your interviewer if these inputs will be possible, or how they want them handled.

Choosing an appropriate data structure is worth slowing down and thinking about, to consider the tradeoffs of one choice over another. If you’re modifying a string in some way, it’s often reflex to convert it into an array of characters and work from there, but halt there and think: can you use a regular expression to accomplish your task more directly? If you’re converting the value of a series of string numbers like ‘zero’, ‘one’, ‘two’ etc… into workable numbers, you could make a Ruby hash or javaScript object and store key-value pairs — but what about leveraging the inherent indexing of an array? Not every solution demands this level of exploration, but slowing yourself down at every stage of the process can open up new solution avenues that strengthen you as a programmer. Relying on your reflex to utilize your favourite technique can blind you to the power of other techniques.

When you get to the Algorithm stage, don’t ever allow yourself to be vague about how or what you’ll be doing. Don’t leave things to solve for later while actually writing your code. If your plan is to swap every third letter in a string with a ‘*’ character, that language is far too vague. Break that down, and try to use words that relate to the computer language that you’re using. Are you going to iterate over the string, or do you need to convert it to an array first? Where will you start iterating and to where? What type of iteration will you be performing? Is it transformation (map), selection (select/filter), iteration with side-effects (each/forEach), reduction (reduce) or other? Will you use the index?

I suggest your pseudocode should look more like:

— Split the string into an array of characters

— Iterate over each char from index 0 to the end

— IF index % 3 === 0 (transform char to *)

— ELSE return char as-is

— Join and return result

This problem is simple, but the point is that by increasing the level of detail in the pseudocode, it makes writing the actual code much easier. By using language like ‘transform char’ it hints that map is the appropriate iterating technique. Being used to writing pseudocode at this level of detail will benefit you when the problem or the pressure of the situation makes things more difficult.

Sometimes I catch myself trying to be too clever in my problem solving approach and I forget to utilize the raw power of computers by choosing a brute-force solution. Especially while under pressure in a live-coding interview, in some cases a brute-force solution is perfectly fine, and is probably much easier to implement. If you catch yourself getting lost describing the problem, your idea might be too complicated for the conditions.

VSCode on the left, iTerm2 running node on the right

It’s especially helpful to have a terminal window running side-by-side with your code editor so that you can hop into IRB or Node and test each little problem snippet as you work through the algorithm. My technique (I use an old macbook) is to fullscreen both my text-editor and console, utilize the four-finger swipe up on the trackpad to reveal my multiple desktops, then drag iTerm2 into the right-side of VSCode so that I have my preferred console side by side with my preferred text-editor. This technique will save you pain especially in the SQL courses, where composing your queries in a text-editor and copying them into your command line database interface will aid in your defence against typos.

I’m a big booster of VSCode, and wrote this code snippet to easily create PEDAC boilerplate that helps me to address each concern, and to provide a more structured place to record my thoughts while working my way through a problem. Feel free to copy it and use it or modify it to your needs. Not every problem requires a full PEDAC style approach, but in general even the simplest problems benefit from taking a structured approach to solving it. In the very least you should explicitly state your expected inputs and outputs. With this boilerplate, as soon as you start typing ‘PEDAC’ within either a .js or .rb file, the intellisense will recognize the keyword (whatever the “prefix” is in the JSON) for the snippet, and pressing tab will insert the PEDAC comment block.

(Copy/Paste into) Code > Preferences > User Snippets > New Global Snippets File
Ruby Version

I hope this helps you tackle your own problems in some way, by encouraging you to slow yourself down and simmer on the problem phase before leaping to the solution phase of problem solving.

** All Photos by me

--

--