How to scope an MVP so the first version ships
How to scope an MVP: cut what needs something new, note where it plugs in. Two agents cost Relay 11 existing files; ElevenLabs, 40.
Senior Full-Stack Engineer- Published
- 8 minReading time
- ProductCategory
Listen to this post· AI-narrated
0:00 / 9:57How do you decide what not to ship in an MVP, when everything you cut might cost you a rewrite later? Most guides on how to scope an MVP stop at the feature list. I wanted to know what the cut half actually cost, so I went back through the git history of Relay, the app I make my content in. Its plan, written on 28/09/2026, kept version one to two coding agents, Claude Code and Codex, and put every other integration on an "Out" list under one sentence: each later addition plugs in behind a seam that already exists. Version one, built with coding agents as I build everything now, was in the first commit, about four hours after the empty project. The cut half arrived over the next days: the first two integrations touched 11 files I'd already written, and ElevenLabs touched 40.
TL;DR: To scope an MVP, cut anything that needs something new and write where it plugs in later. In Relay, two coding agents arrived the next evening and edited 11 existing files. ElevenLabs, a voice generator, edited 40, because it brought paid credits and remote voices that no seam covered. Write those down next to each cut.
What do most guides tell you to cut from an MVP?
Two of the guides ranking for "how to scope an MVP" this month, both from development shops, show the usual cut. Aidev (opens in a new tab) sorts features with MoSCoW (must, should, could, won't) and scores what survives with RICE (reach, impact and confidence over effort). Coding Crafts (opens in a new tab) runs seven steps from the riskiest assumption to a list of what stays out of scope.
Neither says where the cut leaves the code. If you leave payments or a second integration out of version one, you worry that adding it later means reopening everything you built without it. So you build it now, and the first version slips. I add a second note next to each cut item: where it attaches when it comes back.
How I scoped Relay's MVP
Relay is a macOS app I built for myself. I chat with coding agents in it, and they write, draw and record audio into my project folders. It's private and runs only on my Mac. The plan kept ten items in: the background process that owns the app's state, a web interface and a thin desktop shell around it, projects, sessions, chat with Claude Code (Anthropic's coding agent) and Codex (OpenAI's) on my own subscriptions, tool calls, files the agents create, a markdown renderer, Relay's own tools for agents, and exactly one generator, a local text-to-speech model.
Everything else went out: Grok and Antigravity (the coding agents from xAI and Google), image and video generators, Higgsfield (an AI image and video service), ElevenLabs, SharpOS (the platform my studio runs on), automations, voice cloning, packaging and more. My test for each item: does it need a new vendor, a new runtime or a new kind of account? Then it waits.
Above the Out list, the plan (a markdown file in Relay's repo) says: "Every later addition (Grok, Antigravity, Higgsfield, ElevenLabs, SharpOS, automations) is a new adapter behind an existing seam, not a rewrite." A seam here is a registry that every item of one kind plugs into. Chat agents plug into a provider registry, and anything that makes files (audio first, later images and video) plugs into a generator registry. Both shipped in version one, so each later item had a named place to go.
Did the integrations I cut plug in without a rewrite?
They plugged in within days, and two of the three I could measure cost little outside their own code. For each one I took the commit that added it and split its files into the adapter's own folder and the files that already existed and had to change. I count files because git records them, and each existing file edited is a place the change reached into code that already worked.
| Integration | Landed | Files in its adapter | Existing files edited | Lines added / deleted in them |
|---|---|---|---|---|
| Grok and Antigravity | 29/09/2026, the evening after the plan | 17 | 11 | +84 / −6 |
| ElevenLabs | 01/10/2026 | 16 | 40 | +1,331 / −105 |
| Grok Imagine (images) | 21/10/2026 | 3 | 3 | +13 / −37 |
| Higgsfield | never needed one |
Grok and Antigravity came in together, behind one shared adapter, in a single commit. Outside it, the provider registry gained 24 lines and lost one, its types gained three, and the settings screen gained a section for the new agents.
Grok Imagine, an image generator added on 21/10/2026, edited three existing files and deleted more than it added, because the image settings it shares with GPT Image, the first image generator, moved into one file.
Higgsfield never needed an adapter. I use it through its own MCP server (Model Context Protocol, the standard way agents call outside tools), and Relay's record step, which files any outside image or video with its prompt, already covers what it makes.
The first image and sound-effect generators also came from the Out list, on 29/09/2026, but each landed inside a commit mixed with unrelated work, so I couldn't count them cleanly and left them out.
Why did ElevenLabs edit 40 existing files?
I expected every later integration to look like Grok's, a few lines outside its own folder. ElevenLabs is a voice generator, and the generator registry took it in four lines. By then four generators already used that registry (speech, sound effects, images and text), so the seam itself held. The other edits, sorted by what they were for, served things Relay had never had:
- An API key. Every earlier generator ran locally or through an app I was already signed into, so settings had to learn to store a key.
- Runs that spend credits. Nothing before it cost money per run. Now a run estimates its cost and asks before going past a per-run spending cap, so the run service gained 91 lines and the status bar learned to show credits.
- Voices stored on someone else's server. My voices were local files; ElevenLabs voices sit in my account there, so the voice library and the voice picker changed.
Why the seam didn't catch it
Existing code lost 105 lines in a commit that added more than 7,000. The plan only asked which registry each item would use. A registry holds things of the same kind, and money per run, a stored key and remote data were new kinds that spread through code the registry never touches.
Is a seam for later items premature abstraction?
A seam is premature when it's built for one imagined case. Relay's provider registry had two real users in version one, Claude Code and Codex, so its shape came from two agents that already worked, and Grok and Antigravity fit it a day later. The generator registry started with one local speech model and had four generators by the time ElevenLabs arrived, all fitting it.
I'd draw a seam where version one already has two of something, or where the Out list holds several things of the same kind that you're sure are coming. For a single feature you might build someday, the Out list entry is enough.
How to scope an MVP with a Later list
Here is how I'd scope the next one, and how to check it afterwards. Relay's plan called the cut list "Out"; here it's the Later list.
- Write the In list as the shortest path to the one thing the product must prove. For Relay that was chatting with an agent that can make a file.
- Move everything that needs a new vendor, runtime, account or kind of data to the Later list.
- Next to each Later item, write the seam it plugs into. If you can't name one, plan a new seam for it, or ask whether it belongs in this product.
- Next to each Later item, also write what new kind of thing it brings: money per use, a user's credentials, data stored elsewhere, a new permission. Those spread past any seam, so plan them as their own work.
- When a Later item lands, count its cost. Run
git show --stat <commit>on the commit that adds it and count the existing files edited outside its folder. A handful means the seam held. Dozens means it brought something the list didn't name, and that's worth a line in the next plan.
Who pays for the cut
The cut also costs the people who wanted the half you left out. SnapForge, my browser app for framing screenshots and recordings in device mockups, says in its README: "Native recording comes later." Recording well needs a desktop app and the click and cursor data a plain video file doesn't reliably carry, so version 0.2.0 shipped on 11/10/2026 without it, and anyone who wants to record their screen uses another tool for now.
| Later item | Plugs into | Brings something new |
|---|---|---|
| Grok, Antigravity | provider registry | nothing new |
| ElevenLabs | generator registry | an API key, paid runs, remote voices |
| Native recording (SnapForge) | the editor's media import | a desktop app, click and cursor data |
I used the same approach when SharpOS needed clean module boundaries without a rewrite, in how to move to a modular monolith without a rewrite.
What I'd do differently
I'd write that last column from the start. Relay's plan named the right registry for every later addition and never asked what else each one would bring, so ElevenLabs, which looked like one more generator, edited nearly four times as many existing files as two whole coding agents.
If you want more builds measured like this, follow me on X (opens in a new tab) or Instagram (opens in a new tab). If you're hiring a senior full-stack engineer, or you need a product scoped and built end to end, get in touch on gabs.app.