Sweet Pal
Coming to the App Store

The Sweet Pal blog

Notes from the people building an app for type 1 diabetes: artificial intelligence, vibe coding and everything in between.

Friday, 9 October 2026

Vibe coding with rules: why a CLAUDE.md beats a clever prompt

Vibe coding on a real product only holds up when the rules live in files the agent rereads every session. Ours, one by one, and the reason behind each.

Vibe coding works on a real product too, on one condition: the rules can't live in the prompt. They have to live in files the coding agent rereads at the start of every session. A clever prompt lasts one conversation; a well-written CLAUDE.md lasts for every conversation that follows. At Sweet Pal the rules that really matter are few, each one born from a mistake, and each one carries its reason written next to it.

Today Sweet Pal is four folders and four repositories: the iOS app in SwiftUI, the admin dashboard, the website with the technical documentation, and the old Expo app, archived but still running. All four talk to the same Supabase project, where the tables of two product generations live side by side, with names similar enough to mislead. A coding agent, however capable, walks in with no memory. It doesn't know that the folder that looks like "the app" might be the old one, that the website's repository has a historical name suggesting the whole project, or that a table called meals isn't the one today's app uses. Tell it in the prompt and it knows for an hour. Write it in a file and it knows every time.

That's why the instructions come in layers, like the folders. There's a short CLAUDE.md in the folder that holds everything, and it does one thing: it orients. It says which folder is the current product, what the other three are, where each one ships, and that the database is shared; the first line that matters, in bold, asks you to make sure you're in the right folder before touching "the app". Then there's a file inside each project that goes into detail: how to run it locally, how to deploy, which variables are needed, which traps we already know. The iOS app's file is the densest, because that's where the binding rules live.

A few rules, all mechanical

The first is also the most important: before creating, look at what's already there. With a database shared between two generations, an agent's most dangerous move is also its most natural one: creating a new table for something that exists, or changing an old table believing it belongs to the new product. Next to it sits a trivial rule, and that's exactly why it works: every new table gets the swp_ prefix, with one declared exception, users, which is shared. The other families of names belong to the previous system, and the file says so plainly. A prefix turns a doubt into a mechanical check.

Then there's the rule that surprises newcomers most: never supabase db push. The local migration history doesn't match the remote one, and a push would send the old migrations along in bulk. So every schema change is prepared as SQL ready to paste, and a person pastes it into the SQL Editor. We had to learn the consequence: code reaches production with a push, the migration when someone pastes it, and for a few minutes or a few hours one is ahead of the other. On 22 September the admin dashboard stayed broken for a few hours exactly like that. Since then the dashboard recognises the missing-table error and says which SQL file to paste, instead of showing the raw database message.

Keys for model providers and other services live in Supabase secrets, never in the app and never in the repository; the file adds a detail that saves half an hour of confusion, namely that the dashboard shows the key's fingerprint, not its value. And there's a rule with a date, 8 July. The main working tree almost always holds half-finished work; one day a change made by the agent landed in the same file as an ongoing job, and a colour tweak got committed together with a feature that wasn't ready. Since then every job that changes files starts from an isolated git worktree on a dedicated branch, builds there, and enters main with a fast-forward merge. Never commit someone else's work in progress. The last one is about style: many small files, not a few huge ones. With an agent it matters more than with a person, because a two-thousand-line file gets read in pieces, and in pieces it gets misunderstood.

The why has to be written down

A rule without a reason gets bent at the first edge case. "Don't use db push" sounds like a preference; "the local and remote histories don't match, and a push would send the old migrations" is a fact nobody argues with. That's why almost every rule in our files has its reason next to it and, when there is one, the date and the name of whoever decided it. The same goes for technical traps: the dashboard's file carries a warning about the middleware, which until 28 September sat in the wrong folder and never ran, with nothing to signal it. The file doesn't just say where it belongs; it explains how to check, after the build, that it's actually active. A rule that contains its own check is a rule the agent can apply on its own.

Project instructions aren't documentation, though. They don't describe the code, which reads for itself, and they don't track the state of the work, which changes every day and has its own place: the handoff documents between sessions, which we'll cover in another post. They hold only what an agent can't work out by looking at the files: conventions, prohibitions, reasons, mistakes already made.

The phrase "vibe coding" suggests a light way of working, where you describe an idea and the code shows up. In part it is. But on a product that handles clinical data, with a shared database and four repositories, you can only afford that lightness once you've written the boundaries down. Rules don't slow the agent down: they take away the decisions it shouldn't make, and leave it the ones it makes well.

In short:

  • A prompt lasts one session; an instructions file in the repository lasts for all of them.
  • Instructions come in layers: one file that orients across the workspace, one detailed file per project.
  • The most useful rules are few and mechanical: look before creating, a prefix for new tables, migrations pasted by hand, secrets out of the code, one worktree per job.
  • Every rule carries its reason and, when possible, the way to check it.
  • The state of the work doesn't belong in the instructions: it needs its own document, updated every session.

Posted by Simone Tolomelli at · Labels: Vibe coding · 5 min read