Constructivist Apprenticeship through Antagonistic Programming Activities (information science)

 

Introduction

Computer programming involves more than thinking of a design and typing the code to implement it. While coding, professional programmers are actively on the lookout for syntactical glitches, logic flaws, and potential interactions of their code with the rest of the project. Debugging and programming are therefore not to be seen (and taught) as two distinct skills, but rather as two intimately entwined cognitive processes. From this perspective, teaching programming requires instructors to also teach students how to read code rigorously and critically, how to reflect on its correctness appropriately, and how to identify errors and fix them.

Recent studies indicate that those students who have difficulties in programming courses often end up coding without intention (Gaspar & Langevin, 2007). They search for solved exercises whose descriptions are similar to that of the new problem at hand, cut and paste their solutions, and randomly modify the code until it compiles and passes the instructor's test harness. This behavior is further exacerbated by text topics, which only require students to modify existing code, thus ignoring the creative side of programming. Breaking this cognitive pattern means engaging students in activities that develop their critical thinking along with their understanding of code and its meaning.

This article discusses constructivist programming activities that can be used in undergraduate programming courses at both the introductory and intermediate levels in order to help students acquire the necessary skills to read, write, debug, and evaluate code for correctness. Our constructivist apprenticeship approach builds on earlier field-tested apprenticeship models of programming instruction that successfully address the learning barriers of the new generations of novice programmers. We go one step further by realigning such approaches to the genuine difficulty encountered by students in a given course, while also addressing some pedagogical shortcomings of the traditional apprenticeship instructional practice. This is achieved by introducing a strong pedagogical constructivist component at the instructional level through so called antagonistic programming activities (APA). We conclude with a manifesto for a new multidisciplinary research agenda that merges the perspectives on learning found in both the computing education and evolutionary computation research communities.

background

Novice programmers and their Learning Barriers

The study of the learning barriers encountered by novice programmers is critical to the computing education research community. Recent studies describing the misconceptions and preconceived notions held by novice programmers (Chen, Lewandowski, McCartney, Sanders, & Simon, 2007; Kolikant, 2005) indicate that these learning barriers evolve with each new generation of students. In this context, a phenomenon known as "programming without intention" has been identified as an attempt by students who encounter difficulties in programming to mechanize the programming thought process. Their heuristic boils down to the following: (a) reading the description of the program to write and look up available documentation (solved exercises, Google, Krugle, etc.) for another similar, already-solved exercise, (b) cutting and pasting the solution to that exercise as a starting point for the current assignment, and (c) compiling and running the program and, since it most likely does not do what is expected, modifying it. Due to the lack of understanding of the solution being reused and the lack of time devoted to understand the programming activity from the ground up (e.g., learn the syntax, learn the role of statements, learn when to use which), these modifications often boil down to a series of almost random changes until the program seems to execute according to the requirements.

This obviously random-based development approach has very little to do with programming and leaves students unable to explain why a particular statement is in their code. In some occurrences, students stated, "I have the code now for this assignment; I need to understand it." This indicates a complete reversion of the programming thought process leading from ideas to implementations. Instead, intentionality is lost, and statements are manipulated in an almost mechanical manner without second thoughts. Essentially, students are utilizing skills at the lower end of the knowledge framework by demonstrating cognitive functions that Bloom (1956) would have termed as knowledge or understanding with no ability to analyze, synthesize, or evaluate the programming process itself.

Criticizing this approach is, however, insufficient. Understanding what reinforces our students' belief that they are problem solving when developing code this way is what can really help us lead them to overcome this particular learning barrier. The nature of the exercises typically found in some introductory programming courses might be partly responsible for this situation. Often, novice programmers are only required to reuse already working programs and modify them slightly (under heavy guidance) to do something new. While analogical thinking is essential to the professional developer when learning new languages, technologies, and paradigms, it is not safe for it to be the only conceptual tool developed by students during their first programming experience. Creative thinking, critical thinking (e.g., debugging), and problem solving are all essential components of the programming thought process, which, if not given proper attention from the beginning, might fuel the misconception that programming is just a matter of pattern matching in a big topic of existing solutions.

