Twelve questions to ask about a system you already own — and for each one, the answer that should worry you and the check that settles it for good. These are the exact guardrails we enforce inside client codebases, written plainly enough to apply this week without us. Every one declares how it is enforced, because in our design-partner engagement a rule the computer enforced held every single time, and a rule that depended on someone remembering leaked onto the next surface, every time.
Yours to keep. No form, no email, nothing to sign. Take it to your own team and use it as your own.
Not “remember to filter by tenant” — a database-level policy that rejects the read outright. Application-level scoping is the first line; this is the backstop for the day someone writes a query in a hurry, or an agent writes one for them.
Do this first if you serve more than one customer, site or branch from one database.
The documented footgun on our design partner’s platform was exactly this: forget the registration and that table’s data crosses between customers. It is now a test that enumerates the schema and fails on anything unregistered — which caught a real omission within two weeks of being written.
Totals, tax and discounts get recomputed and constrained where the data lives, in integer minor units — never floating point. If the application and the database disagree, the write fails rather than quietly recording the cheaper number.
Deactivation usually stops the login and forgets the live session, the API token, the shared folder and the scheduled export. Write the test that revokes and then attempts each one. It is the single most common gap we find in an SME estate.
Hiding a button is presentation, not security. The check belongs on the server call. A route-level default of “deny” plus an explicit allow per action gets you most of the way; the rest is review.
A secret scanner on every push and a blocked merge on a hit. Rotate anything that was ever committed, including in history — a deleted commit is not a deleted secret.
A version column, or a unique constraint on the thing that must not duplicate. The user gets told their copy is stale. Nobody notices this class of bug until a number is wrong and nobody can explain it.
One transaction where the database can do it; an idempotency key and a stated retry rule where it cannot. Half-finished orders are the most expensive kind of data to clean up, because by the time you find them somebody has invoiced from them.
Agents produce a convincing interface faster than the substance beneath it, so a screen can cheerfully confirm an action that never happened. Every “success” message worth having is a round trip: write, read back, then tell the user.
If the gate can be overridden by the person in a hurry, it is not a guardrail — it is a suggestion with extra steps. Make the override require a second person and leave a record.
A backup nobody has restored is a belief, not a backup. Restore into a scratch environment, check a row you recognise, and record the date. Then put a reminder in the calendar — that part is a person, honestly.
One shared piece of code and one automated check — never a second note in a document. This is the ratchet, and it is why a review that is working finds less each cycle instead of the same things forever.
What the full rulebook adds. Yours would be tailored to your stack, placed inside the risky folders of your own repositories so it reaches whoever is building — and each rule would name an owner. This list is the part that generalises. The other part is the engagement.
How the engagement works →