Tools of the Trade
"Why is programming fun? What delights may its practitioner expect as his reward?
First is the sheer joy of making things... Second is the pleasure of making things that are useful to other people... Third is the fascination of fashioning complex puzzle-like objects of interlocking moving parts and watching them work in subtle cycles, playing out the consequences of the principles built in from the beginning... Fourth is the joy of always learning, which springs from the nonrepeating nature of the task...
Finally there is the delight of working in such a tractable medium. The programmer, like the poet, works only slightly removed from pure thought-stuff. He builds his castles in the air, from air, creating by exertion of the imagination. Few media of creation are so flexible, so easy to polish and rework, so readily capable of realizing grand conceptual structures. Yet the program construct, unlike the poet's words, is real in the sense that in moves and works, producing visible outputs seperate from the construct itself. It prints results, draws pictures, produces sounds, moves arms. The magic of myth and legend has come true in our time. One types the correct incantation on the keyboard, and a display screen comes to life, showing things that never were nor could be. in common with all men."
- Frederick P. Brooks Jr., The Mythical Man-Month: Essays on Software Engineering
I love programming, and this quote from Brooks explains why in a way better than I ever could. There is something so satisfying about creating something from nothing, playing God in the smallest way, solving problems, and building something useful. I feel fortunate to have a career doing something that I enjoy so much. For me, the delight in "working in such a tractable medium" has increased as I've spent time honing my tools and configuring them in a way that lets me focus on building and problem-solving. There is nothing more frustrating while "build[ing] castles in the air" and "playing God" than being held back or interrupted by unfamiliarity with a tool, an over-complicated workflow, a poorly designed user interface, or software defects. This investment in my tooling hasn't just made me more productive, it has made me a better developer. There is something to be gained by being curious, learning how things work, and forming the ability to solve problems for yourself. Not only has the time I've spent customizing my tooling been rewarding and fun in and of itself, but it has also enabled me to be productive and to enjoy the time I spend programming even more.
In this blog post, I want to walk through the different tools I use every day and how I use them. I want to highlight how tools like Zsh help me use the "correct incantations on the keyboard", how Neovim makes a program "flexible...easy to polish and rework", and how my keyboard setup itself lets me work closer with "pure thought-stuff". Mostly though, I want to highlight the tools I've found most useful in my workflow in hopes that it inspires you to learn about them or more about the tools you use. I hope this inspires you to take the time to make your workflows more productive, both because it's fun to do so and because it will make your everyday tasks easier.
The Terminal
Every developer has to interact with the terminal. I live in the terminal. Most of my applications run in the terminal,
I edit code in the terminal, and I'm writing this blog post in the terminal. Did I mention I spend a lot of time in the
terminal? Most of the time I see software engineers only learn the bare minimum they need to perform their everyday job
functions on the command line. They don't realize the power of learning the built-in unix utilities and how
to combine them to write powerful commands and scripts. Utilities like awk
, grep
, sed
, find
, and xargs
allow me
to write, debug, and test code quickly and efficiently.
One reason I prefer terminal applications is because I don't like using a mouse. I find it's always slower to have to reach for a mouse. In the past, I've had to deal with Repetitive Strain Injury (RSI) and I found I had more wrist pain the more I switched back and forth between mouse and keyboard. Having many applications in one place also makes me more productive. I recently heard ThePrimeagen talk about how frustrating it is to watch someone who spends 40% of their week on a computer fumble around for windows looking for things. If I'm spending 40% of my week doing something, I want to be able to jump exactly where I want to be as I think, without having to look for a window or an option in some menu.
Kitty
Another reason I like terminal applications is how much customization they offer and how extensible they are. For example, I use a terminal emulator called Kitty. The most important reasons I use Kitty are that it's fast, and I can configure it from a simple config file that lives in my dotfiles repo. There are also killer features like hints that I can no longer live without. I use Kitty hints to open URLs in my browser, like for creating or viewing pull requests, or opening a tab in my browser pointed at the local server I just started running. You can also copy Git SHAs and URLs to the clipboard. While it may sound like a small feature, it's ingrained in my daily workflow and I couldn't imagine working without it. You can check out my Kitty config here. It's 80 lines including whitespace and sets up a theme, a font, and keybindings for hints.
Zsh
The default shell I run in Kitty is Zsh. I've been using it since I started programming - long before Apple wised up and made it the default on Mac. I use Zinit to configure zsh. You can view the different packages I use here but I'm not going to go into them since there are so many "how to have an amazing" terminal blog posts out there and I want to focus on the biggest terminal apps and commands that I find the most useful.
Amazon Q, formerly known as Fig, is top of my couldn't live without list. Amazon Q provides IDE-style completions for your terminal. I've tried several other alternatives and I haven't found any that work as well as Amazon Q. If I were going to recommend an alternative I'd look at Inshellisense. I don't use any AI features but the command autocompletion is good. Not only does it show you arguments and flags that you can use but does so based on the context of your current directory. That means it shows you what git branches or commits are available or what scripts can be run from your package.json. I feel like a snail in a terminal without Q.
The next terminal app that I use is Tmux. Tmux lets me jump through all the applications and processes running in my terminal by keyboard shortcut. One of my favorite Tmux enhancements I use is a script that allows me to me fuzzy search all my Git projects and opens the selected project in a separate Tmux session. I've shamelessly stolen this script from ThePrimeagen and then added a couple of tweaks. I've bound the script to cmd+f so that no matter where I am in the terminal I can easily switch to another project. I also use Tmux for window management within my project sessions. I can write simple scripts for opening up projects in specific configurations that I use all the time. For example, when I worked at Ditto we had at least 4-6 different processes (frontend, backend, figma plugin, jobs, services, etc) running locally for the app. I could run a single command and it would open all the windows I want where I wanted them. Some people will use Tmuxinator for this, but I've never found a need to install a whole package for this when my setup scripts are only 5-10 lines long. Another thing that is nice about Tmux is that if I close Kitty my sessions persist until I kill them or restart my computer which has saved me a panic attack or two when I've accidentally killed my terminal while in the middle of something.
I have a couple of other scripts I've bound to different Tmux keys. I can open the Chrome Node Debugger which I use frequently without having to click through the annoying Chrome UI to open it every time I need it. I also have a command in Tmux to take the logs from the current window and dump them into vim. I've found that I can jump through logs easier in Vim than in a terminal and find specific things I'm looking for during local development. I can scroll back through logs more easily in Vim than in Tmux or Kitty. Lastly, Tmux has so many built in commands that I use every day like listing all open sessions, going directly back to my most recent session, and opening a new window all a keypress away.
I don't have too many zsh-specific things I want to call out in this blog post. You can look through my Zsh config if you want to see how I configure zsh. The only thing worth calling out here is Zsh spelling correction. I've set Zsh (see first four lines) to prompt me with command autocorrections when I mistype something. It's something super small but it's a huge quality of life improvement.
Favorite Terminal Apps
The best terminal apps reduce feedback loops while I'm developing. The faster that I can make changes, test them, and then iterate on them, the better my code gets and the faster I get things done. There is nothing more frustrating while coding than making a change and having to wait a long time just to realize that you made a mistake and need to make an update and try again. Posting, Giraffe, and K9s all have helped me iterate quickly and have made me a more productive and happy developer.
Posting
If you are a web developer, you've most likely used Postman or one of it's alternatives like Insomnia or Bruno. I've tried using them all at some point and I have beef with each of them for different reasons. I could write a whole blog post about my issues with Postman but instead, I'll just give you two alternatives that I use on a near-daily basis. The first is Posting. It's a TUI HTTP client, essentially Postman in my terminal. I love how easy it is to use and how easy it is to share collections. Each request is stored as a YAML file which can be shared or checked into source control.
Giraffe
The only thing that Posting is missing is support for GraphQL. My current job requires writing and testing GraphQL APIs. I was so frustrated using Postman, that I decided to build what Posting was lacking. The result was Giraffe. If you are looking for a terminal GraphQL client (I'm guessing there aren't many people who are) give it a shot. It was heavily inspired by Posting. You can store requests in local JSON files, generate GraphQL operations and variables from an introspected schema, and generate code for a request in Javascript, Ruby, cURL, etc. Other than a little shameless self-promotion, (this is my blog after all) what I really hope you take away from this is that not only was it a fun and learning experience to build. It also has made my day job significantly easier and was worth the time I spent customizing this part of my workflow.
K9s
I spent a lot of my time at SimpleNexus involved in infrastructure and ops. I've now left that behind and focused on application development but one of the things I did at SimpleNexus was containerize the application and migrate the server infrastructure to Kubernetes. K9s was a key part of my workflow and is one of the best terminal applications I have used. I haven't used it in at least two years yet it still deserves an honorable mention.
Neovim
Speaking of blog posts from a few years ago, I wrote one on why I chose to use Emacs. Well, it turns out that I was wrong about Emacs being the best text editor - it's actually Neovim! Ok, again I'm just kidding - lots of IDEs and text editors out there are great but I feel like Neovim had all the benefits I was looking for in Emacs and then some. Since then I've switched from Magit to using the Git CLI and from using Ivy to quick-fix lists but I still use snippets and can easily extend my editor with custom functions, just in a different language now.
The biggest reason I switched to Neovim over Emacs is speed. I can't believe how much faster Neovim feels to use. Emacs in its own right was 100% faster than VSCode but just feels like I really can code as fast as I can think in Neovim. I can load larger files, and instantly jump where I need to to start editing.
The second reason was complexity. I was able to completely reproduce my Emacs setup in less than 200 lines of Lua. My Emacs config had gotten ridiculous and I had to fight it to get my workflow what I wanted it to be. Lua > eLisp and easier to write and maintain (just my opinion). I was already using Evil mode in Emacs so switching to Neovim only made things easier since I liked Vim motions and bindings over Emacs.
Navigation
Navigating in Neovim is a dream. I remember watching the engineer who introduced me to Emacs as he was coding. It was
mind-blowing how fast he jumped through files and made edits here and there. In my mind, the difference was as stark as
watching a little kid learn to play the piano and a trained prodigy playing La
Campanella. When a developer has the same mastery over their editor as a
pianist who can play La Campanella (stay with me here with the analogy) they are going to be able to pull off a more
impressive performance. I'm not trying to say that developers who use Neovim are going to be better than developers who
don't (apparently I'll just imply it), but that developers who have mastery over their editor, regardless of which
editor it is, will be able to accomplish more and may be able to handle larger and more difficult
compositionsprojects.
Let's look at some of the packages that make navigating smooth and simple in Neovim for me. Starting with inside a single buffer, I can use a package called Hop.nvim to jump to any character I can see on the screen with a couple of key presses. Harpoon lets me jump between files I'm working on at the touch of a button. Neotree lets me see where I am in the project and yet it's so much more than a file tree. I really like how I can view the files in my git status that have been modified in a tree view and see their relation to each other in the project and I like that I can do the same for open files as well. Originally I tried using Nvimtree but it had several bugs and there were some serious performance issues in large projects that were too annoying. Neotree has been faster and more feature-rich.
Edits
Between built-in Vim motions and additional packages, there are also quite a few things that speed up how quickly and efficiently I edit text. One package that was quite nearly life-changing was Oil.nvim. I'll let you watch the demo in the README.md of the repository but I frequently use oil.nvim to edit my file structure as if it were a text buffer in vim. It's not uncommon for me to create between 10 and 20 directories and files in one go as I'm designing or implementing something. It handles things like renaming and moving files and directories so intuitively it's hard to imagine not using it now that it's a part of my workflow.
Two other tools that cannot go unmentioned are substitute and cdo which I often use in tandem. If you ever use find
and replace in your IDE, :substitute is the equivalent feature. I frequently use commands like :%s/foo/bar/gc
which
means substitute foo for bar across a whole file and confirm each replacement. These tools can also be used to
replace globally with the aid of an amazing package called Telescope
(more on this package later). After searching for a variable across the whole project I can press ctrl+q
to take my
Telescope results and put them in a quickfix list. Then I can run :cdo s/foo/bar/gc
to find and replace foo across the
project. One great thing about quickfix lists is that I can review each occurrence and then before replacing I can
remove any unwanted occurrences from the quickfix
list.
Vim motions make things like changing casing, or quote types, and deleting everything within quotes or braces easy
(sometimes with the help of a package, thank you Tim Pope). When I want to change the casing
of a variable to camel case, uppercase, or snake case thanks to Vim-abolish I
can press crc
, cru
, or crs
respectively. If I want to change the quotes on something from single quotes to double
quotes thanks to Vim-surround I can press cs"'
and the quotes on both sides
will change. No package is needed to delete everything within quotes or braces. The built-in Vim motions ci"
or di\{
work perfectly.
Searching
Telescope makes searching in Neovim a breeze. Not only can you
search through project files, directories, and their contents, but you can also search through help tags, keymaps, open files,
commands, and the list goes on and on. I especially want to focus on keymaps. In my Neovim config, I add a description to
every keymap. Most of these I use
often, but sometimes there is a random complicated command I want to save with a description and then I can look through
these commands by pressing <space>sk
and then fuzzy finding through descriptions. My Telescope
configuration keeps the UI
looking very minimal and similar to what Ivy looked like in Doom Emacs.
Alright, you've heard enough about Neovim. I just need to add two more things here. Snippets aren't unique to Neovim, but they are essential to my workflow. Adding custom snippets lets me avoid writing repetitive boilerplate code. Lastly, I love that I have the ability to write custom scripts and then execute them with a keypress. For example, In Javascript I write a lot of index files. I wrote a little 20-line script that will automatically generate those files for me and that alone saves me several minutes of typing each day which really adds up over time.
Keyboards
In a previous post, I mentioned I enjoy buying and building mechanical keyboards (a surprisingly expensive hobby). Mechanical keyboards, at least ones that support customized layouts, are a major component of a productive developer workflow. I could (and maybe will) write a whole blog post about the different mechanical keyboards and switches that I've tried. For the purposes of this post, I just want to highlight a keyboard that I love and has made a big difference in my work. The the Moonlander Mark I is about as near as you can get to perfect. It's a split, ortho-linear keyboard that I purchased from an awesome company called ZSA. In the Moonlander, I run the default keycaps with Kailh Speed Copper switches.
After starting a new job in August 2023, I started developing some sort of RSI or Carpel Tunnel Syndrome or maybe both. I had developed consistent pain in my knuckles and wrists and I was getting really nervous that it would be something I'd have to deal with long term. I tried a lot of different things over the period of several months to fix the pain.
After doing hours and hours of research on ergonomics and a lot of experimentation the things that I found that really helped were a good desk and chair, the SecretLab Titan Evo 2022, at proper height and I bought and started using the Moonlander. I can't prove it, but I think the Moonlander was really what made the largest difference for me. The keyboard itself has great wrist rests and tenting which are game changers. The speed copper switches also have a really high actuation point and are tactile which has helped me stop bottoming out while typing which I think contributed to a lot of my pain. What helped even more than that though was the custom layout I designed that let me keep my hands on home row or as close to it as possible. No more stretching my pinky along the entire side of the keyboard for ctrl, shift, escape, tab, delete, enter, etc. It also allowed me to reach symbols and numbers without having to stretch as well. I've attached screenshots here below of the layout I use, but if you want more detail or to see my latest configuration you can look at the layout in Oryx online.
The first image below is the base layer of the Moonlander. While typing my fingers will stay within the red boxes and on the thumb clusters. The thumb clusters are what I use for switching between layers, and all the keys I used to use my pinkies for. Shifting layers means that while I hold one of the thumb cluster keys, the rest of the keys change behaviors according to which key I hold. The other layers that can be toggled appear in the following screenshots. Also of note is that I no longer use a shift key, instead have enabled the auto shift option which will shift a key automatically if you hold it for longer than normal.
This is the default layer.
This is the navigation layer. It keeps my arrow keys close, and I use ctrl+n and ctrl+p often in Vim. I also have some macros saved for saving and exiting files which are key combinations I use frequently.
This is the symbol layer. I use this often in coding, not only becaues I need to type the symbols but in vim different symbols have different movement behaviors as well.
This is the numpad layer. One of the benefits of an ortho-linear keyboard, besides reduced strain, is that I can use a layer to have a ten-key again.
That's a Wrap
If you've made it to the end of this blog post, thank you! I hope that you came away with some ideas of what you can do to make your workflow more productive or found some new applications to try. All of my configuration for my setup can be found in my dotfiles repo. If you have any comments or questions don't hesitate to reach out.