How to Pick Tools for Each Layer of a Solo Stack

A solo stack rarely fails because one tool was bad. It fails because the tools were chosen in the wrong order โ€” an orchestrator picked before anyone knew what had to be sequenced, a database added before there was any state to keep. What follows is the order I use, the single question that settles each layer, and the note I write at every point where two tools touch.

Decide in dependency order, not by enthusiasm

Most people start with the glue, because the glue is the fun part. Start instead with the thing that leaves the system. Each layer below constrains the one after it, so answering them out of order means paying for the same decision twice.

LayerQuestion that settles itDefault if you cannot answer
Output contractWhat exact file, row, or message leaves this, and can I check it by hand?One JSON file per run, on disk
TriggerWhat starts a run, and what stops it starting twice?Cron plus an idempotency key
JudgmentIs the decision rule code I can read, or does it need a model on messy input?Plain code until it demonstrably fails
StateIf the process dies halfway, what must I resume from?Nothing; no database yet
GlueHow many outside systems does one run touch, and who changes the step order next?One Python file
ObservabilityAt 2am, which single line tells me which step broke?Append-only JSONL log, one line per step

Two details in that order matter. The judgment layer sits above the glue layer: decide whether you need a model before you decide which canvas will call it, because a canvas that is good at connectors is not automatically good at prompt iteration. And observability is a layer, not a cleanup task โ€” you choose its format alongside the output contract, since the log line and the artifact share a shape.

Orchestrator or hand-wired script

The question is not which is better. It is how many failure points one run touches, and who will edit the order of steps after you. Hand-wiring wins when the run is one process, one input, one output, a fixed sequence, and the job fits in a file you can read in one screen. That covers more of a solo studio than tool marketing implies.

An orchestrator earns its place when any of these holds: the run must hold state for hours or days between steps; a step needs per-step retries and backoff you do not want to write yourself; a human must approve something in the middle; or someone who does not read diffs needs to change the order. That last one is the real test for a one-person shop โ€” if you are the only editor, the canvas buys less than it costs.

Hand-wiring has a bill too: you own the scheduler, secret rotation, retry policy, and the machine that must stay up. Keep the entry point boring so the bill stays visible.

#!/usr/bin/env bash
set -euo pipefail
RUN_ID="digest-$(date -u +%Y%m%dT%H%M%SZ)"
LOG=/var/log/studio/digest.jsonl
printf '{"run":"%s","step":"start"}\n' "$RUN_ID" >> "$LOG"
python -m jobs.digest --out "out/$RUN_ID.json"
printf '{"run":"%s","step":"done"}\n' "$RUN_ID" >> "$LOG"
# crontab -e
15 6 * * * cd /srv/studio && ./run.sh >> /var/log/studio/cron.log 2>&1

The run ID is the whole trick. Without it a rerun and a scheduled run look identical in the log, and the first real debugging session turns into guesswork.

Low-code or code

The settling question: does the builder’s canvas express the whole decision, or does it hide a decision you need to see when it fails? A visual tool is a strong answer for connector work โ€” OAuth refresh, webhook intake, a client intake form โ€” because that work is repetitive, documented, and dull to own. It is a weak answer for logic you would want to unit test, and a bad answer where reviewing the change matters more than the speed of making it.

ConcernLow-code builderHand-written code
Connector setupFast; credentials handled in the UIYou write and rotate the auth
Debugging a failureNode-level logs, limited local reproStack trace, breakpoints, fixtures
TestingManual runs against live dataFixtures and a test command
Reviewing a changeDiff of a JSON exportOrdinary code review
CeilingHits a wall at branching or data reshapingBounded by your patience
Cost at volumePer-task pricing on hosted plansYour compute bill

The failure that costs most is not picking wrong; it is splitting one decision across both sides. A code node that grows into a small application inside a canvas is the most expensive object in a solo stack, because your test command cannot see it and the canvas’s own logs barely can.

Keep the seam narrow: the canvas decides when and in what order, the code decides what the content is. One code node, one HTTP call, one job.

