The terminal as a cockpit

As developers, we’re constantly seeking ways to improve our efficiency and workflow. While GUI tools have their place, there’s something uniquely powerful about mastering the terminal. By placing the terminal at the center of our development workflow, we unlock a world of flexibility, speed, and automation that graphical user interfaces simply can’t match.
This post is about how one can set up the terminal as a highly customizable, powerful IDE that makes you never want to use the mouse again.
Everything in this post relates to my own personal configuration files/dotfiles.

Unix-based Environment

The Unix philosophy in action

The foundation of an effective terminal-centered workflow starts with a Unix-based host system. It doesn’t matter if you’re on macOS, Linux, or using WSL. The important thing is to be able to script in bash and have Unix/Linux binaries available.

The Unix philosophy states that all programs should be small in scope and composable with others. This gives us an incredible base for automations due to the scripting capabilities of Unix environments.

For example, we could automate the killing of a locally running webserver that gets stuck sometimes like so:

kill $(ss -lptn | grep 5173 | grep -o "pid=[0-9]*" | cut -d "=" -f2)

First we use ss -lptn to get a list of all processes including the process ids (PIDs) and the socket they are listening on. Then we use grep to extract the line with the port that our webserver is using. Afterwards we use grep again and cut to further shrink the string and finally extract the PID. The result is fed into kill, which shuts down the process.

You might think that coming up with such seemingly cryptic commands is not worth the effort, but as soon as you get to know some unix tools you’ll be able to to come up with these one-liners in a matter of minutes - and if you wrote it once, you have it in our repertoire forever.

The superiority of Unix in this space is confirmed by the sheer amount of Open Source CLI programs on Github, which are only available for Unix based systems. The truth is that Microsoft jumped too late on the bandwagon of good CLIs and people have never really caught on to develop such programs for Windows to a meaningful capacity (that’s probably one of the reasons why WSL was developed).

Arch Linux WSL

My WSL distribution of choice is the Arch Linux WSL by yuk7. While Arch Linux is notoriously difficult to set up directly, with its WSL version we just have to set up our user and are ready to go. There are two key factors that make me favor Arch Linux above all other distros:

  • It comes as a rolling release. As soon as a new version of a program is published, I can get it without having to upgrade my entire system like it is the case e.g. with Debian
  • The AUR (Arch User Repository) is a community-driven package registry that contains every tool you could wish for. In fact, I never had to install a software manually on Arch (except for internal tools)

Who would not like to be able to install everything they need with a single command and always have the most up-to-date version?

Terminal Multiplexer

When starting our Terminal emulator of choice, by default we end up with a single window containing a single shell instance. A lot of terminal emulators have support for tabs, some even support panes, so that we can have multiple shell windows side by side.

CLI tools that provide the same tab and pane functionality inside a single terminal window are called Terminal Multiplexers. The most prominent examples include tmux and my personal favorite zellij.

You might be thinking why use a terminal multiplexer and not just use the tab functionality of your terminal application. The answer is that terminal multiplexers are considerably more customizable and they are not dependent on the terminal application.

The functionality of both zellij and tmux goes far beyond simple pane and window management. Among many other features, they allow for detaching and reattaching to/from sessions, to put the entire shell scrollback into an editor and save and load window layouts.

zellij stands out for its user-friendliness, nice UI and a comprehensive documentation, which makes it my personal tool of choice. Even though it has not been around for as long as tmux, I have yet to find a usecase that zellij doesn’t cover.

zellij screenshot

When using programs in the terminal it is crucial to know the keybindings of the applications and zellij is no different. At the very top, our tabs are displayed. At the bottom, zellij presents us a statusline, which acts as a nice cheat sheet for the keybindings. For example, by pressing alt+t, we can enter tab mode. Then the statusbar updates to show us the new actions we can perform in tab mode:

zellij screenshot

This works the same for any of the other modes, which makes it very easy to learn zellij. Just as easy is creating a configuration file with all the default keybindings included. One simply has to issue this command:

zellij setup --dump-config > ~/.config/zellij/config.kdl

Personally I change tabs very often, that’s why I bind alt+a,alt+s,alt+d and alt+f to quickly open tabs 1-4. The methodology that works best for me is to use alt bindings for my terminal multiplexer and some useful OS bindings like switching virtual desktops, while keeping Ctrl free for programs running inside the terminal.
You can take a look at my zellij config here.

Bash

More than a REPL

Bash (the Bourne Again SHell) has been the default shell on most Unix-based systems for decades. No matter which Unix system you are on, you can always run bash. By learning bash, we gain the ability to script on Windows (WSL), Linux and MacOS, which is an absolutely incredible capability to have. Aside from desktop systems, server side and cloud infrastructure developers also gain great benefit through the automation capabilities of bash. After all, most of the systems in this space run on Linux or are at least managed by tools running on Unix systems.

If we only thought about a shell as a REPL, we would do it no justice. We can think of our shell as the kernel of the terminal that we use - a kernel that can do a lot more than just call programs and show their output.
For example we can create any custom keybinding we like. This includes the customization of built-in functionality like clearing the screen (default Ctrl-l), but also the invocation of any external program like so:

