Workflow automation

Choosing between n8n, Make and custom code

The tool argument is the wrong argument. What decides your stack is where the business logic lives, how often that logic changes, and who gets paged when a run fails at two in the morning.

On this page
  1. The short answer: choose by change rate and blast radius
  2. What the three options actually are
  3. The cost that decides this is maintenance, not licence
  4. The Canvas Line, the test to run before you pick a tool
  5. Where canvas tools break
  6. Where custom code breaks
  7. The hybrid that usually wins: thin canvas, fat services
  8. A decision table by workload shape
  9. Migrating without a rewrite
  10. Decide it this week

The short answer: choose by change rate and blast radius

The short answer

Put transport on a canvas and business logic in code. A visual tool like n8n or Make is the right call when the workflow mostly moves data between systems that already have connectors, when the logic fits on one screen, and when a failed run can be replayed by hand without anyone getting hurt. Custom code is the right call when the workflow remembers something between runs, branches more than about five ways, or touches money. When both are true, which is most of the time in a real business, you want the hybrid: a thin canvas that moves things, calling versioned endpoints that decide things.

  • TransportConnector auth, pagination, webhook endpoints and per-vendor retries are what a canvas is genuinely excellent at.
  • StateAnything the workflow remembers between runs belongs in a database with a unique constraint, not a variable inside one execution.
  • One screenA workflow you cannot read without scrolling has stopped being a diagram and become a program that happens to be drawn.
  • The diffIf nobody can see a line by line diff before a change reaches production, you do not have version control. You have a save button.

These are not three points on one line running from easy to hard. They are three answers to a single question: where does the program live? On a canvas the wiring is the program and the vendor owns your control flow. In custom code you own the control flow and the vendor owns nothing. The hybrid splits them deliberately.

The feature comparison is a trap. Every canvas tool eventually adds the connector you need and every language can call an HTTP API, so the feature sets converge. What never converges is the cost of changing the thing eighteen months from now, held by a different person. Ask how often the logic changes, what breaks if a run does the wrong thing twice, and who is on call. Those three answers pick the tool, and our workflow automation builds start there.

What the three options actually are

n8n and Make are canvas tools: each node is a connector or a small code step, and the wiring between nodes is the program. Make runs hosted. n8n is commonly run hosted or self-hosted, though read its current licence terms before planning a commercial deployment around self-hosting, because those terms have changed before. Custom code means a repository, a runtime you choose, and a queue or orchestration library doing the job the canvas would have done.

The vocabulary that makes this decision easier
Orchestrator
The component that decides what runs next, in what order, and what happens when a step fails. Every automation has one. The only question is whether you chose it deliberately or inherited it from whichever tool you opened first.
Canvas tool
A visual workflow builder in which each node is a pre-built connector or an inline code step, and the connections between nodes are the control flow. n8n and Make are canvas tools, as is almost every product marketed as no-code automation.
Escape hatch
The node inside a canvas tool that runs arbitrary code. Its limits set the ceiling of the whole platform: which runtime it gets, which libraries it imports, whether it reaches a private network, whether it can hold a secret.
Blast radius
The set of records, systems and people affected when a single run goes wrong. Blast radius, not complexity, is the property that should decide how much engineering rigour a workflow deserves.
Metering unit
The thing your platform bill counts: a run, a step, an operation, or a second of execution. It decides whether a loop over five hundred rows costs one unit or five hundred, so find out what yours is before designing any loop.

A canvas is not a drawing surface. It is a bundle of infrastructure you would otherwise write yourself, and it is worth being precise about what is in the bundle.

  • Connector authentication, including the OAuth refresh dance you would otherwise reimplement per vendor.
  • Pagination across a few hundred APIs whose documentation you will never read.
  • A run history with input and output payloads captured per node, the fastest debugging surface most teams ever get.
  • A trigger surface: public webhook URLs with TLS terminated, schedules, and polling with cursors, without standing up a listener.
  • A screen a non-engineer can open to confirm the thing ran, which is the item code teams most often forget to replace.

Custom code gives you none of that on day one and all of the control forever. That is the trade in one sentence.

The cost that decides this is maintenance, not licence

The licence fee is the number people compare and the number that matters least. Three costs sit underneath it: what the platform meters, what changes cost in labour, and what running your own runtime costs in operations. Only the first appears on an invoice, which is why the other two are the ones that surprise people.

