As developers, one of our primary tasks is waiting. We wait for our code to compile, our tests to run, to verify our work, we wait for our application to deploy and load, and to navigate to the page we are changing.
I have joined many teams that were either stuck with or content with a lot of waiting. Often, I found that these wait times very fixable and I was able to make significant improvements. This series will cover some of the strategies that I use when trying to improve build times and workflows. But to start with, in part 1, I want to address the question ‘why we should care about slow builds and workflows?’.
Spending time to save a few seconds might not seem worthwhile. However, those gained seconds can slowly accumulate to get you over some thresholds that can trigger a dramatic change in how you work. You can reduce context shifts, enter a flow state, free up your mental space to focus on your actual tasks, and make the best of your limited amount of time each day.
In my experience, the time we spend waiting for our builds to compile and our application to load can fall into three distinct categories. In the first category, the wait is unobtrusive; we have an unbroken chain of thought and we switch seamlessly between making changes and trialling them. In the second category, we are waiting long enough that we notice, and our train of thought is broken. We notice the boredom and feel the temptation to context shift. In the final category, we predict that the wait will be too long and alt-tab to find something else to do while we wait.
The size of these three thresholds varies from person to person, as well as from day to day. I tend to find that I will notice a wait longer than 15 seconds and feel the urge to context shift around 45 seconds.
Each category forms a tipping point of sorts; small improvements add up to a much more dramatic shift.
Reduce context shifting
When faced with a long wait, we tend to start doing multiple tasks in parallel. We might start trying to chip away at the rest of the tasks we have to do, or perhaps just staving off boredom. We’ll alternate between writing code and reading through a pull request, email or Slack, or checking your preferred blog site. Usually, switching to a second task is a false economy; we’re much worse at multitasking than we think we are. We lose energy each time we need to stop and put our heads back into a different task. We’ll feel a lot more exhausted at the end of a day full of context shifting.
Distraction also makes it challenging to realise that the action we were waiting on has completed. Even once we do recognise we can return to our original task, we’re conflicted, what do we finish first? Now, our multi-step process to check our change, interspersed with waiting, takes much longer than the sum of its parts as we keep losing and regaining focus. Build, wait, deploy, wait, load, wait, login, wait, navigate, wait, check your change. \*repeat\*. The more steps you need to wait for, the more times you context shift and lose that bit more energy.
We don’t wait until we’ve hit our context shifting threshold before we deciding to pick up a second task. Instead, we change activities based on our prediction that the upcoming wait will be too long. This means that when build times are variable, we will decide to context switch based on the worst-case scenario for that build.
As wait times reduce, the temptation to context shift reduces. We might get to the point where we still notice the wait but can tolerate it without switching tasks. All of those minor background tasks are still important, but it’s better when we can dedicate the time to them, rather than performing them in the background alongside other tasks.
Commonly referred to as being ‘in the zone’, a flow state occurs when the task we are doing has just the right balance of challenge and interest. Not so easy that we get bored, but not so difficult that we experience anxiety and uncertainty. During a flow state, we become so engaged in what we are doing that we stop noticing the passage of time and become solely focused on the task at hand. Being in a flow state is both a pleasant and productive experience.
Commonly associated with video games, it is also achievable while writing code. However, it does takes some effort to arrange and enter. If you don’t have all the information at hand or don’t know how to proceed, your task becomes too difficult. If you’re writing too much boilerplate, the task becomes too easy. Likewise, if you’re having to keep stopping and waiting for builds, you get bored, lose engagement and drop out of flow state. Idle periods are an obstacle to maintaining a good flow.
There are already a lot of good articles about entering and maintaining a flow, so I won’t repeat them here, except to say that wait times are an obstacle to flow and that flow is a reward for creating a smooth workflow.
Verifying a change can be long, not just due to waiting for compilers and loading, but because the steps involved are too manual. Rather than enshrine the extra tasks and decisions in code, the development team has to perform the actions themselves. I like to think of this as ‘mental debt’; like technical debt, it gathers over time and has a cost that needs to be paid down. However, rather than living in your codebase, mental lives in your developers’ memory and your team’s processes.
These manual steps can take a lot of forms: config files to be tweaked, settings in your admin page to update, tables, caches and logs to blow away, scripts to run, files to copy, special use cases to remember to check, deciding which set of test data to use.
This overhead causes us a lot of problems. It slows down our builds; it consumes our focus, memory and other mental resources; it increases the number of decisions we need to make; it makes it harder to onboard new staff and makes it harder to change an application we haven’t touched for months. The complexity can mean that we can pull the wrong lever or forget to turn a dial, requiring you to restart the build.
The more you can do without thinking or needing to make decisions, the more space and energy you have to focus on work that matters.
Don’t waste your good hours
The focus and attention we require for writing code and solving problems consume a lot of energy. We might be at the office for 8 hours, but we usually can’t sustain our pace for the entire duration. We may have as low as 3 or 4 hours worth of quality effort available per day. The remainder we fill with planning, requirements gathering, communication, less challenging programming, learning, and all the other bits and pieces that go into being a software engineer.
Waiting means that not only are we draining our limited reserves by context switching, but we are losing time from the best parts of the day. If your build times are costing you half an hour a day, that’s not coming out of your eight-hour workday, but from your much shorter and more valuable high-quality work period.
- Slow builds snowball. Once a build is slow, it’s easy to accidentally more time to your build. Adding 5 seconds to a 45-second build might not seem so bad until it happens again, and again.
- All improvements you make can be shared across everyone else that is also working on that project.
- Waiting for builds to complete is frustrating and tedious.
- Faster builds mean you can check your work more frequently. Rather than try to figure out which of the half-dozen batch of changes has broken everything, your most recent fix will be at fault.
Sometimes slow builds are part of being a developer: legacy applications, parts of our ecosystem that rarely get touched, verifying complicated edge cases. Too often, however, I see teams accept slow builds, test runs, workflows and processes as part of life. Waiting does not have to be an integral part of being a developer. A little bit of investigation and creativity can have considerable benefits to our quality of life, engagement and our productivity. We can reduce context shifting, work within a flow state, reduce our mental overhead, and make the best of our limited human capacity.
In part 2, without being specific to any technology or platform, I will be covering the broad approaches I have taken in the past to reduce waiting.