Home/ Blog/ Article

How to scope an MVP that you won’t have to rewrite

·

Most first versions get rewritten, and the usual explanation — “we moved fast and took on technical debt” — is wrong about the cause. Speed is not what forces rewrites. Rewrites happen when a small number of decisions made early turn out to be load-bearing and wrong, and every subsequent feature has been built on top of them. The useful skill in scoping an MVP is separating the decisions that are expensive to reverse from the very large number that are not, then being ruthless about the second group.

Decisions that are expensive to change later

Your data model, specifically its identities and relationships

This is the one that actually causes rewrites. Not the column types — those are a migration. The cardinalities. If you assume a user belongs to one organisation and later need users in several, that assumption is in every query, every permission check, every screen and every exported report. The same applies to assuming one currency, one timezone, one language, or that a record has a single owner.

The discipline is not to build multi-tenancy or multi-currency into version one. It is to spend an afternoon asking, for each core entity, whether the “one” in every one-to-many relationship is genuinely one forever. Where it plausibly is not, model the relationship correctly even if the interface only ever exposes a single value. A join table with one row per record is nearly free now and saves a structural migration later.

Identity, authentication and the permission model

Who a user is, and how the system decides what they may do, gets referenced from everywhere. Retrofitting roles into a codebase that assumed every authenticated user is equivalent means auditing every endpoint. Decide early whether permissions are per-user, per-role, or per-resource, and put the check behind a single function rather than scattering conditionals through the handlers. You can ship with exactly two roles. You cannot easily ship with none and add them later.

Where personal data lives and how it can be removed

Under UK GDPR you will eventually need to export everything about a person and delete it on request. If personal data has been denormalised into logs, analytics events, cached documents and third-party services with no record of where it went, that request becomes an archaeology exercise. Keep an inventory of where personal data is written, from the first commit. This costs almost nothing early and is genuinely painful to reconstruct.

Whether anything is irreversible

Money movement, external notifications, hardware commands and destructive deletes all share a property: you cannot undo them by fixing the code. For these paths specifically, invest early in idempotency, an audit trail of what was attempted and what happened, and the ability to replay or reconcile. In IoT work this is unavoidable from day one — an over-the-air update sent to a fleet cannot be recalled, so the rollout mechanism, staged deployment and rollback path are part of the first version rather than a later refinement. The same logic applies to a payments flow in a web product.

Deployment and observability

Not a full platform — a repeatable path from commit to production, a way to roll back, structured logs and error reporting. Teams postpone this and then spend the entire first month after launch debugging by guesswork. The cost is a day or two upfront.

Decisions that can wait, and usually should

  • Scaling architecture. Microservices, queues, sharding, caching layers and read replicas are answers to problems you can measure once you have users. A well-structured single application on a managed database carries a real workload comfortably, and it is far easier to split a clean monolith later than to reunify premature services.
  • Anything an administrator can do manually at low volume. Onboarding, refunds, plan changes, data corrections. If it happens five times a week, a developer with database access is cheaper than an admin interface. Build the interface when the manual work becomes the bottleneck — by which point you will know what it should actually look like.
  • Configurability. Settings screens, feature toggles per customer, custom fields and theming are enormous multipliers on complexity and testing. Hard-code the choice until a second customer disagrees with it.
  • Breadth of integrations. One integration done properly beats four done shallowly. The second one is much cheaper anyway once the first has forced you to build the abstraction.
  • Native mobile applications, at first. Unless the product needs background processing, offline operation or specific hardware access, a responsive web application answers the product question sooner and for less. Where the hardware genuinely is the point — Bluetooth, local radio, peer-to-peer connectivity — that calculation reverses entirely, and you should be honest about which case you are in.

Cutting scope without cutting foundations

The wrong way to reduce scope is to build every feature to a lower standard. The right way is to build fewer features properly, and to cut along particular seams.

  1. Cut breadth before depth. One user type, fully served, teaches you more than three, all half-served.
  2. Cut automation before correctness. A manual step behind a correct data model is fine. Automation on top of a wrong model is worse than nothing.
  3. Cut the edge case, keep the failure handling. You can decline to support an unusual scenario. You cannot decline to handle it gracefully when it occurs anyway.
  4. Cut features, not the test coverage on what remains. Tests on the paths that touch money, data loss or personal information are the ones that let you move quickly afterwards.

Deliberate debt versus accidental debt

Deliberate technical debt is a decision recorded at the time: what shortcut was taken, why, what would trigger revisiting it, and roughly what fixing it would cost. “Hard-coded VAT at the single UK rate; revisit when we sell outside the UK; roughly two days plus a data migration.” That is a manageable liability.

Accidental debt is the shortcut nobody noticed taking, discovered eighteen months later by whoever inherits the code. The difference is not the quality of the code — it is whether anyone can see it. Keep a short file in the repository listing the known compromises. It takes a minute per entry and it is the single cheapest thing you can do to make a codebase survivable.

One category is never worth taking on deliberately: shortcuts in authentication, access control, secret handling or anything touching payment data. The cost of getting those wrong is not measured in engineering days.

Signs the scope is running away

  • New requirements arrive in the form “while we’re in there, could we also…”.
  • Nobody can name the single user and the single task the first release exists to serve.
  • Features are justified by a hypothetical future customer rather than an identified current one.
  • The word “just” appears frequently in requests. “Just add a filter” is rarely just anything.
  • The launch date has moved twice and the feature list has never got shorter.
  • Configuration options are being added because a decision could not be agreed.
  • An admin panel is being built before there is anything to administer.

A practical constraint that catches most of this: hold the scope of the first release fixed and let the date be the only variable, or hold the date fixed and let scope be the only variable. Teams that let both move rarely ship. And when a new feature is proposed mid-build, the question to ask is not whether it is a good idea — it usually is — but which decision it would let you make sooner. If the answer is none, it belongs in version two.

Filed under: