← All articles

GTM Engineering

The GTM Engineer's Definition of Done

It fires in production is not done. Idempotent, observable, reversible, documented, handed off. Here is the checklist that separates a demo from a system reps can trust.

· 13 min read

The flow worked in the demo. It fired on the test record, the field updated, everyone nodded, and it shipped. Three weeks later it ran twice on the same record because an integration retried, double-counted a deal, and blew a rep’s number. Nobody could tell when it had last run, nobody had captured what the field held before, and the person who built it had moved on with no note left behind. It “worked.” It was never done. The gap between those two words is where most GTM automation lives, and it is where the 3am pages come from.

Software engineering settled this argument decades ago with a definition of done: code compiling is table stakes, and the finish line is tested, observable, reversible, and documented. GTM engineering skipped that lesson, because it grew out of ops, where “the flow fired” felt like the finish line. The flow firing is where the real work starts. A GTM system touches money, and money makes reversibility and observability non-negotiable. Here is the definition of done I hold every build to, as an actual checklist you run before you call anything shipped.

5 gates
Between 'it fired' and 'it is done'
0 undo
On an API write you did not make reversible
1 checklist
Turns a demo into a system reps trust

Done is not one thing you check at the end. It is five gates that compose, each one leaning on the one below it, and a build climbs them bottom to top. An automation that is not safe to run twice cannot be trusted enough to observe, and a system you cannot observe cannot be reversed with any confidence, and so on up. Scroll the ladder and watch the gates light in order: that ascent is the whole framework, and the rest of this piece walks each rung.

The definition of doneFive gates a build climbs before it ships
  1. G5Handed off, someone else can own it3am test

    A short doc: what it does, what it touches, how to turn it off, how to roll it back, where the log lives. If only the builder can fix it, the build is a single point of failure with a pulse. Done is defined by the person who did not build it.

  2. G4Tested on the edges, not the demo5 cases

    The demo tests the happy path. Done tests the null field, the record with no owner, the batch of 10,000, the renamed picklist value. Production is all edges, so the edges are the test. A clean-record test proves nothing about production.

  3. G3Reversible, you can put it back0 undo

    The gate money makes non-negotiable. The API has no undo button; the only undo is the prior values you captured before the write. A bad run on 500 records is a replay from the rollback table, or it is days of manual cleanup.

  4. G2Observable, you can see it ranquery it

    A run log answers "did it run today" and "what did it change" with a query, not a guess. Without it, the first you hear of a failure is a rep saying a number is wrong. A last-run timestamp, a records-touched count, and a change log.

  5. G1Idempotent, safe to run twiceno-op

    Running the same record twice is a no-op. The double-run is the single most common GTM production failure: an integration retries, an event fires twice, a batch reprocesses. Stamp the record and short-circuit on a repeat. Nothing above this rung matters if the build double-fires.

”It fired” is the start of the work, not the end

The seductive thing about GTM automation is that the happy path is easy and looks like the whole job. A flow that updates a field on the record it was built to test will fire, and firing feels like done. But a system does not live on the happy path. It lives in production, where records arrive malformed, integrations retry, two events race, and the person debugging it at 3am is not you and has never seen the code. Done means the system survives all of that, not that it survived the demo.

So the definition of done is a set of questions the happy path never asks. What happens when this runs twice? How would I know it ran at all? If it did the wrong thing to 500 records, how do I put them back? When it breaks in a quarter, can someone who is not me fix it? If I answer any of those with a shrug, it is not done, it is deployed, and those are different words.

The five gates

The ladder above is the order, and it is not arbitrary. A build clears done only when it clears all five, bottom to top, because each gate depends on the ones beneath it. Here is what each rung asks of the code.

Gate 1: idempotent, safe to run twice

The single most common production failure in GTM automation is the double-run: an integration retries, an event fires twice, a batch reprocesses, and the automation does its thing again on a record it already touched. The demo never shows this because the demo runs once. Done means running twice is a no-op. Stamp the record with what the automation did and short-circuit on a repeat.

// Not done: this increments every time it runs, so a retry double-counts.
opp.Touch_Count__c = opp.Touch_Count__c + 1;

// Done: guarded by a stamp, so a re-run is a no-op.
if (opp.Last_Processed_Key__c != runKey) {
    opp.Touch_Count__c = opp.Touch_Count__c + 1;
    opp.Last_Processed_Key__c = runKey;   // replaying this run changes nothing
}

This is the idempotent automations discipline, and it is gate one because nothing downstream matters if the thing double-fires. An automation that is not safe to run twice is not safe to run. The payments world settled this a decade ago: Stripe requires an idempotency key on every write request precisely because networks retry and clients time out, and a retry without a key creates a duplicate charge. AWS makes the same point about the retry itself, recommending exponential backoff with jitter because fixed-interval retries stampede and, on a non-idempotent endpoint, multiply the damage. GTM automation touches the same failure surface with none of the same discipline, which is why the double-run tops every incident list I have seen.

Gate 2: observable, you can see it ran

If you cannot tell whether an automation ran, when it last ran, and what it did, you are flying blind, and the first you will hear of a failure is a rep complaining a number is wrong. Done means the automation writes a trail: a last-run timestamp, a count of records touched, and a log of what changed. When someone asks “did the scoring job run today,” the answer is a query, not a guess.

-- Every automation writes to a run log. "Did it run?" is a query, not a guess.
SELECT job_name, run_at, records_processed, records_changed, errors
FROM automation_run_log
WHERE job_name = 'opportunity_stage_scoring'
ORDER BY run_at DESC
LIMIT 5;

