Building with Make
Make (formerly Integromat) is the visual middle ground: a colorful scenario canvas with real logic — routers, iterators, aggregators, error handlers — that Zapier can't match, while staying more approachable than n8n. This chapter builds an AI scenario and teaches the data-flow model that makes Make click.
Learning objectives
- Build an AI scenario on Make's visual canvas.
- Master the data-flow model: bundles, routers, iterators, aggregators.
- Add error handling and control flow.
- Choose Make vs Zapier vs n8n vs code.
code/nc3-make/ in the course, with a README on how to run it. Read here, run there — no need to copy-paste.1 · The visual middle ground essential
You've probably hit this wall. You built a Zap to handle incoming orders — trigger, filter, send an email — and it was gorgeous, five minutes of clicking and done. Then the requirement grew: "and for each line item, categorize it, and if it's perishable route it differently, and don't fall over when the shipping API times out." Suddenly Zapier's neat one-step-after-another chain has no place to put a loop, no branch, no retry. Your only escape hatches are "code step" (so now you're programming anyway) or a wall of duplicated Zaps. That gap — too much logic for Zapier, not enough appetite to hand-roll everything in n8n or raw code — is the exact hole Make fills.
Think of the three tools as kitchen appliances. Zapier is a toaster: one job, no dial, brilliant at it. Raw code (or a self-hosted n8n) is a full professional range — infinite control, but you'd better know how to cook. Make is the countertop multi-cooker: real dials for sauté, pressure, and slow-cook, but every setting is a knob you turn, not a recipe you write. That "real logic without writing code" is the whole pitch, and it shows up on screen as a canvas rather than a list. Where Zapier draws a vertical stack of steps, Make draws circles (modules) connected by lines, and you literally watch coloured data flow along those lines when the scenario runs.
Two vocabulary words unlock the rest of this chapter, so meet them now. A module is one node on the canvas — a single action like "Google Drive: watch files" or "OpenAI: create completion." A scenario is the whole wired-together diagram Make runs on a trigger or schedule (Make's word for what Zapier calls a "Zap" and n8n calls a "workflow"). The common beginner mistake is to treat the canvas as decoration — pretty boxes hiding the same linear list underneath. It isn't. The visual layout is the logic: a fork in the lines is a real branch, a module that swallows an array and spits out many lines is a real loop. Read the picture and you've read the program.
Compare the two halves side by side: the left is how Zapier lays out the same job, the right is how Make lays it out.
- On the left, the boxes are stacked top-to-bottom with a single arrow between each — a strictly linear Trigger → Filter → Send email chain. There is only ever one path down, which is why the caption underneath reads "no branch, no loop, no retry."
- On the right, each circle is a module and each line is data flowing between them. The flow forks after the
itermodule — a solid arrow up to the AI route and a dashed arrow down to an alternate route — showing a real branch, then the branches rejoin at the greenaggmodule. - The payoff is the fork itself: the same trigger-and-action work becomes a shape that can split, loop, and merge — logic Zapier's straight stack cannot express.
In short: a Zap is a list; a Make scenario is a wired diagram, and its shape encodes branches and loops you can read straight off the canvas.
2 · The data-flow model essential
Here is the single misunderstanding that generates 90% of "why did my Make scenario do that?" support tickets. Someone builds a scenario that fetches an order, and downstream sends a confirmation email. They test it and get seven emails for one order. Nothing looped on purpose; no button said "repeat." The scenario simply did exactly what its data told it to — and the person didn't yet have the one mental model that would have predicted it. That model is the bundle, and it is genuinely the whole game. Learn it here and the rest of Make stops being surprising.
A bundle is Make's word for one packet of data travelling along the wire — one record, one item, one "thing to process." If you know Python, a bundle is basically a dict; if you know n8n, it's an "item." The rule that governs everything is deceptively small: a module runs once for every bundle that arrives at it. Picture a conveyor belt in a factory. Each module is a worker standing at the belt. If one box (bundle) rolls past, the worker acts once. If ten boxes roll past, the worker acts ten times — not because anyone told them to loop, but because there were ten boxes. Your email module sent seven messages because seven bundles reached it, full stop.
So the question you must always be able to answer at any point on the canvas is: "how many bundles are flowing here right now?" Most modules are one-in-one-out — a bundle comes in, a (possibly enriched) bundle goes out, count unchanged. The interesting, and dangerous, modules are the ones that change the count: an iterator turns one bundle into many, an aggregator turns many back into one, a router sends bundles down different lines. The table below is your cheat-sheet of these count-changing primitives, each paired with the plain code construct it mirrors. Keep the conveyor belt in your head as you read it — every row is really a rule about how many boxes come off the belt.
Read the four labelled panels left to right; in each, the small boxes are bundles and the arrows show how many come out compared to how many went in.
- The Normal module (blue) is the baseline: one bundle in, one bundle out — the arrow count doesn't change. Most modules behave this way.
- The Iterator (amber) takes one bundle and fans it out into three — the single input arrow becomes many, mirroring a
for-loop. The Aggregator (green) is its mirror image: three bundles converge into one, like areduce. - The Router (purple) keeps the count at one but chooses a path — a solid arrow to the taken branch, a dashed arrow to the branch not taken, like an
if/elif. - The payoff is the red bar at the bottom: the three count-changing primitives (iterator, aggregator, router) are exactly where a scenario's bundle count — and therefore its behaviour and its bill — can surprise you.
In short: ask "how many bundles come out?" at each module — 1→1 is safe, and 1→many / many→1 / branching are the moments to watch.
| Primitive | Does | Analogy |
|---|---|---|
| Router | split into branches | if/elif |
| Iterator | one bundle → many | for-loop / flatten |
| Aggregator | many bundles → one | reduce / join |
| Error handler | catch + route failures | try/except |
3 · Intermediate — iterators & aggregators intermediate
Recall the seven-emails story — this section is its cure. The reason one order became seven emails is that somewhere a bundle carrying an array (the seven line items) hit an Iterator, which is Make's fan-out module. An iterator's one job is to take a single bundle whose contents include a list, and explode it into one bundle per element. One order with seven items goes in; seven separate bundles come out, and from that point on the belt every downstream worker acts seven times. That is not a bug — it's the feature that lets Make do array work Zapier simply can't. The bug was leaving it un-collapsed.
The partner that collapses it is the Aggregator: the fan-in module. It waits for all the bundles from an iterator, then merges them back into a single bundle — summing numbers, joining text, or building one array. The two are a matched pair, like an opening and closing bracket. In plain code the shape is instantly familiar: the iterator is your for item in items: loop header, the middle module is the loop body, and the aggregator is the reduce / sum / "".join(...) at the end that turns the many results into one. If you open a bracket you must close it; if you fan out with an iterator you must, before any once-per-order action like an email, fan back in with an aggregator.
So the mental checklist whenever you place an iterator is: where does the fan-out end? Trace the lines downstream and find the aggregator that brings the count back to one before the "act once per order" step. If there isn't one, that step will fire once per item — the classic duplicate-email surprise. The lab below is a deliberately code-free-feeling Python stand-in: it takes one order, iterates its items, "categorizes" each (the spot a real scenario would call an AI module), then aggregates the per-item results into a single summary count — the exact iterate → process → aggregate arc drawn on a Make canvas, on one screen you can run.
Follow the arrows left to right — the bundle count widens in the middle and narrows again before the last step.
- The blue box on the left is one order carrying an array of three items. The amber Iterator is the fan-out: its single input arrow becomes three, one per item (
1 → 3). - The three white AI boxes are the middle module running once per bundle — milk, book, eggs each get categorized independently. This is where a real scenario's per-item AI cost lives.
- The green Aggregator is the fan-in: the three arrows converge back into one (
3 → 1), so the email box on the right receives a single bundle and fires exactly once (×1). - The payoff is the red warning underneath: if you delete the aggregator, those three bundles reach the email directly and it fires three times — the classic duplicate-email surprise.
In short: the iterator opens a loop and the aggregator closes it; put every "once per order" action after the fan-in, never inside the fan-out.
scenario.pydef scenario(order):
# iterator: one order with many line items -> a bundle per item
items = order["items"]
# each bundle: "AI" categorizes the item
processed = [{"name": it["name"],
"category": "food" if it["perishable"] else "goods"}
for it in items]
# aggregator: collapse bundles back to one summary
summary = {}
for pi in processed:
summary[pi["category"]] = summary.get(pi["category"], 0) + 1
return summary
print(scenario({"items": [{"name":"milk","perishable":True},
{"name":"book","perishable":False},
{"name":"eggs","perishable":True}]}))
{'food': 2, 'goods': 1}
In Make you don't write code — you drag modules onto a canvas and data flows through them left-to-right as bundles (think: one bundle = one record). A module runs once per bundle it receives. This Python function is a plain stand-in for a real Make scenario so you can see that flow on one screen: it takes an order that contains a list of line items, categorizes each item, then counts up the categories. The three comments (# iterator, # each bundle, # aggregator) mark the exact points where a Make scenario would use each of its three key primitives.
- Iterator (one bundle → many).
items = order["items"]pulls the array of line items out of the single incoming order. In Make, an Iterator module takes that one array and explodes it into one bundle per item, so everything after it runs once per item — like afor-loop that Make draws for you. - Process each bundle (the 'AI' step). The list comprehension (
processed = [ ... for it in items]) is the work done to every item: here it labels each item"food"if it is perishable, otherwise"goods". In a real scenario this middle module might be an AI call that categorizes the item — and it fires once per bundle the iterator produced. - Aggregator (many bundles → one). The
for pi in processedloop tallies how many items fell into each category into a single dictionarysummary. This is Make's Aggregator module: it collapses the many per-item bundles back down to one combined result — otherwise you'd fire the next step (an email, say) once per item instead of once for the whole order. - The final
print(scenario({...}))feeds in a sample order with three items (milk, book, eggs) so you can run the file and watch one order fan out to three bundles and collapse back to one summary.
What the output means: {'food': 2, 'goods': 1} — two perishable items (milk, eggs) became food and one (book) became goods. One order went in, three bundles flowed through the middle, and the aggregator returned a single count. That 'many bundles collapse to one' is the whole point of an aggregator.
Try this: Add a fourth item such as {"name":"cheese","perishable":True} to the list and re-run — the food count jumps to 3. That is exactly the Make surprise to watch for: more items in the array means more bundles, which means the middle module (and its cost) runs more times.
4 · Advanced — error handling & control advanced
Every automation works beautifully until the day an external service hiccups. A shipping API returns a 503 for four seconds, or one bundle in a thousand has a malformed address, and a naive scenario does the worst possible thing: it stops. Worse, it stops mid-batch — of your seven fanned-out items, three processed, the fourth threw, and items five through seven never ran at all. In a hand-written program you'd wrap the risky call in try/except and decide what happens next. Make gives you the same power without code, through error handlers you attach directly to the module that might fail.
The reason there are four named handlers — Resume, Rollback, Ignore, Break — is that "handle the error" isn't one decision, it's a menu, and picking wrong is its own bug. Here is what each actually does, phrased as the question it answers. Ignore: "skip this bundle and carry on" — good for a non-critical enrichment, dangerous for a payment. Resume: "substitute a fallback value and keep going" — the graceful-degradation choice. Rollback: "undo the whole run's committed changes" (only for scenarios wrapped in a transaction), the all-or-nothing choice. Break: "retry this bundle a few times on a schedule, and if it still fails, park it and move on to the remaining bundles." Break is the one that saves the seven-item batch: item four's failure no longer poisons items five through seven.
Start at the red "module fails" box on top and follow the four arrows fanning down — each is one handler you could attach to that module.
- The top red box is the moment a module throws. The four arrows fan out to the four choices Make offers — you pick one per module.
- Each coloured panel names a handler and, in the mono line beneath it, its plain-code equivalent: Ignore ≈ swallow the exception, Resume ≈ substitute a default, Rollback ≈ undo a transaction, Break ≈ retry then queue.
- Only Break (purple) has a follow-on flow: the amber arrow runs it through retry ×N and, if that still fails, into a dead-letter store — so item four's failure no longer stops items five through seven.
- The payoff is the red bar at the bottom: Ignore makes the error disappear by throwing the bundle away — the tempting choice that quietly loses data.
In short: pick the handler by asking where the failed bundle should go — Break (retry → dead-letter) keeps a batch alive; Ignore just hides the loss.
- Retry belongs on Break. A Break handler takes a retry count and interval, so a transient 503 quietly succeeds on attempt two and nobody is paged.
- Parking beats dropping. Pair Break with a "dead-letter" data store: the bundle that never succeeds is written somewhere with its error, so you can replay it later instead of losing the event forever.
- Routers are your branches. A Router splits the flow into labelled routes with filters, so after a call you can send the good bundles one way and alert on the rest — the visual equivalent of
if/elif/else.
The mistake to name out loud: reaching for Ignore because it makes the red error go away. It does — by silently discarding data. The reliability question is never "how do I stop seeing errors," it's "where does a failed bundle go," and a well-built scenario always has an answer that isn't the void.
5 · Professional — operations budget professional
The bill is where the bundle model comes back to bite you in the wallet. Make doesn't charge by the scenario or by the hour — it charges by the operation, and one operation is one module executing once, on one bundle. Every worker touching every box on the belt is a coin dropped in the meter. That pricing is fair and predictable right up until an iterator gets involved, at which point the meter starts spinning in a way that catches teams off guard: they budgeted for "500 runs a day," ran fine in testing, and got a bill sized for something ten times bigger.
Walk through why, because the arithmetic is the lesson. Suppose your scenario has two modules before the iterator and three modules after it, and it runs 500 times a day. The two front modules see one bundle each — cheap, 2 operations. But if each run's array holds 10 items, the iterator fans out to 10 bundles, and each of the three downstream modules now runs 10 times: 30 operations, not 3. So one run costs 2 + (10 × 3) = 32 operations, not 5 — and across 500 runs a month that gap is the difference between a comfortable plan and an overage. The dominant term is always the module count after the fan-out, multiplied by the fan-out width. Everything before the iterator is rounding error.
That single insight dictates the whole optimization playbook, and it's the opposite of how beginners tune. They try to shave the cheap front of the scenario; the money is at the wide back. So: filter early — drop bundles you don't need before the iterator, so the multiplier acts on fewer items. Aggregate before expensive modules — collapse back to one bundle before an AI call or an HTTP write, so that costly step runs once, not per-item. And batch instead of loop where you can — one AI call that categorizes all ten items in a single prompt is one operation and one API charge, versus ten of each inside the fan-out. Watch the operations log the way you'd watch a cloud bill: it's the honest record of how many boxes actually rolled down the belt.
Read the top row left to right as the scenario runs, then read the two tally bars below it and the total at the bottom.
- The two blue modules on the left sit before the amber iterator, so each sees just one bundle — together they cost only 2 operations, shown in the blue bar below them.
- The iterator fans one run out to 10 items, so the three red modules on the right each run 10 times. The red bar tallies them: 3 × 10 = 30 operations. That is why the whole right side is coloured red — it's the expensive part.
- The green bar at the bottom adds them:
2 + (10 × 3) = 32operations per run — far more than the five modules would suggest if you ignored the fan-out. - The payoff: the dominant term is the module count after the fan-out times the fan-out width, so filtering and aggregating before the wide back is where the savings are.
In short: count operations as modules × the bundles reaching them; everything after an iterator is multiplied, so shrink the back, not the front.
6 · Tech-lead — placing Make in the toolbox tech-lead
The failure mode of a technical lead here isn't picking the wrong tool — it's picking one tool for the whole org. Someone decrees "we're a Zapier shop" or "everything goes in n8n," and six months later half the automations are fighting their platform: the Zapier ones are grotesque towers of duplicated steps faking a loop, or the n8n ones are self-hosted overkill for a two-step email. The tool is not an identity. It's a per-automation decision, and the skill a lead brings is reading a requirement and knowing which rung of the ladder it lands on before anyone starts clicking.
The reason it's a genuine ladder — Zapier → Make → n8n → code — is that each rung buys you more control at the price of more effort, and you never want to pay for control you won't use. Zapier is the top of the ladder for simplicity: unmatched connector breadth and a linear stack of steps, perfect when the flow is "when X, do Y, then Z" with no loop. Make is the rung you climb to the moment the words "for each" or "and if" appear — its iterators, aggregators, and routers are exactly the array-and-branch logic Zapier can't express, still without code. n8n is next when you need to self-host for data-residency reasons, want per-run pricing at high volume, or need to drop into real code nodes. And raw code is the bottom rung you reach for when the thing genuinely needs unit tests, version control, code review, and the kind of scale where a visual canvas becomes the bottleneck.
So the reflex to build is diagnostic, not dogmatic. Ask of each automation: does it loop over arrays? (→ Make, not Zapier.) Must it run on our own servers? (→ n8n.) Is connector breadth on a simple flow the only thing that matters? (→ Zapier.) Does it need the engineering rigor of a real codebase? (→ code.) And the harder, quieter judgment: knowing when a scenario has outgrown its rung — when a Make canvas has sprawled into forty modules that nobody can debug and it's time to graduate it to code. Picking the lowest rung that meets today's need, while planning the jump for the day the need grows, is the entire craft of this section.
Read the boxes as a rising staircase from bottom-left to top-right — height encodes how much control and effort each tool carries.
- The lowest, left-most rung is Zapier: linear flows and the widest connector catalog, the least effort. Each rung to its right sits higher because it buys more power.
- Make (green) is the second rung — the one you climb to when an automation needs loops or branches, the array-and-fork logic Zapier can't express. n8n (amber) adds self-hosting and code nodes; raw code (purple) at the top adds tests, version control, and unlimited scale.
- The arrows point up the ladder to show the direction of increasing control, and the two axis labels underneath state the trade: left is simpler with less control, right is more effort for more control.
- The payoff is the shape itself: you climb only as high as the requirement forces you, because every rung up costs effort you shouldn't pay unless you'll use the control.
In short: match each automation to the lowest rung that meets its need — climb to Make for loops/branches, higher only when self-hosting, code, or scale demand it.
Exercise NC3.1 — Build an iterator/aggregator scenario
Context: The exercise that ties the module together is the fan-out/fan-in pattern with an error path bolted on — and being able to say what each of Make's control primitives maps to in code.
Your task: Design a Make scenario that iterates an array, runs an AI categorization per item, and aggregates the result; model it with scenario.py, add an error path, and map Make's four control primitives to code.
Requirements:
- An
Iteratorfans the array out; an AI module categorizes each bundle; anAggregatormerges results back to one bundle - An error path (retry / Break to dead-letter) covers a failing per-item call
scenario.pyreproduces the iterate→process→aggregate flow offline- Each control primitive (Iterator, Aggregator, Router, error handler) is mapped to its code equivalent (for-each, reduce, if/branch, try/except)
💡 Hint: Model the fan-out as a loop and the aggregator as a reduce — the error handler is just the try/except wrapped around the per-item call.
🪜 Practice ladder beginner → industry
Six graded exercises, easy to real-world. Try each before opening its solution.
Context: Make connects modules that pass “bundles” — its unit of data, much like an n8n item or a dict. A scenario is just a chain of modules moving bundles forward.
Your task: Design a scenario for “watch new files in Drive → OCR → store text in Airtable”, naming each module and what its bundle carries.
Requirements:
- A trigger module (
Watch Files) emits one bundle per new file - Each downstream module adds fields to the bundle (OCR adds
text) - The final module writes the bundle's text to an Airtable record
- Fields are referenced by mapping from earlier modules, e.g.
{{1.fileId}},{{2.text}}
💡 Hint: A bundle is Make's dict-equivalent — trace which module first sets each field the later modules read.
Show solution
Make's linear module chain (a 'scenario'):
[ Google Drive: Watch Files ] --bundle--> [ OCR module ] --bundle--> [ Airtable: Create Record ]
emits 1 bundle per new file adds bundle.text writes text to a row
A bundle is Make's unit of data (like an n8n item / a dict). Fields are referenced by mapping from earlier modules, e.g. {{1.fileId}}, {{2.text}}.
Context: Make lets you transform data inside a mapping with built-in functions, so you often need fewer modules than a strictly node-per-step tool. The inline conditional is the sharpest of these.
Your task: Design an Airtable record whose field mappings call Make functions (upper, formatDate, and the inline if) to derive values from earlier modules.
Requirements:
- At least one field is transformed inline (e.g.
{{ upper(1.name) }}) - A date field is formatted with
formatDate(now; "YYYY-MM-DD") - A status field uses
if(cond; then; else)— note the semicolon-separated arguments - The inline logic replaces what would otherwise be extra modules
💡 Hint: Make's if() uses semicolons, not commas, to separate its arguments — the inline transform is why you need fewer modules than a node-per-step tool.
Show solution
Mappings can call built-in functions inline:
Airtable "Create Record" field mappings:
Name = {{ upper(1.name) }}
Created = {{ formatDate(now; "YYYY-MM-DD") }}
Status = {{ if(2.confidence > 0.8; "auto"; "review") }}
Source = {{ 1.fileName }}
The if(cond; then; else) function is Make's inline conditional (note the semicolon separators). Equivalent logic offline:
def status(confidence):
return "auto" if confidence > 0.8 else "review"
print(status(0.9), status(0.5)) # auto review
Make's expressions let you transform data inside a mapping, so you often need fewer modules than in a purely node-per-step tool.
Context: Make's signature feature is the split/merge pair: an Iterator fans one array-bearing bundle out into many bundles, and an Aggregator fans them back into one. This is the array/loop work a linear Zap cannot do.
Your task: Design “split an order's line items → price each → sum back to one total” using an Iterator and an Aggregator.
Requirements:
- The order arrives as one bundle with an array field of line items
- An
Iteratoremits N bundles, one per line item - A module prices each bundle, adding a per-item line total
- A numeric
Aggregator(SUM) merges the N bundles back into one bundle carrying the total
💡 Hint: Iterator = for-each fan-out, Aggregator = reduce back to one — the loop lives between the split and the merge, not in a single module.
Show solution
The split/merge pattern that defines Make:
[ Webhook: order ]
| order.items = [ {..}, {..}, {..} ] (one bundle, an array field)
[ Iterator: order.items ] --> emits N bundles, one per line item
[ Set/HTTP: price each item ] --> each bundle now has .lineTotal
[ Array Aggregator (Numeric: SUM of lineTotal) ] --> back to ONE bundle: {total}
[ Airtable: write total ]
The fan-out / fan-in, modeled offline:
def iterate_price_aggregate(items, price):
per = [{"sku": i["sku"], "lineTotal": i["qty"] * price[i["sku"]]} for i in items]
total = sum(p["lineTotal"] for p in per) # the Aggregator step
return per, total
items = [{"sku":"A","qty":2}, {"sku":"B","qty":1}]
per, total = iterate_price_aggregate(items, {"A":10, "B":25})
print(per); print("total =", total) # total = 45
Iterator = for-each fan-out; Aggregator = reduce back to one bundle. This pair is why Make handles array/loop work that a linear Zap cannot.
Context: Make attaches error handlers to individual modules (Resume / Rollback / Break) and branches with Routers. Combined, they let a flaky API call retry, then park a failed bundle in a dead-letter store instead of losing the event.
Your task: Design robust handling for a flaky API call: retry with backoff, a Break to a dead-letter store on final failure, and a Router that branches on the response.
Requirements:
- The risky module carries a
Breakerror handler with a retry count and interval - On final failure the bundle is parked in a data-store dead-letter (bundle + error), not dropped
- A
Routerbranches after the call — a success route to processing, a fallback route to an alert - The retry uses increasing waits (backoff), and no event is silently lost
💡 Hint: Break parks the failing bundle so the scenario keeps running on the rest — the dead-letter store is what guarantees the parked event is never silently lost.
Show solution
Attach an error handler directive to the risky module:
[ HTTP: call API ]
error handler: Break (retries: 3, interval: 60s) -- retry, then park the bundle
on final failure --> [ Data store: dead-letter (bundle + error) ]
Router (branch after success):
Route 1 filter: {{status = 200}} --> [ process ]
Route 2 fallback --> [ Slack: alert ]
Retry-with-backoff, modeled offline:
def call_with_retry(call, tries=3, base_s=60):
for attempt in range(1, tries + 1):
ok = call(attempt)
if ok:
return f"success on attempt {attempt}"
wait = base_s * (2 ** (attempt - 1)) # exponential backoff
# (Make waits `interval`; shown here as the schedule)
return "dead-letter after retries"
print(call_with_retry(lambda a: a == 2)) # success on attempt 2
print(call_with_retry(lambda a: False)) # dead-letter after retries
Break parks a failing bundle so the scenario keeps running; the dead-letter store means no event is silently lost — the reliability spine of a Make scenario.
Context: Make bills per operation — roughly one module execution per bundle — and an Iterator multiplies operations because every module after it runs once per fanned-out bundle. Sizing a scenario means sizing the fan-out, not the run count.
Your task: Build a calculator that estimates monthly operations for a fan-out scenario given runs per day, modules before the iterator, items per run, and modules after the iterator.
Requirements:
- Modules before the Iterator run once per run (one bundle)
- Modules after the Iterator run once per fanned-out item, so they multiply by items-per-run
- Monthly total scales the per-run cost by runs/day and days
- The estimate makes the Iterator's multiplier the dominant term
💡 Hint: The cost trap is the module count after the Iterator — that block is multiplied by the fan-out, so it dominates the operation count.
Show solution
Operations scale with bundles, and Iterators fan out. Runnable estimator:
def monthly_ops(runs_per_day, modules_before_iter, items_per_run,
modules_after_iter, days=30):
# before the iterator: 1 bundle; after: items_per_run bundles
per_run = modules_before_iter + items_per_run * modules_after_iter
return per_run * runs_per_day * days
ops = monthly_ops(runs_per_day=500, modules_before_iter=2,
items_per_run=10, modules_after_iter=3)
print(f"~{ops:,} operations/month") # iterator multiplies the after-modules by 10
The lesson's cost trap: an Iterator turns one run into N bundles, so every module after it multiplies your operation count — size scenarios by the fan-out, not the run count.
Context: Zapier, Make, and n8n form a complexity ladder: each rung buys more control for more effort. A lead chooses per automation — not once for the org — based on loops, connector breadth, self-hosting, and cost model.
Your task: Encode the Make-vs-Zapier-vs-n8n selection criteria into a router that recommends a tool given whether loops are needed, self-hosting is required, connector breadth is critical, and volume is high.
Requirements:
- A self-hosting requirement points to n8n (per-run pricing, code nodes)
- A need for loops/array work points to Make (iterators/aggregators)
- A need for the widest connector catalog on simple linear flows points to Zapier
- The router returns a concrete tool with its reason, chosen per automation
💡 Hint: There is no single org-wide answer — route each automation by its dominant need (loops, self-host, or connector breadth).
Show solution
Decision router across the three tools from the module:
def pick_tool(needs_loops, self_host_required, connector_breadth_critical,
high_volume):
if self_host_required:
return "n8n (self-hostable, per-run not per-task, code nodes)"
if needs_loops or connector_breadth_critical is False:
return "Make (iterators/aggregators, visual data-flow)"
if connector_breadth_critical and not needs_loops:
return "Zapier (widest connector catalog, simplest linear Zaps)"
return "prototype in whichever the team knows"
print(pick_tool(True, False, False, False)) # Make (loops)
print(pick_tool(False, True, False, True)) # n8n (self-host + volume)
print(pick_tool(False, False, True, False)) # Zapier (connectors)
The portfolio view: Zapier for connector breadth and simple flows, Make for array/loop-heavy data work, n8n when you need self-hosting or per-run economics — pick per automation, not once for the org.
✓ Checkpoint — you can move on when you can…
- Build a visual AI scenario.
- Explain bundles, routers, iterators, aggregators.
- Add error handling; manage the operations budget.
- Place Make vs Zapier/n8n/code on the ladder.
Knowledge check check yourself
In Make, what does an iterator do to a bundle, what does an aggregator do, and why does forgetting the aggregator cause the classic 'why did this run 40 times?' bug?
Show answer
Make bills by operations (module executions). Given how bundles multiply, what design guidance does the lesson give to control the operations budget?