Computational Thinking
The mental moves — decomposition, pattern matching, abstraction, and algorithms — that separate "knowing syntax" from "solving problems with code".
Programming is not the same as coding. Coding is typing the right symbols in the right order. Programming is figuring out what to type. The thinking that lets you do the second is often called computational thinking, and it has nothing to do with any particular language.
There are four classic moves to it. We will go through each one and practice them on small JavaScript problems.
1. Decomposition
Decomposition is breaking a fuzzy, big problem into smaller, well-defined problems you can actually solve.
Imagine someone says: "Write a program that figures out a fair way to split a restaurant bill, including tip."
That sentence is not yet a program. It is a goal. To turn it into code, you have to break it apart:
- How is the bill given to the program? A single number? A list of items per person?
- How is the tip calculated? A fixed percentage? Adjustable?
- How do we round it — to the nearest cent? The nearest dollar?
- Do we output a single number per person, or a detailed breakdown?
Each of those questions defines a smaller sub-problem. Each sub-problem becomes a function, a calculation, a step.
Here is a tiny version where the sub-problems are obvious:
The big problem ("split the bill") got cut into three small problems (compute tip, split, round). Each small problem fits in a few lines. Each could be tested and fixed on its own.
The skill of decomposition is mostly the skill of asking: what is one small piece of this I can solve right now?
2. Pattern matching
Pattern matching is noticing when a new problem looks like something you have solved before, so you can reuse the same idea.
Once you've written splitEvenly(total, people) above, the next
time you face "divide one quantity equally among N consumers", you
will recognise it. You will not redesign from scratch — you will
think "oh, this is the same shape".
Patterns can be very small ("looping over a list and summing") or very large ("client/server communication"). Programmers build a library of patterns in their heads over time. A lot of "experience" is exactly this: more patterns recognised, more quickly.
Here is one of the most fundamental patterns in all of programming — accumulating a result over a list:
Notice that the shape of all three solutions is identical:
- Start with a blank result (
0,numbers[0],0). - Walk through the list.
- Update the result based on each element.
- Print or return the result.
Once you see that pattern, you see it everywhere. Half of all "loop-and-do-something-with-a-list" problems are just an instance of it.
3. Abstraction
Abstraction is hiding details behind a name, so you can think at a higher level.
When you call Math.sqrt(25) you don't think about how the runtime
actually computes a square root. The name Math.sqrt abstracts
all of that away. You think: "this gives me a square root", and
move on.
Every time you write a function, you are creating an abstraction. Every time you give something a meaningful name, you are creating an abstraction. Good abstractions are the difference between code you can read at a glance and code that makes you re-derive the meaning every time.
Compare:
// No abstraction — what is this even doing?
const t = b + b * r;
const p = t / n;…with:
// With abstraction — the meaning is in the names.
const tipRate = 0.18;
const total = bill + bill * tipRate;
const perPerson = total / people;Both pieces of code do the same thing. One is readable; one is a small puzzle. The difference is purely abstraction — choosing good names and grouping related steps.
We can abstract further by giving the steps themselves names:
Notice how the bottom block reads almost like English: total with tip, per person, log it. The numerical details — the multiplication, the division — are hidden inside the functions, where they belong.
4. Algorithmic thinking
Algorithmic thinking is designing a precise sequence of steps that will reliably solve a problem.
An algorithm is just a recipe: a clear, ordered set of instructions that, if followed exactly, gets you from inputs to outputs.
You use algorithms all day without naming them. "How do I make coffee?" is an algorithm. "How do I get from home to work?" is an algorithm. The skill in programming is making them precise enough that a computer — which has zero common sense — can follow them.
Suppose I ask you to write a function isPrime(n) that returns
true if n is a prime number and false otherwise. Before
typing a single character, talk yourself through the algorithm:
- If
nis less than 2, it is not prime. - Otherwise, try dividing
nby every integer from 2 up ton - 1. - If any of those divisions has a remainder of 0,
nis not prime. - If none of them do,
nis prime.
Now we translate the recipe into code:
Notice how the algorithm came first, in English. The translation to JavaScript was almost mechanical. This is the right order.
(There are faster ways to test primality. The point right now is not the cleverness of the algorithm; it is that you specified the recipe before you wrote the code.)
Putting all four moves together
Here is a slightly bigger problem we can solve using all four moves. The problem:
Given a list of student grades (numbers 0–100), report the average, the highest, the lowest, and how many students passed (60 or above).
Decomposition — what are the sub-problems?
- Compute the average.
- Find the highest.
- Find the lowest.
- Count passes.
Pattern matching — three of those (average, max, min) are instances of the "accumulator over a list" pattern. Count is too.
Abstraction — give each sub-problem a name.
Algorithm — the steps for each are clear; we already know them.
Notice how the top-level function, reportGrades, reads like a
description of the task. The numerical machinery is tucked into
small, named helpers. If you had to change what counts as passing,
you would know exactly where to look. If you had to add a new
statistic (say, the median), you would know exactly where it would
go.
That is computational thinking in action. The code is almost a side effect of the careful thought you did before writing it.
Challenge
Write a function countWords(text) that returns how many words are in the given string. A word is any group of non-space characters separated by one or more spaces.
For example:
countWords("hello world")should return2.countWords("")should return0.countWords(" spaced out ")should return2.
Hint: "a b c".split(/\s+/) splits on one-or-more whitespace characters. You may need to filter out empty strings.
Which of the four computational-thinking moves is most directly responsible for turning a vague task like "split a restaurant bill fairly" into something you can actually code?
Decomposition — breaking the big task into small, well-defined sub-problems
Pattern matching — noticing that this looks like a previous problem
Abstraction — hiding details behind names
Algorithmic thinking — writing precise step-by-step recipes