Why pair programming worked for our small team
When our small development team began sharing keyboards, the arrangement looked inefficient. Two people were assigned to one workstation while another desk sat empty, and our project manager worried that we were paying two salaries for one visible line of code. The usual picture of productivity was still individual: each developer took a task, disappeared into a cubicle and returned with a completed feature.
We had a different problem to solve. Our projects were small, deadlines were close, and every person carried knowledge that nobody else fully understood. A defect in authentication could delay a release as easily as a missing database index. When one developer was away, progress often stopped because the relevant decisions existed only in that person’s memory.
Our office was in Melbourne, although clients and contractors were spread between Sydney, Adelaide and Brisbane. A local software market built around consultancies and short contracts rewarded teams that could move quickly between unfamiliar systems. It also punished expensive rework. We could not afford long handovers, elaborate management layers or weeks spent rediscovering how an older application worked.
Pair programming gave us a practical way to share responsibility while the work was happening. One person typed and the other examined the design, tested assumptions and watched for mistakes. The benefit was less about two people producing twice as much code and more about making better decisions before those decisions became expensive.
The problem was concentrated knowledge
Our first applications were typical of small-team software from the middle of the 2000s: Windows clients, SQL databases, web forms and a mixture of well-understood and inherited code. We used Visual Studio on some projects, Eclipse on others, and Subversion for source control. Documentation existed, but it was usually written after a release, when everyone was tired and the details were already fading.
This created a fragile division of knowledge. One developer understood the billing rules, another knew the deployment scripts, and a third could explain why a particular table contained duplicated records. If the right person was sick or working at a client site, the rest of us became cautious. We avoided changing unfamiliar code, which meant that old defects remained in place.
Pairing made the codebase a shared workplace rather than a collection of private territories. A developer who normally worked on the user interface could sit with someone responsible for the data layer and learn the constraints directly. The knowledge transfer happened through ordinary conversation, while the system was being changed for a real reason.
That mattered during staff leave and client visits. In Australia, even a short trip from Melbourne to Sydney could remove a developer from the office for much of a day once airport travel and meetings were included. We did not need a formal training session to keep the project moving; a pair had already exposed another person to the relevant files, tools and decisions.
A pair gave every task a second review
The person at the keyboard was the driver. The other developer was the navigator, checking the code as it was written and asking whether the proposed solution matched the requirement. We changed roles regularly, often after a small task or a natural stopping point, so that neither person became a permanent typist or supervisor.
The navigator’s work was active. They noticed a missing validation rule, questioned an unclear variable name and remembered a similar function elsewhere in the application. They also read error messages aloud, considered boundary cases and checked whether a shortcut would create trouble during deployment. This was a continuous code review, rather than a review request placed in a queue at the end of the day.
For a small team, that timing was valuable. A defect found while a method was still being written cost little to fix. A defect discovered after a week of independent work could require a new database migration, revised test data and an awkward explanation to a client. Pair programming moved review closer to the point where the decision was made.
It also reduced the emotional weight of criticism. A comment such as “this will fail when the account has no address” referred to the shared task, not to the character or ability of the programmer. Because the conversation occurred in real time, we could resolve the issue immediately instead of collecting grievances for a formal performance discussion.
The practice improved design before implementation
Many of our early technical disagreements were really questions about design. Should a rule live in the user interface, the stored procedure or a service class? Should a report query calculate totals each time, or should the application maintain a summary? Working alone, a developer might make a reasonable choice and only reveal it when the feature was complete.
A pair made those choices visible sooner. Talking through an approach forced us to state its assumptions. The driver explained what they intended to change, while the navigator tested the idea against existing behaviour. Sometimes this produced a simpler design. Sometimes it showed that a quick fix would duplicate logic already present in another part of the system.
The conversation was especially useful in unfamiliar code. The person with historical knowledge could explain why a strange-looking condition existed, while the newer developer could ask why it was still necessary. That combination helped us distinguish a genuine business rule from an accident left behind by an earlier release.
We did not treat discussion as a substitute for experimentation. When the answer was uncertain, we wrote a small test, inspected a query plan or built a disposable prototype. Pairing made that investigation focused. Two people could agree on what they were trying to learn, then discard the experiment without confusing it with production code.
Small teams need shared ownership
Individual task allocation can create an invisible competition. Developers begin to protect their own areas, measure progress by personal output and feel responsible for making their code look successful. In our group, pairing changed the unit of ownership from the individual feature to the working product.
That shift was useful when a release went wrong. If a pair had implemented a calculation incorrectly, the response was to inspect the assumption, reproduce the failure and correct it. We were less inclined to ask who had typed the offending line. The team owned the behaviour, and the team fixed it.
Shared ownership also helped with routine maintenance. A developer could improve a build script, update an Ubuntu workstation or repair a test without worrying that the change belonged to someone else. People became comfortable moving through the repository because they had seen more of it in use. The result was a broader practical skill set rather than a set of narrow specialties.
We still had individual strengths. One person was better at database tuning, another at interaction design and another at diagnosing Windows deployment problems. Pairing did not erase those differences. It allowed those strengths to circulate, so expertise became an asset for the whole team instead of a dependency on one employee.
The office routine mattered as much as the method
Pair programming failed when we treated it as a rule that required two people to sit together every minute. Some work was better done alone: reading a long specification, investigating a production log, attending a client call or thinking through a difficult algorithm. We used pairing for design, implementation, debugging and tests, then separated when concentration or privacy made that sensible.
The physical setup helped. We placed two monitors where possible, kept the shared keyboard and mouse comfortable, and used a larger screen for code and test output. In a warm Melbourne afternoon, poor ventilation and an awkward chair could damage cooperation faster than a disagreement about architecture. Basic workplace comfort was a technical concern because tired people stop communicating clearly.
We also arranged regular switches rather than waiting for frustration. A driver who had typed for three hours could become attached to a weak approach without noticing it. Changing seats refreshed attention and gave the navigator a genuine chance to shape the implementation. Short breaks were useful, particularly during long debugging sessions when the same failing test had begun to dominate everyone’s thinking.
Our clients occasionally needed reassurance about the arrangement. We explained that the second person was reviewing, testing and reducing delivery risk, not watching over a junior employee. The Australian market was already conscious of consulting costs, and some clients compared a visible pair with a single contractor working alone. Showing the quality of the resulting build, the speed of defect resolution and the continuity during leave made the value easier to understand.
Trust made the economics work
The financial argument for pairing was never that two people could type the same amount of code as one. The argument was that code was only a small part of the cost. Misunderstood requirements, defects, support calls and delayed releases consumed more time than typing. Pairing spent more effort at the beginning of a task to reduce those later expenses.
We measured the practice informally. We watched how often work returned from testing, how long new developers needed before making safe changes and whether a deployment depended on one person being available. The results were uneven, but the pattern was clear: complicated areas benefited most. Straightforward data-entry screens often needed less pairing, while security checks, payment calculations and integration points usually justified it.
Security work was an important example. When we handled personal information, we considered access controls, password storage and logging with two sets of eyes. The Privacy Act 1988 and the expectations of Australian clients did not remove the need for technical judgement, but they reminded us that careless handling of customer records could have consequences beyond a failed test. Pairing encouraged us to ask who could see data, where it was copied and whether diagnostic output revealed too much.
Trust also extended to management. A manager had to accept that productivity could look quiet: two people discussing a query, reading a test or deleting code that did not work. We made progress visible through completed stories, passing tests and stable releases rather than counting lines or pretending every hour should produce a new feature.
What remained after the project
Pair programming worked for our small team because it addressed a real weakness rather than serving as a fashionable process label. We needed fewer private assumptions, faster feedback and enough shared knowledge to remain useful when people changed projects or took leave. The practice helped with all three.
It was not effortless. Pairing demanded patience, clear communication and the willingness to expose half-formed ideas. A dominant developer could silence a quieter colleague, and an unsuitable pairing could make a simple task exhausting. We had to change partners, take breaks and allow solitary work where it made sense. The method improved our habits only when we treated those conditions seriously.
The lasting benefit was resilience. A feature was less likely to depend on one person’s memory, and a defect was less likely to survive because nobody had reviewed the original assumption. New developers entered the system through conversation and practice rather than by studying a neglected manual.
For us, the shared keyboard became less important than the shared understanding behind it. Two people working closely could challenge a design, test a belief and leave the code clearer than they found it. In a small Australian software business, where time, staffing and specialist knowledge were limited, that was a practical advantage with a lasting effect.