Workflow orchestrators like n8n and purpose written services do the same job: react to something, transform data, call other systems. Choosing between them per workflow rather than per organisation produces a much better estate than picking one and forcing everything into it.
Four criteria
How often does it change. A routing rule that changes when the team reorganises, a notification that changes when someone new joins, an enrichment step that gains a field: these benefit from being editable without a release. A calculation that has not changed in two years does not.
How many vendor systems does it touch. Existing connectors can reduce adapter work, but check the particular operations, pagination and error behaviour you need. A connector's presence does not prove that it handles your provider's limits or every authentication mode.
How intricate is the logic. Branching on three conditions is fine in a workflow. A pricing calculation with fourteen rules and exceptions is not, and the version built from forty nodes cannot be reviewed, tested or explained.
What is the volume. Measure execution overhead, queueing delay and downstream limits using a representative workload. There is no universal execution count at which a service becomes cheaper than an orchestrator.
The pattern that works: thin workflows, thick services
The most durable arrangement is not one or the other. It is an orchestrator handling the plumbing and calling services that hold the logic.
The workflow receives the trigger, verifies it, deduplicates it, fetches related records through connectors, calls your service with a clean payload, and routes the result. Your service does the part that needs to be correct, tested and explainable.
That split gives you both properties. The parts that change often are editable without a release. The parts that must be right are under test.
It can also make review easier. Exported workflows can be versioned and tested, but a large graph may make business rules hard to compare. Moving those rules into a small service can give reviewers a clearer diff and focused tests.
What an orchestrator still has to do properly
Choosing a workflow tool does not remove the engineering.
Verify the trigger. An open webhook URL is an open door, so check a shared secret or a signature before anything else runs.
Deduplicate before acting. Store processed event identifiers and exit immediately on a repeat, because vendors retry.
Key the actions. Sends and writes carry a value derived from the record and the step, so a replayed execution does not send twice.
Handle errors explicitly. The default behaviour on failure is silence. Every workflow needs an error branch that records the payload and raises a notification, and a queue the run can be replayed from.
Cap concurrency. Parallel execution against a rate limited API produces a burst of failures rather than faster work.
Keep credentials in the credential store with scoped access and a documented rotation procedure, never in node parameters.
What code still has to do properly
The mirror image applies. A service is not automatically better.
It needs the same deduplication, the same idempotency, the same retry policy with backoff and jitter, the same dead letter queue and the same run visibility. The advantage of code is that all of those can be tested. The disadvantage is that none of them are provided.
Self hosting matters
Where the orchestrator runs is a separate decision from whether to use one. Self hosting puts its execution and storage under your operational control. Calls to external services still transfer data, so review each connector and telemetry path rather than assuming all information remains inside your network.
It also means you are responsible for updates, backups and availability. Budget for that rather than discovering it.
A practical rule
Start a new cross system process in the orchestrator. If it grows past roughly a dozen nodes, or if you find yourself writing conditional logic that would benefit from a test, move that part into a service and let the workflow call it.
The boundary should make the system easier to explain and test, not merely reduce the number of visible nodes.
Trace a changed rule through a running execution
Imagine a lead routing flow waiting for an enrichment response. While it waits, an operator changes the territory rule. Decide whether the waiting execution uses the old rule or the new one. Either policy can be appropriate, but a workflow that silently mixes versions is difficult to investigate.
Store a workflow version with each execution and a business rule version with each decision. If an operator resumes failed work after a release, show which version will run. For irreversible actions, preview the material differences before allowing a replay. The workflow editor is not an excuse to bypass the change review expected of code.
A useful test fixture contains the original trigger, the external responses and the expected routing result. Replace live connectors with controlled responses, including a timeout, an empty page and a rejected credential. Run the fixture against a proposed workflow change before publishing it. This gives a visual workflow a repeatable acceptance test without moving all its steps into a custom service.
Finally assign operational responsibility. Someone needs to rotate connector credentials, notice a stopped execution and decide whether stale work is still useful. Record that role beside the flow. A graph that anyone can edit but nobody maintains is not a simpler system.
Keep a disabled copy of the previous workflow version and document how to return to it. Rolling back the graph does not undo external actions already taken, so recovery instructions must identify those actions before an operator repeats a run.