Start with metering, because it changes your design and not just your bill. If your plan counts operations, a loop over five hundred rows costs five hundred units, and the fix is to batch the fan-out into one call to your own endpoint that loops internally. If the plan counts execution seconds, the expensive pattern inverts: a wait node parked inside a running execution costs real money for doing nothing.

Maintenance arithmetic: canvas versus code

Every default below is a stand-in for you to overwrite, not a measurement. Put your own team's numbers in and the answer often flips.

0Canvas, cost per month
0Code, cost per month
0Difference over a year
This arithmetic flatters the canvas on purpose

It leaves out the day the canvas cannot do the thing and an engineer spends a week building a workaround inside a tool never designed to hold it. Price that week, decide how often it happens, and add it to the canvas column. Then add the migration you will run if the platform changes its metering unit, a cost that exists on only one side of this table.

The Canvas Line, the test to run before you pick a tool

Framework

The Canvas Line

Draw one line through the workflow. Above it sits transport: triggers, authentication, moving bytes, fanning out, retrying. Below it sits meaning: business rules, data shape, state, money. A canvas above the line is a gift. A canvas below the line is a liability that stays invisible for about a year. These five checks find the line for a specific workflow in under an hour.

01
Name the state

Ask what this workflow must remember between runs: a cursor, a last-seen id, a partial batch, a lock, an approval still pending. If the honest answer is nothing, a canvas is fine. If it is anything at all, that state needs a store with a unique constraint on it. Idempotency in automation covers why the store beats the variable.

02
Count the branch points

Count every if, switch, filter and error path. Under about five, a canvas stays readable to a stranger. Past that it becomes a picture of a program rather than a program, and nobody changes it confidently. Move the branching into one function with a test file and let the canvas call it once.

03
Price the diff

Ask exactly how a change gets reviewed. If the truthful answer is that somebody opens the production editor and drags a node, the workflow has no review and its blast radius is the whole business. Export, version pinning and environment promotion buy the diff back. Confirm your plan has all three.

04
Test the escape hatch

Open the code node on your actual plan. Which runtime, which libraries, does it reach your private network, can it hold a secret without printing it into a run log? That answer is the ceiling of the platform for you. A generous escape hatch makes a canvas a deployment platform. A crippled one makes it a demo.

05
Name the on-call

Write down who gets paged when this fails at two on a Sunday morning and the runbook they open. If that person cannot read the tool, you picked the wrong tool whatever it can do. This check reverses more tooling decisions than the other four combined, because it prices the two years after launch.

The line moves as a workflow matures. A nightly sync of two hundred records can become the thing finance closes the month on. Review the line once a quarter for your top five workflows and you catch that drift while it is still cheap.

Where canvas tools break

Canvas tools break at the point where a workflow stops being a pipe and starts being a program. Three failure classes cover most of it, and all three arrive quietly.

Loops, fan-out and the bill

Each item in a loop is usually a metered unit, so a batch job that reads fine on the canvas can cost far more than the same job as one call to your own endpoint. Most canvas loops also run items one at a time, so five hundred items means five hundred sequential round trips, which eventually meets an execution timeout you did not know existed. The fix is to stop looping on the canvas.

Data shaping

Mapping expressions inside node fields are code without the things that make code survivable. No test, no type, no diff, no search across the workspace, and no way to reuse one transformation twice without copying it. The day a source field changes shape you find every copy by hand.

Concurrency and ordering

Two executions of the same workflow will eventually run at once on the same record. Canvas tools rarely offer a lock, and the ones that do usually offer a per-workflow lock rather than a per-key one, which serialises everything. If two concurrent runs on one customer would corrupt something, that guarantee lives in your database as a unique constraint.

The expensive failure is not the one that stops

A workflow that crashes gets noticed within a day. The costly failure keeps running while quietly doing the wrong thing, usually because a node returned an empty array and the next node treated empty as success. Nobody notices until a customer does. Read error handling that stops silent failures before putting anything on a canvas that writes to a system of record.

Where custom code breaks

