Interactive Practice with Immediate Feedback

In a “virtual interview” for, Bazlur Rahman asked me: “You've hinted at the importance of 'learning by practice with immediate feedback' as an effective method for skill acquisition. Could you please expand on that? How could this concept be implemented more effectively within coding and data science education, particularly in the context of Java programming?” I couldn't figure out how to answer that in a couple of sentences, so I wrote this article instead.

How do people learn? And how can they learn more effectively? These are age-old questions that are not easy to answer because we don't really have a good idea how our brains learn. Unlike large language models, we don't have the luxury of being trained by all textual material in the internet.

We have all experienced one way of learning that isn't very effective: listening to a lecture.

Don't believe me? Here are two lecture videos, one introducing for loops, and another showing how to delete a node from a binary search tree. I purposefully chose two well-produced videos. You can have a lot of fun finding truly wretched ones on Youtube.

Pick one of them and imagine yourself to be someone with a shaky knowledge of what came before (while loops, the definition of a binary search tree)

Ok, now that you've watched it, can you do the next task that the instructor has planned for you? The for loop lecture is from this Khan Academy course, and the next task is a challenge to draw lined paper.

With the binary search trees lecture, the next task is to implement a tree map from scratch.

Of course, some viewers will be able to make these mental leaps, and you, dear reader, may well be one of them. But having taught computer science for 35 years, I can confidently tell you that many students will not.

The education theorist David Kolb claims that learning must come from concrete experience. Watching a video is, of course, an experience, but it is rarely enough to move on to the next step, “reflective observation”. And then there are two more phases—see this diagram from the engaging book “The Art of Changing the Brain” by biology professor James Zull, who maps the phases of Kolb's learning cycle to different brain regions.


How can learners have better concrete experiences? That's where interactive practice comes in.

Practice Makes Perfect

“Your first ten thousand photos are your worst”—Henri Cartier-Bresson

During the MOOC craze of 2013, I designed a CS1 course for San José State University and Udacity. I learned something very valuable from the Udacity instructional designers. Each video should be less than two minutes. And then there should be a nontrivial question. But not too challenging either. At the time, we were limited what questions we could ask. Multiple choice. And autograded code. The latter saved the day. The course has close to 250 tiny programming assignments. Not quite ten thousand, but an order of magnitude more than what many students experience in their first programming course. This may astound readers who took such a course and remember long and complex programming projects. Those who complete the projects learn a lot. Those who join a “study group” where someone else does that work, not so much. And one can't necessarily blame all students who chose the latter strategy. The jump from the lecture to a programming project may feel unsurmountable.


So, how do you give bite-size practice? Multiple choice and fill-in-the-blank questions can work when they are well-designed. How hard can this be? Consider this depressing example. Never mind the subject matter. The student must fill in 26 answers, and then submits the whole batch to find out what they did wrong. That's not how people naturally learn. The poor design makes reflective observation really hard. This exercise would be far more effective if the correction came instantly after each question.

Such instant feedback is trivial to implement with a bit of JavaScript. And with a bit more programming effort, one can produce far more interesting activities.

As a first example, consider loop tracing. This page asks the student to use a code animation that steps through a for loop. Impressive and potentially useful, but who knows what goes on in the students' heads as they click Next, Next, Next (19 times). Try it out below. What went through your head?

Here is a better way. Let the student actively participate in the tracing. Because it's a bit dull to trace through a trivial loop, here is a more interesting example:

This exercise was made with the CodeCheck Tracer toolkit.

Once students can reliably carry out code traces, they should be ready to produce loops from prefabricated pieces. (Such an exercise is called a “Parsons puzzle”) Here is an example. Drag the tiles from the right to the left, then check if you did it right.

In ed speak, we say that a task in which part of the structure is given, is “scaffolded”. This allows the learner to focus on specific skills without being overwhelmed by inessential complexity.

An advantage of these exercise types is that, at least for now, they are not easily solved with ChatGPT!

After some Parsons puzzles, students can move on to simple programming exercises. Note the scaffolding in the example below: Unimportant parts such as public static void main and the Scanner are given, so that the student can focus on the new material. Another advantage: it becomes more work for the student to adapt the solution that their cheatbot of choice provides.

Binary Tree Deletion

Does this interactive and incremental approach work? Let's try it with binary search tree deletion. I assume that you have seen the definition of a binary tree, but you probably can't recall the intricacies of the deletion algorithm. (I couldn't recall them either.)

Here is the basic explanation from my Big Java book, interspersed with tracer exercises. The last exercise practices the complete algorithm, making you decide which of the three strategies you need to use. Click on Start Over to get a new instance of each problem until you feel that you understand it completely. Hopefully, upon completion you will feel confident that you have mastered this algorithm, and that you can master it again when you need to.

There are three cases, depending on the number of children of the node to be removed.

In all three cases, we must first find the node to be removed. That is a simple matter, due to the characteristic property of a binary search tree. Compare the data value to be removed with the data value that is stored in the root node. If it is smaller, keep looking in the left subtree. Otherwise, keep looking in the right subtree.

Now that we have located the node that needs to be removed, continue with one of the following cases, depending on the number of children.

No Children

This is the easiest case. If the node to be removed has no children at all, then the parent link is simply set to null.


Practice this with the following tracer activity until you are confident.

One Child

When the node to be removed has only one child, the situation is still simple. To remove the node, simply modify the parent link that points to the node so that it points to the child instead.


Again, use the tracer activity until you are confident.

Two Children

The case in which the node to be removed has two children is more challenging. Rather than removing the node, it is easier to replace its data value with the next larger value in the tree. That replacement preserves the binary search tree property. (Alternatively, you could use the largest element of the left subtree—TODO this is a good exercise).

To locate the next larger value, go to the right subtree and find its smallest data value. Keep following the left child links. Once you reach a node that has no left child, you have found the node containing the smallest data value of the subtree. Now remove that node—it is easily removed because it has at most one child to the right. Then store its data value in the original node that was slated for removal.


The following tracer activity provides practice for this case.

Finally, practice the entire algorithm with this tracer activity. Do it until your fingers move the mouse to the right place before you are done thinking through each step 😁 At this point, you may well feel that you'll never forget the algorithm. But if you do, just come back here and refresh your memory.


Comments powered by Talkyard.