As a software engineer and as a manager, I’ve frequently worked on distributed teams. On a distributed team, synchronous communication tactics quickly break down, so an asynchronous approach becomes necessary. However, even a co-located team can benefit from this way of working!

At Facebook, at one point our team spanned London, Menlo Park, and Seattle—with close partner teams additionally located in New York and Boston. Even though some of these offices are much smaller than the headquarters, we were able to develop a working style where each location was included as an equal partner in the collaboration.

During the time I worked at GitHub, more than half of the company worked remotely. And although we had a few co-working spaces leased as offices, I mean that most of those remote employees would work from home, coffee shops, the beach, or wherever else they wanted! On my team alone, we had people spread across North America, the United Kingdom, Australia, and New Zealand.

Through each of these experiences, I’ve been able to observe what works well and what doesn’t work well on a distributed engineering team. Although every team is different—with its own identity, culture, and values—I have some general principles I always keep in mind, and concrete tactics I like to use.

A distributed team has unique advantages

When a team is forming or storming, it can feel like being distributed is just another burden—a millstone that is getting in the way of building a high-performing team. The advantages of the setup are not apparent while team members are all trying to develop an initial understanding of each other, while being constrained by the physical separation.

I won’t bury the lede: it is harder in several ways! Most notably, working asynchronously increases latency.

However, I believe there are unique advantages that come from having a distributed team too:

  • The expanded talent pool. This applies regardless of how big or small your workforce is: the best person for the latest opening on your team may not be local. Having a location strategy that allows you to scale teams across locations can allow you to expand the pool of potential hires.
  • Working “around the clock.” Not individuals, of course! But if your team is spread across timezones, you get more coverage all day. For example, oncall rotations can be split geographically, or the investigation of gnarly bugs can be “handed off” to a different group of people as one part of the team ends their working day.
  • Local points-of-contact for partners and customers. Most of us have to occasionally meet with customers or partner teams (internal or external) as part of our job. By spreading your team about, you have the opportunity to co-locate to more of these stakeholders, which can build a stronger working relationship, or help everyone stay more in sync.
  • Flexible working hours and holiday schedules. This point often gets overlooked. How many times have you come back from a vacation, only to have no idea what you’ve missed? Most of the strategies for distributed teams will also benefit people who want to take time off, work from home, or otherwise shift their working day to accommodate their personal life.

And, importantly, getting good at working asynchronously will increase the strength and execution ability of a team, regardless of whether it is distributed. Collaboration isn’t just a “distributed team problem” — it’s just that being distributed will surface issues much more quickly.

Even in co-located teams:

  • People will take time off
  • You’ll forget what you were doing six months ago
  • New hires will start with zero context and need to ramp up on the project quickly
  • Team members will want to disconnect outside of work

Figuring out how to work well asynchronously can make all of this easier.

Dealing with geographic separation

In some sense, this is the easier problem. If your team is distributed across the US west coast, for example, it’s always possible to “hop on a quick video call” to sort out most problems.

There are still potential communication issues here, though. Think about all the context you pick up just from sitting near your colleagues. Geographically separated groups don’t get that from each other, so need to replicate that experience in other ways.

In-person communication is uniquely low-latency and high-bandwidth. For anyone with headphones or not physically present, it’s also lossy. Lossy, synchronous channels are best for informal, unstructured conversations between people who already happen to be around. It also builds empathy and social cohesion!

Through this lens, chat can approximately replicate the experience of the in-person conversation. For example, consider having synchronous discussions in WhatsApp, Workplace chat, Slack, or even IRC. This provides everyone with the opportunity to participate, even if they’re not physically present.

Informal “water cooler” chat about vim key bindings
Chat can fulfill a social function on a distributed team

Lossless channels are important too. Nobody enjoys catching up on hours or even days of chat they’ve missed. When the signal-to-noise ratio is poor (e.g., the discussion meandered before arriving at a firm conclusion), people are more likely to declare bankruptcy, skip the catch-up exercise, and just hope they didn’t miss anything important.

At Facebook, we use Workplace for this. At GitHub, we often used Issues for this purpose (in addition to their usual function of tracking bug reports and feature requests). Many companies will use email for this.

Putting aside the exact choice of technology, these tools are best for the important updates that nobody should miss, regardless of when they come online next. For example, write-ups which summarize important discussions and outcomes can make it much easier to get back up-to-speed after a week away from work.

Example of a post documenting the outcome of a meeting
Document the outcomes of meetings, so others can catch up on their own schedule

However, keep in mind that text doesn’t convey tone well. Words can come across completely differently in text than they would if the author were speaking in-person, because of the absence of body language and intonation. Generally, assume best intentions and focus only on what is actually written, not the subconscious connotation assigned in your head.

Even though the goal is ultimately to work asynchronously really well, occasionally spending time together in-person can be really helpful also! Meeting up in the real world can help you build empathy: you get to ingest much more information about how your teammates think, and will be able to start reading messages in their voice.

Dealing with timezone separation

With a team spread across multiple timezones, synchronous communication quickly becomes untenable. Even if we just consider the 3-hour time difference between the west coast and east coast of the US, that means that each timezone will be doing roughly three hours of work while the other is offline. A lot can happen in that time!

Working asynchronously ensures that those hours without overlap can be maximally productive.

The three most important principles are:

  1. Overcommunicating
  2. Avoiding synchronization points
  3. Prioritizing blockers and batching changes

Overcommunication

The number one anti-pattern for asynchronous communication is the message which just says, “Hey, are you around?”

Include context in messages, so the recipient can respond meaningfully without you having to be there. This isn’t just important across timezones — it’s also important as people duck in and out of meetings, or poke their head out of coding, or in-between any of the other tasks occupying their day.