Leveraging Apprenticeship in Programming Courses

This learning barrier can be addressed by an apprenticeship model of teaching (Kolling & Barnes, 2004), which can take on several distinct forms. The most obvious one is instructor-led live coding: An instructor presents a problem to her or his students, lets them work on it for a definite time, and then introduces the solution. Instead of presenting students with a detailed explanation of the complete solution, the instructor builds the solution from scratch in front of his or her audience. This diverges from the usual instructional pattern, which leads students to build a dictionary of problem-solution pairs that were introduced in class. Such courses encourage students to memorize data in the hopes that they will be able to simply regurgitate it at the next exam. If a question dares differ from a previously solved problem in any significant way, they will then attempt to fit the memorized solution to this new problem by applying a couple of minor adjustments, which could be stumbled upon almost randomly. By developing the solution in front of the students, the instructor's teaching is aligned with the learning outcomes of the course: the programming thought process itself vs. its outcomes. This approach is clearly illustrated in the work of the BlueJ team and their text topic (Kolling & Barnes, 2004).

Other implementations of the apprenticeship model of teaching are closer to problem-based learning approaches; students are taught the programming thought process by applying it frequently to solve new problems from scratch. This learn-by-programming or learn-by-doing approach also leads students to realize the importance of creative and critical thinking in the programming activity while reducing the benefits of memorization-only or analogy-only strategies. In complement, these pedagogical strategies are often coupled with peer learning approaches (McDowell, Hanks, & Werner, 2003; Willis, Finkel, Gennet, & Ward, 1994).

These apprenticeship pedagogical strategies address the above-mentioned learning barriers by aligning the skills being practiced by students during exercise sessions with the authentic learning outcomes expected from an introductory programming course. This in itself complements nicely with constructive alignment theory (Biggs, 2003), which aligns assessment tools with expected learning outcomes.

From Apprenticeship to Constructivist Apprenticeship

Despite these significant pedagogical achievements, the apprenticeship model of instruction can be further improved from the instructional method perspective. Let us take a critical look at the above-mentioned apprenticeship activities: instructors demonstrating the programming thought process while solving a problem live, classmates developing code while other students play the role of a peer programming observer, students coding against each other in a game-based learning environment (e.g., Bierre, Ventura, Phelps, & Egert, 2006).

These activities are essentially instructivist in nature; students are presented with a problem, they work on it, and then the instructor (or their peer) corrects them or even develops a complete solution for them. Even though the thought process is the focus of the demonstration rather than the solution itself, the teaching process is mostly unilateral. The "sage on the stage" (or next seat) strikes again and leads students to adopt a rather passive attitude as they receive their instruction.

Besides the motivational or attention-span issues that such approaches can cause, the work invested by students to develop their own solution is completely ignored in the instructional process (a hallmark of instructivist pedagogies). They are therefore never corrected, improved, or even leveraged to understand how the student learns. The student is instead required to replace his or her misconceptions with the correct ones. This process is not transformative and often leaves students wondering why their own solutions were incorrect. Down the road, students will commit errors when trying to apply a knowledge that was accepted on top of very blurry foundations.

In such a scenario, the quality of instruction is expected to make up for this lack of connection with the students' cognitive models. Its quality is based on the instructor's past experience with similar student populations or on the instructor's knowledge of studies published on traditional learning barriers encountered by sometimes significantly different student populations. While necessary, this knowledge too often fails to capture the authentic learning barriers encountered by students in the classroom being currently taught. Indeed, such studies do not account for the evolution of the learning barriers, cognitive models, and preconceptions with which each single generation of students arrives. If they did, the computing education research community would have certainly converged by now toward an optimal pedagogical strategy for novice programmers. This mislead-ingly portrays the pedagogy of programming research as a static problem that might end up being solved once and for all for the ages to come regardless of the evolution of students' learner profiles and the programming technologies and methodologies themselves.

