Reading a problem before writing code
The five minutes that save an hour: inputs, outputs, edge cases, and the example you work by hand first.
The most expensive habit in programming is starting to type before you know what you are building. It feels productive — there is code on the screen — and it produces code shaped like your confusion, which then has to be unpicked.
This lesson is the five minutes that come first.
Four questions, before any code
1. What exactly am I given? Not "a list of orders" — what is in an order? Can the list be empty? Can it be huge? Is it sorted? Can a field be missing?
2. What exactly do I want? "A total" is not an answer. Per customer or overall? Including cancelled orders? What if there are none — zero, or nothing at all, and are those different?
3. What should happen when it goes wrong? Empty input, a negative number, a name with an apostrophe in it. You will decide this eventually; deciding it now is cheaper than discovering it in production.
4. Can I do it by hand on three rows? If not, stop. You do not yet understand the problem, and no amount of typing will fix that.
Work the example by hand first
Take the task: given a list of orders, find each customer's total.
Three rows, on paper:
order customer amount
1 alice 30
2 bob 10
3 alice 20Do it by hand and narrate what you are doing:
"I'll keep a running total for each customer I've seen. Alice, 30. Bob, 10. Alice again — she's already there, so 30 plus 20 is 50. Done: Alice 50, Bob 10."
You just described the algorithm, including the piece of state (a running total for each customer I've seen) and the decision (already there or not). That narration is the thing to translate into code — not the vague idea you started with.
Now watch the narration become code, phrase by phrase. Nothing is invented in this step — every line is something you already said out loud:
String[][] orders = { {"alice","30"}, {"bob","10"}, {"alice","20"} };
// "I'll keep a running total for each customer I've seen"
Map<String, Integer> total = new LinkedHashMap<>();
for (String[] order : orders) {
String customer = order[0];
int amount = Integer.parseInt(order[1]);
// "she's already there, so 30 plus 20"
if (total.containsKey(customer)) {
total.put(customer, total.get(customer) + amount);
} else {
total.put(customer, amount);
}
}
System.out.println(total);{alice=50, bob=10}The map is the running total for each customer. The containsKey branch is already there or not. You did not design a data structure; you noticed you were already using one.
And the edge cases appear here rather than in production. What if an order has no customer? What if an amount is negative — a refund, or bad data? Doing it by hand forces the question because your pen stops.
Making it smaller
When a problem is too big to hold, shrink it until it is not:
- Fewer cases. Make it work for one customer before all customers.
- Smaller input. Three rows, not three million. Performance is a later problem and usually a different one.
- Ignore a requirement. Get totals right, then add the currency conversion. Two correct steps beat one confused one.
- Assume the hard part is done. Write the code as if
exchangeRate(from, to)already exists. Now you have one clear problem instead of a tangle, and the leftover is a well-defined function you can write next.
That last one has a name — programming against an interface you have not built — and it is what lets a person work on a large system without holding all of it in their head.
When you are stuck
Stuck is a signal, not a state. It almost always means one of:
| Feeling | What it usually is | What to do |
|---|---|---|
| "I don't know where to start" | the problem is still too big | decompose it; solve a smaller version |
| "It works but I don't know why" | you guessed and got lucky | that is a bug that has not happened yet — go and understand it |
| "It should work" | your model of the code is wrong somewhere | stop reasoning, start observing: print it, debug it, check the input |
| "I've tried everything" | you have tried variations of one idea | write down the assumption you have not questioned |
The fourth is the common one. "I've tried everything" nearly always means the actual cause is something you are certain about and have not checked — the file you think you are reading, the branch you think you are on, the value you think is not null.
Try it yourself
Answer the four questions
Do not write code. For this task, answer all four questions in writing:
Given a list of employees, each with a name, a department and a salary, produce the average salary per department.
The answers that matter
Given: a list that can be empty; an employee whose department could be missing or blank; a salary that could be zero (an intern?) or negative (bad data).
Want: one number per department that has at least one employee. A department with no employees does not appear — or does it, as zero? You have to choose. Averages are fractional, so the answer is not a whole number, which matters in a language that truncates integer division.
When it goes wrong: an empty list gives an empty result, not an error. A missing department needs a decision: skip, or group under "unknown"? Skipping silently loses data, which is the kind of bug nobody reports because nobody sees it.
By hand: three employees, two departments, and one of them appearing twice — so you can see the running sum and the running count, which is the piece most people forget until their average is wrong.
And the truncation is not a hypothetical. A hundred rupees across three employees:
int average: 33
real average: 33.333333333333336An int division throws the remainder away without saying so. Nobody gets an error; the report is simply wrong by a third of a rupee per department, every time.
That last detail is the whole point of working it by hand. "Average per department" sounds like one number per department. It is actually two — a sum and a count — and you only notice when your pen has to write both down.
Misconceptions
- "Planning is slower." It is slower for the first ten minutes and faster for the rest of the day. The measurement people never make is how long they spent unpicking code they wrote before they understood the problem.
- "Real engineers just know." They ask the same four questions; they are just quick about it, and they have been wrong enough times to know which questions bite.
- "I'll handle the edge cases later." Later is after the structure is built around not having them. The empty list is not an edge case, it is Tuesday.