My Developer Setup (with Vim, Tmux, and Zsh)

Customizing your development environment can be both important and fun. This is how I got started with using Vim, Tmux, and Zsh.

My Developer Setup (with Vim, Tmux, and Zsh)
Photo by Gabriel Heinzer / Unsplash

Whether you're an industry veteran or a new coder, you should probably invest more time configuring your development environment. When you're staring at the same screen day after day, polishing that screen becomes one of the highest leverage tasks you can pick up in the long run.

In this blog post, I share what I've done to customize my development environment.

GitHub - MrPickles/dotfiles: My personal dotfiles.
My personal dotfiles. Contribute to MrPickles/dotfiles development by creating an account on GitHub.

Why should you care?

While customization is a personal choice, I still believe every developer should toy around with configurations at least once. It pays dividends in a couple of ways:

  • You work faster when you're familiar with the tools at your disposal.
  • You enjoy your work more if you're working in a comfortable environment.
  • Configurations force you to dig through some abstractions and understand the tools you're using.

(These are true from personal anecdote, at least.)

My development environment revolves around a zsh / vim / tmux setup. Most of my choices were made to optimize for speed and portability. Everything happens in the terminal, so there's minimal context switching. Certain tools I use (like Vim) have a learning curve, meaning you'll struggle to pick it up at first. But once you've achieved a certain level of competence, you become very productive. Everything works in any POSIX environment, meaning that I can clone a repository and have a working environment in a single command.


The Terminal and Oh-My-Zsh (~/.zshrc)

On POSIX-based machines, I use Zsh as my default shell. (If I ever need to do development on a Windows machine, I install the Windows Subsystem for Linux. Then I set my shell to Zsh.)

While most terminals default to Bash, Zsh stands out due to its exceptional plugin support. That makes Zsh a preferable choice if you intend on customizing your shell. There's a rather popular Zsh framework called Oh-My-Zsh that I use. It allows you to track things like the status of your git repository and Python virtual environment out of the box. You're also able to change themes at will, meaning you can get a pretty terminal with minimal effort. My Zsh theme of choice is called powerlevel10k. It allows me to have a terminal that looks like the screenshot below.

My terminal can display the git status, command error code, battery percentage, and many other things.

For my terminal color scheme, I use Solarized Dark. It's been sold as an "objectively better" color scheme since the color contrast was designed to be easier on the eyes. To my knowledge, no other color scheme makes any claims of objective superiority. Since I didn't have strong opinions on the color of my terminal background, I picked Solarized Dark. If its claim is true, then I've chosen the "best" color scheme. If not, I suffer no penalty. Either way, I'm no worse off than before.

Some of the core commands you run daily might have been written before you were born. While that generally implies that these are reliable tools, it also means they're old and there exist modern alternatives. I've included a table of my favorites below:

Purpose Traditional Tool My Alternative
List Directory ls exa
Display File Contents cat bat
General Search grep rg
File Search find fd
Diffing diff delta

For the most part, all you need to do is to install the replacement command and then alias it to your original command. It becomes a drop-in replacement, and your life becomes better without any additional effort.

alias ls="exa"
alias cat="bat"
# and so on...

Fuzzy Finder

One of the most underappreciated tools in the developer's arsenal is fuzzy searching. You type in approximately what you're looking for, and you get reasonable results. The specific tool I use for fuzzy searching is called fzf. It's a general-purpose command-line fuzzy finder, so you can use it in conjunction with most shell operations. It's hard to explain without going into specifics, so I'll share a few ways I use fzf.

Suppose I'm looking for active Chrome processes on a machine. I might list processes and pipe them into grep (or rg). The problem here is that I have to know exactly what to search for immediately. If we pipe it into fzf instead, we can arbitrarily type search queries without having to reload the command.

ps aux | grep Chrome # grep is case sensitive
ps aux | rg chrome # For grep/ripgrep, we get a static result.

ps aux | fzf # With fzf, we can comb through results on demand!

