Chapter 4. The Human Factor
So why aren’t more people practicing CTBD already? Understanding why people might not follow full trunk-based development can help you think seriously about how you might persuade them to do it. I’ll share some “mea culpa” stories of times when I’ve found myself not following good practice. Nobody’s perfect, right? After that, I’ll look at how to encourage people to adopt more good practices, and the chapter will finish with some techniques to help avoid the debilitating effects of guilt and shame.
Mea Culpa Stories
Good CTBD practice—or, let’s face it, all good practice—is easy to preach but not always easy to carry out consistently. When aiming for high ideals, we’re all capable of falling short, so it’s helpful to examine how we fall and why. I’ll describe some practices I’ve been guilty of and then look at the role friction plays—and how gold-standard CTBD practice might help us to avoid that friction.
Overusing Branches
When you’re writing code or trying to identify the cause of or fix for a bug, you will often be trying stuff out. Wanting those experiments to somehow live independently from the rest of the codebase is understandable. At the lowest level, this can result in many small functions with names like TempIdea
, or tiny chunks of code being commented and uncommented repeatedly. For larger and longer-lived experiments, branches are the natural go-to tool. It’s so easy to create a branch where your experimental half-written code can exist without getting in the way.
What I’ve learned is that, although they feel like a nice and quick solution in the moment, these “sandboxes” (small commented-out chunks of code, experimental branches, and so on) nearly always cause problems over time. They last longer than you thought they would, or you forget to clean up after yourself. And then later you’re left with code that looks like it might be important but you can’t remember why, or you’re trying to merge it back into a main branch that has long moved on.
I’ve often created unnecessary branches, and because I’m not perfect I’ll probably continue to do it (even though I know it’s a bad idea). The root lies in friction: there is friction involved in thinking about how to experiment in the main branch without breaking everything else. Later in this chapter, I discuss ideas on reducing friction.
Not Paying Attention to the Pipeline
If you enjoy coding and solving problems, you might lose interest once something feels “done.” You know you ought to care about what happens to your code once it has left your hands, but at that point you’re more interested in the next problem. You might be even less likely to pay attention to the pipeline if you’re not quite sure how it works, where you’re supposed to look, or what to do if a build fails. If you’re worried that trying to fix any problems will just expose your ignorance, you’re even more likely to run away after you hit the pipeline button and just hope it’ll all be OK. Maybe someone more knowledgeable will step in and fix everything.
Because it’s so easy to defer the responsibility onto an automated pipeline, some argue that integration should be manual rather than automated, but I’m always wary of anything that introduces friction.
Not Building and Testing Locally Between Merging and Pushing
If you know there’s a magical pipeline that will integrate everything and run all the tests for you, you might be tempted, particularly if you’re in a hurry, to cut a corner by allowing the next build and test to happen on the remote server rather than locally. It’s also possible to simply forget that you haven’t built and tested since the last merge. I’ve been there!
Good unit-testing practices will help with this problem. Ideally, you’re practicing test-driven development; at the very least, get in the habit of running tests after every code change and running a continuous background testing tool such as NCrunch. All that’s left is to make sure you build and test between pulling the latest code and pushing the merged result to the remote copy of the main branch.
Solutions
Effectively practicing CTBD takes a certain amount of team maturity and skill. If you don’t already work in tiny chunks, commit frequently, write tests in tandem with code, run tests every time you commit, and generally write code in a way that’s designed to be merged several times a day, you’ll need to create those habits as part of your journey to trunk-based development. (And it’s important that you see it as a journey, rather than something you can magically introduce at the drop of a hat.) So how can you help those habits to emerge?
Reduce Friction
When people face friction, they do what they can to avoid it, which can make them less likely to follow good CTBD practices. If people don’t feel confident about regularly merging to the main branch, they may hold back on committing or pushing their code, which won’t have the desired effect.
Don’t shy away from these issues. Rather than blaming and shaming, think of ways to reduce or remove that friction. One of the best ways is to help people build new “muscle memory” and neural pathways. If people practice and repeat the desired behaviors, those behaviors become second nature, and the friction melts away. This is one good reason for practicing code katas.
Give people ample opportunities to learn, practice, and explore. Offer training. Bring in experienced experts. Offer mentoring. And above all, be patient. Learning takes time.
The advice discussed in the following sections can help you to reduce friction.
Cultivate an Open, Honest Culture
Another way of addressing friction is to be open and honest about it. Allow one another to admit mistakes without blame or shame. Allow yourselves time to pause and tidy up messes. As long as problematic code feels shameful, people are less likely to admit to it and more likely to leave it as it is, with some vague hope they may be able to fix it in the future. It’s particularly important that senior members of staff not only encourage the less experienced developers to admit to messy code but deliberately model this behavior themselves.
Use Information Radiators
One way to facilitate effective CI is to put in place high-quality “information radiators” that communicate the state of your pipeline—and therefore the status of your product—to anyone within your organization who might be interested. An information radiator can consist of a simple web page that gathers data from your automated pipeline. Many office-based teams display such radiators in a common working area. Remote workers can keep the site open in a browser tab and regularly check for updates. You can use techniques like webhooks to create shared channel updates and alerts when problems occur. You can even make your radiators a bit playful, in whatever way makes sense for your team.
Collaborate and Communicate
It can be tempting to focus on technical practices, but I don’t believe any areas of effective software delivery are purely technical. As soon as there is more than one person working on a piece of software, you are collaborating whether you like it or not—and you ignore this fact at your peril.
Communication and integration are very closely related. Given that different people have worked on the components being integrated, some level of communication will be required. The more integration you have, the more communication you need. The whole thing creates a virtuous circle in which better communication and collaboration lead to easier and more effective integration (and therefore TBD), which in turn leads to an improved culture, and thus to better communication and collaboration.
I highly recommend including both pair programming and ensemble work—often called mob programming—in your CTBD toolkit. These approaches dramatically increase developers’ knowledge of the whole codebase, as well as their understanding of how to dovetail their work together. As engineer and CI advocate Eli Mydlarz has put it: “We use branches when we don’t mob or when we modify shared code. Nobody likes it, which in turn encourages us to mob more.”1 This collaborative work also improves everyone’s ability to value one another’s work and to take ownership of all elements of the software pipeline.
Get Help, not Heroes
It may take some time to get used to the working practices associated with CTBD, as well as to prepare your pipeline, codebase, and test suites. Bringing experienced CTBD practitioners on board (whether full time, part time, internally, or as external consultants) can be a great help. In my view, not enough emphasis is placed on nurturing the skills and experience of our teams. Technical coaches can be particularly useful in this sphere—again, either as internal or external hires—working directly with teams to help them learn the skills and practices outlined in Chapter 3.2
Even if it feels difficult, the more time you invest in spreading skills across the team, the more likely it is that people will take on tasks independently without being asked.
Sometimes people naturally become specialists within their team, often because they find a particular area interesting or have more experience in it. Everyone praises the person who swoops to the rescue. But even if playing the hero starts out being enjoyable, the role can end up being a millstone around the “hero’s” neck. It also prevents the rest of the team from learning skills and taking responsibility, and it introduces risk if that one specialist is not available for any reason.
It’s also vital for team members to feel able to speak up when a learning process isn’t working for them. Too often, “pair programming” sessions consist only of a specialist zipping through a series of activities while their partner barely manages to follow or absorb what they’re saying. Even if they follow in the moment, if the learner isn’t active in the process, they might be unable to reproduce the technique when they try it on their own. Yet they don’t say anything, because they don’t want to look ignorant or inexperienced. An open, honest culture allows people to admit when they don’t understand something and ask for help.
If in doubt, use the following principles in pair programming:
-
Encourage the less experienced person to have their hands on the keyboard and to do the work under minimal guidance, rather than asking them to observe the more experienced person.
-
Encourage the less experienced person to ask questions and make notes. Give them an opportunity to try the task on their own as soon as possible, and to do so repeatedly, so that their knowledge and confidence can build and stick.
Change can be gradual and slow, and it is often most effective when it is. As Martin Fowler says, “Patience and steady application does seem to regularly do the trick, so don’t get discouraged.”
1 Personal communication to the author, February 9, 2023.
2 Emily Bache is doing some excellent work in this sphere, building and growing a collaborative network of technical coaches.
Get What Is Trunk-Based Development? now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.