Systematic Approach

Programming can be hard. To organize our thought process, and reduce the cognitive load, we can break problems down into components.

The following is my technique for solving programming problems. It is very similar to, and based on, the process taught by Launch School. The Exercises page shows recent exercises I've done using this approach. The following assumes we're working with JavaScript functions, but can be otherwise adapted.

Step 1: Requirements

  • Are there expected inputs? How many and what type?
  • Are there expected outputs? How many and what type?
  • Is there an expected return value? What type?
  • Does the function mutate the input or another object?
  • Additional requirements, rules, or details as mentioned in the problem description.

Ensure you have made note of each requirement

Step 2: Edge Cases

How should the function handle inputs other than the expected input type? More specifically:

  • How should the function handle numbers?
    • How should the function handle 0, negative numbers, decimals, NaN, and Infinity?
    • Are there additional numeric edge cases depending on the problem? E.g. odd, even, boundary, or other numbers.
  • How should the function handle strings?
    • How should it handle empty strings or those longer than expected?
    • Should all characters be considered, including numbers, special characters, whitespace, upper and lowercase characters?
    • If dealing with words/sentences/names, what characters are valid delimiters. What if there are more or fewer substrings than expected?
    • Are there additional string edge cases depending on the problem?
  • How should the function handle null, undefined, or boolean values?
  • How should the function handle objects?
    • How should it handle empty objects?
    • Objects with fewer or more elements than expected?
    • Objects with numeric, string, null, undefined, boolean, or object elements?
    • If it accepts array objects, how should it handle sparse arrays or arrays with object properties?
    • Are there additional object edge cases depending on the problem?
  • How should the function handle fewer inputs than expected?
  • How should the function handle more inputs than expected?

Ensure you have a edge case for each testable requirement

Step 3: Test Cases

Go back through the edge cases above. For each edge case, If that edge case needs to be handled, create a test case for that edge case, unless one is provided.

Step 4: Algorithm

Looking back over the requirements, edge cases, and test cases. Consider what data type would be the most conducive to producing the result you need. Begin writing pseudo-code as ideas come to you. If at any point you get stuck, consider alternative paths. When you have one that you believe will work, spend a few moments considering if there is a more efficient way. Once you're satisfied with your algorithm, ensure each of your test cases will work with the algorithm. If any won't work, change your algorithm.

Step 5: Code!

Line by line, convert your algorithm to code. Test your code often as you go, to avoid compound errors. When you think you're finished, run your tests. If any test case fails, revisit your algorithm and code accordingly.

If you'd like to see examples, proceed to the exercises.