{
  "name": "digest",
  "nodes": [
    {"name": "Webhook", "type": "n8n-nodes-base.webhook",
     "parameters": {"path": "digest", "httpMethod": "POST"}},
    {"name": "Shape", "type": "n8n-nodes-base.code",
     "parameters": {"jsCode": "/* SEAM -> summarise service */ return items.map(i => ({json: {body: i.json.body, ids: i.json.ids}}));"}},
    {"name": "Summarise", "type": "n8n-nodes-base.httpRequest",
     "parameters": {"url": "http://localhost:8080/summarise", "method": "POST"}}
  ],
  "connections": {"Webhook": {"main": [[{"node": "Shape", "type": "main", "index": 0}]]}}
}

For the wider tradeoff between canvases and scripts, the onboarding and ceiling comparison goes deeper. When you later swap one side, do it as a single-layer change โ€” upgrading a layer in place is a much smaller job than a rebuild.

Document the seam, not the tools

Next year you will not wonder how the webhook node works. You will wonder why the summarise step is a separate service instead of three nodes, and whether it is safe to delete. That is a question about the seam, so write the answer at the seam.

Put a block like this above the code that crosses the boundary, and keep a copy in stack.md at the repo root so it is greppable:

# seam: webhook -> summarise-service
owner: me
contract_in:  { body: string, ids: string[] }
contract_out: { summary: string, model: string }
lives_here_because: retries and OAuth live in the builder; prompt logic needs fixtures
would_move_if: prompt logic exceeds one file, or per-run token cost needs tracking
fails_loudly: HTTP node retries twice, then the run is marked failed and logged
test: make test-seam   # posts a fixture, asserts the response matches contract_out
reviewed: 2026-01-12

Four lines carry the weight. The contract is what actually breaks when either side changes. “Lives here because” records the reason you would otherwise re-argue with yourself later. “Would move if” turns a vague migration itch into a trigger you can check. The test command proves the seam is still a seam and not two copies of the same logic drifting apart.

Write it the day you build the seam. The reason is fresh for about a week.

What to do next

  1. Write the output contract for one workflow โ€” the exact artifact โ€” as three lines in stack.md. Every other layer is judged against it.
  2. List each outside system a single run touches. If the run holds state between steps or needs an approval in the middle, move it to an orchestrator; otherwise leave it in a script.
  3. Move any canvas code node that has grown past a page into a module with a fixture and a test command, and call it from the HTTP node.
  4. Paste the seam YAML block above every boundary crossing, and add a make test-seam target for it.
  5. If you want these layer decisions collected in one place, The One-Person Studio is a 5-page PDF playbook for running an AI automation studio solo: mindset, core tech stack and when to pick each tool, orchestrator choice, and the solo delivery loop. It is $12 one-time at https://slashmaster6.gumroad.com/l/one-person-studio.

Get The One-Person Studio

The One-Person Studio โ€” $12, one-time payment, instant download. See the full breakdown on the review page.

๐ŸŽ Want to try before you buy?

Both samples are 100% free. Enter your email to unlock the download links and get our 3-day AI toolkit drip in your inbox.

No spam. Unsubscribe anytime. We respect your privacy.

Or browse the full catalogue in the Slashman Tools storefront.

About the author
Published by slashman413 โ€” writing practical, evergreen guides on money, productivity, developer tooling and the web. More about this site โ†’

Frequently Asked Questions

Do I need an orchestrator if I only have one workflow?

Usually not. A single fixed sequence with one input and one output is cheaper to own as a script with a cron entry and a run ID, unless the run must hold state between steps or wait for human approval.

What should a seam note contain?

The input and output contract, why the logic lives on that side, the condition that would make you move it, and the test command. Those four lines answer the question you will actually ask next year.

When should logic move out of a low-code tool?

When you want to test it with a fixture, when a change needs code review, or when the code node inside the canvas has grown past a page. Move it into a module and call it over HTTP.

๐ŸŽ Recommended Tools

๐Ÿ“š Related Articles

๐Ÿ“ฌ Free Weekly AI Product Guides

New tools, templates and automation walkthroughs โ€” plus hands-on updates on the Slashman Tools catalogue. One email a week, zero fluff.

Free forever ยท No spam ยท Unsubscribe anytime ยท Sent instantly

๐ŸŽ Get Free Toolkit