Decomposition and Abstraction
Computational thinking is a way of approaching a problem so that a computer can help solve it. Two of its main techniques are decomposition, which breaks a problem into smaller parts you can tackle one at a time, and abstraction, which removes or hides detail you do not need so you can focus on what matters. You use both together as you analyse a problem, design a solution and implement it in code.
Imagine you have been asked to write a program for sports day that turns event results into a house leaderboard.
| Stage | Decomposition | Abstraction |
|---|---|---|
| Analyse | Split the job into reading results, scoring each event, adding up each house's points and displaying the leaderboard. | Decide a result only needs the school house and the finishing position; names, ages and race times are irrelevant to the points. |
| Design | Plan each part separately and agree what information each one takes in and hands back. | Model a school house as just a name and a points total, rather than everything about that house. |
| Implement | Write and test one part at a time, then combine the tested parts. | Store a simple list of houses and their points, not a full record of the whole day. |
The techniques support each other: breaking the problem down gives you small pieces that are easy to simplify, and simplifying each piece makes it clearer how the parts fit together. You might sketch the breakdown as a structure diagram, then simplify each part before writing it. Working this way through analysis, design and implementation keeps the solution focused on what the program actually needs to do.
Decomposition is a computational thinking technique: you take a large problem or system and break it into smaller sub-problems, each of which is far easier to understand and solve than the whole. You then solve the parts and build them back together into the finished thing.
Decomposition is not only about programs. It can be applied to the problem itself (what needs solving), to the solution (how you build it) and to algorithms (the steps that get the job done).
Suppose your year group is running a trip to a science museum. As a single problem it feels far too big to start; decomposed, it becomes transport, cost, permission letters and lunch. In the same way, an algorithm that works out the average score in a class splits into: read in the scores, add them together, divide by how many there are, print the answer.
One split is rarely enough. Keep breaking each sub-problem down until every task is small enough to design, write and test in one go. Under transport you could have "how many coaches are needed", "the cost of hiring one" and "pick-up time". A task you can hold in your head is the point to stop.
Each sub-problem is solved and checked separately, and the finished parts are then combined to produce the overall solution. Because the parts were planned to fit together, a fault in one can be found and corrected without unpicking everything else. The breakdown can be shown as a structure diagram.
Decomposition is breaking a complex problem into smaller parts. The benefits come from what those smaller parts let you do: understand the problem, share the work, test each piece and change one piece later without disturbing the rest.
A large problem can feel overwhelming because there are too many details to consider at once. Once it is split, you can analyse each part on its own: what it needs, what it produces and how it connects to the other parts. For example, planning a school charity fair could be split into booking stalls, selling tickets, arranging first aid and advertising. Each part is small enough to think through clearly, and together they make the whole event easier to solve.
Different people or teams can take separate parts and work on them at the same time, which can speed up development. A developer can also focus on one small part without needing to consider every irrelevant detail of the whole system. In a program, one person might build the part that works out the price of a cinema ticket, including any student discount, while another builds the part that prints the booking confirmation; each can get on with their own task.
Smaller parts can be tested and corrected before they are combined. If something goes wrong later, you can check each part in turn rather than searching one huge solution. This reduces errors and makes a solution easier to maintain, because a change can be made to one part while the others stay the same. For instance, changing how a quiz stores player names should only affect that storage part, leaving scoring and timing untouched.
Decomposition is about splitting a problem into parts; it is not the same as abstraction, which hides unnecessary detail.
A structure diagram shows how a problem has been broken down, or decomposed, into smaller subtasks. The main problem sits at the top, the subtasks it splits into sit underneath, and lines link each task to the parts it breaks into.
You read a structure diagram downwards. Each lower box is a finer breakdown of the box above it, and the bottom row shows tasks that are small enough to tackle on their own. For example, organising a school fun run could be broken down like this:
| Level | What it shows |
|---|---|
| 1 | Organise the fun run |
| 2 | Register runners Plan the route Set up the finish line |
| 3 | Under “Register runners”: Collect entry forms Take payment Send confirmation |
Each line is a “belongs to” link: the three level 2 tasks together make up the level 1 problem, and, under “Register runners”, the three level 3 tasks together make up that task. Read downwards to see a task broken into smaller tasks, and upwards to see which larger task a box belongs to.
A structure diagram is not a flowchart. A flowchart shows the step-by-step order in which processing happens, with arrows telling you what to do next. A structure diagram only shows the breakdown, so the lines say which larger task a subtask belongs to. Two boxes side by side are not “do this, then that” — they are simply two parts of the same job, and the diagram does not say which one you would do first.
Abstraction is a computational thinking technique that removes or hides unnecessary detail so you can focus on the essential features of a problem. It lets you build a simplified model of something real, containing only what matters for the job you are doing.
For example, a school canteen ordering system might model a meal with its name, price and allergens. The meal's cooking method, photo and smell are left out because they do not help take an order. They are not inaccurate details, just irrelevant to this purpose.
What counts as unnecessary depends on the purpose of the model. A nutritional analysis tool for the same canteen meals would need each meal's calories and ingredients, so it would keep those and ignore the price.
| Feature of a meal | Ordering system | Nutrition tool |
|---|---|---|
| Meal name | Kept | Kept |
| Price | Kept | Ignored |
| Allergens | Kept | Kept |
| Calories | Ignored | Kept |
| Photo | Ignored | Ignored |
The abstraction is only useful if the details you keep are the ones the problem needs, so its benefits depend on choosing details that are relevant to the problem.
By reducing the amount of detail you must handle, abstraction makes a problem simpler to analyse, understand and solve. You can reason about a small set of relevant features instead of a messy real-world object with hundreds of properties. Modelling the real world in software therefore means choosing which real-world features to keep and which to ignore.
Abstraction is not about making a model vague or throwing away all detail: you deliberately select the features the problem requires. It is also different from decomposition, which splits a problem into smaller parts rather than stripping away detail.
A subprogram is a named block of code that performs one specific task and can be called from other parts of a program. You write it once, give it a name, and call it whenever that task is needed.
Using subprograms is one way decomposition appears in code: the overall job is split into smaller tasks, and each task becomes its own subprogram. The main program then reads as a short list of calls, plus any logic that does not belong in a subprogram. Plenty of what a real program does — working out a price, checking a password, drawing a shape — happens inside subprograms.
Because a subprogram can be called many times, the same code never needs copying and pasting. That cuts development time and reduces errors, since there is only one version to get right. If the task changes, you edit the subprogram once and every call uses the new version. That makes the program easier to maintain, and easier to adapt because one task can be updated without rewriting the whole program. Separate programmers can also write different subprograms and join them together.
A call hides the internal logic: you only need the subprogram's name, the values it needs and what it gives back. That is abstraction, and it happens whether or not the name is descriptive — the call hides the code, not the name. Built-in and library subprograms such as len() and print() work the same way: you use them without knowing how they are written inside.
In this booking program, one subprogram works out the cost of one booking line. It is called once for climbing and once for kayaking, so the multiplication is written once but used twice, and the main program just adds the results together.
def line_total(quantity, unit_price):
# cost of one line of the booking
return quantity * unit_price
climbing = line_total(4, 12.50)
kayaking = line_total(2, 18.00)
print("Climbing: " + str(climbing))
print("Kayaking: " + str(kayaking))
print("Total: " + str(climbing + kayaking))Output:
Climbing: 50.0
Kayaking: 36.0
Total: 86.0