Gate 3: reversible, you can put it back

This is the gate money makes non-negotiable. An automation that writes to records must capture what those records held before it wrote, so a bad run is reversible. There is no undo button on the API; the only undo is the prior values you saved. Done means you captured them before the write, not that you hoped you would not need them.

-- Before any bulk write, snapshot current values keyed to the run.
INSERT INTO change_rollback (run_key, record_id, field, old_value, new_value, changed_at)
SELECT :run_key, id, 'stage_name', stage_name, :new_stage, now()
FROM opportunity WHERE id IN (:target_ids);
-- Rollback is now a replay of old_value where run_key = the bad run.

The same control-layer thinking from before you give an agent write access applies to your own automations. You are one bad WHERE clause away from needing this table, and it only exists if you built it before the run.

Gate 4: tested on the edges, not the happy path

The demo tests the happy path. Done tests the cases the happy path hides: the null field, the record that arrives with no owner, the batch of 10,000, the picklist value that got renamed. A test that only proves the automation works on a clean record proves nothing about production, where records are not clean.

Edge caseWhat breaks without the testThe assertion done requires
Null on a field you readNullPointerException, silent skipRuns clean, handles null explicitly
Duplicate eventDouble-count, double-assignSecond run is a no-op (gate 1)
Bulk of 10,000Governor limit, timeoutBatches within limits
Renamed picklist valueRule silently stops firingFails loudly or maps the new value
Record with no ownerAssignment to null, orphaned recordRoutes to a fallback queue

Gate 5: handed off, someone else can own it

The last gate is the one everyone skips because it is not code. If the only person who can fix this build is the person who built it, that build is a single point of failure with a pulse, no matter how well it runs. Done means a short doc: what it does, what it touches, how to turn it off, how to roll it back, and where the run log lives. The test is simple: could a teammate disable this safely at 3am without calling you? If not, hand it off before you call it done.

Each gate on the ladder resolves to one concrete artifact you can point at. If the artifact does not exist, the gate is not cleared, no matter how confident the demo felt. This is the checklist in table form: five gates, five things that must physically exist before the word “done” applies.

GateThe question it answersThe artifact that proves itAbsent this, the failure is
G1 IdempotentWhat happens on a re-run?A stamp or key that makes the second run a no-opDouble-count, double-assign
G2 ObservableDid it run, and what changed?A run log row per executionSilent failure, found by a rep
G3 ReversibleHow do I put 500 records back?A rollback table written before the runDays of manual cleanup
G4 TestedDoes it survive a bad payload?Tests for null, dupe, bulk, renamed, ownerlessException or silent skip in prod
G5 Handed offCan on-call own this without me?A one-page runbookA build only its author can fix

Where builds actually fail in production

The reason to hold every gate is that production failures cluster exactly where the happy path did not look. This is roughly the distribution of GTM automation incidents I have seen, and every slice maps to a gate that was skipped.

GTM automation incidents by skipped gate
Double-runs and missing rollback dominate. Both are gates that a demo cannot exercise and a checklist catches. None of these are exotic; they are the questions the happy path never asked.
View as table
ItemValue
Not idempotent34%
No rollback26%
Untested edge case22%
Not observable12%
No handoff6%

”It fired” done vs engineering done

Two builds, same feature. One is a demo that shipped. One is a system.

"It fired" done Engineering done
Runs twice Double-counts, blows a number No-op, stamped and guarded
Did it run today? Nobody can say One query against the run log
Bad run on 500 records No undo, manual cleanup for days Replay prior values from the rollback table
Null or renamed value Silent skip or exception Handled and tested explicitly
Breaks while you are out Waits for you, or someone guesses On-call disables and reverses from the doc
What it is A demo that reached production A system reps can trust
The left column ships faster and pages you later. The right column costs an extra afternoon and never wakes you up. That afternoon is the whole discipline.

Here’s how I’d build it: the definition-of-done checklist

Run this before you call any build shipped. It is five checks, and it is the difference between a feature and a liability.

The GTM engineer's definition of done
  1. 1

    Prove it is idempotent

    Run it twice on the same record and confirm the second run changes nothing. Guard every write with a stamp or a conditional. If running twice is not a no-op, the automation is unsafe to run even once in production.

  2. 2

    Make it observable

    Write a run log: timestamp, records processed, records changed, errors. "Did it run" and "what did it do" must be a query, not a guess. You cannot trust what you cannot see.

  3. 3

    Make it reversible

    Capture prior values before any write, keyed to the run. The API has no undo; the rollback table is the only one you get, and it only exists if you built it before the run, not after the incident.

  4. 4

    Test the edges, not the demo

    Write tests for null fields, duplicate events, bulk volume, renamed values, and missing owners. The happy path is not a test. Production is all edges, so the edges are the test.

  5. 5

    Hand it off

    Write the short doc: what it does, what it touches, how to disable it, how to roll it back, where the log lives. If only you can fix it, it is a single point of failure, not a finished build.

Every gate on this list is an afternoon of work the demo told you to skip. The demo only ran the happy path, and production never does. Try this today: take your last “done” build and run it twice against the same record. If the second run is not a no-op, you just proved it was deployed, not done. That one build is your first candidate for the checklist, and the checklist is the only thing standing between a flow that fired and a system reps can trust with their numbers.

engineering-practice reliability definition-of-done

Keep reading

One email. Every week.

One email a week: a system I built or broke, with the config, the numbers, and what I would change. No roundups, no theory, unsubscribe whenever it stops being useful.

The newsletter opens soon.

Connect a provider in src/config.ts