
August 17, 20266 min read
The brand change was easy. The parts of the old name wired into browser storage, cookies, and a soon-to-ship desktop app were not.
I’ve been running an SVG editor at a domain I chose years ago, svg-editor.griffen.codes, that grew into something bigger than the name it started with. This month I renamed it to Subpath (subpath.dev) and used Kiro to actually execute the rename. Here’s what that involved. (I wrote about the bigger year-long story separately in One Year In: How SVG Path Editor evolved into Subpath; this post is just the mechanics of the rename itself.)
This wasn’t a rebrand for its own sake. Through July I’d been using Kiro to build the parts that turn a browser toy into a real product: accounts, a cloud sync layer, the billing and plan logic behind the AI features. Once that existed, svg-editor.griffen.codes stopped making sense; you don’t put a paid cloud service behind a URL that announces itself as somebody’s personal subdomain, because that’s exactly what it was. The rename was the last piece, and it landed after Kiro had already spent a month on harder problems than a name change.
A rename sounds like find and replace. It isn’t, because the old name doesn’t only live in headings and page titles. It’s also baked into things you can never touch again once someone’s browser has data under them: localStorage keys, cookie names, analytics IDs. Change those and you don’t just get a cosmetic problem, you silently delete every document someone’s already saved, with no way to get it back, because the code that reads the old key is gone.
The spec I wrote for Kiro puts it this way:
This is a brand change, not a product change. Don’t touch editor capabilities, plan boundaries, pricing, or anything a user has already saved. Here’s the rule that overrides everything else: users who already have documents in their browser, and share links already out in the wild, need to experience this as cosmetic. Nobody loses access to their work.
On top of that, a desktop app had landed on the project a few weeks earlier and added a second, worse version of the same problem. A bundle identifier and a URL scheme, once a single build reaches a single real user, get locked in by the OS forever. There’s no migration path for that the way there is for localStorage. Get it wrong and you’ve permanently split your own user base into two apps that can’t talk to each other.
Before touching anything, the spec’s design doc split every occurrence of the old name into four groups, based on what happens if you get it wrong:
| Tier | What it is | What breaks if you touch it carelessly |
|---|---|---|
| 1. Brand surfaces | Headers, dialogs, page copy, assets | Nothing serious, it’s just words |
| 2. Identity and infrastructure | Domains, env vars, Stripe and Supabase dashboards | Has to happen in order, or you get an outage window |
| 3. Frozen identifiers | 14 localStorage keys, two cookie names, analytics IDs | Every saved document in the field, gone, unrecoverably |
| 4. Desktop pre-release identity | Bundle ID, deep-link scheme, Rust source | Fine today, permanently broken the moment one build ships |
Tier 3 was the one that mattered most. About 60 of roughly 220 occurrences of the old name across the web app were actually frozen identifiers wearing the old name as a prefix, not brand references at all. The fix wasn’t to migrate them to a new prefix, it was to stop thinking of them as the brand name in the first place:
Keep the
svg-editor:prefix on these keys. It’s not a brand reference at this point, it’s just what they’re called. Add a one-line comment inlib/storage-keys.tsexplaining why, so future-me doesn’t “clean it up” once I’ve forgotten the reason.
That one sentence is doing a lot of work. Without it, the natural instinct six months from now is to see svg-editor: in a keychain and assume it’s a leftover bug.
I wrote this up as a spec: requirements first, then a design doc working through the four tiers above, then a task list broken into 13 groups covering the desktop app, the brand definition, page metadata, every component with hardcoded copy, the frozen-identifier verification pass, docs, local config, and infra.
Kiro ran the whole thing in one sitting in early August. Every task execution is logged with a timestamp and a session ID, and all 195 of them trace back to the same single session. 12 of the 13 task groups are fully checked off; the last two open items are just running the full verification suite and the end-to-end tests, the kind of thing you do right before shipping, not a sign anything’s unfinished.
The part I’d have gotten wrong doing this by hand: the desktop app’s Rust source and release scripts hardcode the product name in more places than the config file suggests. tauri.conf.json looks like the single source of truth, but the window title, the native menu items, and the release script’s artifact matching are all separate hardcoded strings that had to move together, in the same commit, or the release pipeline would silently stop recognizing its own build output.
There’s a name for this now: spec-driven development. Write the requirements, design, and constraints down before any code moves, then let the agent work from that instead of a running chat. Kiro builds it into the tool directly, spec mode is how you hand it anything bigger than a single file, and GitHub shipped its own version of the same idea, Spec Kit, open sourced last year. I’d already been running this workflow through July for the accounts and billing work before the rename ever came up.
The rename is just the clearest case for why the approach earns its keep on something this wide. Tell an agent to rename SVG Path Editor to Subpath and, taken literally, it will, storage keys included. Tell it to work from a spec that spells out four tiers and fourteen frozen identifiers, and it won’t.
The product is live at subpath.dev. Formerly SVG Path Editor, same editor, same documents, new name.
If you’re ever staring down a rename of something people already have data in, I’d be curious what your tier three looks like.
Discussion
Comments are powered by Disqus. Sign in once, comment anywhere.