Fuzzy searching isn't just for piping other commands. If working in a large git repository and want to open up a file in Vim, it's a pain to type in the specific file. Tab completion helps, but you still need to type in a majority of the path. Fortunately, fzf comes with a few keyboard shortcuts. I can type vim <ctrl-t> and fzf will show me a list of candidate files to open. I type in the approximate path to the file, and it can locate what I want with minimal effort on my end. There are similar keyboard shortcuts for shell history search and directory search. I just type in approximately what I want, and fzf does the rest.

I no longer need to remember exact file paths. Now I can type in an approximate name and let fuzzy searching do the heavy lifting. All I had to type was <ctrl-t>.

Text Editing (~/.vimrc)

I don't use IDEs or "modern" text editors when coding. Instead, I use Neovim as my preferred text editor. (It's a drop-in replacement for Vim with a few added benefits.) There two are reasons why it stands out above any alternatives: it's fast and it's portable. Notice how those are the exact features I'm looking for!

Vim has been infamous for its keyboard shortcuts. If you know how to operate the text editor, your hands never need to leave the keyboard. You can also create macros within the text editor, meaning it's possible to automate any repetitive typing tasks. Vim is also a lightweight program. It starts up in milliseconds and can support files that are too large for "modern" text editors to even open. Everything just works. Since Vim runs within the terminal, it's possible to have the same experience across machines, even if you're developing over SSH. Just make sure the program has been installed and you're done.

While many consider Vim an "old" text editor, it can support modern features such as plugins and language servers. For package management, I use vim-plug to install general Vim plugins. For language server support, I use coc.nvim. That way, if I need autocompletion or jump-to-definition for a language, I just need to type in a single command to install a new language server. Vim works well with fzf too. If I want to open a new file, I can hit <ctrl-p> and open a fzf buffer. It allows me to find arbitrary files without knowing their paths or closing my editor.

Editor features like code search, autocompletion, and jump-to-definition either come out of the box or with a single command. I can almost do everything without closing Vim.

Tmux (~/.tmux.conf)

If you're a terminal-centric developer (like me), it makes sense to use a terminal multiplexer like tmux. A terminal multiplexer allows you to split a terminal into multiple panes or create virtual tabs with new terminals. If you develop on a remote machine, terminal multiplexers become even more useful. You only need one SSH connection to maintain multiple terminals. Your tmux session also lives independently of your SSH connection. If you choose to log off, you don't lose your work. Just detach from tmux and reattach whenever you want to resume your work.

Tmux is a great way to control multiple terminals, even over SSH. You no longer need to worry about SSH connections failing, and it's trivial to keep processes running even if you need to close your laptop.

Version Control (~/.dotfiles)

To wrap everything neatly in a bow, I keep all of my dotfile configurations under version control. There's a single git repository that holds all of my configuration files. Any local config file is just a symlink to a corresponding file within the git repository. If you're looking to get started, copying my dotfiles is a great way to get your feet wet.

GitHub - MrPickles/dotfiles: My personal dotfiles.
My personal dotfiles. Contribute to MrPickles/dotfiles development by creating an account on GitHub.

Since we have everything under version control, it's simple to track incremental changes over time. Additionally, I'm free to make experimental changes without fear of regressions. Version control also trivializes migrating the configs to a new machine. All you need to do is to clone the git repository and run a setup script that creates symlinks to the files within the repository.

For my dotfiles, I've created the following one-liner that does all the repository cloning and setup. I run the command, wait a few seconds, and the new machine turns into a familiar environment. And that's it. You're done.

# This command is literally all you need to get started...
curl -L andrew.cloud/dotfiles.sh | sh

Final Thoughts

And that's all I have to share. Tinkering with configs has made me pickier, but it has also turned me into a better developer. We tend to have too much tolerance for default tooling, and that slows us down in the long run. A few years ago, I started to wonder why we didn't focus more on our development environments.

I hope reading this makes you question it as well.


Thanks for reading! If you want to read new blog posts like this, consider subscribing to be kept in the loop.