DEV Community

Dexterlung
Dexterlung

Posted on • Originally published at coffeeshooters.com

Offensive audit — How to periodically audit a whole business flow (for self-taught developers)

May 2026 · Series "Trace Lock — Governance notes from pairing with AI to write code" · Post 3 of 9


The previous post (A1 Defensive) covered "locking down a known cross-layer relationship" (defensive, reactive). This post covers the opposite direction: proactively running an audit pass to find unprotected chain nodes across a whole business flow, then fixing them all at once (offensive, proactive).

The two mental modes are complementary: A1 is "lock it down after you trip on it," this post is "patrol regularly to find unexploded bombs."

This post is still the plain-language version for self-taught developers. Engineering details (6-piece fix pattern templates, how to write a governance rule, how to run SQL tests) go into B2 (engineer-facing).


The starting point: a customer's screenshot of a frozen button

May 25th, midday. Customer screenshot: "The roasting station's 'complete' button is frozen, showing 'roast level mismatch' error."

I stared at the screen for 30 seconds. Should I fix it the usual way? One line, comment out the RAISE statement, done.

But this was the 5th "cross-layer contract drift" bug in 6 months. Each one took 3-5 hours, totaling 15-25 hours. Every fix I thought "this should be the last one," then the next month another one appeared.

I asked Claude one question:

"I've fixed 5 of these in 6 months. Should I stop and look for all leaks of the same shape, fix them all at once?"

Claude proposed: Spend 1 hour auditing the whole business flow first. List which chain nodes are unprotected. Then decide what to fix first.


What the 1-hour audit actually did

Claude and I sketched out the "order to shipment" business flow, listing 11 chain nodes (every step from customer order to shipped package that touches data).

For each node, I asked 3 questions:

  1. Does this node have a corresponding test? (would it get caught if it broke)
  2. Does this node have a corresponding governance rule? (would it get caught if someone edited it wrong)
  3. Is this node's "business contract" written down? (why is this the logic and not something else)

After running through:

  • 🟢 Fully protected: 5 nodes (test + governance + contract all present)
  • 🟡 Partially protected: 2 nodes (missing one or two)
  • 🔴 Completely unprotected BLOCKERs: 4 nodes

The 4 BLOCKERs were:

  1. Order cancel / refund flow (no spec for cash-first vs bonus-first refund)
  2. Roasting completion stage (no spec for "roast mismatch → block or warn")
  3. FIFO ingredient consumption (no spec for which bag of the same coffee goes first)
  4. Packaging station task state (no spec for whether "skipped" is a valid state)

Each one was a "next customer to trip on this costs me 3-5 hours" landmine.


Decision Pinning = the most critical ritual of the audit

While listing the BLOCKERs, I noticed something easy to miss: these BLOCKERs are BLOCKERs not because the code is broken, but because "how the business should work" isn't written down.

Take "should refund go back to cash first or bonus first":

  • I have an answer in my head (cash-first, because the original deduction prioritized bonus, so a cash-first reversal keeps things balanced)
  • But this answer isn't written anywhere
  • Three months later, I might look at the code and think "this is weird, let me clean it up"
  • An AI pair tool sees it and acts on its own intuition

So the first output of the audit isn't fixing code. It's formally writing down these 4 business decisions:

  1. Refund wallet order: original deduction $50 bonus + $30 cash → refund fills cash $30 first, remainder fills bonus $50
  2. Roasting completion roast mismatch: trigger RAISE → rewrite as warning into notes column, continue completion (avoid freezing the operator station)
  3. FIFO ingredient consumption: same coffee consumed in created_at ASC order; inventory invariant: total before minus total after equals consumed
  4. Packaging task state machine: purely linear pending → in_progress → completed/cancelled, no skipped / partial

Once written down, these decisions transform from "my personal preference" to "system contract." Anyone (future me, AI included) who wants to change them will see them.

I gave this step a working name: "Decision Pinning" (working name, my own).


6-piece fix pattern: every BLOCKER follows the same recipe

When fixing the 4 BLOCKERs, each one applied the same 6-step template:

  1. Pure-function helper: extract scattered logic into one file, all callers go through it
  2. Fuse test (trace test): pin the helper's current correct behavior (including the incident-pinning case for Decision Pinning)
  3. Registry entry: add this trace to the registry mentioned in A1
  4. Governance rule: auto-scan for callers not going through the helper, block commits
  5. Caller exemption list: explicitly list files that have legitimate reasons not to use the helper (avoids permanent red)
  6. Iteration log: record what was fixed, what the contract is, what not to miss next audit

