Mohit Rawat

Code is entropy

Every line of code is a liability. Someone has to read it, understand it, test it, and keep it working while everything around it changes. The goal is to solve the problem with only the necessary code.

This comes from one observation: code you didn’t write never breaks. It never confuses a new teammate, never shows up in a diff, never needs a migration. The cheapest component in any system is the one that doesn’t exist.

Most of my engineering defaults follow from this one idea.

Design for the second use case, not the tenth

The classic failure mode is building for imagined futures. A function with one caller today ends up with four config parameters for callers that may never arrive. Each parameter adds entropy: a branch to test, a doc to explain, a decision the next reader has to reverse-engineer.

Planning one step ahead is right, but ten steps ahead is a waste because you’re guessing. If a second use case is visible, design so it fits. When the tenth use case finally arrives, it will need a different shape than the one you built into the code years earlier.

The practical version: before any non-trivial change, ask what would be hardest to change later, and decide that one thing now. A database schema is hard to change, but a function body is not. Spend the design effort on the schema and keep the function body simple.

Dead code gets deleted today

Commented-out blocks, _old suffixes, functions with zero callers. None of it is saved for later. Deleted code is not lost. It is one git log away in git history.

Keeping it has a real cost, because every reader has to work out that the code is dead before they can ignore it. That cost repeats for every reader.

Verify nothing references it, then delete it.

Duplicate until the right abstraction is clear

DRY applied too early is how wrong abstractions get built, and a wrong abstraction is harder to undo than almost any amount of duplication. Two similar-looking blocks of domain logic are often about to diverge. Merge them early and you get a shared function with a boolean flag for each caller. You added complexity while trying to remove it.

Wait until two or three real cases exist and you can see what actually varies. The exception is logic where two copies must stay in sync (URL builders, schema mappers). Inline copies of those will drift apart and break. The signal to merge is not that two blocks look similar, but that they are required to agree.

The same question at runtime: what does the second run cost?

Entropy is not only lines of code. Code runs more than once: retries, schedulers, a human re-running a job after a crash. For any operation, review what the second run costs.

Some operations are safe to re-run. A pure transform gives the same output every time, and a write keyed on a natural ID updates the same row every time. The expensive operations are the ones that need design attention: calls that spend money, minutes, or external quota on every run. Give those a short-circuit (an input hash, a freshness window) plus an explicit override for when you need to force the run. A retry should also be cheaper than a first run. If a retry costs as much as the first run, the pipeline is missing a checkpoint.

LLM calls are the clearest example. A batch job that re-tags a hundred thousand items because nobody checked for unchanged inputs shows up as a bill, not as a bug report. The same discipline says every LLM output should record the model that produced it, and every LLM judgment should return its evidence along with the label. An output without that record is a different kind of entropy: a result you can no longer audit, reproduce, or trust.


None of this argues for clever, compressed code. Code that is short but hard to read is its own liability. The argument is against code that exists without a reason: the config parameter for a caller that never came, the block kept just in case, the premature abstraction, the expensive re-run nobody measured. Every line should have a clear reason to exist.