Here's a familiar scene: you finish a JavaScript lesson on reduce, understand every line, and then your own cart total says 15 when the receipt should say 27. That gap is the lesson. This article builds a complete practice session around the bug—what to ask before running code, which test makes the mistake visible, what feedback should say, and how to check the idea again without the answer open. The solution is included. The goal is to leave with a method you can use on the next function, not another paragraph telling you to ‘practice more.’
Start with the mistake the learner is likely to make
The first version of our cart function adds item.price for every line. It looks plausible because the method, callback, and initial value are all in the right places. Give it one notebook and it passes. Give it two notebooks on one line and it fails. The learner has to notice that a line item carries both price and quantity. That is a much clearer target than “learn arrays.”
One basket, two totals
- Input
- 2 notebooks × 12; 1 pen × 3
- Expected
- 27
- Starter
- 15
- Missing
- The quantity of each line item
Before showing any code, ask for a prediction: two notebooks at 12 each, one pen at 3. The expected total is 27. Then ask what a function that simply adds 12 and 3 would return. The learner now has two numbers—27 and 15—and a reason to inspect the difference. A failed check is informative when its input exposes exactly one assumption.
The underlying learning move is retrieval: try to bring the rule to mind before rereading it. A study by Karpicke and Roediger found an advantage for repeated retrieval over restudy in a vocabulary task. It did not test JavaScript functions. Our use of prediction in a coding lesson is a practical application of that idea, not a claim that one paper validates every detail of this exercise.
Give the session a beginning and an end
This can fit into twenty minutes because the task is narrow. Give five minutes to reading the reducer and predicting the two outputs. Spend ten minutes changing the expression and running the cases. Use the final minutes to write a one-sentence explanation and a question for a later session. If it takes longer, that is fine; the structure exists so you know what success looks like, not so a timer can grade you.
Run the cart-total loop
Keep the answer visible after the attempt; no locked solution is needed.
- Write 27 on paper before pressing Run.
- Predict what the starter function returns and name the missing property.
- Edit only the reducer expression, then run a multi-quantity case.
- Run an empty-cart case and explain why it returns 0.
- Close the answer; leave yourself one question for tomorrow.
A course often puts reading, editing, and testing in separate places. That adds friction. If you are learning from SovranCode's JavaScript course, keep a small scratch file or browser console beside the lesson. Copy the minimum code needed to test the idea. Do not rebuild the full shopping cart UI just to learn how one reducer handles quantity.
Work the cart problem all the way through
Read the left-hand code first. The input is two notebooks at 12 each and one pen at 3. Write down the correct total, then trace the reducer by hand: sum starts at 0, adds 12, then adds 3. The function returns 15. Only after you can explain that trace should you compare it with the solution. We show the answer immediately because the explanation is part of the lesson; a hidden solution would not make the practice more honest.
Repair the cart total
Predict the result for two notebooks and one pen. Change the reducer so quantity matters, then check what it returns for an empty cart.
function total(items) {
return items.reduce(
(sum, item) => sum + item.price,
0
);
}function total(items) {
return items.reduce(
(sum, item) => sum + item.price * item.quantity,
0
);
}
total([
{ price: 12, quantity: 2 },
{ price: 3, quantity: 1 }
]); // 27
total([]); // 0Why it works. The callback adds each line's price multiplied by its quantity. The initial accumulator of 0 means an empty array has a defined total instead of throwing or returning an undefined value. A next exercise could decide how to reject a negative quantity; that is a separate business rule.
Do not stop at the displayed // 27 comment. Run assertions so the explanation has a consequence. The first case catches the missing multiplication; the second checks the initial value; the third makes sure zero quantity contributes nothing. These are three different questions, not three copies of the happy path.
const basket = [
{ price: 12, quantity: 2 },
{ price: 3, quantity: 1 }
];
console.assert(total(basket) === 27, "quantity matters");
console.assert(total([]) === 0, "empty cart starts at zero");
console.assert(
total([{ price: 12, quantity: 0 }]) === 0,
"zero quantity adds nothing"
);console.assert is enough for this small lesson. In a real codebase, put equivalent cases in its test runner. For money, use integer cents or a suitable decimal representation; this example uses whole numbers to isolate the reducer decision.
If you remove the initial 0 from reduce, the empty-cart case throws instead of returning a total. If you add quantity only to the first line item, the mixed basket fails. If you let negative quantities through, the arithmetic still runs but the business rule may be wrong. That last question belongs to a separate validation exercise. A practice task is strongest when it is explicit about what it is and is not teaching.
Write feedback that names the failed assumption
A learner who sees “Wrong answer” has to guess whether the syntax, input, loop, or expected result is wrong. For this task, the feedback can be exact: “For two notebooks at 12 and one pen at 3, expected 27; your function returned 15.” That sentence tells them the test input and the gap. It does not paste the solution into the error message.
Then give a useful hint in two steps. First: “Which property on a basket item says how many were bought?” If that is still too broad: “Trace the expression added to sum for the notebook line.” A hint should reduce the search area. The worked answer should remain available and explain why the change works; people who are stuck need a way forward, not a gate that protects an answer from them. That is how we want SovranCode exercises to treat questions, solutions, and explanations.
After success, change the data. Try three pens at 3 each. A learner who hardcoded 27 for the first basket will get 27 again; the new case should be 9. Now the check is about a general rule. Passing one familiar example is encouraging. Passing a changed input and being able to predict the result is stronger evidence that the code follows the intended relationship.
There is a limit to how much feedback belongs in one exercise. A negative price, floating-point currency, inventory limits, tax, and discounts are all real shopping-cart concerns. They are not the same lesson as multiplying price by quantity. Put a short note beside the answer about those boundaries, then build separate tasks for the ones learners need next. Otherwise one failed check can have five possible causes and the practice loop loses its focus.
If you write lessons, design the check first
The same cart example can guide someone building a course. Start with the behavior you want to observe: a learner can calculate line totals from unit price and quantity. Write the smallest input that distinguishes that rule from the common wrong one. One notebook at 12 will not do it; the broken function also returns 12. Two notebooks on one line reveal the mistake immediately. Only then write the prompt and starter code.
Keep the question specific: “This function returns 15 for a basket worth 27. Change it to account for quantity. What should it return for an empty basket?” A good reference solution shows the changed expression and explains the initial zero. The hint points at quantity, while the feedback reports the exact basket and observed total. Those pieces serve different moments in the learner's work; none should be replaced with an unexplained pass/fail badge.
Finally, test the exercise itself. Can the obvious wrong implementation pass every visible check? Does the solution handle an empty list? Does the explanation say why the code works for a new basket, not merely repeat the line of code? Let another person attempt it without your narration. If they solve a different problem than you intended, the prompt needs repair before the learner does.
Tomorrow, change the shape of the problem
If you rerun the cart code with the answer still beside it, you are mostly checking recognition. Tomorrow, use a different data shape and a different loop. A workshop sells seats; each booking stores seats and pricePerSeat. Two seats at 10 and one seat at 15 should produce 35. The prompt below is deliberately familiar without being a copy-paste task. Try the left side before reading the solution on the right.
Transfer the rule to workshop bookings
The starter code ignores the number of seats. Repair it with a `for...of` loop. What should an empty bookings array return?
function revenue(bookings) {
let total = 0;
for (const booking of bookings) {
total += booking.pricePerSeat;
}
return total;
}function revenue(bookings) {
let total = 0;
for (const booking of bookings) {
total += booking.seats * booking.pricePerSeat;
}
return total;
}
revenue([
{ seats: 2, pricePerSeat: 10 },
{ seats: 1, pricePerSeat: 15 }
]); // 35
revenue([]); // 0Why it works. The loop syntax changed, but the business relationship did not: each row contributes count times unit price. Initializing `total` to 0 gives the empty list a sensible result. If you can explain that without mentioning `reduce`, you have learned more than one method call.
There is no universal return interval. The next day is a convenient first check; a later week is another. What matters is whether the rule survives without the worked answer on screen. Keep a small note such as “I remembered multiplication but forgot the empty case.” That tells you what to repair. “I am bad at JavaScript” gives you nothing to test.
When you get stuck, inspect one iteration
The full basket has only two lines. Put a console.log({ sum, item }) at the start of the reducer callback and run it once. On the notebook line you will see sum: 0 and an item with price: 12, quantity: 2. The next sum becomes 12. That is the exact step to question. You do not need a debugger tour of the whole application to find it.
function total(items) {
return items.reduce((sum, item) => {
console.log({ sum, item });
return sum + item.price;
}, 0);
}
total([{ price: 12, quantity: 2 }]);
// Logs: { sum: 0, item: { price: 12, quantity: 2 } }
// Returns: 12. Expected: 24.For a focused debugging pass, use one line item. Once the bug is clear, remove the log and rerun the full set of checks.
If you still need help, ask with that tiny case: “For one item at 12 with quantity 2, I expect 24 and get 12. I traced the callback; it adds price to sum. What should I check next?” Another learner, a mentor, or an assistant can answer that without reconstructing your whole project. The SovranCode community is more useful when a question includes the input, expectation, actual result, and the step you already inspected.
Keep a record small enough to reread
A useful study log can fit in four lines. You are not documenting the whole session; you are leaving a clue for your future self. Write the prediction, the observed result, the rule you repaired, and one question for the next attempt. It should take less time to read than the tutorial you would otherwise reopen.
Predicted: 27 for two notebooks and one pen.
Observed: starter returned 15.
Changed: line cost = price × quantity; empty total starts at 0.
Next: can I explain the same rule with a for...of loop?A note such as “finished arrays” records activity. This one records a decision and a test you can repeat.
The pattern transfers beyond arithmetic. After the Semantic HTML article, you could predict whether a form label is connected to its input, inspect the rendered DOM, repair the for/id pair, and revisit the idea on a different form. A tiny deliberate edit with a visible result is often more instructive than copying an entire page.
Courses give you an order in which to meet ideas. Practice gives you evidence that you can use them. For this exercise, the evidence is specific: you can predict the total, explain why the first implementation returned 15, write a general fix, and handle the empty case. When those answers survive a new input and a later attempt, move to the next concept.