Duet Programming

Duet programming is a protocol for collaborative programming that provides structure and organization for computer science learners. The purpose is to define roles that allow both students to actively engage in the design and implementation process at all times, albeit on different aspects of the same program.

This is accomplished by modifying the Test-Driven Development programming process. Test-Driven Development is inspired by the idea that if tests are written after the code has been designed and implemented, then it is harder to integrate new functionality into the code than if the tests were identified first. By designing tests and code together, many mistakes can be prevented from entering the implementation.

This approach provides symmetry in the process, in which the same stage of different tasks may be performed in parallel. That is, students design their tests and code simultaneously, discussing any discrepancies and making sure they are consistent before both moving on to the implementation stage. For larger problems / projects or multi-question assignments, students trade off between the tester and developer roles in order to learn both important aspects of program development.

This protocol has been created to address a disadvantage of Pair Programming, the dominant protocol for collaborative computer science learning. In that protocol, only one person actively develops at a time (the driver), with the second member assisting when necessary (the navigator). This protocol can make it difficult for the navigator to stay engaged.

There are two main aspects to collaborative work. The first is how to communicate with each other respectfully and constructively in order to provide a supportive learning environment. The second is to follow specific protocols that provide phases of working independently and working together in order to build skills towards completing software development entirely independently.

Maintaining a supportive learning environment

Do:

Don't:

Duet Programming Protocol

Duet Programming is divided into four phases. Each phases begins with independent working and ends with a discussion. From a high level, partner A will be the Implementer, and partner B will be the Strategist. The Strategist has three jobs: Think through all design decisions for their logic (rather than trial and error), serve as the pair's spokesperson (all questions to instructors must go through the Strategist), and design and implement the tests for this function. The basic roles are augmented with mechanisms to mitigate two potential disadvantages of such a split: load imbalance and missed learning opportunities.

Load imbalance occurs if one partner finishes before the other. In this case, the finished partner needs to find productive work to assist the second partner. We have several suggestions below.

Missed learning opportunities occur if each partner only ever learns the content of his or her own code. It is critical that students understand how the entire code works, not just his or her own portion. Therefore, students will debug the code the other partner wrote.

Before beginning duet programming, make sure you have completed the preparatory work. This will make your collaborative programming more efficient. Specifically, you need to have a version of main.c, x.h, and x.c that compiles. main.c should contain a single function call to every function in x.c. x.h provides function declarations for any function that is defined in the assignment and/or main.c needs to call. x.c contains a skeleton implementation for every function - for functions that return a value, they return a value of the proper type.

One round of duet programming is performed for each problem. A problem is defined as a single unit of completable work, typically a function. In early labs, each function will involve a completely separate execution of the protocol. It is conceivable that with more complex applications, a family of functions would be created to solve a single problem. These would be discussed together at the beginning and then solved separately during the protocol.

Phase 0: Problem Clarification

Discuss the problem at hand. Make sure both of you have a shared understanding of the task. Then begin to structure the solution, clarifying the following aspects:

Questions you should discuss and agree on:


Outcome: The result of this phase is a shared understanding of the problem and what you need to do to solve it.

Phase 1: Solution Co-Design

Implementer: In this phase, you will design your code and record that design in the form of code comments (though you are also welcome to create diagrams to either aid in the design development or to better communicate your ideas to your partner).

Strategist: Identify input ranges (valid inputs and non-valid inputs with borders), identify which inputs should be test cases, and calculate their expected results. Categorize test cases as "normal," "boundary," or "error." Sketch out your tests as comments in main.c. These tests are called black box tests because you know only the interfaces, not the actual code, when you design them.

Load Imbalance Mitigation: If one partner is done substantially earlier than the other, here are some ways to stay productive:

Once you are both finished, you are ready to discuss your work. Read each other's code. Discussion: The purpose of this discussion is to see if either design informs the other design. Check to see if the function design satisfies all of the tests, and check to see if the tests test all uses of the function design. Look for flaws or limitations in both designs so that you can solve them before implementation. In addition, the Strategist needs check for the solution making logical sense.

Outcomes: At the conclusion of this phase both students will have a tangible plan, recorded in code comments, for the implementation phase of the protocol.

Phase 3: Implementation

Implementer: Implement the function(s) you just designed. (in the .c file). Compile only your code (using the -c flag).

Strategist: Implement the tests for the core function(s). Remember to create test functions that encapsulate verification of specific tests. Compile only your code (using the -c flag).

Load Imbalance Mitigation: If one partner is done substantially earlier than the other, here are some ways to stay productive: Once you are both finished, you are ready to discuss your work. Discussion part 1: Inspect the tester's code. Look at input ranges from the black box tests. Is there separate code to handle each case? If not, are the different ranges equivalent?
Inspect the code for the logic and reasons behind design choices.
Discussion part 2: Inspect implementer's code. Do the boundaries for if and loops match the boundaries for the test cases? Do the test cases exercise all paths in the code? Does the tester understand the coding decisions the implementer made?
Inspect the code for the logic and reasons behind design choices.
Outcome: The result of this phase is a complete function(s) and full set of test cases for the function(s) that compile individually.

Phase 3: Compilation and testing

This phase is performed together, however, primary responsibility for editing files swaps in order to allow both students to familiarize themselves with the code their partner worked. The tester debugs the function code, and the implementer debugs the test code. In the final phase of the protocol, the final compilation occurs followed by running of test cases on the implemented function(s). If any errors or omissions are found, the pair jointly debugs the solution. For this phase, the two code bases are swapped, so the Strategist is in charge of editing the Implementer’s code and vice versa.
Responsibility: Both are responsible for the completion of a functioning program and complete set of passing test cases.
Discussion: Throughout this phase, the pair works together to debug the program and write new test cases if needed. Concluding prompts may be included for specific problems by the instructor.
Outcomes: The result of the phase is a correctly functioning program and a comprehensive set of passing test cases.