03 Jul 2026 (2 months old) |
2230 words
· 0 LoC,
~12 min read
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
03 Jul 2026 (2 months old) |
2577 words
· 0 LoC,
~13 min read
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
03 Jul 2026 (2 months old) |
2902 words
· 0 LoC,
~15 min read
The current discussion about the Linux kernel reaching another milestone measured in tens of millions of lines of code follows a familiar pattern. Some people treat the number as evidence of extraordinary engineering. Others present it as proof that Linux has become bloated, incomprehensible, or badly designed.
Both reactions give the number more meaning than it has.
Any specific total needs a qualification: it depends on the kernel revision, the counting tool, and which languages and file types are included. More importantly, the Linux source tree contains much more than the code running on a typical machine. It contains drivers for vast amounts of hardware, architecture-specific implementations, filesystems, platform support, comments, build files, generated code, tests, build scripts, documentation, and code excluded by a particular configuration. Much of it will never be built for one machine, and not everything that is built will be loaded.
The real problem is that people often confuse large codebase with bad codebase. Lines of Code can indicate rough size, provide context, or suggest that a subsystem deserves closer inspection. It cannot tell us whether the software is maintainable, well-owned, secure, testable, operable, or safe to change.
Read more
30 Jun 2026 (2 months old) |
1340 words
· 0 LoC,
~7 min read
You are halfway through a feature when another branch suddenly needs your attention. Perhaps a pull request needs reviewing, a hotfix needs testing, main has moved, or you need to compare your work with the latest upstream code.
In a single working directory, that interruption usually starts with cleanup. You stash unfinished changes, switch branches, wait for dependencies or generated files to change, do the urgent work, switch back, restore the stash, and try to remember where you were. If the changes are awkward to stash, you might create a temporary commit instead. If this happens often enough, you might clone the repository several times and accept the extra disk usage and maintenance.
None of those approaches is impossible, but all of them add friction. The real cost is context switching: your editor, build artifacts, running services, and mental model all belonged to one task, and switching the folder underneath them disrupts that context.
Git worktrees solve this cleanly. Instead of switching branches inside one folder, you switch folders.
Read more
27 Jun 2026 (2 months old) |
4219 words
· 0 LoC,
~22 min read
Engineers spend a remarkable amount of time arguing about tools.
Should we use Renovate or Dependabot? Kubernetes or a simpler container platform? Flux or Argo CD? Terraform or OpenTofu? Helm, Kustomize, Docker, GitHub Actions, GitLab CI, Jenkins, Backstage—the list is endless, and every tool has enthusiastic supporters and experienced critics.
Those discussions are not useless. Tools have different capabilities, constraints, costs, and failure modes. But “Is this tool good?” is rarely the most important question.
The same problem appears when teams discuss processes. Someone proposes mandatory approvals, new push rules, another release gate, a branching policy, or a change board because it sounds mature or because another company does it. A process is adopted before anyone has defined which concrete failure it should prevent.
The better questions apply equally to tools and processes: What problem are we solving? Which system behavior should change? How easy will the new flow be to follow and automate? What will happen when it fails? Who will own it? Most importantly, how will we measure whether it worked?
That is the thinking behind my famous formula:
Speed = Process + Automation
Read more