Custom code breaks on the days nobody is thinking about the automation. It is rarely the logic that fails. It is the surrounding work the canvas was quietly doing for free.

  • Connector rot. You wrote the client for an accounting API, the vendor shipped a new major version, and the engineer who wrote it has moved on. A canvas vendor absorbs that across all their customers. You absorb it alone.
  • Trigger plumbing. A public webhook endpoint needs TLS, signature verification, replay protection, a queue in front and a dead letter behind. That is a week that never makes the estimate because it sounds like one route handler.
  • Observability. A canvas records the payload at every node by default. Teams who skip building that in code end up debugging production by adding log lines and waiting for the failure to repeat.
  • Visibility for everyone else. Nobody outside engineering can confirm the thing ran, so the team becomes a lookup service for the question did it work.
  • The three line change. Someone wants one more field in a Slack message. On a canvas that is a minute. In code it is a branch, a review, a deploy and a busy person, so the request waits.

This is why the pure code answer is wrong more often than engineers expect. The code is not the expensive part. The scaffolding is, and the canvas vendors already built it and are amortising it across thousands of customers.

The hybrid that usually wins: thin canvas, fat services

Systems that survive three years tend to look alike. A thin canvas moves things and does nothing else, calling a small number of versioned HTTP endpoints that do all the deciding. The canvas keeps triggers, auth, connectors, per-node run history and a screen a non-engineer can read. The code keeps rules, state, money, anything worth a test.

Logic on the canvasLogic behind an endpoint
How a change is reviewedSomeone drags a node, often in productionA pull request with a diff and a second reader
TestingRun it and look at the outputA test file that runs in CI before merge
RollbackUndo history, if your plan has itRedeploy the previous tag, in under a minute
Debugging a bad runPer-node payloads, genuinely excellentWhatever logging you remembered to build
Onboarding an engineerAn afternoon clicking through nodesReading a repository in a language they know
Leaving the vendorA rewrite of the whole thingRewire the triggers, keep every rule

The artifact that makes the split real is a one page contract written before anyone opens a tool. It forces the four decisions people otherwise discover in production. Copy this and keep it in the repository beside the service it describes.

workflow.contract.jsonjson
{
  "name": "invoice-to-ledger",
  "owner": "ops-platform",
  "on_call": "#ops-oncall",

  "trigger": {
    "kind": "webhook",
    "source": "billing-provider",
    "signature_header": "X-Signature",
    "replay_window_seconds": 300
  },

  "idempotency": {
    "key": "provider_event_id",
    "store": "postgres:automation.processed_events",
    "unique_constraint": true,
    "ttl_days": 30
  },

  "above_the_line_on_canvas": [
    "receive webhook", "verify signature", "enqueue",
    "call decision service", "write to system of record", "notify humans"
  ],

  "below_the_line_in_code": {
    "service": "ledger-rules",
    "endpoint": "POST /v1/classify",
    "pinned_version": "2026-08-01",
    "tests": "repo:ledger-rules/tests"
  },

  "retry": {
    "attempts": 5, "strategy": "exponential",
    "base_ms": 500, "cap_ms": 60000, "jitter": "full",
    "retry_on_status": [408, 425, 429, 500, 502, 503, 504],
    "never_retry_on_status": [400, 401, 403, 404, 409, 422]
  },

  "dead_letter": {
    "sink": "postgres:automation.dlq",
    "human_review": "Monday 09:00",
    "alert_after_items": 1
  },

  "budget": {
    "metering_unit": "READ YOUR PLAN AND WRITE IT HERE",
    "max_units_per_run": 12,
    "alert_at_percent_of_plan": 80
  },

  "slo": {
    "p95_end_to_end_seconds": 120,
    "heartbeat": "every run writes a row, alert if no rows in 6h"
  },

  "exit_plan": "all decisions are HTTP calls, so replacing the canvas rewires triggers only"
}

Two fields do most of the work. The idempotency key decides whether a retry is safe, which decides whether you can automate retries at all. The metering unit decides your loop design. For the retry numbers, rate limits, retries and backoff explains why jitter matters more than attempt count.

A decision table by workload shape

Workload shapeStart hereWhyThe trap
Move records between two SaaS systems on a scheduleCanvasConnectors, auth and pagination are the entire jobField mapping grows into business rules nobody can test
React to a webhook and post a messageCanvasTrigger plumbing is the expensive part and the tool owns itNo signature check, so anyone who finds the URL drives your workflow
Anything writing to a ledger, an invoice or a paymentCode, called by a canvasBlast radius is money and every replay must be provably safeRetries with no idempotency key, which is how one refund becomes three
Enrich tens of thousands of rows nightlyCodePer-operation metering and per-run timeouts fight you at onceLooping on the canvas and meeting the bill after the month closes
Multi-step approval with waits measured in daysCode with a durable storeA run parked mid-flight is state, and state belongs in a databaseWait nodes holding executions open until a platform restart loses them
LLM calls sitting in the pathEither, with an eval harnessThe model is a dependency that changes with no release noteNo pinned prompt and no output schema, so format drift breaks the next node
Start here, then move the Canvas Line as the workflow matures.

