Software teams have many options when it comes to deciding who is going to work on what. Should areas of code have "owners". Should engineers work story to story from a prioritised backlog? Should the team adopt a capital-A "Agile" process like Scrum?

A pattern that's worked well for me in the past is to identify long-lived streams of work, with a pair of engineers focus on one stream to reach a four to seven week "milestone". After the milestone, the team re-organises so that most people are working with a new pair, and/or have changed streams. I don't have a fancy name for this pattern, so let's just call it streams and pairs.

This works well for teams of 6-8 ~full time programmers, giving three or four streams. The ideal number of engineers working closely together is two, hands down the best balance of communication costs against diversity of ideas. A stream of work focusses on one area of the code or group of features. The pair on a stream are responsible for technical design, breaking the work up (writing tasks/stories), prioritisation, communicating their decisions, and of course implementation. Streams may live longer than the pair working on it, and one or both engineers will rotate to a different stream.

One key tradeoff of any who-does-what process is between the effectiveness boost of sustained focus and specialisation and the agility and responsiveness offered by late assignment. In all but the most unstable contexts, engineers being able to focus on one area of the code for a sustained period is key to realising high velocity. Four or more weeks is long enough to build good momentum, get a deep understanding of the problem and code in question, and realise the benefits of that knowledge. A sustained focus period also motivates improving the working conditions with tools or overdue refactors, benefitting subsequent engineers too.

Specialisation can be taken too far, though. Too much threatens the team's ability to react to changing needs. Too rigid a team structure can hurt long term architecture. Periodic rotation is essential to agility and to distributing deep knowledge among multiple team members.

A prerequisite for this pattern to work well is at least half of the team having enough context and experience to lead a pair. The ideal pairing is one person already knowledgable about the problem or code in question, and one who's new to it. This offers the perfect opportunity to end the milestone with two experts, who take their deep knowledge with them as they move on to other problems. It's a great mentorship opportunity for new team members and a good way to build the fully-connected graph of strong relationships. A pair on a work stream make obvious code-review buddies for the duration, with well-aligned priorities.

In comparison with other models of task assignment, streams and pairs:

  • reduces context-switching costs and allows engineers to maintain context between tasks,

  • offers more opportunity for engineers to build broad and deep knowledge,

  • distributes much of the work of scoping and design, otherwise centralised with the lead/PM,

  • minimises interruptions or blockages when one engineer needs something from another (communication will mostly be within a pair).

On the downside, stricter focus focus can lead to:

  • higher latency of delivery for an overloaded stream (in return for overall team throughput),

  • loss of visibility into streams as discussion becomes more focussed (mitigate with weekly demos, end-of-milestone reviews, etc).

A distributed team has a choice between pairing up within the same office/timezone, or across them. For design-heavy streams, having pairs close by is usually best, since they'll spend a few weeks in heavy communication. For iterative or well-specified implementation work, tag-teaming across timezones can work really well, giving an artificial parallelism on an otherwise single-threaded stream or work.

For bonus points, if your team is subject to external interruptions like customer requests, production maintenance, or community engagement, designate one of the streams as the interruptible stream up front. This stream will be the least-high priority work, and the pair working on it will take interruptions first, to protect the momentum of the others.