I have been doing pair programming for a while, and I would like to share a set of rituals that I use while doing pair programming. They are not the kind with hard boundaries, but you should try keeping the checklist in mind while talking to your pair.
Also, I do not claim that either this is the only or the best way of doing pair programming - I am just sharing what worked for me so far.
I divide every pair programming session I have into three parts: handshake, drive, and endgame.
In Handshake, you and your pair should discuss and agree on three topics.
The first topic is “scope,” you both must precisely decide what the deliverable is. It is crucial to agree on the scope to avoid later divergence. You might have experienced this divergence in the past. You notice it when one of you starts talking about a topic and the other does not follow along only to later acknowledge: “Oh OK, I thought you were referring to something else.”
Once the scope is evident the following topic is “design.” You both must decide on the design of the solution to the task. Make it concrete in a sheet of paper or some whiteboard. The debate on design is necessary to create a common vocabulary for the solution you are going to code. You both should try using the same name for the same things.
Finally, you have to agree on what “failure” is, because that is what prevents a pair programming session to drag on and on. If you make a design mistake and you do not have an agreement on failure, you might be tempted on just powering through the problem instead of backing off and regrouping.
The drive is a smooth dance. The key here is communication. Keep talking about what you are thinking while you are typing. That is key to let the passenger know when to interrupt or make questions. A considerate passenger waits for cues before talking so to avoid derailing the line of thought of the driver.
I think there are two moments to be aware. When the passenger has a complicated idea to share and the committing point.
Surely there are many adequate moments to switch drives, but you should not overlook the importance to switch when the passenger has a complicated idea to share. Being in the driver seat is the best way to explore what the idea is in code.
You should also pay attention to commits. It is quite easy to complete an essential part of the work, and instead of committing the code and call it a partial victory, you keep coding. It is not ideal for two reasons: first, you do not internalize the progress - commit points are like small trophies along the way that help you to feel energized; but also because when you end your day, you have a massive change that is hard to partially rollback when something goes wrong.
In summary, partial work commits are both safety and victory points.
You finished everything you needed to do. Time for the endgame. At this moment you should focus on polishing the code and refactor.
By polishing, I mean checking any leftover “TODO” marks, remove or update old comments, check if the variables, functions and method names still align with the initial design (either by acknowledging that you changed the design or you changed the names) and any other code grooming task that you might think.
If you are used to “Test Driven Design,” you probably have refactored the code along the way. That is fine and valuable. However, I mean Refactor here in a broader meaning. You are not just refactoring code locally to make it better. You are refactoring towards the initially agreed design of the task: removing moving parts and aiming to make it as simple as possible.