We suggest that the next step in improving the apprenticeship model of learning and teaching resides in developing and replacing inherent instructivist dynamics with constructivist ones. This work led us to define constructivist apprenticeship as a general pedagogy. In the context of computing education research, this pedagogical strategy proved to be particularly suited for introductory and intermediate programming courses in which it could be implemented as antagonistic programming activities (Gaspar & Langevin, 2007).

constructivist apprenticeship as a programming pedagogy

constructivist Apprenticeship and Antagonistic programming Activities While the constructive alignment theory (Biggs, 2003) aligns assessments with learning outcomes, constructivist apprenticeship completes it at two levels. First, it aligns the learning activities themselves (focus on programming thought process and apprenticeship) with these same learning outcomes. Second, it aligns, through constructivism, the pedagogy of instruction with the authentic learning barriers students encounter (as opposed to the assumed ones). In a programming course, such a pedagogical strategy can be implemented through programming activities that adhere to the following principles. First, the activity must develop programming skills and prevent students from reaching acceptable results through mere cut-and-paste strategies. Second, it must reduce the instructivist dimension of the learning experience by fostering a symmetrical dialog between instructor and student (or among students for peer learning). Last, it must reduce this instructivist dimension by de-emphasizing the importance of showing students the "right way." The last two principles led us to consider the benefits of situations in which students work against one another. This reflection led us to experiment with various classes of antagonistic programming activities.

APAs are programming activities aimed at honing the students' critical thinking and troubleshooting skills in order to develop authentic programming skills at the higher levels of Bloom's (1956) taxonomy. These active learning activities enable groups of students to work together but with antagonistic goals, in face-to-face or distance education settings, under the instructor's supervision or purely as peer learning. These activities also leverage constructivist teaching methods in so far that very little of the solution is communicated to students; instead, their errors are pointed out by the instructor or their peers, but left to be fixed by them. The following sections examine the core idea of each APA variant, provide application examples, and discuss their salient features.

APA 1: Student-led live coding (design-focused variant). Many instructors have already realized the benefits of developing solutions in front of their students (live coding). This apprenticeship teaching (Kolling & Barnes, 2004) successfully refocuses the teaching effort on the programming skills themselves instead of the memorization offinished code solutions. However, students are still passively taught the correct thought processes and accept it as they would have accepted a complete solution, while their own errors are still left out of the learning experience.

In response to the instructor-led live coding's shortcomings, one can let students solve problems in front of their classmates. This student-led live-coding variant requires, for each exercise, the teacher to pick a student who will operate the podium PC with a wireless keyboard (Hailperin, 2006). The student's work is projected, thus enabling the instructor to solicit classmates for corrections, improvements, and discussions about the thought process itself and not the complete finished solution only (unlike most peer learning pedagogies). Most importantly, the students' errors provide the instructor with an authentic understanding of his or her students' problems on which to base the lecturing and class discussions. This marks an improvement compared to the way instructors usually base their pedagogy on their own past experience or published studies, which, while statistically significant, might relate to a population significantly different from their current students. This is also the trademark of a constructivist approach, which values and integrates the students' errors as part of their learning experience.

APA 2: Student-led live coding (alternative-focused variant). In the alternative-focused variant, one student is still given the wireless keyboard. However, while this student develops his or her own solution, others work independently on their own. This allows for more independence and therefore enables the emergence of radically different solutions while still enabling those having difficulty to peek at the chosen student's early work. During the activity, the instructor walks among students, helping them with their individual solutions, answering questions, and assessing the variety of strategies being implemented. After a set time, students are invited to look back at the chosen student's work and contribute comments, fixes, and suggestions for alternative approaches. The instructor can then lead a hands-on discussion while the code is being modified to evaluate the various strategies (good or bad) that were explored independently by students. The following exercise was given as such as an in-class activity for the fourth week of an intermediate programming course right after a recursion lecture:

Implement an iterative and recursive version of a function that will return how many times its (strictly positive integer) argument can be divided by 2 until you get a nonnull remainder. For instance, F(4) will display 2 time(s), F(5) will display 0 time(s), and F(6) will display 1 time(s).

