๐ฆฅ The laziest thing that works
The ladder that stops an AI from building a mansion when you asked for a shed, and the one place laziness stops: logins, money and secrets.
You'll leave with
- The rung-by-rung ladder every fleet app sits on
- A SKIPPED list and a NOT DONE list your AI reads every session
- The ponytail plugin set up, and the habit of shipping small then questioning it
Ask an AI to "build a tool to track X" and it will cheerfully hand you a database (a program that stores and searches data), a login system, three programs that talk to each other, and a setup you can't get running. It's not wrong. It's eager. The fix isn't a better prompt. It's a posture, the one behind my fleet of live apps (websites that do something useful) at traqqit.com. Lazy means efficient, not careless: the best code is the code you never wrote, because there's nothing in it to debug at 3am. Today, Claude Code learns the posture too.
What are Claude Code and VS Code?
VS Code is a free code editor. Claude Code is Anthropic's AI assistant that works in your project folder: it reads files, proposes edits and runs commands, with your permission. Set up your workshop installs both; the numbered steps below need them, on a computer.
The ladder
Before building anything, climb down. Stop at the first rung that holds:
- Does it need to exist? If nobody needs it yet, skip it and write down why (YAGNI).
What's YAGNI?
"You Aren't Gonna Need It": build nothing until a real need shows up. Everything you build must be kept working.
- Does data you already have answer it? A reading you already collect is free.
- Does the platform already do it? The browser has a date picker (
<input type="date">), a dozen lines of inline SVG (shapes the page draws itself) beat a chart library, Caddy (the front-door program on your server, the always-on computer your app lives on) serves files by itself, and Node's standard library covers most of the rest.What are Node, the standard library and a dependency?
Node runs JavaScript outside the browser; its standard library is the toolbox built in. A library is someone else's code you pull in, and once your app needs it, it's a dependency: one more thing to update and trust. npm installs them, as packages.
- Only then write the smallest custom code that works.
"Can we do this with what's already here?" is the highest-leverage question you can ask an AI. Then climb the build ladder one rung at a time, only when something forces you:
| Rung | What it adds | Climb when |
|---|---|---|
| 1. One HTML file | Page, looks and code in one file | Start here |
| 2. A JSON data file | Your data as plain text, apart from the code | The data outgrows the page |
| 3. A Caddy route | A few lines of front-door settings | The browser may not read an API (a web address that returns data) directly |
| 4. A tiny Node container | Your own small program, no npm packages, in a sealed box (a container) on the server | Something must stay secret, or run while the page is closed |
| 5. PocketBase | Ready-made accounts and saved data (Accounts without building auth) | People log in, or their data follows them between devices |
| 6. A framework | A big toolkit, like React, that shapes the whole app | Something specific forces it, and you can name it |
Most fleet apps never left the first two rungs. Even rung 4 stays light: modern Node can fetch from other sites, do the math behind signatures and passwords (crypto), run a web server and open a live two-way connection to another program (WebSocket, a built-in client from Node 22). So several fleet servers, notification senders (like Baby Signs' daily reminders) and test scripts install zero npm packages: nothing to update or break.
Lazy everywhere except the trust boundary
Laziness stops at one line: who is logged in, who paid and where secrets live are always decided on the server, never in the browser.
What's a trust boundary?
The line between what you control (your server) and what you don't (every visitor's browser). Whoever holds the browser can change anything on their side, so nothing that matters is decided there.
Anyone can open DevTools (the browser's developer tools) and flip a "paid" flag to true. Secrets, like an API key (the password one program uses to call another's service), live in an env file on the server: a small text file of settings the server program reads at startup.
๐ Grease, my strength-practice app, shows both halves. Its Pro (paid tier) check needed no new server code: signed-in users may already read their own account record, and what they've paid for lives there, where only the server can write it. The page reads that record from the server, never a flag kept in the browser. Pro billing isn't switched on yet; the check is (Get paid without leaking money).
Lazy never means untested: skip the ceremony, keep the proof. One self-check, a few lines that run your logic on known answers and complain when one is wrong, beats a big test setup; node plan.js runs Grease's self-check (taught in Check the AI's work). Put the boundary in your prompts too: "Login and payment checks are server-side; never unlock anything in the browser."
The SKIPPED list and the NOT DONE list
Write down every feature you didn't build, why, and what would change your mind. It stops scope creep (a project quietly growing one feature at a time) and stops Claude re-proposing the big feature every session.
Start from the core loop, the one thing a person comes to do. Acento, my Argentine Spanish trainer: pick a deck, listen, record, play yours back, see a score. Its first version skipped voice-cloned accent transfer (your own voice played back with a native accent), which needs a machine-learning server (a powerful separate computer running AI models), and recorded human audio. When I wanted accent scoring later, I built it without that server: a dependency-free scorer that runs in the browser.
NOT DONE is the after-launch list. When Acento's accounts went live, it said: no email verification, no payments (Pro was a flag I set by hand), no password-change screen. Marketing describes only what's done.
Every skip needs a trigger. First Bites' notes said "no delete tombstones yet; add if it bites" (a tombstone is a note that an item was deleted, so another device doesn't bring it back: Sync and sharing). It bit right before I readied the app for Google Play, and the plan was already written. Re-read both lists before every launch.
Shortcuts in code get a searchable comment (a note the computer ignores) saying when to revisit. A side project of mine keeps its data in one JSON file, tagged:
// ponytail: fine to low thousands of users; move to sqlite if writes get hot
SQLite is a database in one file. VS Code's search (Ctrl+Shift+F, Mac Cmd+Shift+F) finds every ponytail: line. The shortcut is a documented decision, not a landmine.
Make Claude read the lists every session
Of the files in your project, Claude Code loads one by itself at the start of every session: CLAUDE.md, your standing instructions to it. Your notes file, NOTES.md, loads only if CLAUDE.md points to it with a line reading @NOTES.md (an import: it pulls that file in too). Work with Claude Code set that up and gave NOTES.md a Skipped list. No CLAUDE.md or NOTES.md yet? Do that lesson's step 3 first. Now add the second list and prove Claude reads both.
- In VS Code, open
NOTES.mdfrom the file list on the left. Its Skipped heading is your SKIPPED list. Add this at the bottom, then save (Ctrl+S, Mac Cmd+S):## NOT DONE (launched without) - Nothing launched yet. - Open
CLAUDE.md. It should have a line containing only@NOTES.md, without backticks. If it doesn't, add it as the last line and save. - In VS Code's terminal (View โ Terminal), type
/exitif Claude is still running (a line starting with/is a command for Claude Code itself). Then typeclaudefor a fresh session and ask:
EXAMPLE โ YOU ASK CLAUDE CODE
What's happening underneath: the @ line is an import. Whenever a session starts, Claude Code loads NOTES.md with CLAUDE.md, so edits reach Claude at the next fresh session. Success is an answer that lists your own items, with no Read line under it. If Claude opens the file or can't find a list, check the spelling and the backticks in CLAUDE.md, and that you saved both files.
Before you add anything, ask what you already have
Write the question down precisely first. One of my side projects tracks river flows for paddlers, and runs (stretches people paddle) below dams seemed to need a link to every dam operator. Written down, the question was how much water is in this stretch right now? That's the flow at the gauge right below the dam (a government sensor measuring the water going past), already in data I collected. Runs with no such gauge carry a "dam-controlled" card explaining why the number may mislead; 27 do.
A report one person reads doesn't need a web app either. A private report of mine is a command-line tool (a program you run by typing its name in the terminal) that fetches, scores and publishes one self-contained index.html. Deploying (putting it on the server) is copying that file.
When requirements change, change the rows, not the shape. I wanted each of Acento's example sentences to be its own flashcard. The build step (a small script that turns content into the page's data file; Build static-first) now turns each word into three items with the same fields and ids (the labels progress is saved under) made from the word plus a number: word-1, word-2, word-3. The page, the audio generator and saved progress needed no changes. Derive ids from the content, never at random, so progress survives rebuilds.
A vertical slice every day
Design doc first: ask Claude for a written plan with the core loop, every tunable number (anything you'll want to adjust) and a roadmap by day. Then build the thinnest version that works end to end, the same day: on localhost (your own computer acting as the server) until you've done Deploy behind one front door, on your real address after that.
What's a vertical slice?
A thin piece that works all the way through, from the screen to the saved data, instead of one finished layer at a time. Tiny and ugly, but it runs.
An unlisted browser party game of mine (live, but not listed on my home page) went from a 14-section design doc to a drivable truck on day 1, then parking, phone lobbies and new tracks on later days, each live. A day is done only when its check passes where the app runs (the live address, once you have one), and known problems get written down, not hidden, like that game's "figure-8 physics still chaotic".
Then question it
Shipping small is half the habit; the other half is asking what can go. A plugin nags me to.
What's a plugin?
An add-on for Claude Code: written instructions (skills), sometimes with small scripts. A marketplace is a catalog of plugins you add once.
Install ponytail once. In Claude Code's prompt box, type this and press Enter:
/plugin marketplace add DietrichGebert/ponytail
Then this, which opens a panel listing what the plugin adds (its wording changes between versions):
/plugin install ponytail@ponytail
Choose Install for you (user scope) so it works in every project. When the install summary appears, type /exit, then claude, then /ponytail-help. Success: a reference card whose first table lists Lite, Full (the default) and Ultra. If the short name is unknown, pick /ponytail:ponytail-help from the / menu.
โ ๏ธ ponytail is free and open source (anyone can read its code), written by another developer, not Anthropic or me. Its scripts run on your computer as you, so read that panel first. If Claude offers to add a mode badge under its prompt box, yes or no is fine.
What's happening underneath: the first line registers the plugin's catalog, a public GitHub repository (a project folder on GitHub); the second installs the plugin. At each session start, a small script, run by Node, hands Claude the rules:
- Lazy mode, always on. Claude climbs this lesson's ladder before writing code and tags shortcuts with
ponytail:comments./ponytail litesoftens it,/ponytail ultraquestions the request itself,/ponytail offstops it. /ponytail-auditranks what in your repo (your project folder, whose saved versions git keeps) to delete, shrink or swap for something built in. It changes nothing./ponytail-debtlists everyponytail:comment and flags any without a trigger.
๐ An over-engineering audit of my unlisted browser game deleted about 415 lines and 5.9 MB of screenshots, and turned up the bug that had killed its multiplayer.
Big pivots get a switch, not a deletion. When a paid product goes free, put its payment gate behind one environment variable (a setting handed to a program from outside its code) instead of deleting it, and delete only the buy buttons and old prices:
const OPEN = process.env.MYAPP_OPEN_ACCESS !== '0';
Setting it to 0 in the server's env file and restarting brings the paywall back. The tested payment code is still there; only the buy buttons need rebuilding.
And leave live shared systems alone for a nice-to-have. Most of my apps with logins share one accounts server, so recording which app each person signed up from meant new signup code plus a restart, during which nobody can log in anywhere for a few seconds. It was offered and deliberately not built: existing data could still answer the question. Write such ideas down with their cost.
Do it with Claude: the prompts
No idea yet? Use a placeholder; the next lesson finds a real one. Copy these one at a time (replace YOUR IDEA):
Be lazy. What's the smallest version of YOUR IDEA that delivers the core loop? List what you'd skip and why.
Add a SKIPPED section to NOTES.md: each item, the reason, and the trigger that would make us build it.
/ponytail-audit this repo and give me a ranked list of what to delete.
EXAMPLE โ YOU ASK CLAUDE CODE
What's happening underneath: the plugin is a set of instructions Claude loads that push it toward the standard library, native browser features and fewer files, so it stops at the first rung that holds. Guidance, not a guarantee: if a plan names a server, a database or a framework, ask "which rung is each piece on, and what forces it?" Then read NOTES.md yourself; a skip without a trigger gets forgotten.
Before the audit, commit (save a snapshot git can return to) in a second terminal: the + in VS Code's terminal panel. PowerShell, Git Bash and the Mac terminal all take these:
git add -A
git commit -m "before the audit"
EXAMPLE โ YOU ASK CLAUDE CODE
<input type="date"> does it. Nothing changed; tell me which to apply.โณ Bash ยท git ls-filesโณ Read ยท index.html, old-index.htmlWhat's happening underneath: the audit is the ladder pointed backwards. On a folder as small as yours, expect little or nothing, maybe "Lean already. Ship." Apply one finding at a time ("apply #1 only, then show me the diff", the changed lines), reload your page on localhost, and commit. If a change breaks the page, git reset --hard discards every edit since your last commit, for good.
Your turn
- Add the NOT DONE list to NOTES.md. Done when, in a fresh session, Claude names your SKIPPED and NOT DONE items with no Read line.
- Ask Claude for the smallest version of any app idea, even a placeholder, and save what it skipped to NOTES.md, each with a reason and a trigger.
- Install ponytail:
/ponytail-helpshows Lite, Full and Ultra. - Commit, run
/ponytail-audit, then apply or reject its top finding, if it has one. Done when your page still opens on localhost and any change is committed. - From memory, write the six build rungs in order and mark where your first app starts.
Next, Find an idea the data can carry: where the fleet's ideas came from, and a 20-minute check that the data for yours exists, is allowed, and means what you think.