A client once showed me a Zapier setup with 34 steps in it. It moved a lead from a form into a CRM, enriched it, scored it, notified a channel, created a task and sent a follow-up email. It also broke roughly twice a week, and every time it broke, nobody knew until a customer complained that they never heard back.
We replaced it with a nine-step n8n workflow and one small service that owned the scoring logic. It has not broken since. The lesson was not "n8n is better than Zapier" — it was that a piece of business logic had grown up inside a tool designed for connecting things, and nobody had noticed the moment it stopped being a connection and started being an application.
Deciding where a workflow should live is the actual skill in automation work. The tools are easy. Here is how I make that call.
The Three Places Automation Can Live
A no-code connector — Zapier, Make. Best when the job is genuinely "when X happens in tool A, do Y in tool B." Enormous library of integrations, non-engineers can build and maintain it, and you pay per operation. The value is that the marketing manager can fix it without a ticket.
A self-hosted workflow engine — n8n, or Temporal at the serious end. Best when you need real logic, loops, branching, custom code inside a step, or when you are processing enough volume that per-task pricing hurts. You own the hosting, which is both the cost and the point: your data stays in your infrastructure and the bill does not scale with success.
Actual code — a service, a queue, a worker. Best when the logic is core to how the business works, when it needs proper tests, or when failure is expensive.
Most companies I work with end up using all three, and that is correct. The mistake is not picking the wrong one initially. It is failing to notice when a workflow has outgrown its home.
The Questions I Ask Before Building Anything
Who fixes this at 9am on a Saturday? If the answer is a non-technical operations person, it belongs in a visual tool they can open and understand. If the answer is an engineer, that constraint disappears and you should probably use code.
How many times a month does it run? Per-task pricing is wonderful at a few thousand operations and painful at a few hundred thousand. Multiply the steps by the volume before you commit — a 12-step Zap running 20,000 times a month is 240,000 tasks, and people are routinely shocked by that arithmetic.
What happens if it runs twice? This is the question that separates people who have operated automation from people who have only built it. Webhooks get redelivered. Retries fire. If running twice means a customer gets charged twice or emailed twice, you need idempotency, and idempotency is much easier to do properly in code.
Does it contain a business rule? If someone can point at a step and say "that is our pricing logic" or "that is how we decide priority," that rule wants to be in version control with a test around it — not in a dropdown inside a SaaS UI where a change has no history and no review.
Does sensitive data pass through it? Customer records flowing through a third-party automation platform is a data processing decision, not just a technical one. For anything regulated, self-hosted stops being a preference.
The Hybrid That Works Best
The architecture I keep coming back to: let the automation tool do the plumbing, and let code own the decisions.
The workflow tool handles what it is genuinely good at — receiving the webhook, talking to eleven different SaaS APIs whose SDKs you do not want to maintain, formatting the Slack message, retrying the flaky endpoint. When it reaches a step where something is decided, it calls one endpoint you own.
// POST /internal/leads/score — called from the n8n workflow
export async function scoreLead(req: Request, res: Response) {
const lead = LeadSchema.parse(req.body); // validate, never trust
const key = `lead-score:${lead.email}:${lead.submittedAt}`;
const cached = await store.get(key); // idempotent by design
if (cached) return res.json(cached);
const result = {
score: computeScore(lead), // tested, reviewable, versioned
segment: pickSegment(lead),
owner: assignOwner(lead),
};
await store.set(key, result, { ttl: "7d" });
return res.json(result);
}
Now the scoring rule has a test, a git history and a code review. The operations team can still rearrange the workflow around it without involving an engineer. And when marketing wants to change the scoring, it is a pull request rather than an undocumented edit to a production dropdown.
Where AI Fits, and Where It Does Not
Language models inside workflows are the genuinely new capability here, and they are good at exactly one category of task: turning messy input into structured output.
Classifying an inbound email. Extracting fields from an invoice PDF. Summarising a call transcript into three bullet points and a next action. Deciding which of six categories a support ticket belongs to. This work used to require either a person or a brittle set of regular expressions, and it now works well enough to put in production — with a review step.
What I do not do is let a model decide something irreversible on its own. The pattern is always the same: the model produces a draft or a suggestion, and either a human approves it or a deterministic rule validates it. An AI step that emails a customer with no gate in front of it is a bad day waiting for a slow week.
Two practical notes. Force structured output — use the provider's JSON mode and validate the result against a schema, because a workflow that expects three fields and receives a friendly paragraph will fail in a confusing way three steps later. And add a confidence threshold: below it, route to a human queue instead of continuing. Most of the value of AI in automation comes from handling the 80% that is obvious and knowing when it is not obvious.
The Operational Discipline Nobody Sets Up
This is where most automation projects quietly fail, and it has nothing to do with which tool you chose.
Failures must be loud. The default state of a broken automation is silence, which is the worst possible default. Every workflow I build has an error path that posts to a channel a human actually reads, with enough context to know what did not happen and for whom.
Someone owns it. Write a name next to every workflow. Unowned automations rot, and you discover them during an incident when nobody can explain what the thing does or whether it is safe to turn off.
Version and back up your workflows. Export the definitions into a repository. n8n workflows are JSON; commit them. Otherwise a production process exists only inside a SaaS account with no history.
Review the inventory quarterly. Every company that automates enthusiastically for a year ends up with workflows nobody remembers, some of which are still writing to production systems. An hour spent listing what exists and killing what is dead is well spent.
Measure whether it actually saved anything. Time saved per run times runs per month, against build and maintenance cost. Some of my favourite automations turned out to save four minutes a week and take an hour a month to maintain. Deleting those is a win too.
Start With One Process
If you are a business trying to work out where to begin, do not start with a strategy. Find the process that a person complains about, that happens at least daily, and that involves moving information between three or more systems. Automate the boring 80% and leave the judgement calls with the human.
Get that one working properly — with error alerts, an owner and a rollback plan — before you build the second. The companies that get real value out of automation are not the ones with the most workflows. They are the ones whose workflows nobody has to think about.



