---
title: Pseudocode
source: 'https://academia.sh/en/courses/programming-fundamentals/pseudocode'
course: 'Programming Fundamentals'
language: en
updated: '2026-08-17T18:08:24+00:00'
license: 'CC BY-SA 4.0'
---

# Pseudocode

Writing a solution independent of any language, testing it with a manual trace table, and identifying edge cases.

The previous three lessons introduced control structures directly through code. For a
complex problem, the order reverses: the solution is designed first, then translated
into code. Designing directly in a language's syntax requires thinking about two
different problems — "what to do" and "how to write it" — at the same time.

This lesson defines the ways to write a solution independent of any language, and to
test it before putting it into code.

## What Pseudocode Is

**Pseudocode** is a way of writing a solution close to natural language, but with
enough precision that its steps leave no ambiguity. It is not compiled, it is not
run; it is read.

It has no strict syntax, but common conventions exist:

- An arrow, or the phrase "let ... be", is used for assignment.
- Blocks are shown with indentation.
- Loops and conditions are written out plainly: *for each measurement*, *if ... then*.
- Language-specific details — library names, type declarations, memory management —
  are left out.

The measure is this: two people reading the pseudocode must understand the same
behavior — this text, written before any code, is where the solution is discussed. If
ambiguity remains, it is not detailed enough; if it approaches a language's syntax, it
is more detailed than it needs to be.

## Analyzing the Problem

The problem must be understood before writing pseudocode. Four questions do this
work:

1. **What is the input?** Its type, range, and validity conditions.
2. **What is the output?** A single value, a collection, or a side effect.
3. **Which cases are special?** Empty input, single-element input, equal values,
   invalid values.
4. **How would I solve it by hand?** Putting the steps into words over a small
   example reveals the solution itself.

The fourth question is the most productive. Someone computing the average of five
measurements by hand uses an accumulator pattern without realizing it: the numbers
are summed in order, then divided by five.

## The Shared Problem in Pseudocode

The course's shared problem — the average and the maximum — is written in pseudocode
like this:

```
INPUT: measurements (list of numbers)
OUTPUT: average (number), largest (number)

if measurements is empty
    report error: "empty input"

total ← 0
largest ← measurements[0]

for each measurement in measurements
    total ← total + measurement
    if measurement > largest
        largest ← measurement

average ← total / number of measurements
return average and largest
```

This text contains no detail belonging to any language; yet every step is single and
leaves no room for debate. Notice that the empty-input check is done **before** the
loop: initializing the `largest` variable with the first element assumes the list is
not empty.

The same pseudocode uses two accumulators in a single loop instead of two loops. This
is a design decision: the data is walked once, and two results are produced together.

## Tracing by Hand

A solution's correctness can be tested before it is translated into code, using a
**trace table**. In the table, each row shows an iteration, each column a variable.

For the input `[12, 18, 7]`:

| Iteration | measurement | total | largest |
|---|---|---|---|
| start | — | 0 | 12 |
| 1 | 12 | 12 | 12 |
| 2 | 18 | 30 | 18 |
| 3 | 7 | 37 | 18 |
| result | — | 37 | 18 |

The average is $37 / 3 \approx 12.33$; the largest is $18$. The results match the
hand calculation.

A trace table catches two kinds of errors: a wrong initial value and a wrong update
order. For example, if `largest` were initialized to zero, the table would
immediately show the result coming out as zero for an input where all measurements
are negative.

The method is slow and cannot be applied to large inputs; that is not its purpose
either. An input of three or four elements is enough to expose most logic errors. The
same table can also be built on a running program during debugging: printing the
values of variables on every iteration is having the program itself produce the
trace table.

## Edge Cases

A solution working on ordinary input is not enough. Edge cases are where a solution's
assumptions break down:

| Edge case | Behavior in this problem |
|---|---|
| Empty list | Average is undefined; an error is reported |
| Single element | Average is that element, largest is that element |
| All values equal | Largest is that value |
| All values negative | Largest is the least negative one |
| Very large values | The total can exceed the type's range |

The last row connects to the previous course's overflow lesson: a total accumulating
in a fixed-width type produces a silently wrong result if it goes out of range.

Listing edge cases is also listing the tests that will be written later. In the
software quality curriculum, this is called boundary value analysis.

## Decomposing the Problem into Subproblems

The shared problem fit into a single pseudocode block. Real problems do not; a
solution can neither be written nor traced as a single piece.

The standard method is **decomposition**: the problem is broken into subproblems,
each doing a single job. A program working with measurement data can be decomposed
like this:

```
read(source) → raw lines
extract(raw lines) → valid measurements, skipped count
summarize(measurements) → average, largest
report(average, largest, skipped) → output text
```

Each of the four steps can be written separately, tested separately, and when one
changes, the others are unaffected. The criterion for splitting is whether a step can
be described in a single sentence: a step doing two jobs joined by "and" should
probably be split in two.

The counterpart of this decomposition in code is functions. Naming a step in
pseudocode corresponds to defining a function in code; decomposition is therefore a
design task done before any code is written.

## From Pseudocode to Code

Translation means writing the target language's counterpart of every line of
pseudocode:

```python
def summarize(measurements: list[int]) -> tuple[float, int]:
    """Returns the average and the largest of the measurements."""
    if not measurements:
        raise ValueError("empty input")

    total = 0
    largest = measurements[0]

    for measurement in measurements:
        total += measurement
        if measurement > largest:
            largest = measurement

    return total / len(measurements), largest


print(summarize([12, 18, 7]))          # (12.333333333333334, 18)
print(summarize([12, 18, 7, 25, 14]))  # (15.2, 25)
```

The first output matches the values computed by hand in the trace table — this is
proof that the translation was done correctly.

During translation, decisions that were not in the pseudocode appear: how to report
an error, how to return two values together, what the types will be. These decisions
belong to the language and are not a deficiency of the pseudocode; they are details
the pseudocode **deliberately leaves out**.

## Summary

- Pseudocode is a way of writing a solution independent of any language and without
  leaving ambiguity; its measure is that two readers understand the same behavior.
- Problem analysis is done with four questions: input, output, special cases, and the
  by-hand solution.
- A trace table tests a solution without running it; a wrong initial value and a
  wrong update order are caught this way.
- The list of edge cases is also the list of tests to be written later.
- The decisions that appear during translation from pseudocode to code belong to the
  language; pseudocode deliberately leaves these details out.

## Next Step

The solution in the last example was wrapped in a `def` block and named
`summarize`. This construct has been used without explanation up to now. The next
topic defines functions: how parameters and return values work, in what region names
are valid, and what happens when a function calls itself.
