Programming can be a difficult skill to learn, especially for younger students. Understanding how learning works and applying that knowledge to creating engaging exercises can make a big difference when teaching novices.
Why teach programming? Efficient and engaging learning helps children to become competent and autonomous. Being competent and autonomous, they are able to improve everything else. Problem solving, in particular, is among the universal competencies needed for contributing to society. Introductory programming is an expedient modern approach to teaching problem solving, as it fosters key skills like pattern recognition, abstraction, and problem decomposition, which are collectively labeled as computational thinking.
There is an abundance of strategies that have been shown to increase learning gains. For example, interleaving topics increases the mental effort required to solve problems, and increased student activity leads to better learning. But no strategy is universal. If the current topic is already too difficult for the student, making it even more difficult is likely to further impede learning. To understand why and when specific strategies work, it is therefore helpful to first take a closer look at learning itself.
Principles of effective learning
Instructional strategies that fulfill innate psychological needs increase intrinsic motivation, which leads to greater attention, effort, and, eventually, learning. Although needs differ among students, self-determination theory identifies three universal needs that frequently drive our decisions: competence, autonomy, and relatedness.
In addition to basic motivation, there is another key ingredient for learning: a suitable learning activity. If the activity is too easy, and the student does not need to exert effort, little learning will happen. Similarly, little learning will happen if the activity is too difficult for the student. For efficient learning, the activities should be within the reach of the student—possibly with some support, yet not fully automated. This sweet spot of appropriate difficulty is called the zone of proximal development.
A mismatched difficulty level also leads to negative emotions: boredom if the activity is too easy, frustration if it is too difficult. These negative emotions hurt motivation, consequently decreasing attention, leading to even lower learning. On the other hand, appropriate, skill-stretching challenges support achieving the state of flow, in which the student is completely immersed in the learning activity. According to the flow theory, there are two additional properties of activities needed to achieve the state of flow: immediate feedback and a clear goal.
Finally, learning activities should account for the severe limits of working memory. Cognitive load theory divides cognitive load into three types: extraneous, intrinsics, and germane. Extraneous cognitive load is the effort not directly related to the topic, intrinsic cognitive load is the effort associated with the inherent complexity of the topic, and germane cognitive load is the effort used for actual learning. For efficient learning, cognitive load theory recommends minimizing the extraneous load, controlling the intrinsic load, and maximizing the germane load. The high intrinsic complexity of programming explains why it is so difficult to learn: students need to learn the syntax of a programming language, its semantics and pragmatics (when to use which programming construct), as well as problem-solving and debugging strategies—all at the same time.
Strategies supporting learning
To decrease the otherwise excessive intrinsic cognitive load, introductory programming exercises can feature a block-based programming interface to avoid syntax errors, visualization of program execution in a microworld, and scaffolding in the form of a starter code. The intrinsic load can be further decreased by pretraining, or preceding programming puzzles by worked examples, visualizations, and problems on understanding program execution.
By no means, however, should the learning be effortless. As suggested by the zone of proximal development, too-easy problems are not helpful. We should strive to maximize the germane cognitive load. Active problem solving, for instance, usually leads to better learning outcomes than passively watching a lecture. There are other strategies to achieve desirable difficulties. For example, a cognitive conflict — revealing a student’s misconception — increases attention and helps to build more viable mental models of programming concepts. Interleaving of topics allows practice in not only how to use programming constructs, but also when to use them. Personalized problem recommendations can be employed to select appropriate difficulty based on the current skills of the student.
Other strategies focus primarily on motivation. Many learning systems provide mostly extrinsic motivational elements like points and badges. While useful to increase short-term performance, the extrinsic motivation can sometimes lead to detrimental effects, such as engaging in inefficient but rewarded activities and even undermining the original intrinsic motivation. Strategies that support intrinsic motivation, by fulfillment of psychological needs, should be the primary focus.
The theory of flow suggests three strategies for intrinsically motivating activities: difficulty that matches the skill of the student, well-structured problems with a clear goal, and immediate formative feedback. These strategies are also consistent with self-determination theory, as they support efficient progress towards mastery, which is closely related to the need for competence. Competence can be further promoted by progress visualization and praise. In order to increase long-term intrinsic motivation, praise should be sincere, specific, focused on the process rather than results, and relevant to attainable but nontrivial standards.
The need for autonomy can be fulfilled by giving students some level of control over their own learning. However, since students are not typically able to manage their own learning very well, it is still useful to provide at least soft recommendations, which the students are not forced to follow.
Finally, intrinsic motivation can be further increased by an entertaining story and appealing microworld, which can fulfil the innate needs for playfulness, curiosity, and harmony. Ideally, the story should be endogenous, or directly related to what is learned. A story that is not related to the learning content, such as collecting points for correctly solved math problems, is sometimes called a “chocolate-covered broccoli”, as it only provides extrinsic motivation.
Introductory Programming Exercises
Many of the discussed strategies can be applied directly to programming exercises. A popular example is the use of block-based programming interfaces. Block-based programming decreases intrinsic cognitive load, since the students do not need to remember available commands and their syntax. Furthermore, it makes the structure of the code more visible and helps to build more viable mental models by providing an expert-level view of the code structure.
Another typical feature of introductory programming exercises is a microworld. Visualization of program execution in a microworld decreases intrinsic cognitive load and helps to build a correct mental model of execution, while simultaneously supporting intrinsic motivation by fulfilling the need for competence (when allowing for powerful effects) and playfulness. Typical examples of microworlds are a robot on a grid and turtle graphics.1
Open-ended environments with a block-based programming interface and an appealing microworld can promote autonomy and curiosity, but the lack of guided instruction can result in adoption of bad programming habits. As an alternative to the open-ended exploration, a learning system can provide a series of programming puzzles. Short puzzles — if well designed and ordered from the easiest to the most difficult — can greatly support the state of flow. They provide clear attainable goals, immediate feedback on the performance, and also an appropriate level of challenge. The need for autonomy can be satisfied by other means, for example, by letting the student choose from multiple exercises available in the learning system.
Programming exercises can draw inspiration from successful puzzle games, which share common design principles, such as completing incomplete patterns and gradually unfolding a single path to solution through a sequence of small steps. Various forms of scaffolding, such as an initial partial program to complete, can be used to allow the students to solve interesting challenges from the very beginning.
The individual puzzles are important, but their context in the learning system can greatly impact the experience as well. This includes ordering of problems, tracking and visualization of progress, and recommendations on which problem to tackle next. To support the appropriate level of challenge, the puzzles should be ordered from the easiest to the most difficult. The difficulty, however, is not the only aspect for a successful puzzle progression. For instance, novelty in game elements is important to keep the puzzles engaging.
Adaptive learning refers to the use of data science to support learning and motivation, for example, by selecting problems of the optimal difficulty, optimizing parameters of mastery learning, and iteratively improving the educational content. Adaptive learning builds on the principles of effective learning and leverages methods from statistics, machine learning, and recommender systems. Rather than aiming at complete automation using artificial intelligence, however, the best results are usually obtained by appropriately combining human and machine intelligence. Such an approach is called intelligence amplification.
Iterative development of programming exercises is a good example of such human-machine synergy. Humans are still much better at creating engaging puzzles, but they struggle to estimate how difficult the puzzle will be for the novice programmer. Analysis of collected data can reveal that some problems are considerably easier or more difficult than originally expected or that the students solve the problems without the intended programming construct (e.g., without loops).
Such insights can help us to iteratively improve the exercises by reordering the puzzles, clarifying the problem statements, or providing hints, scaffolding, and feedback. Analysis of common misconceptions can sometimes also lead to ideas for new puzzles to create. This agile and research-based approach proved useful for developing content for https://www.umimeprogramovat.cz, a learning system that contains diverse programming exercises, some suitable for primary school children, others targeting high school and university students.
For more detailed discussion of the guidelines for design and iterative improvement of block-based programming puzzles, read “Design and analysis of microworlds and puzzles for block-based programming,” recently published in Computer Science Education.2
1. You can see various microworlds used in programming exercises at https://www.umimeprogramovat.cz and https://en.robomise.cz.
2. The paper is available at https://doi.org/10.1080/08993408.2020.1832813, or as a preprint at https://www.fi.muni.cz/adaptivelearning/?a=publications.
Thanks are due to Radek Pelánek for supervision and Red Hatters Marek Grác and Milan Brož for facilitation of the academic cooperation of Red Hat Czech and the Faculty of Informatics at Masaryk University.