Appearance
Code vs. Scripts vs. Docs
Problem & Solution
Code, scripts, and docs are three mediums with different execution guarantees. Code runs automatically as part of the system. Scripts run when a human (or cron, or CI) invokes them. Docs never run — they get read, sometimes.
Putting something in the wrong medium silently weakens it. A rule documented in a README is a suggestion; a rule in code is an invariant. A command explained in prose is something a user has to retype correctly; the same command wrapped in a script is one word to invoke. Rationale baked into code via clever naming evaporates when the code is refactored; rationale written in docs survives.
The principle: route each piece of content to the medium that matches the guarantee you actually need.
Pattern
The Three Homes
- Code for rules & behaviors — anything that must always hold or always happen. The machine enforces it. Examples: an access filter, a timeout, a retry policy, a schema validation, a privilege drop.
- Scripts for repeated operations — anything a person would do more than once. The script encapsulates the steps so they don't have to be re-derived. Examples: install, smoke-test, deploy, log-tail, database seed.
- Docs for rationale — anything a reader needs to understand why the system is the way it is. Things the code can't express: trade-offs, history, intent, cross-references to other systems. Examples: threat model, why this architecture was chosen over alternatives, which external systems this depends on.
How to Apply
You're staring at something — a new requirement, a manual process, a piece of context. Ask what is this? and route it:
| If it's a… | Put it in… |
|---|---|
| Rule that must hold (or behavior that must always run) | Code |
| Repeated operation you'd otherwise do by hand | Script |
| Reason, trade-off, or context someone needs to reason about | Docs |
Smells That Should Trigger the Rule
Each one says you're routing to the wrong medium.
- Writing "remember to…" or "be sure to…" in a doc. The fact that you need to remind a future reader means the system isn't enforcing it. → Move it to code.
- Writing a sequence of commands in a README for the user to paste. They'll mistype or drift. → Wrap them in a script and point the doc at the script.
- Explaining in code comments why the code makes a trade-off that only makes sense if you know about an external constraint (a past incident, a vendor quirk, a compliance rule). Code comments rot; a design doc doesn't. → Move the rationale to docs, keep the comment short and pointing at the doc.
- Docs describing what the code does step by step. If the code is the authority, the doc will go stale the first time someone edits without updating both. → Delete the narrative, let the code speak, keep only the why in docs.
- Scripts that encode policy ("skip this host if it's staging"). Policy is a rule, not an operation. → Move the decision into code, keep the script dumb.
When Docs Are the Right Home
Not everything belongs in code. Docs are the right home when:
- The content is about trade-offs or alternatives considered. Code shows the choice made, not the choices rejected.
- The content is rationale that future maintainers need to decide whether to change something. "We picked X because of constraint Y" — if Y ever goes away, they can revisit.
- The content is a pointer to external systems the code depends on ("DNS is served by CoreDNS on the Mac Studio; see macos-config/304-remote-access.md"). Code can't express the dependency; docs and cross-links can.
- The content is onboarding context: how the system fits together, what a newcomer needs to read first.
If the content answers "what does this do?" — it should be obvious from the code or named in a script. If it answers "why is it like this?" — it's docs.
Examples
Example 1: Tailnet-only access filter
A server should only accept traffic from loopback and Tailscale. The naive instinct is to document this in the README: "the dashboard is tailnet-only; do not expose to the public internet."
That's a suggestion, not an enforcement. The README can't reject a request.
Right home: code. An Express middleware calls an isTailnetAddr() predicate on every request, returning 403 for anything else. A smoke-test script verifies this matches reality on every interface. The README explains why — the threat model, the fact that there's no auth, the decision not to expose via tailscale funnel — but the rule itself lives in access.js.
Example 2: Smoke-test the daemon
Checking that a service is healthy involves several steps: probe loopback, probe each network interface, look at process ownership. Documenting those steps in a README puts the burden on the user to run them correctly, in order, and to interpret the results.
Right home: script. npm run service:check encapsulates the entire sequence and exits non-zero if any check fails. The README points at the script; the script carries the knowledge.
Example 3: Why port 80 requires root
macOS requires admin privileges to bind ports below 1024. The dashboard binds 80 by running as a LaunchDaemon that starts as root, then immediately drops privileges to the repo owner. This is subtle — a future reader looking at the server code won't know why it starts as root unless they hit setuid and wonder.
Right home: docs (plus a short comment pointing there). The dashboard's README has a section titled "What happens if the server is ever compromised" that explains the trade-off in plain language. The code has a brief comment near the setuid call naming the mechanism. Neither duplicates the other.
Anti-patterns
"Living documentation" instead of tests
Saying "the README describes how the system works" as a substitute for tests or types. Docs drift; tests don't. If the correctness claim matters, encode it.
Config that's really a rule
A config flag like ENFORCE_TAILNET_ONLY=true defaults to off "so people can opt in." If the invariant is real, don't give the user the option to break it — delete the flag and hardcode the rule.
Scripts that are really docs
A bash script full of echo "Now edit your ~/.ssh/config to add…" with no actual action. The script isn't executing anything; it's just narrating. Either execute the edits (it's a real script) or delete the file and put the instructions in a README (where the form matches the function).
Docs that are really scripts
A README with a 12-step setup procedure that every new engineer follows manually. The first time two engineers do the same steps, it should become a setup script. The README should then be three lines: "run ./setup.sh; see below for what it does."
Why this matters
Each misrouting costs something:
- Rule in docs → someone will break the invariant because they didn't read, didn't remember, or disagreed.
- Operation in docs → someone will mistype, skip a step, or run the steps in the wrong order.
- Rationale in code (only) → the "why" disappears when the code is refactored or rewritten.
- Docs rewriting what the code already says → drift. The doc lies the moment the code changes.
Routing correctly means each piece of information lives where it can do its job: rules enforced, operations one-command-invocable, rationale preserved.