Engineering the terminal - a setup that stands the test of time

2026-03-10


Your terminal should be a foundation for engineering excellence. Who wants to do bad work? Nobody, so why are you letting the terminal hold you back? As engineers we have to interact with the terminal. But too often I see engineers treat that like a burden. It does not have to be. Your terminal can work for you.

And it should. Experience has taught me that a poorly configured development environment always leads to rushed code and technical debt. A well-engineered setup, however, will enable you to work at speed and within an environment that promotes quality and maintainability.

And so here is my promise to you. Follow this guide to the end. Stick with it, practice it, build on it. Don't fold under the learning curve of the lessons in this blog. Do that, and then build in this environment and you will witness a shift in your mindset on the terminal - going from it being an access panel to the inner workings of your computer, to a fully fledged development environment. Through this process there will be pain, but you will come out the other side with your productivity magnified by 10x, context switching crushed, and a renewed passion for software engineering.

Thumbs up hacker man

Make it your way

The most effective terminal setup is one that reinforces good engineering practices for the individual. That is why every successful engineer I've worked with has customised their environment to their needs. Everyone is different and works in their own way - so customising the result of this guide is key to building an efficient well oiled machine. By the time you implement this you would already have identified modifications that would work better for you. Make them.

Throughout this guide we are also going to implement the best practice of storing our changes in some kind of version control. We will use Git to do this, and be storing all configs within one repository. Create this repository in your tool of choice (e.g. GitHub, Codeberg) and mark the location. For the sake of this article, I will reference this repository as ~/git/term-config.

Choosing a Terminal

