Zeroth-World Problems

What Every Software Engineer Should Expect From Their Tools in 2022

Nine years ago, I started my first job as a software developer, writing Ruby on Rails code for Groupon. I brought to the job an abundance of gumption, an over-fondness for domain-specific lang­uages, and my favorite text editor, Komodo Edit, which I’d used throughout college.

We had an open-plan office and I sat across the desk from my manager, Josh Krall. About a week into my tenure there, Josh walked around to my side of the desk to chat about something and happened to see me opening a file in Komodo Edit. This is what I did: I hit command+O on the keyboard, which brought up the Mac OS system dialog for choosing a file. I then clicked through the folder structure, scrolled to the file I wanted, and clicked “Open”.

“Hang on a second,” Josh said. “Does your editor not have fuzzy search? Because if not, we really need to get you a better editor!”

It turned out that Komodo Edit did have fuzzy search: a different hotkey would let me type part of a filename and get a list of matches that filtered automatically as I typed. Another keystroke would open the file under my cursor. Bliss.

So that’s the story of how, fourteen years after I wrote my first program, I learned how to open a file.

Fast-forward to 2021. With COVID driving software teams every­where to distributed work, there’s now a six-digit number of first-year engineers who’ve never set foot in an office. I was very lucky to have Josh as my manager, and lucky that he walked by my desk when he did. But that kind of chance encounter and immediate corrective feedback doesn’t happen automatically when work is remote. Who is going to tell all these new engineers that there’s a better way to open files?

Knowing is half the battle

There are two types of people in the world: those who have never thought to type the words “fuzzy finder” into Google, and those who have. Crucially, if you don’t know that a term exists, you can’t search for it, and so unless someone tells you it exists you can only stumble upon it by pure luck.

The main reason for my file-opening incompetence circa 2012 was that I simply did not know there was a better way. The problem wasn’t that I didn’t know the answer to my question, it was that I didn’t even know there was a question that could be asked. The existence of fuzzy finders was, in Donald Rumsfeld’s terms, an “unknown unknown”.

It may seem like I’m making a mountain out of a molehill here, but the point I’m trying to make is not about fuzzy finders, but about tools in general. I really believe that one of the core problems facing new engineers is a lack of facility with the staggeringly huge ecosystem of dev tools that surrounds them. I wish programming were as simple as Structure and Interpretation of Computer Programs makes it out to be, where once you’ve learned to match parentheses it’s pure algorithms and datastructures and metacircular evaluation, but that’s not the world anyone I know actually lives in. Here in the real world we use Bash and Git and terminal emulators and IDEs and Vim and debuggers and test frameworks and ag and awk and the browser dev tools and the list goes on and on.

Trying to learn all these things is overwhelming

I don’t have a solution. But I can suggest a direction: a philosophy to adopt. It is this: Expect more from your tools. Nay, demand more from your tools.

I suppose that’s not very helpful advice, on its own. What exactly should you expect and demand? Well, I made a list, which is what the rest of this post is.

Some of the following tools may be available in your editor or environment already. For others, you may have to install plugins. I trust that, once you know that your tools can and should do these things for you, you can figure out a solution that works for you and your setup.

You can navigate and select text with the keyboard

On macOS, holding the option key while using the arrow keys will move word by word, while holding the command key will fling the cursor as far as possible, to the beginning or end of the line or the top or bottom of the file. Adding shift to any of these gestures selects text as the cursor moves over it. You can use these key combos to perform most editing operations without taking your hands off the keyboard.

You can switch between apps and windows with the keyboard

On macOS, command+Tab rotates through your open apps. command+shift+Tab does the same in reverse order. command+~ and command+shift+~ switch windows within the current app.

You can move and tile windows with the keyboard

Tools like ShiftIt and Hammerspoon give you an upgrade from clicking and dragging: with a single key combo, you can maximize windows, teleport them between monitors, or snap them to the left or right half of your screen.

You can install or update any software by typing its name

Homebrew is a universal installer for macOS programs and apps. When I was working at Groupon, installing and configuring a Postgres database system for my dev environment was a multi-day effort. Now it’s brew install postgresql.

You can jump to the definition of any variable or function with a gesture

In IntelliJ and related editors, you can hold down the command key (on macOS) and click on any variable or function name to jump to its definition. Similar gestures exist for other editors.

You can easily find all usages of a variable or a function

In IntelliJ on Mac the hotkey is option+F7. Other editors have similar features. The point is, you should have a way to discover all the places a function is called that’s more reliable than simple text search (which often turns up many false positives).

You can re-run tests automatically when you save a file

Four hundred milliseconds. That is the optimal amount of time between changing code and seeing test results. It’s fast enough that you don’t feel you’re waiting, and leisurely enough that you have time to finish your thought before the computer shoves a test failure in your face. Gary Bernhardt talks about this 400 millisecond threshold in “Fast Test, Slow Test”. In the UX world, it’s known as the Doherty Threshold.

If all the tests you care about run in 400 milliseconds, you can run them every time you pause to think. Even better, you can configure your IDE to run them automatically, so you don’t even have to hit a hotkey. If you do that, the test results become an ever-present source of information like the syntax highlighting in your editor. Not a tool you have to decide when to use, but a simple and reliable indicator of status.

Now, some of you may be objecting that a test suite that runs in 400 milliseconds is just not realistic for any project of even medium size. Let me clarify that 400 milliseconds equates, in my calculations, to about 4000 test cases, and I think it’s a rare project in which one team is responsible for more than 4000 test cases.

