Why Your Dotfiles Are a Liability, Not an Asset

by Daniel Reeves

Most developers treat their dotfiles like a trophy collection. Years of .bashrc tweaks, Vim plugins, and Tmux keybindings stacked on top of each other like geological sediment. The assumption is that more configuration equals more productivity. I used to believe that too.

Then I spent three hours debugging why my terminal looked wrong on a new MacBook — only to discover the culprit was a Zsh plugin I'd added in 2019 and hadn't consciously thought about since. That's when the trophy started looking more like a time bomb.

This post is about dotfiles as a liability: what that actually means in practice, how to audit what you've accumulated, and a more honest framework for deciding what configuration is worth keeping versus what's just comfort food for your .zshrc.

The Sunk Cost Hiding in Your Home Directory

Here's the thing about dotfiles: they accumulate the way technical debt does. Each individual addition seems justified. A Tmux binding to split panes faster. A Git alias for git log --oneline --graph --decorate. A custom PS1 prompt with the current branch name baked in. None of these are bad decisions in isolation.

But over five years, you end up with a .zshrc that's 600 lines long, half of which you can't explain without grepping the commit history. And because it works — most of the time — you never audit it.

The real cost isn't startup time (though a bloated Zsh config absolutely does add latency; mine was adding ~340ms on cold shell open before I trimmed it). The real cost is cognitive lock-in. When your environment is so customized that you can't function on a stock terminal, you've made yourself less portable and more fragile. Pair programming on someone else's machine becomes genuinely uncomfortable. Onboarding to a new job gets slower. Cloud development environments like GitHub Codespaces or Gitpod — both of which I've used heavily in the past two years — become friction-filled instead of frictionless.

What a Dotfiles Audit Actually Looks Like

I did a proper audit last spring. Not a "clean up someday" intention — an actual structured review. Here's the process I used.

Step one: list what you have. Run ls -la ~ and ls -la ~/.config. Write down every config file you own. Don't skip the obscure ones like .inputrc or .curlrc.

Step two: for each file, answer two questions. When did I last deliberately change this? And if this file disappeared tomorrow, would I notice within a week?

If the answer to both is "I'm not sure," that's a flag.

Step three: test your actual shell startup time. On Zsh:

time zsh -i -c exit

Anything above 200ms is worth investigating. I was at 380ms. After the audit, I got to 90ms. That's not a rounding error — it's a noticeable difference when you open ten terminal tabs in a row.

Step four: try a stock environment for a day. Seriously. SSH into a plain Ubuntu VM and work there for a few hours. The things you actually reach for and miss — those are the keepers. The things you don't notice are missing? Cut them.

The Plugins Problem Is Worse Than You Think

Plugin managers are where dotfiles debt compounds fastest. Oh My Zsh is the obvious example. It's a genuinely useful project, but it ships with dozens of plugins and themes most people never inventory. I had zsh-syntax-highlighting, zsh-autosuggestions, git, docker, kubectl, and aws plugins loaded — most of them doing lazy completion setup that I could have done in ten lines myself.

The kubectl plugin alone was adding ~80ms to my startup because it was running kubectl completion zsh on every shell open instead of caching it. The fix is one line:

# Instead of letting the plugin run this every time:
# source <(kubectl completion zsh)

# Cache it:
if [ ! -f ~/.zsh_cache/kubectl_completion ]; then
  mkdir -p ~/.zsh_cache
  kubectl completion zsh > ~/.zsh_cache/kubectl_completion
fi
source ~/.zsh_cache/kubectl_completion

That's the kind of thing you only discover when you actually look at what your plugins are doing. Most people never look.

The same pattern applies to Vim/Neovim configs. The lazy.nvim plugin manager (introduced in late 2022) does deferred loading well, but I've seen Neovim configs with 40+ plugins where the owner couldn't name half of them. At that point you're not configuring an editor — you're maintaining a small software project.

When Dotfiles Actually Make Sense

I'm not arguing for a completely stock environment. That's the wrong takeaway. Some configuration genuinely pays for itself.

Git config is the clearest example. A well-maintained ~/.gitconfig with sensible defaults — push.default = current, a good [alias] block, pull.rebase = true — saves real time every day and is trivially portable. It's also readable to a new colleague in thirty seconds.

SSH config (~/.ssh/config) is another one. If you're managing more than three remote hosts, a proper SSH config with Host blocks, IdentityFile mappings, and ProxyJump entries is not optional — it's basic hygiene.

Editor config (.editorconfig) belongs in your project repos, not your dotfiles. That's a category error I see constantly.

The principle I've landed on: configuration that makes you faster on any machine is worth keeping; configuration that only works because you've spent years training muscle memory around it is debt.

A Simpler Dotfiles Architecture

After the audit, I rebuilt from scratch using a much simpler structure. No framework, no Oh My Zsh, no plugin manager for the shell itself. Just:

~/.dotfiles/
  zsh/
    .zshrc          # < 100 lines
    aliases.zsh     # explicit, commented
    completions.zsh # cached, not regenerated every time
  git/
    .gitconfig
    .gitignore_global
  ssh/
    config
  nvim/
    init.lua        # lazy.nvim, < 15 plugins

Symlinked with a plain shell script — no Chezmoi, no Stow (though Stow is fine if you want it). The install script is 40 lines. Anyone can read it.

The constraint I set: if a new machine isn't usable within fifteen minutes of running the install script, something is wrong with the dotfiles, not the machine.

I also keep a DECISIONS.md in the repo. One paragraph per non-obvious choice, explaining why it's there. Future me has thanked past me for this more than once.

What to Do Tomorrow

Dotfiles management is one of those things that feels personal and low-stakes until it isn't. The longer you wait to audit, the worse the debt gets.

Here's the concrete action: run time zsh -i -c exit (or the Bash equivalent: time bash -i -c exit) right now. If you're above 200ms, you have a problem worth fixing this week. If you're below 200ms, do the "stock terminal for a day" exercise anyway — you might be surprised what you don't miss.

The goal isn't minimalism for its own sake. The goal is a dotfiles setup you can actually explain, maintain, and trust on a machine you've never touched before. That's what makes configuration an asset rather than a liability.

If you want to go deeper on the tooling side, I've written about managing local dev environments across machines and the tradeoffs of cloud-based dev setups — both of which connect directly to how portable your config actually needs to be.

Start with the audit. Everything else follows from knowing what you actually have.