If (whilst on whatever operating system you're on) you've searched for terminal you've probably realised that there are many different kinds of terminal emulators. They all have their devoted following. I have used a fair few in my time. This is my opinionated take on terminals. They all perform the same underlying function. It's a matter of preference which one you should use. But if you're struggling to decide, here is a solid set of recommendations given your operating system:

Windows - Windows Terminal. Windows' own terminal emulator. Easily the best terminal emulator on Windows - supports many different targets, from Powershell to Ubuntu (via WSL - more on that in a bit!)

Unix-based OS (Linux & MacOS) - Alacritty. I used it on a whim and never looked back. I find that, more than anything, I want my terminal emulator to get out of the way of what I'm interacting with. Alacritty does a great job at this whilst also providing a very simple and well documented way to manage configuration.

For Windows users, Windows Subsystem for Linux (WSL2) is a must. This will give you the ability to install a Linux VM (e.g. Ubuntu) on your Windows system. This guide is explicitly built for a Unix ecosystem, and so if you are on Windows ensure you have installed WSL before continuing.

The Shell

A distinction needs to be made between the Terminal and the Shell. Terminal, or specifically, a Terminal Emulator, is a piece of software that exposes the interaction to an operating system via a Shell. A Shell is an interpreter for your commands. Many Shells exist, e.g. /bin/sh, bash, zsh, csh, etc.

Bash comes on all Linux OS out of the box. But we can upgrade it to Zsh with 0 cost. My preferred way to do this is with Oh-my-zsh. Zsh offers some nice benefits over Bash such as better tab completion, better scripting, and more personalisation all whilst maintaining compatibility with bash. Oh-my-zsh offers a nice framework for managing the zsh configuration. Configuration lives within a ~/.zshrc file (similarly to a ~/.bashrc for bash).

After you install Oh-my-zsh, it's worth digging through the ~/.zshrc file. There are a bunch of stock options there ready to be turned on/off. Tweak them as you wish.

Whilst we're here, let's add this config file to our repository. We're going to do this by moving the file to the repository and then creating a symbolic link in its place.

mv ~/.zshrc ~/git/term-config && ln -s $HOME/git/term-config/.zshrc $HOME/.zshrc

Next, we're going to get stuck in and start modifying our ~/.zshrc. For all modifications, just append them to the end of the ~/.zshrc file.

Private Environment Variables

We use private environment variables a lot. These are variables that we don't want to commit to source control. I push these into a dedicated file, typically ~/.private_envs. We DO NOT want to store private environment variables in our .zshrc! Here is how I set the variables that I want to make globally available on my account:

export GITHUB_TOKEN="..."

We can then source this file from within our ~/.zshrc:

source ~/.private_envs

Functions

OK, utility functions. These are custom CLI commands that perform simple operations. I use each one daily. They have become a core part of my daily workflow.

Slap

This is a command I use often to quickly navigate to my git repositories. I maintain a structure of storing all my repositories in ~/git. It's implemented like so:

slap() {
  cd ~/git/"$@";
};

This function helps maintain consistent project organisation. Good practice is to store your repositories under a sensible folder structure that you can route to with slap. E.g.:

slap term-config # navigates to ~/git/term-config
slap term-config/nvim # navigates to ~/git/term-config/nvim

Build your own functions and put them here.

Aliases

Aliases allow you as an engineer to do things quicker. Use them enough and they will become muscle memory, drastically reducing cognitive load as you develop.

Here's a carefully curated set that I use daily, starting with some Docker utilities.

Docker

I use Docker a lot and often need to clean up my environment. Here are some useful aliases for that:

# stop all containers
alias dst='docker stop $(docker ps -aq)'
# remove all containers (must be stopped first)
alias drm='docker rm $(docker ps -aq)'
# remove unused networks
alias dnp='docker network prune -f'
# remove dangling images
alias drmi='docker rmi -f $(docker images --filter dangling=true -qa)'
# remove dangling volumes
alias drmv='docker volume rm $(docker volume ls --filter dangling=true -q)'

Use these with caution — they will cause data loss.

I also wrap them into a single nuke command:

docker_nuke() {
  dst && drm && dnp && drmi && drmv
}

System Operations

A set of aliases for navigating common terminal functions, built up over time — whenever I find myself repeating something, I alias it:

alias cl='clear'
alias ls='ls -lah --color=auto'
alias grep='grep --color=auto'
# this command accesses my neovim installation. We will be installing neovim a
# bit later, if you do not want to use it, omit this command.
alias nvim='~/nvim-linux64/bin/nvim'

Git

Git is probably my most-used tool. Rather than typing the same commands repeatedly, I've bundled the most common operations into aliases:

# amend last commit, keep message
alias gcaa='git commit --amend --no-edit'
# stage all changes and amend last commit
alias gacaa='git commit -a --amend --no-edit'
# show last 10 commits
alias gls='git log --oneline -n10'
# show working tree status
alias gst='git status'
# push to remote
alias gps='git push'
# force push current branch
alias gpsf='git rev-parse --abbrev-ref HEAD | gps origin -f'
# force push with lease (safer)
alias gpsfwl='git rev-parse --abbrev-ref HEAD | gps origin --force-with-lease'
# commit with message
alias gcm='git commit -m $@'
# pull from remote
alias gpl='git pull'
# fetch and prune stale remote branches
alias gfp='git fetch -p'
# switch to main
alias gckm='git checkout main'
# push and set upstream
alias gpsuo='git push --set-upstream origin $(git rev-parse --abbrev-ref HEAD)'

Tidy up

That's all the modifications I make to my ~/.zshrc out of the box. As I install tools and such they automatically update my ~/.zshrc (to reference install paths for example), but these commands make up the bulk of my personalisations.

Next up, text editing. Arguably, the most powerful addition to your engineering environment. I have left this to its own post, here -> Text Editing. Spoiler, we're going to dive deep into Neovim and Tmux.

If you're still using VSCode, or worse, Cursor (yuck!), you're probably wondering why people rave about Vim. You may have even tried it once and immediately quit because it slowed you down. That was my experience, but instead I stuck with it. And here is the thing about a text editor like vim: the impact it has on your abilities compounds. GUI based IDEs force your capabilities to hit a ceiling quickly. Whereas a tool like Vim compounds.

3 years on (at the time of writing) from switching I am 10x more productive with vim than I ever was with vscode. There is something about the low stimulation environment of Vim that encourages the growth of your technical capabilities. Compound that with tools like Neovim and Tmux, and you have a recipe for engineering excellence.

And so carry on to my Neovim and Tmux guide if you want to start your journey with a text editor that actively works to make you a better engineer.

← back to blog