When it comes to my development environment, I tend to have a couple simple philosophies that guide what tools I use.

It Has to Run Everywhere

I’ve developed on a variety of systems: Debian, Ubuntu, FreeBSD, Red Hat, CentOS, macOS, and even (rarely) inside WSL. I want the tools I use to work on all those machines and more, even though my daily drivers are Debian unstable and (currently) Ubuntu 24.04, because I never want to be without my preferred tools because they don’t run there.

So my tools have to run everywhere and I typically won’t use something that’s too system-specific or too esoteric. I need to log into servers, so I need my tools to work both in the terminal and in a graphical interface.

I usually use a graphical Neovim (via neovim-gtk), but I also log into machines without a GUI, so my configuration also supports a text-mode Neovim. Similarly, sometimes all I have is Vim, so my configuration is shared across both and written in Vimscript so that I don’t have to maintain two separate configuration files, and my plugins all work out of the box on both. In a pinch, I can use a standard vi, or even ex if I’m stuck without a TTY.

I use zsh because it’s featureful and available everywhere. I run it in as POSIX-compliant mode as possible so that it works ideally with pretty much every tool I use. The configuration is designed to automatically fall back to a less capable terminal type if the terminal type I’m using isn’t available, so that my configuration works even when logging in from a new system to an old one.

For the same reasons, I have some core parts of my dotfiles that use Perl, since it’s available on almost every operating system and the core parts of it are essential[1] on Debian and Ubuntu. I usually use Ruby for most of my other scripting because it provides more features out of the box, including templating and cryptography, but I only use it in things I can live without in a pinch.

It Has to Be Stable

I have a natural touch for breaking software. This happens so often I joke I’m an unofficial QA engineer. For instance, I once installed AT&T ksh and within 90 seconds, had gotten it to segfault.

I certainly like having new features available to me frequently, which is why I use Debian unstable at home, but in many corporate environments, I won’t have the latest features since I’ll be pinned to an LTS version, so I need things that work across a variety of versions. I need my software to work robustly across a various versions without a problem, so I tend to prefer software that has graceful degradation in configuration files and other functionality (such as tmux).

It Has to Be Free Software

With only a few exceptions, I want all of the software in my development environment to be Free Software.

Because I’m very good at breaking software, it’s important that I be able to fix things that break. For Free Software, I can often send a patch to fix things or at least pinpoint the problem pretty closely, which helps it get fixed.

It also means that I can typically make software I use run places it doesn’t already. Since I have owned machines with various architectures (x86, PowerPC, UltraSPARC, ARM, and MIPS), using Free Software means I can fix any portability problems and get things working again, even if upstream doesn’t have hardware using my architecture.

It Has to Work Offline

I travel a decent amount, whether by train or plane, and I sometimes have to be on call for my job. There are a lot of times when I’m somewhere where there isn’t any Internet access (such as on some airline flights) and a lot more where the Internet access is spotty. Even when I’m on call, which is a time I’m effectively guaranteed to have Internet access, I might be out and need to tether to my phone, which doesn’t have unlimited high-speed data. And sometimes my Internet just goes out, such as when the network provider underlying my ISP had a major outage.

In all of these cases, I want to be able to write code or jot down a few ideas for a writing project even if I’m offline, my Internet is bad, or I’m on a metered connection. Don’t get me wrong, I love the Internet, but I don’t want to be constrained if I feel motivated to do something and there’s no Internet access. It is, of course, fine for the tools I use if they runs online but degrade gracefully while I’m offline.

So this means that I don’t use LLMs for writing code or text or most online office suites (e.g., Google Docs) for writing text. I simply do these in Neovim without Internet-connected features.

I do use Docker, Podman, and other container-based tooling, as well as VM-based workflows (for compatibility with other OSes) but I try not to rely on them for things like building except in CI.

It Has to Be Available Out of the Box

When I bootstrap a new development system, I don’t want to have to download lots of random software off the Internet and I definitely don’t want to build lots of stuff. I find that when I build software by hand or download packages manually, it never gets updated and then I have an ancient version of software that has lots of security holes and no feature updates. So typically, I only use software that comes out of the box on the distro.

For these reasons, my scripts and tools tend to target the distro version of Perl, Ruby, Rust, and other toolchains. I don’t want or need to manage multiple versions of these tools, download random binaries from the Internet, or compile a toolchain when starting up an environment.

I also don’t usually use tools that are Electron-based, since those require constant updates for security reasons due to embedding the Chromium browser engine. Distros rightfully don’t want to maintain security patches for various versions of Chromium and Electron, so Electron-based tools will effectively never be in a distro and they won’t be available out of the box. I’ve also had to deal with obsolete operating systems in my job, so I can’t assume that my OS will always be supported by Chromium.

I have looser requirements for software that runs on a graphical machine because I usually have more control over those and they tend to be more up to date, so I’ll accept newer software (like Kitty) that might not be available on older OSes I might need to use in a non-graphical context. I do use the 1Password app (which is, much to my annoyance, an Electron app) for the benefit of using its SSH key agent, although of course that only works on the graphical machines (where, as I said, the requirements are looser).

Tools

With those criteria in mind, I normally use Neovim with neovim-gtk for editing (falling back to Vim or vi), zsh for my shell, 1Password for passwords and my SSH agent, Ansible for bootstrapping new development systems (and Puppet for servers), and Ruby (or sometimes Perl) for scripting. For my terminal, I use Kitty with tmux inside, and similarly I use tmux on remote systems as well.

For writing text, I use groff for letters and AsciiDoc (via Asciidoctor) or DocBook for most documentation and creative writing and I turn those into PDFs to print them if need be (using FOP for DocBook, since it provides better accessibility than LaTeX). I do use Markdown for cases where that’s the standard format, though.

Final Thoughts

I don’t feel like these criteria are really that constraining for me and they actually make my experience better, since I can always be confident of getting my tooling working on whatever system I’m using. Every system I’m working on works pretty much the same out of the box, which is just how I like it.


1. An essential package on Debian-based systems is required for the correct functioning of the system and will effectively always be installed, since removing it breaks the OS.