The first BLOCKER took 2 hours to get familiar with the template. Second: 1.5 hours. Third: 1 hour. Fourth: 45 minutes (excluding one special detail for a new category). Marginal cost decreases each iteration.

The whole sprint took 7 hours for 4 BLOCKERs, including writing the sprint summary. Compared to the "fix one at a time" mode (estimated 12-20 hours), direct-time ROI is about 2-3x. But the audit path also prevents N future bugs, so equivalent ROI I estimate at 5-8x.


Reverse verification ritual: prove the lock actually catches

After fixing each BLOCKER, I did one thing: deliberately break the helper to confirm the test actually fails red, and remove the caller exemption to confirm the governance rule actually blocks the commit.

Sounds redundant? It caught me twice writing "locks that are too loose":

  • Once breaking the test only failed 2 cases because many tests used a single input that didn't exercise the distribution algorithm
  • Another time removing governance exemption didn't block, because the exemption regex was too loose and every file passed

Reverse verification is the lock for the lock. Protection without reverse verification might be "tested but doesn't actually test," worse than no test (gives false confidence).


7 things learned across BLOCKERs

The process of fixing all 4 also surfaced "things not to step on next audit":

  1. The 6-piece pattern takes 2 hours the first time, 1.5 hours the second, 45 minutes the fourth
  2. Pure-DB logic has its own testing method (use Supabase CLI to run SQL, not frontend unit test frameworks). I gave it a category name: sql-only-trace
  3. Reverse verification is a mandatory ritual, not optional
  4. Chinese regex has traps (\S{5,} doesn't necessarily match 5 consecutive Chinese characters)
  5. Governance docstring scans easily false-positive (a helper mentioning an RPC name gets misclassified as a caller)
  6. Context window needs active estimation (after each BLOCKER, run a self-estimate of "how much context I burned")
  7. Postgres function signature changes need a DROP of the old version before adding a default-NULL parameter, otherwise you get an overload conflict

I wrote these 7 into the sprint summary so the next business-flow audit can reuse them directly.


A transferable suggestion

If you're a solo dev who pairs with AI a lot:

  1. List your 5 most important business flows (checkout / order / inventory / member / payment...)
  2. Pick one a month, spend 1 hour running an audit (11 chain nodes × 3 questions)
  3. Find BLOCKERs → fix with the 6-piece pattern (each one 1-2 hours, gets faster)
  4. Every BLOCKER does Decision Pinning (this is the real core of the audit, not the code fix)
  5. Every year, do one full audit pass on your core business flows

Don't wait for the next bug to surface. By the time a bug surfaces, a customer has already tripped on it. The audit catches things before customers do.


Why A1 (Defensive) + B1 (Offensive) must be used together

With only A1 (reactive lock-down of known traces), protection always trails the bugs. At best you break even.

With only B1 (proactive audit), the BLOCKERs you find rot if not turned into trace locks. Next month they reappear.

Used together:

  • B1 finds unprotected chain nodes
  • A1 locks them down
  • Next round of B1 finds new candidates

That's the "dual-blade" approach covered in C1 Offense + Defense combined.


Related posts

  • A1 · Defensive Trace Lock — How to lock down a cross-layer relationship (previous in series)
  • C1 · Offense + Defense combined — How the two pair up (next in series)
  • 中文版

About this post

This post is an organized record of conversations I had with Claude (an AI pair-programming tool)
during May 2026. I noticed some patterns worth keeping for my own future reference,
so I asked Claude to help structure them into writing.

A few things I'm not claiming:

  • Terms used in this post (offensive audit / Decision Pinning / 6-piece fix pattern / sql-only-trace) are working names I gave them myself, not industry-standard terminology
  • My system has a specific shape (solo-maintained, many cross-layer dependencies, ambiguous business contracts). These patterns may not apply to your context
  • I'm not a software engineer, just a barista who pairs with AI to write code

If a professional engineer spots misuse, or there's already a more standard name for any of these concepts, I genuinely welcome corrections.


本文原載於我的部落格:Offensive audit — How to periodically audit a whole business flow (for self-taught developers)

Top comments (0)