Pseudocode as a Thinking Tool
Pseudocode is not a notation for code you have not typed yet. It is a device for finding inputs you forgot, state you did not know you needed, branches you had not considered and failures you had not decided — and it works because it is too small to hide any of them.
The situation, the reflex, and why it stalls
Every lesson starts where being stuck starts: someone has a problem, and the first move that comes to mind feels like progress.
What is pseudocode actually for, and how do you write it so that it finds something rather than restating what you already knew?
I was told to write pseudocode before implementing the cart, so I did: "add item to cart: find cart, add item, save cart." It took a minute and it told me nothing I did not know. Then the implementation took two days because of the questions it did not ask. Either pseudocode is useless or I am doing it wrong.
Write the pseudocode as a summary — the happy path in three lines — because that is what pseudocode looks like in textbooks, and then move on to the real work, which is the code.
A summary confirms what you know and cannot find what you do not. "Find cart" — whose cart, and what if there is none? "Add item" — what if it is already there, what if stock is zero, what quantity? The summary was written at a level where none of those exist, so it produced the reassurance of a plan and none of a plan's content.
- A summary confirms what you know and cannot find what you do not. "Find cart" — whose cart, and what if there is none? "Add item" — what if it is already there, what if stock is zero, what quantity? The summary was written at a level where none of those exist, so it produced the reassurance of a plan and none of a plan's content.
- The questions the summary skipped were asked anyway, later, by the code — one at a time, each with a framework detour, each answered in the moment with whatever was easiest. The two days were the pseudocode session, done expensively.
- Because the exercise found nothing, the conclusion drawn was that pseudocode finds nothing, and the tool was discarded — which is the worst outcome, because the operation that needed it most was the next one.
The move
Precisely enough to apply it to a problem you have never seen — not a slogan.
- Treat pseudocode as an interrogation with four questions built in: what comes in (inputs), what goes out (outputs), what is remembered between calls (state), and where the path splits (branches) — plus the fifth that runs through all of them: what happens when a step cannot proceed (failures). Write the operation, then check each of the five against it; every one you cannot answer from the page is a finding (Inputs, Outputs, State, Branches).
- Write it at the level where the decisions are. For "add to cart" the decisions are about identity (which cart), duplicates (already present), limits (stock, quantity) and persistence (does the cart survive) — so the pseudocode names a cart id, a check for the existing line, a stock comparison and a save, and each of those is a line you can point at and ask "is that right?" (Abstraction Levels).
- Let it be wrong on the page. The point of writing "add item" and then "wait — add or increase quantity?" is that the correction costs a line. Pseudocode is the cheapest medium in which to be wrong, and a session that produced no corrections either had a very well-understood operation or was written too high to be wrong.
- Take what it finds to the right board: a missing input is an interface question, a missing state is a data question, an undecided failure is an unknown with an experiment. The pseudocode does not have to answer them; it has to make them exist (The Unknowns Board).
Summary versus interrogation
The two versions of "add to cart" below are the difference this lesson is about. The first is what most people write when told to write pseudocode. The second is the same operation written at the level where its decisions live, and every line that was not in the first version is a decision that would otherwise have been made mid-implementation.
1# as a summary — confirms what was already known2add item to cart:3 find cart4 add item5 save cart6 7# as an interrogation — written where the decisions are8function addToCart(cartId, productId, quantity):9 if quantity < 1: return rejected("quantity")10 cart = cart for cartId # guest or account? merge on login — REQUIREMENT11 product = product for productId12 if product missing or not sellable: return rejected("product")13 line = cart line for productId14 wanted = (line ? line.quantity : 0) + quantity # increase, not replace — DECISION15 if wanted > product.stock: return rejected("stock", available = product.stock)16 set line quantity = wanted # advisory cap; stock re-checked at checkout17 save cart atomically # a failed save leaves the old cart intact18 return cartEvery capitalised word in the comments is a finding: something to take to a person, a board or a test. The pseudocode did not decide them; it made them visible.
What the session found, as a board
The findings go to the unknowns board, because the pseudocode's job ends when the question exists. Notice that not every finding is an unknown: the increase-versus-replace choice is a decision to make with the product owner, and the guest-cart merge is a requirement that was missing — both are written down beside the unknowns so they do not become mid-code guesses.
- ✓Quantity must be positive; a non-positive quantity is rejected.
- ✓Stock at add time is advisory; checkout re-checks it.
- ✓A failed save must leave the previous cart intact.
- ~Increase rather than replace when the line already exists — a decision, written down so it can be overturned.
? Guest carts.
becomes When a customer with a guest cart logs in and already has an account cart, which lines survive, and does the product owner want the union, the newer cart, or a prompt?
experiment Ask the owner with both carts drawn out; then write the merge as three lines of pseudocode for the chosen answer and check it against a cart with the same product in both.
? Cart persistence.
becomes Does a guest cart have to survive a browser restart, and if so, for how long — and does that answer change where cartId comes from?
experiment Try the store as a guest, close the browser, reopen; decide what you wanted to happen, and confirm it with the owner.
? What does "sellable" mean?
becomes Which product states block adding to cart — discontinued, hidden, zero stock — and which merely warn?
experiment List the product states from the admin side and ask, for each, "can a customer add this?"; the answers become the sellable check.
Choosing the level
The decision below is the one the stalled version got wrong. Pseudocode has a level, and the level is chosen per operation, not once. The criterion is where the decisions are: high enough that the framework is invisible, low enough that a wrong line is possible.
How low should the pseudocode for this operation go?
when Sketching the shape of a whole feature to see its parts; a table of contents, not a design.
cost Finds nothing about any one operation; mistaken for a design, it produces the stall in the situation above.
when Any operation with more than one ending, any state, or a boundary — the default for this module.
cost A screenful per operation and a findings list to act on; takes a real session, not a minute.
when A tricky algorithm where the mechanics are the risk — an allocation across warehouses, a merge of two carts — and the decisions are already made.
cost No longer cheap to be wrong in; at this level the real language with a test is usually better (Going One Layer Deeper).
How to do it
Most important first.
- Write the signature — name, inputs, output — and then challenge each input: where does it come from, and can it be missing or wrong?
- Write the happy path as verbs. Then, under each verb, ask: does this need state that is not in the inputs? Where does that state live, and who else changes it?
- For each verb, write the "cannot" branch and the state it leaves. If you write "handle error", stop and write what the error is and what happens.
- Read it back as a stranger and mark every line you would have to ask about. Each mark is a finding — a decision to make, an unknown to sharpen, or a requirement to confirm (Explain It Back).
- Keep a list of what the session found; that list is the output, and the pseudocode is the tool that produced it.
Worked on a concrete problem
The move has to produce something. This is what it produced.
- Add to cart, rewritten at the level where the decisions are: function addToCart(cartId, productId, quantity). Inputs challenged: cartId — from a session or from an account? Both, with a merge on login: a requirement nobody had written. quantity — can it be zero or negative? Reject. State: the cart's existing lines, the product's current stock. Branches: line exists → increase or replace? Increase, capped at stock. Stock zero → reject naming the product. Failures: save fails → the customer sees the old cart, not a half-updated one.
- Findings from a minute of writing: guest carts and login merge; quantity validation; the increase-versus-replace decision; a stock cap at add time that is only advisory, because stock is re-checked at checkout; and the save's atomicity. Five decisions, one requirement, and the two-day implementation became an afternoon because none of them was discovered mid-code.
- The chat app's "send message", same interrogation: inputs — conversation id, sender, body, and a client-generated message id that was not in the first draft and is the whole answer to duplicates. State — the sender's sequence number, which did not exist until "what if two arrive out of order?" was asked of the branch list. The pseudocode found the two hardest design elements of a chat app by refusing to let "send message" stay one verb.
How you know it worked
What now exists that did not before, and what question you can now ask.
- The session produced a list of findings — decisions, unknowns, requirements — and the list is longer than the pseudocode.
- At least one input, one piece of state or one branch exists on the page that did not exist in your head before you wrote it.
- You can say what the pseudocode is for in this case: "it found the guest-cart merge" rather than "we always write pseudocode".
The questions you can now ask
The field this whole domain exists for. After this lesson, these are the questions to put to an unfamiliar problem.
- ?For this operation: what comes in, what goes out, what is remembered, where does it branch, and what happens when a step cannot proceed?
- ?Which of those five can I not answer from what I have written — and is that a decision, an unknown or a requirement?
- ?What is the level at which this operation's decisions are visible, and am I writing above it or below it?
- ?What did this session find that I did not know before I wrote it?
What can go wrong
- The interrogation is run on every function including the trivial ones, and the findings list for a getter is empty, and the practice starts to feel like ceremony. Use it where the decisions are — operations that cross a boundary, change state or have more than one ending.
- The pseudocode answers its own findings on the spot, with guesses. "Guest cart merge: keep the newer one" is a decision that belonged to whoever owns the product; the tool's job was to find the question, not to close it.
- It is written at the level of the code — with types, with the ORM's method names — and stops being cheap to be wrong in. The level is the operation's decisions, not its implementation.
- A session that interrogates properly takes longer than a summary, and the findings are work — five decisions is five conversations or five experiments, not five lines.
- The cheapest medium to be wrong in is also the one nobody has to keep; the findings have to be moved somewhere durable or they were found for nothing.
- "Pseudocode is a first draft of the code." It is a first draft of the decisions. The code is a separate artefact with its own concerns, and it should be better than the pseudocode, not a transcription of it.
- "If it found nothing, the operation is simple." Or the pseudocode was written too high to find anything. Check by writing the same operation one level lower before concluding.
- "Pseudocode should be language-neutral." It should be framework-neutral; borrowing your language's syntax for loops and conditions is fine and often clearer. The thing to keep out is the framework's defaults, not the language's shape.
Where this applies
Problem-solving advice is stated as universal far more often than it is. These labels say what each method is specific to — and where CONTESTED appears, the note gives the strongest form of the opposing view.
- GENERALThe five-question interrogation applies to any operation in any system; what changes is which question is expensive — for a data pipeline it is inputs and state, for a UI action it is branches, for anything crossing a boundary it is failures.
- TEAM-SPECIFICA solo learner gets the most from the interrogation because nobody else is going to ask the questions; on a team with a strong review culture, the same questions arrive in review, later and more expensively, and the pseudocode session moves them earlier.
- ILLUSTRATIVEThe minute-long summary, the two-day implementation and the five findings are invented to show the difference between a summary and an interrogation; the numbers of findings are the shape, not a benchmark.
Where the depth lives
This domain asks the question and hands the answer off by name.
- — The manifesto's "Don't delegate understanding" at /manifesto applies to pseudocode directly: an assistant can write the interrogation for you, and then the findings are its findings; write it yourself first, then ask what you missed.