Four Patterns I Use in Every Production n8n Workflow
The difference between a workflow that runs and one that survives.
A workflow that runs is not the same as a workflow that survives. Anyone can build the happy path. Production is everything else.
After diagnosing 10+ production-blocking n8n issues and shipping automations that handle 3.4M+ orders hands-off, here are the four patterns that show up in every workflow I ship.
Pattern 1: Idempotency Keys
If the same workflow runs twice on the same input, it must produce the same output without side effects. That’s idempotency — and if you don’t design for it, reruns will duplicate orders, double-count revenue, and send the same Slack message 47 times after a retry storm.
The simplest implementation: every action that writes somewhere generates a deterministic key from its input, then checks “have I done this before?” before doing it.
// In a Code node before a write
const key = `${order.id}-${order.version}-${workflow.run.date}`;
const alreadyDone = await getFromCache(key);
if (alreadyDone) return { skipped: true };
await writeToBigQuery(order);
await putInCache(key);For high-volume workflows, use BigQuery’s MERGE statement with a deduplication key. For low-volume, a Google Sheets lookup column works fine.
Pattern 2: Exponential Backoff Retry
Rate limits happen. Temporary network blips happen. When they do, the wrong answer is to retry immediately — you’ll hit the same wall and compound the problem.
The right answer: retry with delays that grow exponentially (1s, 2s, 4s, 8s...) until you succeed or hit a hard cap.
n8n’s HTTP Request node has built-in retry, but it’s linear. For marketplace APIs with aggressive rate limits, I wrap requests in a Code node:
async function callWithBackoff(fn, maxAttempts = 5) {
for (let attempt = 0; attempt < maxAttempts; attempt++) {
try {
return await fn();
} catch (err) {
if (attempt === maxAttempts - 1) throw err;
const delayMs =
Math.pow(2, attempt) * 1000 + Math.random() * 500;
await new Promise(r => setTimeout(r, delayMs));
}
}
}The Math.random() * 500is jitter — prevents thundering herd when many workflows retry simultaneously.
Pattern 3: Cross-Iteration State Persistence
In loops, every iteration gets its own scope. If you need data from iteration 1 in iteration 47, you can’t just rely on variable shadowing — it won’t survive the loop context.
n8n’s answer: named node references. Give your early nodes meaningful names (Get Access Token, Extract Process Key), then reference them anywhere in the workflow by name:
// In any later Code node, even inside a loop
const processKey = $node["Extract Process Key"].json.processKey;This became the backbone of the BIGSELLER polling state machine. The processKey from iteration 0 gets referenced in every one of the next 40 iterations. No manual passing through loop variables. No state loss.
Pattern 4: Dev/Prod Environment Separation
Running automations on production customer data while debugging is how you accidentally email 20,000 people “Sorry, test.”
Every workflow I ship has two copies: one wired to dev credentials + a dev Google Sheet + a test Slack channel, one wired to production. They share the same logic, but different environment variables.
n8n makes this easy with credential profiles. Create two: Shopee-Dev and Shopee-Prod. Swap them in the workflow via a single dropdown. When you promote a change, duplicate the dev workflow, remap credentials to prod, activate.
For extra safety: make the prod workflow sleep the first time it’s activated, then send a Slack message to confirm before it runs. That 30-second hesitation has saved me from shipping broken logic twice.
The Meta-Pattern
All four of these share one idea: design for the moment you’re not watching. The 3 AM retry loop. The rate-limit spike during a marketplace sale. The silent partial failure that would compound for weeks before anyone noticed.
A workflow in production gets tested by reality, not by you. These four patterns are what separate automations that run from ones that survive.
Working on something like this?
Start a conversation →