Every four months at Fellow, we run an in-person hackathon—something we all look forward to as employees of a remote-first company spread across Canada. One day at lunch during the most recent hackathon, I looked around at the six other engineers at my table and realized that each of them had been born in a different country and had a unique cultural upbringing before immigrating to Canada. We had someone from Brazil, Pakistan, China, Russia, Venezuela, Iran, and Canada (me) at that table alone, with even more countries represented in the broader engineering team and company. And while it might seem like magic, we were all able to work together effortlessly—but it hadn’t always been like that.
While this kind of cultural diversity has always existed for us at Fellow (and in many open source communities), it has become more prevalent over recent years with the shift to remote work and the removal of many location-based hiring boundaries. Working with people across cultures has terrific benefits, including producing more thoughtful software, but it’s not without difficulties. While code is a common language, culture impacts how we write it in many ways. Over the years, I have worked with people from many countries and have run into many situations where there has been confusion and a complete mismatch in expectations due to not understanding one another's culturally-ingrained thought processes and actions—my own included.
It’s important to be aware of the impact that these differences can have when writing code so that shared expectations can be put in place to help engineers better collaborate. Having one explicit team culture for everyone to default to has allowed our vastly diverse team to come together with a shared outlook on software development—but it didn’t happen overnight!
I will outline the realizations we had as we built the team, as well as the steps you can take to put in place a common team culture to minimize misunderstandings and create stronger, more aligned teams. For each of the questions below, I recommend creating a living document of the answers, storing it somewhere accessible to everyone, and including it as required reading during the team’s onboarding process. This is something that I talk about in-depth in my book Remote Engineering Management.
Be explicit about who makes decisions and how they’re made
When I first became a manager, I remember being a little puzzled when I received my first official round of feedback from my team members. While most of the team appreciated how I empowered them to make decisions on their own, one teammate responded that they wanted me to be far more authoritative when it came to making choices about code and how projects should be built. Since I was no longer writing code day-to-day, I didn’t feel like I was the best person to make those sorts of decisions. Instead, I had been implicitly placing the role of decision-maker on the engineers building out each feature—that is, I assumed that everyone knew that they should be in charge of making the majority of decisions for their work, but I hadn’t explicitly said so.
To me, this felt like the obvious approach to take in leading a team of engineers. Nobody likes a micromanager, and I’ve always believed that the people with the most context on a decision should be the ones to make it. In Canada and many other countries, people typically take an egalitarian approach to decision-making, where it’s not a big deal to challenge each other, and there’s no need to run everything by “the boss” before deciding. I took this for granted as the de facto way of doing things because it’s what I’m used to in my cultural context. I didn’t realize at the time that many countries operate differently and instead default to a hierarchical-driven model for decision-making where the team leader is expected to make many decisions for the team.
When I first read this feedback, my first reaction was, “What!? No! You’re wrong!” But, putting myself in the mindset of someone from a more hierarchically-driven culture I could understand how my approach must have seemed equally as wrong to them.
Instead of telling them they were wrong (something that you should never do with feedback if you want to know the truth), I did more research into the cultural differences around decision-making. Different countries have varying expectations about who makes workplace decisions, and how they’re made. I found that being explicit in answering the following four questions, specifically for larger teams and on a per-project basis, cleared up any mismatches in expectations about how decisions should be made, which kept everyone aligned and writing code without always waiting for my direction:
Who makes the decisions? Is it the manager, the team lead, the tech lead, engineers, or…?
Does everyone need to agree on a decision?
Can a decision be made without input from others?
Are there any types of decisions that require input from a team lead, regardless of the above?
There are no right or wrong answers to these questions! The answers will depend not only on the team member’s culturally-defined expectations but also on the context; certain projects may require a different approach to decision-making than what is normally expected.
Specify standards for code review comments
A few years ago, when I would look at pull requests, I would often see comments that fell into one of two categories:
The overly-kind, almost self-deprecatory comments along the lines of “Maybe I’m wrong, but I think you could possibly look into this other way of doing it, but it’s not important” for something that was clearly very important.
The very direct, authoritative comments that read to me as angry, saying something like, “This is wrong, change it to this.”
Both types of comments would come from equally senior engineers but whose cultural backgrounds were very different. And while both types of comments technically result in making the same suggestion as the end result, the way in which the changes are communicated makes a world of difference to the person who opened the pull request.
The differences in how people give and receive feedback are perhaps one of the most obvious cultural differences that you’ll notice on a team because of how often clashes between the many styles will pop up. Engineers get feedback every day on nearly every aspect of our work: as comments during code review, as tickets filed from QA, as support tickets, and as public praise (or dismay) from people using what we build. All of it is extremely important to our work and continued growth, but the comments we get during code review can feel the most personal. After all, it’s feedback on our work from our immediate peers, which is an important relationship to maintain in good standing.
In the example comments above, if one engineer were to write one of those comments on each other’s pull requests, neither would be pleased. The engineer coming from the more direct-feedback culture might feel annoyed at the extra “fluff” in the comment and not take it seriously. For them, the tone might make it sound more like an optional suggestion, leading them to possibly miss out on an important change. The engineer coming from the more passive-feedback culture, on the other hand, might be hurt by the strongly-worded direct comment and read into it as the other engineer not appreciating them or their work. As a Canadian, neither comment feels ideal to me: While one feels ingenuine and over the top (but feeds into my culturally-induced need to be liked by others and not offend anyone), the other feels abrupt and almost hurtful.
We quickly realized at Fellow that to effectively communicate with each other, we would need to set standards that explicitly laid out what we expected these comments to look like. With more than a dozen countries represented on our team, there was an incredibly large nuance in possible tones for pull request comments, and the frequency in which they occur makes standardization necessary.
There are many steps that can be taken to standardize comments on code and make them less likely to trigger an emotional reaction. For example, you can reduce the need for many of them to even exist by ensuring the use of linters, formatters, and continuous integration on the project. For an in-depth look into what kind of content pull request comment style guides should contain, I recommend reading Michael Lynch’s article “How to Do Code Reviews Like a Human,” which touches on the nuances of language and how certain ways of phrasing comments might be perceived.
At a high level though, three of the most important expectations for standardizing the project’s commenting are:
The tone of language that should be used (what level of directness is appropriate).
How to mark comments as optional suggestions or must-have changes.
How to disagree with a comment, and what to do if there is a conflict.
Just like with the previous section about decision-making, there are no right or wrong answers for these points, and it might vary between projects. While I will still default to an empathetic and friendly tone in the main codebase that I work in, I also appreciate the efficiency of being able to write some very quick and direct comments on code written by people from a direct-feedback culture.
Create criteria for merging pull requests
Once in a while, when checking the list of open pull requests in GitHub to get a feel for how a project was progressing, I would notice a few that stood out with a common theme. These pull requests had multiple approvals, had passed all automated checks, and had been open for a little longer than I thought necessary—and they were all waiting on approval from me.
At first I felt bad—had I unknowingly been a bottleneck for all this code? Had I asked for the code to be specifically reviewed by me before merging it and forgotten? I would wonder about this while quickly giving my rubber-stamp approval so that the pull requests could be merged and the project could move forward.
But as time went on and this kept coming up, I realized that this wasn’t just a fluke, and my memory wasn’t failing me quite that badly: The team wasn’t aligned on the expectations around when a pull request can be merged. Some members of the team from highly hierarchical cultures were waiting for my stamp of approval as manager on the code before merging it, while I had expected the engineers to be reviewing and approving each other’s code (having far more context than I did) without waiting on me. Similar to how we ran into these issues with decisions, we had completely different ideas of what’s normal for merging pull requests. We didn’t think to make that explicit because it felt so “obvious” that that’s how work should be done, based on our own backgrounds.
To prevent pull requests from staying open longer than necessary and accidentally blocking progress from being made, I suggest answering the following questions for the team:
Who needs to approve a pull request before it can be merged?
Is there a minimum number of approvals required before merging?
Do all comments on a pull request need to be addressed first? What about non-functional suggestions?
Does any of this change if the comment comes from a team lead or manager?
Does the pull request need to stay open for a certain amount of time before being merged?
Even though code may act as a common language, so many aspects of collaborating on code are impacted by each person’s cultural background—whether it be the culture of their country or even the culture of the teams they were previously a part of. Being as explicit as possible about expectations around communication and collaboration, and creating a cohesive team culture, will eliminate many potential misunderstandings and help to create a strong team that functions well together, no matter who is on the team.