Back in what now is probably in computer terms the mist of time, we used to do test driven development. We engaged in deliberate practice using code katas. This helped us improve our keyboard, refactoring, and coding skills.
Well with the advent of AI and programming agents, we can do that again! Turning the agent into a co-programmer in a remote world.
After COVID, work has become a lot more remote. Pair programming has become more difficult. This is true even with the advances in screen sharing and multi-editing in our integrated development environments. I began to think about the possibility of using copilot as a programming pair. We can work together in the “ping pong” programming style. This style can be so productive and fun. We would get the advantages of AI while allowing me to continue to practice my programming skills. This approach helps in avoiding the loss of productivity, which has been gained by using AI Agents.
“Ping-pong” pair programming is a style of pair programming. Two developers take turns writing tests and production code for a feature.
The sequence starts with the first developer writing a filing test. Then, the second developer writes the production code to make the test pass. They refactor all the code. Then, they write the next failing test. After that, the first developer writes the production code to make the test pass and refactors. The sequence is continued until the feature is finished and there are no more tests to write.
Instructions can guide Copilot. They are added to every prompt. These instructions form part of the context in which Copilot works. These instructions are stored in a file called “copilot-instructions.md” and are made up of text that describes in natural language the guidelines.
# Ping Pong Programming (AI & Programmer Roles)
## Sequence of steps
1. The programmer writes a failing test.
2. The AI (Copilot) makes the test pass in the simplest way possible.
3. The AI (Copilot) refactors the code to improve it, if needed.
4. The AI (Copilot) updates the specification file to reflect that the test has passed.
5. The AI (Copilot) writes the next failing test.
6. The programmer makes the new test pass in the simplest way possible.
7. The programmer refactors the code to improve it, if needed.
8. The programmer updates the specification file to reflect that the test has passed.
9. Repeat steps 2–6, alternating roles, until the feature is complete.
## Rules
Only one failing test should be added at a time.
Each participant (AI or programmer) only does their assigned steps before handing off.
Refactoring should be done after making a test pass, before writing the next test.
The specification file should be updated after a test passes to reflect the current state of the feature.
## C# Styling Guidelines
- Use PascalCase for class names and method names.
- Use camelCase for local variables and parameters.
- Use meaningful names for variables and methods.
- Use `var` when the type is obvious from the right-hand side.
I wanted to include the specification for all the tests as part of the copilot context. This applies to this version of the kata. The specification can be written in any format we want. I used a simple checklist style in markdown. I wrote the list in a function name style so I can copy and paste it into the test file. The agent will sometimes look ahead, which is cheating. but I think we can let him off.
# String Calculator Specification
we will be doing these things in order, when a step is completed mark it as done by adding a checkmark in front of it.
[] Given_an_empty_string_the_result_should_be_0
[] Given_a_single_number_the_result_should_be_that_number
[] Given_two_numbers_separated_by_a_comma_the_result_should_be_their_sum
[] Given_an_unknown_amount_of_numbers_separated_by_commas_the_result_should_be_their_sum
[] The_delimiter_can_be_specified_using_a_special_syntax_at_the_beginning_of_the_string e.g. //;\n1;2 should return 3
[] If_the_input_contains_negative_numbers_an_exception_should_be_thrown_with_a_message_listing_the_negative_numbers
[] If_the_input_contains_numbers_larger_than_1000_they_should_be_ignored_in_the_sum
[] The_calculator_should_handle_new_lines_as_delimiters e.g. 1\n2,3 should return 6
[] The_calculator_should_handle_delimiters_of_any_length e.g. //[***]\n1***2***3 should return 6
[] The_calculator_should_ignore_whitespace_around_numbers e.g. 1 , 2 should return 3
[] The_calculator_should_handle_empty_delimiters e.g. //;\n1;;2 should return 3
[] The_calculator_should_handle_multiple_custom_delimiters e.g. //[;][#]\n1;2#3 should return 6
[] The_calculator_should_handle_mixed_delimiters e.g. //[;][#]\n1;2#3 should return 6
[] The_calculator_should_handle_escaped_delimiters e.g. //[;][#]\n1;2\\#3 should return 6
[] The_calculator_should_handle_escaped_custom_delimiters e.g. //[;][#]\n1;2\\#3 should return 6
[] The_calculator_should_handle_escaped_mixed_delimiters e.g. //[;][#]\n1;2\\#3 should return 6
[] The_calculator_should_handle_escaped_empty_delimiters e.g. //[;][#]\n1;;2 should return 3
[] The_calculator_should_handle_escaped_whitespace_around_numbers e.g. 1 , 2 should return 3
And finally the prompts a prompts file that is useful to copy the prompts I used to do the kata the first time,
# Getting started with the kata
we are doing ping pong programming on this kata, so its your turn there is a failing test, make it pass, refactor, update the spec and write the next failing test for me.
# Next turn
I have made the test pass, refactored the code and written the next failing test.
your turn.
The starting repository is here. It includes a dummy test and read me file. There is a complete setup to do the kata in c#.
https://github.com/DuncanButler/AgentKataTDD.git
One of the issues when working with AI copilots is that we can understand what the code is doing. But, we lose track of how the code actually works. It becomes difficult to know where each part is among the stream of code that copilot can produce.
We want to keep the productivity gain of using agents. We also want to be “in the code,” understanding not only the how but also the where and why. Pair programming with the agent allows us to be involved in the code actively. It involves writing and refactoring the code as it is produced not only by us but also by the agent.