This also applies to issues, pull requests, and posts. Explain all of these things more than you think you will need to! Avoid the latency of back-and-forth Q&A to clarify. Review everything you write as if you were your own teammate, and then proactively edit it to address any questions that occur to you.

Avoiding synchronization points

In a distributed team, like in concurrent programming, synchronization points should be avoided as much as possible. Humans have a high context-switching cost, and every meeting means two synchronization points: once at the beginning and once at the end.

Available meeting time also quickly diminishes across timezones spread further apart. For example, California and the United Kingdom are eight hours apart, which means that there are maybe 1–2 hours each day that are convenient for people in both locations to meet with each other. Other locations can have it even worse!

When those cross-office time slots fill up with meetings, then one or more people has to decide between the opportunity to participate and their own work/life balance. This is a false dichotomy, and unsustainable for the individual and the team.

Generally speaking, if a meeting can be replaced with something less synchronous, get rid of it. Posts, emails, pull requests, and issues are less synchronous than chats, which are still less synchronous than having a meeting. This way, participants can choose their own best time to be engaged, and avoid paying the context-switching cost more than necessary.

This isn’t to say that meetings are entirely without value. Even in concurrent programming, synchronization points are occasionally useful! Meetings are sometimes still useful for high-bandwidth conversations that are unlikely to get resolved well otherwise — for example, very abstract brainstorming or design discussions.

Even then, make sure the meetings as optional as possible. Take notes and share them out, and minimize the number of people who are actually required to attend. And try to avoid sending meeting invites on the day of the meeting, because people in different timezones may not see it until their next working day (at which point, it’s too late).

Example of proposed new direction, asking for feedback, not permission
If you would like to see a change, act on it and ask for feedback, not permission

Finally, although I like democratized decision-making, asynchronously building consensus is nearly impossible. Like the bystander effect, open-ended questions (e.g., “What should we do to solve this in the future?”) asked asynchronously usually fall flat, with no action taken and nobody driving them.

People are much more willing to chime in if they disagree with something concrete. So, instead of asking an open-ended question, assert a direction. Ask for feedback on it, of course, but ultimately one person has to have the authority to make the decision and follow-through — and it should usually be the person who feels strongest about what the correct direction is.

Prioritization and batching

Most engineers naturally establish a personal schedule for, and balance between, writing code and reviewing others’ code. In a distributed team, though, it’s critically important to prioritize the unblocking of other timezones. The negative impact of a blocker is usually more significant than the positive impact created during heads-down coding.

One approach is to set aside time at the beginning and end of the day (and block off the calendar accordingly) to review any code that’s waiting for comments, respond to emails/posts, etc. Or, even if you aren’t able to unblock others because you’re busy, at least let them know with a comment like, “I saw your message, but I can’t respond right now,” so that they can figure out another way to move forward.

Even better, avoid becoming a blocker in the first place by trusting the people you work with to do the right thing. When code reviewing, for example, you can accept a change even if you have concerns, and ask/trust them to fix those points up before shipping it. Then, they can make fixes and ship it in their own time, without having to wait for your next workday. If the code doesn’t land in exactly the state that you hoped, you can still review it after it’s been committed, and ask the author to make more fixes. Even if the changes were really wrong, commits can always be reverted!

Example of Facebook's code review tool encouraging the practice of “accepting to unblock”
Facebook’s code review tool encourages the practice of “accepting to unblock”

As a contributor, it’s also important to avoid blocking yourself. This most commonly occurs when a particular project requires collaboration and input across timezones. Work on multiple things concurrently, even if they slightly conflict, so that getting blocked on one thing doesn’t prevent progress on others. This also means that your asynchronous collaborators can concurrently move all your work forward in one workday — for example, by reviewing and handing back every one of your code changes with comments.

Work/life balance

Even if you do all of these things religiously, maintaining a healthy work/life balance can be an ongoing struggle in a distributed team. It’s essential to set expectations with the rest of your team about when you’re available, so that work happening elsewhere doesn’t pressure you into working too much yourself. Chat app features like Do Not Disturb (for example, on Workplace) can make this easier to signal and enforce.

Pragmatically speaking, there will be times when meetings need to happen out-of-hours — for example, when you want to reach out to someone who may have no reason to accommodate your schedule (e.g., if there’s not yet a working relationship). My usual rule of thumb is to flex my schedule when setting up an initial meeting, but politely insist that additional meetings happen at a sustainable time.

Conclusion

I’ve shared many concrete tactics for work and communication which, in my experience, have helped bridge the gap between distributed team members:

  • Use chat to replicate the feeling of in-person conversation with your whole team.
  • Remember that text doesn’t convey tone well. Assume best intentions, and occasionally spend time together in-person to learn the “voice” of each team member.
  • Include more context in every communication than you think you need, to avoid back-and-forth Q&A.
  • Avoid meetings whenever possible, and make attendance as optional as possible when they do occur.
  • Don’t try to build consensus asynchronously. Pick a direction, get feedback, and then take initiative to move forward.
  • Prioritize unblocking other timezones, so everyone can make forward progress.
  • Trust those you work with to do the right thing — don’t reject changes if you know your teammate can independently address your concerns.
  • Work on multiple things concurrently, to avoid getting completely blocked.
  • Set expectations with the rest of your team about when you’re available.

However, the team’s culture is at least as important as the tactics. I’ve been privileged to work on teams that want to make this work, because they’ve appreciated the advantages: the expanded talent pool, round-the-clock oncall rotations, local points-of-contact, and flexible schedules. This has helped us persist through communication breakdowns, and work to make things better.

It’s amazingly empowering when it works well!

Further reading

Much of my approach has been inspired or reinforced by others. Here are a couple of other thoughts on async working that I’d highly recommend you check out:

1 comment

Leave a Reply