Skip to content
C.W.K.
Stream
Lesson 04 of 04 · published

When Not to Build a Service

~13 min · architecture, decision-making, operational-cost, defaults

Level 0Cold Workshop
0 XP0/35 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete

The Question Most Projects Skip

By the time most tools have a directory, someone has already decided they will be a service. It is rarely a decision anyone remembers making — it arrives with the framework, the template, the tutorial, the deployment target. And because everything downstream assumes it, the assumption never gets tested.

The question worth asking out loud is narrow: does anything need an answer from this at a moment nobody chose in advance? If yes, it needs to be available, and availability means a service. If every single invocation traces back to a person deciding to run it, then availability was never the requirement, and a port is a cost with nothing on the other side of it.

What a Port Actually Costs

The build cost is trivial and it is not the point. The costs that matter start after it exists and never stop:

  • A lifecycle to maintain — it has to start on boot, restart on crash, and survive an upgrade of the language runtime under it.
  • A liveness question — someone eventually has to be able to ask whether it is up, which means a health endpoint, which means something that reads it.
  • An address to defend — a reserved number, a collision to avoid, a decision about who can reach it.
  • An implied contract — this is the expensive one. Anything reachable will eventually be depended on, and the second caller arrives without asking permission.

That last cost is why the refusal is stated as a rule rather than a preference. A batch tool that becomes a service has not gained a feature; it has acquired an unbounded set of future callers whose expectations it did not agree to.

The Honest Counter-Case

This is not an argument that services are bad, and a lesson that pretended otherwise would be useless. Real triggers for a service are easy to state: several independent clients need the same answer; latency matters enough that process startup is unacceptable; state has to be shared and coordinated across callers; something outside your control initiates the work. Any one of those and the cost is earned. Beacon has none of them. It has exactly one caller, startup time is irrelevant against a render measured in minutes, its state is a directory, and every run begins with a person.

What makes this a transferable lesson is the order of reasoning. Do not start from "what shape should this be?" — start from "who initiates work, and when?" The shape falls out of the answer, and it will sometimes be a service. The failure mode is skipping the question, because the default answer is always yes and the cost is always permanent.

Code

A decision table you can actually apply·text
Does something outside your control initiate the work?
  yes -> service.        (a webhook, another system, a user request)
  no  -> keep going.

Do multiple independent clients need the same answer?
  yes -> service.        (shared state, shared cache, one source of truth)
  no  -> keep going.

Is process startup time unacceptable for the workload?
  yes -> service.        (per-request latency budget in milliseconds)
  no  -> keep going.

Must state be coordinated across concurrent callers?
  yes -> service.        (locking, sequencing, transactions)
  no  -> WORKSHOP.

# Beacon walks this table and falls out the bottom every time:
#   one caller, minutes-long jobs, state is a directory,
#   and every run begins with a person deciding it should.

External links

Exercise

Inventory the running services in a system you maintain. For each one, answer the four questions in the decision table without consulting how it is currently deployed. Then compare: how many are services because a question said yes, and how many are services because that was the shape available when they were written? Write one paragraph on the most expensive one you would demote if operational reality allowed.
Hint
The tell is a service whose only caller is a scheduled job you also own. That is a batch pipeline wearing a network interface — it pays the full permanent cost of availability so that one client, on a schedule you control, can make a call it could have made as a function.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.