Now, I will acknowledge that it is actually impossible to run 4000 test cases in 400 milliseconds using the test frameworks popular today. The reason is that the test frameworks are simply too slow. The JavaScript test framework Jest, for example, takes about 4 seconds to run 3000 trivial tests on my machine, and about 600 milliseconds to run a single test. All that time is just overhead incurred by the test framework. But the overhead is not essential. The reason Jest is slow is that it’s doing extra work to accommodate complicated tests for hard-to-test code. A test framework that assumed simple tests for well-designed code could be much faster.

You should expect, and demand, faster tests and test frameworks that can give you feedback at the speed of thought. If you’re interested in pursuing this, you might find my test framework Taste worthwhile.

You can comprehend the output of a failing test

The output of your test suite should be terse. When the tests pass, they should output next to nothing—a green dot for each test is the most I care to see. If a test fails, the output should state succinctly what went wrong, and highlight it in a visible color so you can spot the problem quickly.

Too often, though, I see test suites that spew all kinds of garbage to the terminal—log messages and exception stacktraces and who knows what. The failure message for the actual test is very hard to spot among all the noise. This isn’t really a tooling problem as much as it is a test design problem—you need to set up your tests so that log output that would be printed in production is suppressed or redirected to a file. In the meantime, IDEs can help work around noisy output, by parsing the test results and displaying the failures in a neat list.

You can run the tests in just one file

IntelliJ lets you do this by placing your cursor in a test file and hitting control+shift+R.

You can run a specific test case

Similarly to the above, you can run a single test in IntelliJ by putting your cursor in that test and hitting control+shift+R.

You can debug running code from your editor

Many editors have debugger integration. You can debug code that’s running as part of a test, or in your development environment.

You can see the commit history for any file and understand why changes were made

IDEs will show you this information in a nice GUI; in Git the command is git log <file path>. Even if you can view the commit history, though, you still need to understand what changed and why. You should expect and demand that your coworkers write good commit messages!

You can find the commit that introduced a bug in O(log(n)) time

The tool for doing this is git bisect.

You can see a graph of all your local git commits

This is another feature that IDEs often have; there are also specialized tools like Git-Gui and gitk (which comes with Git, so you probably already have it installed!) Or you can run git log --graph --decorate --oneline --all which I’ve aliased to git lola by putting this in my ~/.gitconfig:

[alias]
  lola = log --graph --decorate --oneline --all

You can create aliases for shell commands

Speaking of aliases, did you know you can create aliases for shell commands you run often? It’s as simple as putting something like the below in your ~/.bash_profile (on Mac) and opening a new terminal window.

alias gs="git status"

You can edit multiple lines at once

One of my must-have editor features is multi-select: the ability to progressively select occurrences of a word or phrase and then edit all of them at once. In Atom and VSCode on Mac, select some text and hit cmd+D repeatedly to select following occurrences. In IntelliJ, the shortcut is ctrl+G.

You are encouraged to cheat

There is a certain type of programmer machismo (not unique to men, by the way) that drives people to “do it the hard way”. For the sake of yourself, your peers, and your company, please don’t. A former coworker of mine once expressed that test-driven development made software development so reliably straightforward that it “felt like cheating”. You are encouraged to cheat. Seek out the ways of doing things that make your work quick, easy, and reliable, and you’ll go home happier at the end of each day. Don’t worry, there will still be plenty of crunchy problems to solve, plenty of mountains to climb. Don’t mistake a slog through the muck for the ascent to the summit.

You don’t have to learn all these things at once

Learning is hard. It’s hard for everyone. The cognitive resources humans have available for learning and other intellectual tasks are extremely easily depleted and can only really be restored by sleep (though they can be briefly boosted by sipping a sugary beverage like fruit juice). So don’t beat yourself up for what you don’t know. You will learn most effectively if you can focus on one thing at a time and learn just that thing until it’s mastered.

For tips on how to do that, watch Kathy Sierra’s talk “Making Badass Developers”.

Pairing is doubly essential when everyone’s remote

If everyone is working from home, moments like my conversation with Josh can’t happen in physical space. They won’t happen at all, unless you intentionally create opportunities for real-time collaboration. That means remote pairing.

Remote pairing is difficult, and even more exhausting than in-person pairing, but I think the learning that results is well worth it. Pair for an hour each day, and see what people say about it at the next retrospective. And remember, even Kent Beck can only pair for 5 hours a day :)

My current favorite tool for remote pairing is Pop.

Your pair will help with anything you’re not trying to learn right now

One of the many benefits of regular pair programming is that it works around the problem of feeling like you have to know everything to get anything done. If you’re pairing with someone who’s an expert in the tools you’re using, you can lean on them to do all the tool related nonsense that you’re not interested in learning at the moment. Then when it comes time to use the tool you do want to learn, you can take the driver’s seat and they can stand by to answer your questions.

And if you’re that expert, it’s your responsibility to accommodate this learning time. Be kind, be patient, and resist the urge to grab the keyboard. It may feel like the coding would go faster if you drove, and maybe that’s so, but the learning would go slower, and it’s learning, not coding, that’s the bottleneck in software development.

Of course, people are not neatly partitioned into novices and experts. People are skilled at different things and struggle with different things. Everyone is interested in learning different things. So at the beginning of your pairing session, consider discussing with your pair what each of you would like to learn, and what you’re not interested in learning right now.

Conclusions

I feel like this post is ridiculously long for a glorified top-whatever list of my favorite editor tricks, but I also feel like I’ve barely scratched the surface. I’m sure your teammates will recommend tools and tricks I’ve never heard of, and that’s kind of the point! My goal was not to write the authoritative list of Things You Must Know, but to provide a starting point that you can use to build a set of techniques that works for your team.