Daniel Meier

Infrastructure, Linux, automation, and operational engineering

Why Git Worktrees Are Awesome for Parallel Development

Git branches isolate history, but a normal clone gives us only one working directory.

That mismatch creates friction as soon as more than one task matters. I may have an unfinished feature in my editor when a production hotfix arrives. I may want to run a pull request beside main, compare two implementations, or let separate automation processes work on independent branches. With one directory, every interruption begins by changing the state underneath my tools.

The usual responses are stashing, temporary commits, repeated branch switching, or cloning the repository again. They work, but they make context switching more expensive than it needs to be.

Git worktrees are one of my favorite Git features because they solve the problem at the correct level. Instead of repeatedly changing which branch one directory represents, I give each active branch its own directory.

Read more

Why Every Repository Should Have a Makefile

Every repository develops a collection of commands.

There is a command to install dependencies, another to run tests, one for formatting, one for building an artifact, and several more for local development, generated code, documentation, containers, or deployment validation. Without a common interface, those commands spread across README files, CI YAML, shell history, package-manager scripts, and team memory.

I recommend putting a Makefile in almost every repository.

Not because every project compiles C, and not because GNU Make is a modern workflow language. I recommend it because make provides a small, familiar, versioned entry point for repository operations. A developer can run make, discover the available workflows, and use the same targets that CI uses.

A good repository Makefile is not a second application hidden beside the real one. It is a thin, documented interface over commands the project already trusts.

Read more

Why I Recommend asdf for Every Repository

Every software repository depends on tools.

Even a small project may require one particular version of Go, Node.js, Python, or another runtime. Larger repositories often add Terraform, kubectl, documentation generators, and code-generation tools. Without an explicit mechanism, developers install whatever their operating system provides, CI uses a different version, and the production build quietly depends on another one again.

These differences often remain invisible until a command changes behavior, a formatter rewrites files differently, or an upgrade works on one machine and fails everywhere else.

asdf is the tool version manager I recommend for this problem. It gives a repository one version-selection interface backed by one small file: .tool-versions. The file can be committed to Git, reviewed like any other dependency change, and used by developers and CI to install the same declared versions.

asdf does not make an environment perfectly reproducible, and it is not a replacement for a package manager or a container image. It solves a narrower problem extremely well: selecting and installing the versions of project tools that should be active in a directory.

Read more

Why I Recommend pre-commit to Every Engineering Team

Many code-review comments should never have required a reviewer.

Trailing whitespace, malformed YAML, forgotten generated files, unresolved merge markers, inconsistent formatting, accidentally committed private keys, and files that fail the repository’s standard lint command are mechanical problems. Discovering them after a push wastes CI capacity and interrupts the developer after the relevant context has already started to fade.

I use pre-commit every day because it moves this feedback to the point where it is cheapest to act on: before the commit leaves my machine. More importantly, it turns local checks into versioned repository configuration instead of relying on every developer to assemble and maintain the same toolchain manually.

That does not make CI unnecessary, and it does not turn a client-side Git hook into a security boundary. It creates a fast, reproducible feedback layer shared by developers and automation. Used with discipline, that small layer removes a surprising amount of friction from daily engineering.

Read more

Why Fish Is My Favorite Interactive Shell

The shell is one of my most frequently used interfaces. It sits between me and source code, Git, Kubernetes, package managers, remote systems, build tools, and almost every operational task I perform. Small usability improvements in that interface compound over thousands of commands.

That is why Fish is my favorite interactive shell.

Fish does not try to be a drop-in implementation of the POSIX shell language. It makes a different and, for interactive work, more useful promise: it is designed to be convenient, discoverable, responsive, and pleasant for a human sitting at a terminal. Syntax highlighting, history-based autosuggestions, descriptive completions, sensible defaults, and built-in navigation features are part of the normal experience rather than a framework I first have to assemble.

The lack of strict POSIX syntax is a real trade-off, but a much smaller one than it is often made out to be. Most commands are external programs and work exactly as expected. When I genuinely need POSIX shell parsing, I can invoke the appropriate interpreter explicitly with sh -c 'command' or run a script through its declared shebang.

Read more