← All posts
Engineering Process

Stop Retyping That Command

The throwaway shell command you run for the third time is trying to tell you something. I started listening, and my dev loop got noticeably calmer.

I noticed I kept retyping the same handful of shell commands. Stopping a stray dev server before a rebuild. Running a little create-then-fetch-then-check sequence by hand to confirm an API actually worked. Each one took a few seconds, so each one felt too small to bother turning into anything. Then I added them up, and I realized I was paying that cost many times a week, getting it subtly wrong sometimes, and losing the exact command the moment the terminal scrolled away.

Two habits came out of that, and they have made my day-to-day work meaningfully steadier. Neither is clever. Both are about treating small automation with the same seriousness as the code it supports.

Write tooling in the language the project already speaks

When I needed a helper or a test harness, my reflex used to be a quick PowerShell or bash one-off, because it felt fast. The problem is that an ad-hoc shell script is the least durable artifact in the whole project. It is not reviewed, it behaves differently across machines and operating systems, it cannot share types or helpers with the code it is testing, and nobody maintains it because it does not feel like part of the codebase.

So the rule I settled on is to write scripts, helpers, and test harnesses in a language the project already uses, matched to the layer being acted on. Backend logic and HTTP checks go in the backend’s language, so they can reuse the real types and contracts. Frontend and cross-cutting dev-loop helpers go in the frontend’s language. For a .NET backend that means an integration test project in C# that spins up the real dependencies, not a shell script poking at endpoints with curl. For an Expo app it means a small Node script for the dev-loop chores.

The payoff is that the tooling becomes a first-class part of the project. It shows up in pull requests, it runs in the existing toolchain and in CI, it reuses the contracts it is exercising, and anyone who works on the project can read and fix it. There is no separate shell dialect to keep correct across machines.

Codify anything you run more than a couple of times

The second habit is to notice when a command has stopped being a one-off. If a validation or test sequence is something I run repeatedly, I stop reissuing it by hand and commit it as a real, repeatable artifact: an integration test, a checked-in script with a name. The act of giving it a home turns a fragile manual ritual into something that runs the same way every time and fails loudly when it should, which is the entire point of a test.

There is judgment in this, and it cuts both ways. A genuinely one-time check is not worth codifying, and over-codifying truly throwaway work just adds maintenance noise. The trigger I use is simple: the moment I am typing a command for the third time, or it has become part of a validate-and-test loop, it earns a file.

Why this is really about leadership, not scripting

When I wrote about turning my AI assistant into a teammate, the theme was that good management is structure, guardrails, and feedback loops, and then trusting the work to happen inside that frame. This is the same idea pointed at my own tooling. An ad-hoc command is the opposite of all three. It has no structure anyone else can see, no guardrail against running it slightly wrong, and no feedback loop because it vanishes the moment it scrolls off screen.

Codifying it is how you give a recurring task a structure that survives you, a guardrail that catches a mistake the same way every time, and a feedback loop that fails loudly instead of silently. A good engineering leader does not want heroics remembered in someone’s terminal history. They want the important, repeated work written down somewhere it cannot be forgotten, fumbled, or missed by teammates.