Students came up with a wide range of solutions including iterative ones (e.g., while loop), recursive ones building their results on call returns (expected as the closest of the lecture's examples), and recursive ones building their results during the call (using an extra parameter). This resulted in discussions about tail recursion optimization and detailed explanations (based on program execution stack diagrams) of each solution to ensure they were understood by all. Finally, students also generated unanticipated solutions involving global and static local variables, which motivated a minireview session on these topics. This variant illustrates how a slight protocol change can foster significantly different learning dynamics, which can be further supported by open-ended exercises.

APA 3: Test-driven peer code reviews. So far, APA relied on instructors to serve as a central hub, coordinating the students learning. In larger classes, it becomes difficult for a single instructor to coordinate many students while still allowing all of their voices to be heard. In such settings, peer learning strategies are generally more successful. The very principles of constructivist apprenticeship can also be leveraged in a peer learning context by enabling students to correct each other in a noninstructivist manner. Most peer learning pedagogies rely on collaboration between peers of equivalent levels. In practice, the peers might end up exchanging complete solutions unilaterally or, in a less extreme dysfunctional scenario, exchange corrected code fragments or partial solutions. Even if this teaching and learning dynamic is not unilateral, each peer's contribution too often ignores the others' and boils down to a "your code doesn't work, mine does" reaction. How can we switch this learning dynamic to something more balanced (both students get to develop their solution and improve them) and more constructivist (their individual attempts are matured in a satisfying solution rather than ignored in the acceptance of correct solutions)? The following exercise, adapted from the Javabat applets (Parlante, n.d.), illustrates how this can be done.

The students were paired and given a problem description:

The squirrels in Palo Alto spend most of the day playing. In particular, they play if the temperature is between 60 and 90 (inclusive). Unless it is summer, then the upper limit is 100 instead of 90. Using Raptor, write a flowchart which is going to ask the user to provide a temperature value (between 0 and 130) and a number summer which will be equal to 0 or 1. Depending on the values that were passed to you by the user, you will determine whether the squirrels are playing or not and display on the screen an appropriate message.

In each pair, both students developed their own solutions independently along with a test harness. A test harness, in this case, was simply a written list of tests to be performed on the programs along with their expected and observed outcomes. Once satisfied with their code, students applied their test harnesses to their peer's code. As they did so, they read the code itself, taking note of which tests failed and adding new tests to capture flaws they spotted in their peer's code. Then, they exchanged again their programs to improve them based on the failed tests. This process was reiterated until both programs were successful through each test harness.

The main pedagogical benefit of this APA is its constructivist nature; students instruct each other without exchanging code and without directly fixing errors (a more subtle form of instructivism). Instead, each peer can only adapt their test harness to reflect the flaws they perceived in both programs. This allows students to develop their critical thinking and debugging skills without being shown how to improve directly. Misconceptions in students' minds, along with their incarnation as bugs in their respective programs, are therefore addressed without relying on an instructivist exchange.

The activity is also antagonistic; students "attack" each other's code through test harnesses. However, unlike most game-based educational activities (Bierre et al., 2006), students are not allowed to confuse "learning by blasting" with "learning to blast. In many competitive learning settings the task at hand (e.g., piloting a tank) ends up overpowering the educational outcome (e.g., learning to program). Code quality, sometimes even correctness, become secondary to performing well in the game. Depending on the game itself, the winning strategy can cost students their learning experience. In our activities, this obstacle is removed by having students compete on code correctness through test harnesses instead of programs' outcomes.

future trends

Constructivist apprenticeship realigns the pedagogy of contents with learning outcomes and the pedagogy of instruction with the authentic learning barriers experienced by students, thus complementing the achievements of constructive alignment theory. From the applicability perspective, this principle is extremely flexible, and its benefit can reach beyond the programming courses (introductory and intermediate) we have been focusing our discussion on so far. Any course conveying a problem-solving skill to students can benefit from this pedagogical strategy (e.g., accounting, software engineering, algorithms design, etc.). In this expanded context, live coding and code peer review activities can be more broadly perceived as peer-reviewed problem solving. Besides its application to a wider range of courses and disciplines, constructivist apprenticeship has also the potential to serve as ambassador for a new interdisciplinary research agenda bringing together the understanding of learning dynamics from two apparently unrelated fields: evolutionary computation and computing education. The following example illustrates how APA can draw inspiration from and leverage evolutionary computation techniques to inform and improve educational practices.

Using test harnesses in APA allowed us to leverage constructivist peer learning dynamics. Instructors can use similar strategies to teach students by, for instance, crafting an array of values to fail a student's sorting algorithm. Such an approach convinces students of their errors without forcing a solution, which would fail to explain why their codes are not acceptable. Interestingly, similar strategies are used when coevolving artificial neural networks. The way students develop iteratively new test cases meant to fail their peer's code as it is being improved from its confrontation with previous test harnesses can be seen as a coevolutionary dynamic. Test harnesses and programs are defining their respective fitness (i.e., goodness) as a function of how they perform against one another. Similar coevolutionary dynamics can be found in predator-prey models, leading each species to improve under the selective pressure of their counterpart's own evolutionary drive. This dynamic has also been successfully applied to the design of artificial neural networks (Mayer, 1998). A series of neural networks are created randomly, thus forming a population that will be opposed to another population made of training samples, that is, a set of input-output pairs that represent the expected behavior neural networks are expected to learn. The two populations undergo an evolutionary computation transform mimicking the mutation, recombination, and selection scheme found in natural evolutionary systems (Holland, 1992). The quality of a neural network is then quantified in terms of the number of training samples it can successfully solve. Inversely, the quality of a training sample is measured by the number of neural networks it can cause to produce a wrong error. By improving the quality of one of the two populations, we improve the quality of the other, thus leading to a so-called coevolutionary scheme.

conclusion

This article describes constructivist apprenticeship and its applications to programming courses through antagonistic programming activities. We stressed the potential of this approach to help students overcome a learning barrier characterized by a loss of intentionality when designing programs and its applicability to both instructor-focused and peer-learning scenarios. Ramifications and synergies with other educational theories were discussed as well as the potential for constructivist apprenticeship to benefit other computing courses, or even disciplines, that focus on teaching problem-solving skills. Besides the educational framework in which this approach has been developed, we see constructivist apprenticeship as a herald of an interdisciplinary research agenda bringing into the computing education research field an understanding of learning dynamics from the perspective of evolutionary computation researchers. As discussed, results in the latter can mature into and inspire improved pedagogical strategies.

KEY TERMS

Antagonistic Programming Activities: These are programming learning activities meant to motivate students by leveraging competitive dynamics focused on scrutinizing, critiquing, improving, and troubleshooting classmates' code. These activities embody the constructivist apprenticeship principles as applied in both instructor-supervised and peer-learning contexts.

Apprenticeship: An apprenticeship is an educational approach historically employed to train crafts practitioners adapted to computing education as a way to teach programming skills through instructor-led demonstrations of from-scratch problem solving (instructor-led live coding).

Constructive Alignment Theory: It is an alignment of learning and teaching activities with the course outcomes and constructivist principles. It was introduced by Professor John Biggs (2003)

Constructivist Apprenticeship: This is a variant of the apprenticeship model of teaching that realigns the teaching practice to incorporate constructivist educational practices. It is applied to programming courses through antagonistic programming activities.

Evolutionary Computation: It is the field of research that deals with the design, and application to engineering problems (e.g., optimization, learning), of bio-inspired algorithms that embody the quintessential characteristics of natural evolutionary systems.

Student-Led Live Coding: It is an antagonistic programming activity in which a student's programming thought process is made visible to all in order to enable an apprenticeship learning based on the authentic learning barriers encountered by students (as opposed to the assumed ones).

Test-Driven Peer Code Review: It is an antagonistic programming activity that pairs students to work on a given exercise, yet allows them to develop their own solutions independently, thus ensuring the symmetrical involvement of both peers. Solutions are then exchanged and test harnesses developed to fail the peer's code. These test cases are the only instructive information exchanged between peers, thus ensuring a constructivist learning dynamic.