My first full-time job was teaching wilderness survival to adjudicated youth in the deserts of Utah. Though the technologies were primitive, I learned one fundamental skill that I carried throughout my career: the ability to break complicated processes down into simple steps. Starting a fire with bow and drill is not so different from handling asynchronous HTTP requests. To become a lifelong learner, problem solving is the most important skill any individual can develop.
Want to level up your problem solving skills? I write a weekly newsletter about programming, problem solving and lifelong learning. Join now
What Problems Does Programming Solve?
We can distill programming-related problems into two categories:
With algorithms, our goal is to find the most efficient way to perform an operation, such as sorting or searching. With automation, our goal is to find a way to repeatedly perform an operation, such as processing a dataset or dynamically rendering HTML. In many regards, algorithms and automation are one and the same problem.
What is Programming?
Programming is the act and art of writing instructions to be executed by a machine. These instructions must follow a predetermined, formalized, set of rules. These rules determine what we can write and how we can use those whats. A programming language is, fundamentally, a combination of logic and syntax, or a set of instructions for writing instructions. So meta!
One of the biggest challenges in learning to program is making the transition from natural language to formal language, or, learning to think like a computer. Why don’t we learn procedural thinking from daily life? Giving directions or following a recipe are exercises in procedural thinking, “[b]ut in everyday life, procedures are lived and used, they are not necessarily reflected on.” Programming is a metacognitive activity. To be successful, we need to improve our ability to think about thinking.
What is Problem Solving?
According to Wikipedia, problem solving…
…consists of using generic or ad hoc methods in an orderly manner to find solutions to problems.
Sounds a lot like programming, doesn’t it?
In the classic How To Solve It, George Polya outlines a methodology for problem-solving in the context of mathematics. We can easily apply it to programming. Polya’s heuristics can be distilled into four ordinate categories, each with specific questions to ask:
- Understand the Problem: What is the problem? What is the goal? Describe the issue or bug. What is the console or terminal telling you? If there is no feedback, why might that be so and what can you do about it?
- Devise a Plan: Have you seen this problem, or something like it, before? If you haven’t seen this problem before, can you restate it in terms you are familiar with? Can you break the problem down into smaller problems that are familiar and accessible?
- Execute the Plan
- Examine the Solution: What is the solution? Explain it. Can it be modified? If so, what could be improved, if anything? Could we use this solution for other problems?
Problem solving is an iterative activity for developers.
- First, we implement a solution
- Then, we troubleshoot the implementation
We don’t always get it right the first time, which is part of the iterative process and why it’s important to prioritize problem solving as a fundamental skill. First, we attempt to solve a given problem; then, we attempt to solve the problem(s) created by our solution. We as developers know we can always take our solutions another step further with refactoring. The processes of iterative development and refactoring are metacognitive. We are thinking about thinking. When our first attempt at a solution throws an error (or is inelegant), we are forced to confront and evaluate the limits of our intuitions.
Programming is Problem Solving
One does not expect anything to work at the first try. One does not judge by standards like ‘right—you get a good grade’ and ‘wrong—you get a bad grade’. Rather one asks the question: ‘How can I fix it?’ and to fix it one has first to understand what happened in its own terms.
In Mindstorms, Papert introduces the concept of microworlds. In programming, a microworld is a self-contained environment in which the learner can model and test assumptions, or intuitions. Each activity, each homework assignment, each project is a microworld. The boundaries of the microworld are simultaneously defined by the language and by the learner’s approach to the language. When programming, we will have intuitions about how it should work but we may be presented with evidence that it does not work (bugs!). Papert sees the computer as helping learners confront their intuitions in two ways:
The computer allows, or obliges, the [learner] to externalize intuitive expectations. When the intuition is translated into a program it becomes more obtrusive and more accessible to reflection.
Computational ideas can be taken up as materials for the work of remodeling intuitive knowledge.
The key to long-term success in programming is in this two-fold process of reflection and remodeling, or, learning how to think about thinking. Ultimately, what each of us needs is a better understanding of ourselves. Why did you think this approach would or would not work? Why do you think it does or doesn’t? What does this situation reveal to you about your assumptions and intuitions? Your biases? Bugs are “an intrinsic part of the learning process”, not something to be avoided. It is through debugging that we learn the most about ourselves.
Reflect and Remodel
The key technique is asking yourself why you made a particular mistake, rather than just fixing the mistake and moving on.
If the choice is ‘Program or Be Programmed’, the answer is obviously the former, not because of some economic inevitability, but because the primary skill one acquires through programming is problem-solving. A problem-solving mindset not only equips one with the ability to think critically about a given problem, but to think critically about oneself.
Note: Amazon links are affiliate.