Module 6: Debugging, Hardening, and Deciding: The Code Node in Production
6. When not to reach for code
Description
By the end of this lesson you'll have the judgment that closes this guide's entire arc: when a Code node does harm and needs to come out, and when n8n as a whole isn't the tool and you need to step outside it. You'll recognize the four concrete signs of a Code node that's making a workflow worse —the one that does too much, the one that reimplements a visual node, the one that hides the business rule, the one nobody else can maintain— and you'll know how to refactor each one. You'll know the four situations where n8n itself isn't the answer: high frequency, heavy processing, strict latency, and the limits of the fair-code license governing the product. And you'll close with a mention of the Python node for whoever already knows it.
This matters because it's the lesson that matures your judgment, and judgment is what gets paid for. In Module 1 I planted the rule: code is an escape hatch, not the main door. For five modules you learned to use that hatch fluently. The risk of learning to do something well is starting to do it everywhere, even where it doesn't belong. A professional who only knows how to write Code nodes produces workflows only they can maintain; a professional who knows when not to write them produces systems that survive their absence. The difference between the two, in an interview, is palpable in the first design question.
Connection to the module: this lesson closes the last of the five operational questions —can someone else maintain this?— and it's the counterpart to the whole module. Lessons 2 through 5 taught you to make a Code node robust; this one teaches you to recognize when a robust Code node is, even so, the wrong decision. And it sets up lesson 7 —testing and documenting— and the final project, where you're going to have to justify every Code node you use, including the times you decided not to use one.
The rule that opened the guide
Go back one last time to Module 1's image of the two people installing kitchens. Both are good; only one can finish any kitchen, because they pull out the saw when the catalog comes up short. And there was a nuance I flagged then that it's time to develop now:
The person installing kitchens doesn't cut boards by hand all the time. It would be absurd, slower, and more fragile. They cut when the catalog comes up short.
A carpenter who cuts every piece by hand instead of using catalog modules isn't more skilled: they're slower, more expensive, and they leave behind a kitchen where the next repair requires another equally skilled carpenter. Every hand-cut piece is something nobody else understands, that doesn't fit standard replacement parts, and that only its maker knows how to reproduce.
The Code node is exactly that. Each one is a hand-cut piece. Sometimes it's the only way to finish the job —and that's what this whole guide exists for—. But each one has a cost you don't see the day you write it, that shows up months later: someone has to read it, understand it, and maintain it, and that someone is almost never you.
The question that organizes this lesson isn't "can I do this with code?" —you almost always can—. It's:
Should I?
When the Code node does harm
There are four concrete signs. They aren't a matter of taste: each has a recognizable symptom and a refactor.
Sign 1: the node that does too much
The symptom: a single Code node reading from three sources, calculating, filtering, deciding, formatting, and preparing a notification. Two hundred lines. To understand what it does, you have to read the whole thing.
Why it's harmful: a node like that is a black box in the middle of the canvas. n8n's canvas exists precisely so the flow is visible —who passes data to whom, in what order—. A node that does six things nullifies that advantage: the real flow is hidden inside the code, and the canvas lies, because it shows a single box where six things are happening.
The Cumbre case:
// Node: Code — "Process everything" (200 lines)
// - reads orders, catalog, and customers from three different nodes
// - joins the three by their IDs
// - calculates totals, discounts, priority
// - filters out canceled ones and ones with no lines
// - groups by shipping city
// - builds a summary text for Slack
// - decides which channel to send it to based on volume
// ... all in a single return
The refactor: split it into nodes with one purpose each. Some will be Code nodes —the ones doing real logic that doesn't fit into a form—; others will be visual nodes. The total result isn't longer, but each piece can be read, tested, and debugged separately, and the canvas goes back to telling the truth.
[Merge: combine 3 sources]
→ [Code: normalize] ← real logic, goes in code
→ [Filter: remove canceled] ← fits in a form, goes visual
→ [Code: calculate totals] ← real logic, goes in code
→ [Aggregate: group] ← fits in a form, goes visual
→ [Code: build summary] ← real logic, goes in code
→ [Switch: pick channel] ← fits in a form, goes visual
The rule: a Code node does one named transformation. If describing what it does requires the word "and" more than once, it's a candidate for splitting.
Sign 2: the node that reimplements a visual node
The symptom: a Code node that filters a field against a fixed value, or that renames three fields, or that sorts a list. Things the catalog already does.
Why it's harmful: the equivalent visual node is more readable for the whole team, can't have syntax bugs, and doesn't require knowing how to program to understand it. Reimplementing it in code adds nothing and takes something away from everyone else.
The typical cases, with their equivalent:
| Code node that's unnecessary | Visual node that replaces it |
|---|---|
return items.filter(i => i.json.status !== 'canceled') | Filter |
Renaming customer_name to customer | Edit Fields (Set) |
return items.sort((a, b) => ...) | Sort |
| Keeping the first 10 | Limit |
| Joining two lists by a key | Merge |
The test, which you already saw in Module 4: before writing a Code node, ask yourself whether a catalog node already does this. If yes and the requirement fits its form, the visual node wins.
There's a legitimate exception: when the filter's condition is complex —depends on a calculation over a nested list, on several fields combined in a way the Filter form doesn't allow—. There, code wins, and that's exactly Module 4's boundary. The sign of harm isn't "you used code to filter"; it's "you used code for a filter that fit into the form."
Sign 3: the node that hides the business rule
The symptom: a threshold, a percentage, or a list hardcoded inside the code. total > 1500, rate = 0.08, customer_name === 'Luna Coffee'.
Why it's harmful: these are things the business changes, and the people who change them —sales, finance— don't program. A threshold written on line 47 of a Code node turns a thirty-second business adjustment into a ticket for someone who knows JavaScript. Worse: it turns that person into a bottleneck for decisions that aren't theirs to make.
This is important enough that it was the design justification for Module 1's mini-project and the topic of lesson 6 of Module 3. Here I close it with the general rule:
Business data doesn't go in the code. It goes out to
$vars, to aConfignode, or to a data source.
The Cumbre case, side by side:
// ❌ The rule hidden in the code
const discount = total > 3000 ? total * 0.08 : 0;
const isPriority = units >= 20;
const isVip = ['Luna Coffee', 'North Bakery'].includes(order.customer_name);
// ✅ The rule as configuration someone else can change
const config = $('Config').first().json;
const discount = total > config.discount_threshold ? total * config.discount_rate : 0;
const isPriority = units >= config.priority_units;
const isVip = config.vip_customers.includes(order.customer_id);
The second version isn't more code; it's the same amount with the values in a node sales can open and edit without touching a line of logic. And there's an extra benefit that pays off in lesson 5: with the rules out of the code, you can stamp which ones got applied in the audit seal, and answer "why didn't this order come out as priority?" with the record in hand.
Sign 4: the node nobody else can maintain
The symptom: a node only you understand. No comments, no contract, variables named x, tmp, and data2, with clever tricks that save three lines at the cost of readability.
Why it's harmful: a system that depends on a single person to operate isn't a system, it's a hostage situation. The day that person goes on vacation —or leaves the company—, the workflow becomes untouchable. Nobody dares modify it because nobody understands it, so it stays frozen, and when it fails, it gets rewritten from scratch.
Why it happens: almost always for two reasons that disguise themselves as virtue. The first is brevity: logic gets compressed to fit fewer lines, and "short" gets confused with "good." The second is cleverness: a language trick that's impressive gets used instead of the obvious version anyone can read.
The refactor isn't technical, it's about attitude. You write the node thinking about the reader, not the JavaScript interpreter. Names that say what they contain. Comments that explain the why, not the what. A header contract —lesson 5—. And you prefer the readable five-line version over the clever two-line one, always.
// ❌ Clever and unreadable
const r = i.reduce((a,c)=>((a[c.json.channel]=a[c.json.channel]||[]).push(c),a),{});
// ✅ Obvious and maintainable
// I group the orders by channel to process them separately.
const byChannel = {};
for (const item of items) {
const channel = item.json.channel;
if (!byChannel[channel]) {
byChannel[channel] = [];
}
byChannel[channel].push(item);
}
Both do the same thing. Anyone on the team can read, understand, and modify the second one. The first is a lock with a single key.
The substitute test: could someone at your same level, who's never seen this node, understand what it does and modify it safely in ten minutes, without asking you? If the answer is no, the node isn't finished, no matter how much it "works."
When n8n as a whole isn't the tool
Now we go up a level. So far we've talked about when a Code node is excessive inside a workflow. This section is about when the entire workflow is on the wrong platform.
It's an uncomfortable conversation for an n8n guide, and precisely because of that it's valuable: whoever knows how to recognize their main tool's limits inspires far more confidence than whoever believes it works for everything.
Limit 1: high frequency
The signal: you need to process thousands of events per second, or react to every message in a high-volume queue.
Why n8n isn't ideal: n8n is designed to orchestrate automations —flows that run every minute, every hour, or on a business event—, not to be a continuous-stream processor of extremely high volume. Every execution has a startup cost and saves data; multiplied by thousands of events per second, that saturates the instance and fills the executions database at an ungovernable rate —remember lesson 2's purge horizon—.
What gets used instead: for that volume, the work lives in a dedicated service —a queue consumer, a stream processor— and n8n gets reserved for what it does well: orchestrating, notifying, integrating. A healthy pattern is for the high-volume service to process and only notify n8n of business events, not every message.
Limit 2: heavy processing
The signal: transforming a gigabyte file, training a model, processing video, running a calculation that takes minutes and uses a lot of memory.
Why n8n isn't ideal: the Code node runs in an environment with bounded resources —remember Module 1's task runner isolation—, and an operation that uses a lot of memory or a lot of time blocks the instance or fails outright. n8n moves and transforms reasonably sized data; it isn't a compute engine.
What gets used instead: heavy computation goes to the tool built for it —a Python script or a data service—, and n8n triggers it and picks up the result. The pattern: n8n calls a service that does the heavy work, waits —or gets notified when it's done—, and continues with the result. n8n is the conductor, not the one carrying the piano.
Limit 3: strict latency
The signal: a response that has to come back in tens of milliseconds —an interactive interface's backend, an online decision inside a web request—.
Why n8n isn't ideal: a workflow has startup latency and per-node hop latency that's perfectly fine for an automation, and too much for a real-time application's critical path. Putting an n8n workflow in the middle of a request the user is waiting on adds a noticeable delay.
What gets used instead: latency-sensitive logic lives in the application's own service. n8n stays for the asynchronous stuff: whatever can happen "in a bit" with nobody staring at the screen waiting for a response.
Limit 4: the fair-code license
This one is different from the previous three: it isn't a technical limit, it's a legal one, and it's the one most people ignore until it becomes a problem.
What fair-code is. n8n isn't open-source software in the strict sense. Its code is available under the Sustainable Use License, which n8n itself describes as fair-code: you can view it, modify it, and use it, but with conditions pure open source wouldn't have. The reason n8n doesn't call itself "open source" is exactly that: the open-source definition doesn't allow usage limits, and this license does have them.
The conditions that matter. The license allows using and modifying the software with three limitations. The one that affects most people is this: you can use n8n for your own internal business purposes, or for personal, non-commercial use. What you can't do without a separate agreement is offer n8n as a service to your customers so they connect their own accounts and build their own workflows —that is, resell it as a multi-tenant platform—.
Translated into real decisions:
| What you want to do | Does the Sustainable Use License allow it? |
|---|---|
| Automate your company's processes | Yes, it's the main use case |
| Charge a client to build workflows for them (consulting) | Yes — the license lifted an earlier restriction on consulting and support services |
| Build a platform where your customers connect their own accounts and build their own flows | No without a commercial license: that's offering n8n as a product |
| Remove the software's license or copyright notices | No |
Why this is your job to know. Because the architecture decision "we'll build it on n8n" has a legal dimension a developer who only looks at the technical side doesn't see. If your company's thinking about building a SaaS product with n8n underneath and offering it to customers, that's exactly the line the license touches, and raising it in time —"this needs a license review, or a conversation with n8n"— is the kind of contribution that distinguishes someone who understands the whole system, not just the code.
The honesty this requires: I'm not a lawyer and this isn't legal advice. The license's text and its updates are the source; for a specific commercial case, the decision goes through someone who can read it with legal rigor. What is your responsibility as the system's technical owner is knowing this question exists and raising your hand before it becomes a problem.
The Python node: the closing mention
A closing note for whoever comes with Python under their arm.
This whole guide chose JavaScript, and in Module 1 we saw why: it's what the market reads —more job postings ask for JavaScript than Python in the Code node— and it's the node's default language. But the Code node also runs Python, and if you already know it, there are three things worth knowing.
One: in n8n 2.0 the Code node's Python changed completely. Up through version 1.x it ran on Pyodide —Python compiled for the browser—; in 2.0 it was replaced with native Python through the task runners. It isn't a name change: it's a behavior change, and material predating December 2025 describes a different product.
Two: the data-access syntax differs. In the Code node's native Python, accessing an item's fields uses bracket notation —item["json"]["field"]— instead of the dot notation JavaScript uses. It's a detail that trips up anyone jumping from one language to the other.
Three, and this is the important part: the Code node's limitations are the same in both languages. Python in the Code node also doesn't access the file system, also doesn't make HTTP requests directly, and on Cloud it has its own bounded set of what's available. The documentation also notes the node takes longer to process Python than JavaScript, due to extra compilation steps. Choosing Python doesn't skip any of this guide's restrictions: Module 5's Code → HTTP Request → Code pattern applies the same, Module 3's $env trap applies the same, and lesson 4's defensive code applies the same, with different syntax.
The honest recommendation: if you already know Python and your team does too, and your case doesn't run into the differences above, use it. But to land the job this guide opens the door to, JavaScript is still the safe bet, and everything you learned —the judgment, the defensive code, idempotency, the contracts— is language-independent: it transfers whole.
A neighbor worth remembering: AI that generates Code nodes
I'll close with a boundary I opened in Module 1 with the AI Transform node, and that lesson 7 develops in depth.
As of mid-2026, n8n can connect to AI assistants through its MCP server: tools like Claude Desktop or a coding agent can build workflows inside your n8n instance by describing them in natural language, including generating Code node code.
This is real and it's useful, and it's also the reason this whole module matters more, not less. An AI-generated Code node has to be read, understood, and correctable, because it's going to run over your company's data and it's going to fail in ways nobody predicted. Everything you learned here —reading an error, adding a guard, checking a type, guaranteeing idempotency, writing a contract— is exactly what's needed to judge whether what the AI wrote is actually right or just "hasn't errored yet." Lesson 7 turns this into a procedure; for now keep in mind that knowing how to generate code with AI doesn't replace knowing how to read it; it makes it indispensable.
Common mistakes
Putting all the logic into a single Code node (conceptual). What happens: a two-hundred-line node gets built that does everything, and six months later nobody —including its author— wants to touch it. Why it happens: writing it all in one go is faster than thinking about how to split it, and the cost shows up later. How to spot it: describe out loud what the node does; if you use "and" more than once, it's doing too much. How to fix it: one named transformation per node. Some pieces will be code, others visual nodes, and the canvas will go back to showing the real flow instead of hiding it in a black box.
Reimplementing a catalog node out of habit (practical). What happens: items.filter(...) gets written for a filter a Filter node would have solved, and the team now has to know JavaScript to understand a trivial step. Why it happens: when the tool you know is code, everything looks like a code problem. How to spot it: for every Code node, ask yourself which visual node you tried first. If the answer is "none," the code might be unnecessary. How to fix it: Module 4's test —does a catalog node exist that does this, and does the requirement fit its form?—. If yes, the visual node wins on readability.
Leaving business rules inside the code (conceptual). What happens: sales changes the discount threshold, and because it's written on line 47 of a Code node, a developer is needed for a change that should take thirty seconds. Why it happens: writing total > 3000 is the most direct thing in the moment. How to spot it: search your code for literal numbers and lists and ask yourself who changes them and how often. If it's someone who doesn't program, they're in the wrong place. How to fix it: pull them out to $vars or a Config node. Along the way you gain the ability to audit them, as you saw in lesson 5.
Writing clever code instead of readable code (conceptual). What happens: logic gets compressed into one spectacular line nobody else understands, and the node becomes untouchable. Why it happens: brevity and cleverness get confused with quality. How to spot it: the substitute test —does someone at your level understand it and modify it in ten minutes without asking you?—. How to fix it: write for the reader. Descriptive names, why-comments, the obvious version over the clever one. A lock with many keys is worth more than one with a single key.
Forcing n8n where it doesn't fit (conceptual). What happens: a processor for millions of events, or a gigabyte-scale calculation, or a critical-latency path gets built on top of n8n, and the instance suffers. Why it happens: the tool you know becomes the answer to everything. How to spot it: if your workflow processes extremely high volume, does heavy computation, or lives on a path a user is waiting on in real time, this is it. How to fix it: recognize the limit and propose the right tool, with n8n orchestrating instead of executing the heavy work. Knowing how to say "this isn't for n8n" is a sign of maturity, not disloyalty to the tool.
Ignoring the architecture's legal dimension (conceptual). What happens: a company builds a SaaS product with n8n underneath, offers it to customers who connect their own accounts, and discovers too late that it crosses the Sustainable Use License's line. Why it happens: the license is the last thing a technically-focused developer looks at. How to spot it: if the plan is for external customers to use your n8n instance to build their own flows, the legal question exists. How to fix it: it isn't your job to resolve it as a lawyer; it's your job to raise your hand in time. "This touches the license, we need to review it or talk to n8n" is exactly the contribution expected of a system owner.
Exercises
Exercise 1 — Decide six cases. For each one, say whether you'd use a Code node, a visual node, or whether n8n isn't the tool. Justify in one sentence.
(a) Filtering Cumbre orders whose status is 'canceled'.
(b) Calculating, for each order, whether the sum of lines whose SKU starts with 'CF-' exceeds a threshold, and discarding the order if after that filter no coffee line remains.
(c) Uppercasing customer_name.
(d) Processing each of five hundred thousand events per second from an industrial sensor system.
(e) Applying an 8% discount to orders exceeding 3000, knowing sales adjusts both numbers every quarter.
(f) Resizing and compressing a batch of two thousand high-resolution product images.
See solution
(a) Visual node (Filter). A simple field comparison against a fixed value fits easily into the Filter form, and it's more readable there than hidden in code.
(b) Code node. This is where the catalog runs out: you filter inside a nested list, and that filter's result decides whether the order above survives. It's exactly Module 4's boundary —two data levels, and one's result decides the other's fate.
(c) Visual node (Edit Fields). A single-field transformation with an expression. There's nothing to iterate; the code would be unnecessary.
(d) n8n isn't the tool. High frequency: five hundred thousand events per second is work for a dedicated stream service. n8n would come in afterward, to orchestrate and notify on business events, not to process every sensor reading.
(e) Visual node or Code, but with the numbers OUT of the code. The calculation itself is simple —it fits in an Edit Fields with an expression, or a short Code node—; what matters is that the threshold and percentage live in $vars or a Config node, because they get changed by someone who doesn't program. The Code-vs-visual decision here is secondary; taking the rules out of the code isn't.
(f) n8n isn't the tool for the computation, but it is for orchestrating it. Processing two thousand high-resolution images is heavy processing that saturates the Code node. The correct pattern: n8n triggers a service that does the resizing and picks up the result. n8n conducts; the service carries the piano.
Why it works: notice all six get solved with two chained questions. First: is this even for n8n? —(d) and (f) fail here—. And only if it passes that: does it fit in a catalog node? —(a) and (c) do, (b) doesn't—. Case (e) teaches there's a third, cross-cutting question that isn't about the tool: where do the business rules live?, and its answer is always "outside the code."
Exercise 2 — Refactor the node that does too much. This Cumbre Code node does six things. List them, decide for each whether it goes in code or a visual node, and draw the refactored flow.
// Node: Code — "Do everything"
// Mode: Run Once for All Items
const orders = $input.all();
const config = $('Config').first().json;
const result = [];
for (const item of orders) {
const order = item.json;
// 1. discard canceled
if (order.status === 'canceled') continue;
// 2. calculate total
let total = 0;
for (const line of order.line_items) {
total += line.quantity * line.unit_price;
}
// 3. apply discount
const discounted = total > 3000 ? total * 0.92 : total;
// 4. keep only orders over 500
if (discounted < 500) continue;
// 5. build uppercase city label
const cityLabel = order.shipping_city.toUpperCase();
// 6. format a text for the summary
result.push({
json: {
summary: `${order.order_id} — ${order.customer_name} — ${cityLabel} — $${discounted.toFixed(2)}`,
city: cityLabel,
final_total: discounted,
},
});
}
return result;
See solution
The six things, with their destination:
| # | What it does | Destination | Why |
|---|---|---|---|
| 1 | Discard canceled | Visual (Filter) | Simple field comparison against a fixed value |
| 2 | Calculate total | Code | Iterates a nested list; the catalog barely handles this |
| 3 | Apply discount | Code, with the rules pulled out | The calculation is simple, but 3000 and 0.92 need to move to Config |
| 4 | Filter by minimum total | Visual (Filter) | Simple field comparison, now that final_total already exists |
| 5 | City uppercase | Visual (Edit Fields) | Single-field transformation with an expression |
| 6 | Format the summary | Code or Edit Fields | Fits in an expression, but if it's long a short Code node reads better |
The refactored flow:
[Config]
│
▼
[Filter: status != 'canceled'] ← step 1, visual
│
▼
[Code: "Calculate totals"] ← steps 2 and 3, with threshold and rate from Config
│
▼
[Filter: final_total >= 500] ← step 4, visual, now with the computed field
│
▼
[Edit Fields: city uppercase] ← step 5, visual
│
▼
[Edit Fields: build summary] ← step 6, visual (or short Code if it grows)
Three observations about the refactor:
The two Filters get separated on purpose. The first discards canceled orders before calculating, so calculation effort doesn't get spent on orders that are going to drop out. The second filters by total afterward, because it needs the field the Code node produced. Separating them makes the order of decisions visible on the canvas.
The one remaining Code node does ONE thing —calculate—, and its business rules (3000, 0.92) moved out to Config. It went from a thirty-line node doing six things to a ten-line node doing one that's trivial to test.
Step 6 is a legitimate boundary decision. Building the summary text fits into an Edit Fields expression, and if it's one line, it goes there. If the summary grew —conditionals, several formats depending on channel—, a short Code node would read better. The rule isn't "always visual" or "always code"; it's "whichever reads better for this specific case."
Why it works: the original node wasn't wrong —it produced the correct result—. It was unmaintainable: six responsibilities in a black box, with two business rules buried inside. The refactor didn't change what it does; it changed who can understand and modify it, which is exactly the fifth operational question.
Exercise 3 — The architecture conversation. Your team wants to build a product: a platform where small businesses sign up, connect their own Gmail and Shopify accounts, and build their own automation workflows from an interface you control. The tech lead proposes building it on self-hosted n8n "because we already know it." They ask for your opinion. Write the response you'd give, covering both the technical and non-technical sides.
See solution
A reference response. Notice it says neither "yes" nor "no": it opens the two dimensions the tech lead didn't mention.
"Technically n8n can do a good chunk of that, and knowing it works in our favor. But before deciding the architecture, there are two things we need to put on the table, and neither is about code.
The first is legal, and it's the one that worries me most. What we're describing —external customers connecting their own accounts and building their own flows on our instance— is exactly the case n8n's Sustainable Use License doesn't allow without a commercial agreement. The license lets us use n8n for our internal processes, and even charge for building workflows for clients as consulting; what it doesn't allow is offering n8n as a multi-tenant platform to third parties. I'm not a lawyer and this needs to be verified by someone who can read the license seriously, but it's a question we need to answer before writing a line of code, not after we have customers. Talking to n8n about a commercial license is a perfectly valid option; ignoring it isn't.
The second is about scale and operations. Every customer with their workflows running generates executions, and those executions get saved. With many active customers, the executions database grows fast and the default debugging horizon —about fourteen days— falls short for us to give support. We'd need to size retention, isolation between customers, and who can see whose data, which on a multi-tenant platform is a hard requirement, not an extra.
My concrete proposal: before committing to the architecture, let's resolve the license question with whoever's appropriate, and let's estimate execution volume with a realistic number of customers. With those two answers, the decision makes itself. And if the commercial license fits, n8n is still a strong option."
The three things that response does, worth distinguishing it from a purely technical opinion:
It opens the legal dimension, which is the one the tech lead didn't see. A developer who only looks at the technical side would have said "yes, n8n can handle it" and the company would have found out about the license problem with customers already signed up. Raising that question in time is the whole response's most valuable contribution.
It doesn't turn the doubt into an absolute position. It doesn't say "it can't be done" or "n8n forbids this." It says "it needs to be verified with someone who can," which is honest —you're not a lawyer— and constructive —there's a path, the commercial license—.
It closes with a plan that unblocks the decision. Two concrete questions —resolve the license, estimate the volume— whose answers make the architecture "decide itself." That turns an objection into a next step.
Why it works: this exercise has no single correct technical answer, and that's the point. The skill being evaluated —seeing that an architecture decision has a legal face and an operational one, not just "can the tool handle it?"— is exactly what separates someone who owns a system from someone who just builds it. And it's a conversation that, in an interview for the profile this guide opens the door to, is worth more than any snippet of code.
Summary and next step
In this lesson you closed the arc Module 1 opened: code is an escape hatch, not the main door, and knowing when not to use it is half of professional judgment.
A Code node does harm in four cases, each with its own refactor. The one that does too much —six things in a black box— gets split into nodes with one purpose each, and the canvas goes back to telling the truth. The one that reimplements a visual node gets replaced by the catalog node, more readable for the whole team. The one that hides the business rule gets its thresholds and lists pulled out to $vars or a Config node, where someone who doesn't program can change them. And the one nobody else can maintain gets rewritten with the reader in mind, using the substitute test as the yardstick: does someone at your level understand it and modify it in ten minutes without asking you?
And n8n as a whole isn't the tool in four situations. High frequency —thousands of events per second— is work for a dedicated stream service. Heavy processing —huge files, intensive computation— goes to a service n8n triggers and picks up the result from. Strict latency —a path the user waits on in real time— lives in the application itself. And the fair-code license —the Sustainable Use License— allows internal business use and consulting, but not offering n8n as a multi-tenant platform to external customers without a commercial agreement; recognizing that legal boundary in time is a system owner's contribution.
You closed with the Python node: it changed completely in 2.0 —from Pyodide to native Python—, uses bracket notation to access data, and has exactly the same restrictions as JavaScript, so choosing it skips none of them. Everything you learned transfers whole. And with AI that generates Code nodes via the MCP server, which makes this module more necessary, not less: knowing how to generate code doesn't replace knowing how to read it.
Before moving on you should be able to: name the four signs of a harmful Code node and the refactor for each; state the four situations where n8n isn't the tool; and explain, in one sentence, what the Sustainable Use License does and doesn't allow.
Lesson 7 gives you the two practices that turn a correct Code node into a trustworthy one: testing it and documenting it. You're going to formalize lesson 2's pinned data into a test battery with the cases that matter —the happy one, the edges, the broken ones—. You're going to write the documentation that makes reading the code unnecessary, building on lesson 5's contract. And you're going to learn a concrete procedure to review AI-generated Code node code before trusting it to production, using everything this module gave you as review criteria.
Resources
- Using the Code node — n8n Docs — the node's two languages, the shift from Pyodide to native Python in 2.0, and the restrictions common to both.
- Sustainable Use License — n8n Docs — the text of the fair-code license governing the product, with its three limitations.
- Choose how to use n8n — n8n Docs — n8n's usage modes, useful for placing where each option fits and where it doesn't.
- AI Transform node — n8n Docs — the node that generates code from a description, and the reason knowing how to read it matters.
- Use n8n MCP server — n8n Docs — the connection to AI assistants that can build workflows inside your instance, lesson 7's topic.
- Set up task runners — n8n Docs — the isolated environment with bounded resources where your code runs, the technical reason behind the heavy-processing limit.