These are the working rules I keep returning to. None of them are original. All of them are things I have learned the expensive way at least once.
Build the smallest thing that could work
Complexity is not paid for when it is written. It is paid for every time somebody has to change the system afterwards, and that bill arrives long after the decision that caused it.
The practical version: prefer boring technology for anything load bearing, ship a small working version before planning a large one, and be suspicious of any abstraction introduced for a second use case that does not exist yet.
Measure, do not estimate
The question “did that change make it better” should have an answer, not an opinion.
In ordinary software that means tests and metrics. In work involving models it means an evaluation set of real cases, scored the same way each time, because without it the system drifts toward whoever argued most recently.
Absolute numbers matter far less than the direction of travel between two versions.
Design the failure first
Every system has bad days. Deciding in advance what happens on those days is what separates a feature people trust from one they learn to work around.
For AI features this is the whole game. The component is unreliable by nature, so the design question is not whether it fails but whether it fails visibly, cleanly and recoverably, or silently and confidently.
Keep the clever part small
The more of a task handled by plain deterministic code, the less surface there is for unpredictability to cause trouble.
I would rather have a system where one narrow step involves a model and everything around it is boring and testable, than one where the model is asked to hold the whole thing together.
Write things down where the decision was made
Most of the time lost in a codebase goes to somebody not knowing why an odd looking thing is the way it is, and then either preserving it out of fear or removing it and reintroducing the bug it prevented.
A comment at the point of the decision costs nothing and saves that entire loop.
Own what you merge
Code arrives faster than it used to. That makes it easier than ever to approve something nobody fully understands.
If I cannot explain what a block does and why it is there, it is not ready, regardless of what produced it.
Delete more than feels comfortable
The volume of code a team carries determines how hard it is to change anything. Removing what is no longer earning its place is one of the highest value things available in a mature codebase and one of the least likely to be prioritised.
More on the reasoning behind these in the software habits that survive AI.
