Writing Code as if Deep Work is Impossible
If you work in your own office, and regularly have uninterrupted stretches of deep work, congratulations, you and your organization are doing many things right. But consider for a moment if that’s the situation for everyone on your team. And then consider if it’s likely to stay true for you indefinitely. I doubt it will. You’ll likely get back to a place where 1-2+ hour blocks of time to get in a flow aren’t possible.
And I’m here to tell you that if you don't refactor as if you’ll only be able to work on it in 5 to 10 minute bursts in a noisy open office, you’re hurting your team.
Most modern working environments are not conducive to deep work. You’ve likely lived through this in an open office. Or more recently thanks to COVID the “home office” that was never meant to be an office, shared with your partner, roommates, and/or children. We should push back on this trend, but that's an uphill battle that most of us have little control over in the near term.
We should craft our development process to deal with interruptions. Future you will thank you, even if in the future you has succeeded in getting your own walled office.
If you can fix bugs or ship new features without having to build up context it will make your development and maintenance experience much more enjoyable. More than that it will be much less frustrating. Nothing is quite as irritating as building up context, getting into a flow, and being ripped out of it before you can accomplish what you’re working on.
So how do you craft your code to allow changes in 10 minutes increments? Start with the following:
- Be explicit about defining data. Avoid global variables, or data that can be modified by multiple processes. If possible pass them into functions as explicit parameters. Define explicit data types to represent the data in your application rather than using primitives.
- Use concise explicit naming. You don’t want every class to be a
CSHCFIdoesn’t tell anyone what’s going on. Given the choice between the two, pick the more explicit. Editors have autocomplete. Avoid single character variables, except where idiomatic.
- Minimize conditionals. The more branching your code has, the harder it is to keep in your head. This means you have to have more tests to cover all of the cases, or attempt to hold it all in your working memory. Either way it’s more bug prone. You can use things like the null object pattern, enumerables, polymorphic interfaces, and many other techniques to minimize conditionals. That’s a topic for another post.
- Keep functions small. By minimizing conditionals and using concise explicit naming you should enable yourself and your team to have many small functions that can be composed together in easy to use ways.
- Create purposeful modules. Compose them upward. If you have well named functions, and modules, composing them upward provides an easy way to find where things belong and how they relate.
- Use bounded contexts/domain driven design. Encourage the entire organization to use the same words for users, products, etc. It minimizes what you need to be keeping in memory at any given moment, and helps to clarify any confusion that may occur when discussing a concept.
- Write tests. Tests allow you to be confident in your ability to make changes and have those changes positively affect the code base. Tests also act as convenient documentation and breakpoints in the event that you get interrupted. Testing from the perspective of the end user is the most valuable in my experience for ensuring confidence. But for allowing your flow to be interrupted it’s best to take behavior driven development (BDD) to the function and class level, and use those unit tests to drive your interrupt driven development flow.
- Use mocks. All modern languages have excellent mocking libraries and support. Utilize these to ensure your tests only test the subject at hand, rather than larger parts of your code base.
How to Practice This
- Before committing your code to the mainline, or submitting it for review, step away from your code for 5 or 10 minutes. Go get lunch, take a walk, whatever it takes to pull yourself out of the contextual bubble. Then review your code to see how well you can step through each line without rebuilding state.
- Practice the Pomodoro technique - it’s both a known practice for improving productivity, and consciously time boxes you for more conscious interruptions.
Accept Life with Interrupts
I'm not suggesting that you should give up on deep work. The benefits for that are legion. But we have to play the hand we're dealt, so if we're in a noisy interruption driven environment, and we can take steps to make writing and maintaining code easier on our future selves, let's do that.