Read the trap column first. The trap decides the tool more reliably than the recommendation does, because every recommendation here is defensible and every trap is expensive. If you cannot defuse the trap inside the tool you prefer, that is your answer.

Migrating without a rewrite

You do not choose once and live with it. Moving logic off a canvas is straightforward in the right order, and the right order is counterintuitive: the trigger moves last, because the trigger is the part the canvas does best.

  1. Freeze the canvasDay one

    Stop adding logic to the tool you plan to leave. New rules go into a service from today. This costs nothing and stops the problem growing while you work.

  2. Extract the rules into one functionWeek one

    Copy every mapping expression, filter and branch into a single pure function that takes a payload and returns a decision, with no network calls inside. Write the tests from real payloads pulled out of the canvas run history, which is exactly what that history is for.

  3. Put the function behind an endpointWeek two

    Deploy it as one HTTP route returning the decision as JSON. Version the route in the path or a header and pin the canvas to that version, so a later deploy cannot change behaviour under a live workflow.

  4. Shadow run bothWeeks two to four

    Have the canvas call the endpoint and log any disagreement with its own logic without acting on it. Every disagreement is either a bug in the new code or an undocumented rule in the old canvas, and you need to know which before you cut.

  5. Cut over, move the trigger lastAt zero disagreements

    Delete the canvas logic and keep the canvas as transport. Move the trigger into your own infrastructure only if the vendor becomes a problem, because by then that rewrite is a webhook handler and a queue rather than a business system.

The sequence works in reverse too. Teams drowning in trigger plumbing can move triggers onto a canvas first and leave the services untouched, which is also the ordinary path off brittle bots in migrating off RPA.

Decide it this week

This decision does not need a two week evaluation. It needs one workflow, one hour and honest answers.

The one hour tooling decision
0 of 8 done

If seven of the eight have clean answers, build it. If only the exit plan is blank, build it and fix that in month two. If the state question or the on-call question is blank, stop, because those two turn into an incident.

Cite this

ChatGPTalker, Choosing Between n8n, Make and Custom Code: put transport on a canvas and business logic behind a versioned endpoint, and settle the split with the Canvas Line test rather than a feature comparison.

Questions readers ask next

Is n8n better than Make?
They are better at different things and the honest comparison is specific to your plan. Judge them on four axes rather than node count: what the escape hatch can run, whether workflows export as files you can version, what the metering unit is, and whether self-hosting is permitted and practical for you.
Can I run an entire business on a canvas tool?
You can run a great deal of one, and many companies do for years. The parts that should not live there involve money, legal obligations or state that persists across runs. Those want a database, a test suite and a code review, so keep the canvas as connective tissue.
When should I move logic off a canvas tool?
Four signals, any one of which is enough. The workflow no longer fits on one screen. Only one person can safely change it. It has produced a duplicate or a double charge. Or the same mapping expression now exists in three workflows, which is the earliest warning and the one teams ignore.
Does self-hosting n8n save money?
Sometimes, and by less than the spreadsheet suggests, because the platform cost moves rather than disappears. You now own upgrades, backups, certificates, queue workers and the page when a container will not start. Self-host for data residency or private network access, not to shave a subscription.
How do I version control a visual workflow?
Export the workflow definition to a file and commit it, ideally through the tool's own API on a schedule so the export cannot be forgotten. Review the diff on a pull request like any other change. If your plan cannot export a machine readable definition, treat that as a hard limit.
Should I use the built-in LLM nodes in these tools?
They are fine for drafting and classification where a wrong answer is cheap and a human reviews it. They are not fine where a wrong answer writes to a system of record, because you need a pinned prompt version, a validated output schema and an eval harness to catch drift.
Cite this

ChatGPTalker. "n8n vs Make vs Custom Code: How to Actually Choose." chatgptalker.com, 2026-08-26. https://chatgptalker.com/guides/n8n-make-or-custom-code/

Rather have it built than read about it?

Send the process you want automated. You get a scoped plan back, with the build shape, the stack and a realistic timeline.

Start a project