In most projects, the amount of work is so large that one person alone cannot handle. Thus, we form teams to handle the workload. A team is more than just a group of people. It is an organization. The team members are the components of this organization and usually affect how good the team functions but it is important for a team to be seen as a unit by an external observer. The key elements of a team are communication, vision, and trust. These elements form the team culture and act as the glue that keeps the members together.
In software development, we deal with code. We write code, we refactor code, and at the end of the day, we deliver code. Note: the code is the product of the intellectual work that the developers do, it is not the actual work (more on a future post). The code belongs to the team thus the team owns it and is responsible for it. Being responsible for the code our team writes is something that every professional developer should do. Apart from this professional obligation, it is also a prerequisite for a great outcome. Behind every success, there is a team of highly motivated and engaged people who are responsible for what they do, not because they have to but because they want to.
Nowadays, we deal with microservices where every team has (usually) the responsibility of the full stack for its services (backend, frontend, and even dev-ops related stuff). The communication within the team as well as across teams is crucial to the success of the team as the Conway’s Law applies. Thus, it is essential to share the responsibilities in the team in a way that a) improves the team’s performance, b) makes the communication more efficient and c) reduces the cost of changing the formation of the team. By giving the responsibilities of a particular area to one or two members of the team, the team can suffer if these members leave the team (see bus factor). Also, the other members will not feel they own the code of this area as they do not maintain it and it will be difficult for them to be responsible for this code they will probably avoid maintaining it. In that way, the team will eventually be fragmented into sub-teams without clear structure and then will be dysfunctional, since communication will be limited, and the initial common goal of the team will be transformed into, possibly conflicting, goals of each sub-team. Note that this is entirely different from having well-defined sub-teams split by technological or functional area. In this case, a sub-team is considered as a team. The problems start when some specific members of the team deal only with specific areas of the code where other members could also work on these areas, as we will see in the next section. A great post on the problems of individual members owning the code instead of the team owning the code is this one.
Having a solid team culture with strong foundations is crucial. The team culture defines the team. Team culture is everything that new member notice when they join the team. The communication structures, the processes, the way that the code is designed and written, the mission and the vision of the team, etc., are elements of the team culture. In this post, we will focus on the culture regarding the code and the processes.
First of all, the team members have to believe that the processes they choose are going to help the team and themselves grow and perform better. Regarding the code, they need to have a common understanding of the standards they have. All the members should be sure on how the outcome (code) should look like, even if the less experienced ones might need some help to achieve it. This is important for the conflicts to be reduced since all the members will know what is the right thing to do. The ultimate test is for someone outside the team not to be able to tell who wrote the code by just looking at it. The code should look like it is written by the team and not by individuals with different coding standards or worse, code formatting. Consistency matters! As soon as they agree on that, they need to decide on ways (processes) to ensure it. Code reviews and pair programming are the most effective process to ensure the quality of the code.
Code reviews work asynchronously. A developer creates a pull request and asks for one or more team members to review it. The reviewers check that the code follows the coding standards of the team and works appropriately and submit some comments that need to be addressed for the pull request to be approved. A technique that I have found useful for code reviews is described in this post.
Pair programming, on the other hand, is synchronous. Two or more developers write code together, so the feedback loop is practically zero as the in-person discussion replaces the code review comments. Out of all the processes I have dealt with so far, I found pair programming to be the most efficient in many ways, especially for new team members. Except for the instant feedback loop, it also helps the new team members get familiar with the other members and the processes and builds up the teamwork. I find it more efficient for myself to pair with someone and learn how the code should be written and what patterns we should follow in the code instead of reading a wiki page. The discussion during the pair programming helps on building the culture of the team.
Another process that helps the new members be and feel part of the team is to have them do something productive that helps them learn. Instead of spending the first few weeks reading wikis and browsing the code it is better to do some hands-on work. Since she might not be able to produce code that meets the quality standards of the team and not be familiar enough with the codebase to make changes in it, it is better for them to start writing tests. Tests are quite useful because they have a limited scope so even if the new member is not experienced, she can follow and also, they are a good way for a developer to get familiar with the code. Moreover, having someone else to write tests for you might not be compliant with TDD but helps in identifying more corner cases, since the author of the tests is testing on the specification, completely unbiased on the implementation. So, it is a win-win situation!
Obviously, for code review and pair programming to work all the members of the team should work on almost all the code. Team spirit, good communication, and collaboration are prerequisites for all these processes.
There is a trap that the team should avoid when applying these processes. Less experienced members might feel overwhelmed or less important if the level of the quality is set very high from the beginning. In that case, the team gets exactly the opposite results as it is split into experienced and less experienced members and have the problems mentioned earlier. In order to overcome this issue, the level of the quality should be set around the average level the team can handle and, as the time goes by, be increased. By doing so, the level of the team is increased, and the members are comfortable with the quality standards. The speed of this increment requires careful planning. This is where the more experienced members, along with the tech lead, can help more. They can be the accelerators of the process by mentoring the others, advising on new techniques and push for higher quality. For example, if a team has no experience on TDD it is not easy to start practicing it. The team should start by practicing testing, then move on to refactoring the code and eventually reach to the point of writing the tests up front and refactoring the code in each TDD cycle. Before moving to the next step, the members should feel comfortable on the current one. A great talk on how the tech lead (and experienced members) can help the team achieve its goals and the members be improved is “Why Technical Leadership Matters”.
The incremental improvement of the quality does not mean that we should compromise the quality of the deliverable or create huge tech debt but instead have better quality and a team that performs better in the long term. It might be more difficult than it sounds as there could be deadlines that move the team in the opposite direction, but as in every good practice, the long-term results are almost always better. Consider TDD or testing in general, one might argue that writing tests slows the development process, but we know that it is not true!
“The whole is greater than the sum of its parts.”