bind -x '"C-g":"clear; git status"'

With this configuration, pressing Ctrl-g clears the screen and then executes git status. Keybindings should be reserved for things that are needed really, really often. If one does not live up to this rule, it will become a hassle to find available keybindings at some point. For this reason I only I have 2 bash keybindings in my bashrc:

bind "C-p":previous-history # Selects the previous entry from histroy
bind "C-n":next-history     # Selects the next entry from history

To still get things done with only a few keystrokes, we can use aliases instead. Aliases act as personal shortcuts, transforming lengthy commands into (muscle-)memorable abbreviations that save precious keystrokes. Functions take this concept further, allowing us to define reusable functionality while also having the freedom to define the code on multiple lines and use input arguments. These are my personal favorite aliases:

# quick ls
alias l='ls -l'
alias la='ls -a'
alias lla='ls -la'
alias lt='ls --tree'

# quick git
alias gs='git status -u'
alias gd='git diff'
alias gdc='git diff --cached'
alias gdb='git diff development --color-moved=dimmed-zebra --color-moved-ws=ignore-all-space --find-renames'
alias gcm='git commit -m'
alias gl="git log --graph --pretty=format:'%C(yellow)%h %Cred%ad %Cblue%an%Cgreen%d %Creset%s' --date=short"
alias gaa='git add "*"'
alias gfa='git fetch --all'
alias gp='git pull'

For those seeking even more features, alternatives like Zsh or Fish offer enhanced functionality while maintaining most bash compatibility, but I personally just stick to bash because it covers all of my use cases and I enjoy not having to thing about compatability. By installing oh-my-bash, we can obtain a very nice looking prompt that even displays meta information like the current git branch we are on:

oh my bash prompt

For fish and zsh, there is also a plentitude of shell plugins available.

The smallest plugin system in the world

Another thing that I have configured in bash is what I call the smallest plugin system in the world and the entire magic is at the very top of my bashrc:

shopt -s nullglob
for filename in ~/.config/local_bash_plugins/*; do
  source $filename
done
shopt -u nullglob

First we set the nullglob option. By default when globs without a match are expanded in bash, the resulting string is the glob itself (which would make script execution fail if there were no plugins). The actual magic is just the source call to every file inside /.config/local_bash_plugins/*.
Inside this directory we can put all the files we want bash to load on startup. This allows us to

  • group functionality we only need on one machine without having to commit the changes to our dotfiles repo
  • create project-specific configurations, which we do not want to make public
  • enable/disable certain plugins by putting a return into the first line of the plugin

Fuzzy searching anything with fzf

fzf screenshot

fzf is a general purpose fuzzy filtering program. For example it can be used to

  • filter git commits
  • search for a database to connect to
  • search for a process

fzf is worth a post of its own, but I will show you the two most important of its commands, which I also registered as bash keybindings (because they are so good).
If we install fzf as a system package, we usually also get its shell keybindings script installed.

If we source that file like so: source /usr/share/fzf/key-bindings.bash, we gain two incredible useful keybindings:

  • Ctrl-R, which fuzzy searches through the command history
  • Ctrl-T, which fuzzy searches directories

Both of these keybindings save me from a lot of acrobatics daily.

Dotfiles Management

As you customize your terminal environment, you’ll accumulate configuration files (dotfiles) for your shell, multiplexer, editor, and other tools. Backing these up becomes important, especially when working across multiple machines. What many people do is just create a GitHub repository that holds their dotfiles, just like the one I linked in the intro section.

In my personal dotfiles I use setup scripts to easily install my entire configuration on a new system within seconds, you can see how it works here. The way this works is quite simple. For each file in the repository, I define a target location on the system. Then the installation script loops through all files and creates a symlink to the target destination of every file.
Not only does this mean that all the configuration is available in the right places, but this also means that i have a central place where I can update my configuration. I can just push/pull changes, and I have performed an update of all of my local dotfiles.

Using this method is of course not only limited to programs running in the terminal. My dotfiles also include my .ideavimc for the ideavim IntelliJ plugin and my windows terminal settings.

Conclusion

Embracing the terminal as the center of your development workflow is not about looking like a hacker.
It’s about leveraging decades of tool development and Unix philosophy to create a powerful, scriptable environment.

Start small by incorporating one tool at a time into your workflow. Learn the basics of a terminal multiplexer, get comfortable with bash, and gradually add text manipulation tools like grep, find, and tr. As you build confidence, consider moving more of your editing to Neovim or another terminal-based editor.
Trading a sophisticated IDE for a text editor is not necessary though. In the end, whether you like terminal based text editors or not comes down to your personal taste and needs.

The skills you develop will transfer across machines and operating systems and will serve you for the upcoming decades of your carreer.

plant
plant
2025-present Stefan Sommer. All Rights Reserved. stefan.sommer.dev@gmail.com
plant
plant
plant