Back to BlogStartups & MVP

How Not to Build an MVP: 9 Engineering Decisions That Sink First Versions

Rupak Amin

Founder & Lead Engineer, RAITHub

12 min read

Don't build an MVP by making decisions that are cheap in week two and expensive in month six. The nine that sink first versions most often: splitting into services too early, letting screens design the database, storing money as floats, checking permissions only in the UI, writing your own login, trusting payment redirects, editing production data by hand, doing slow work inside requests, and shipping blind.

This is the engineering companion to two earlier posts. Five MVP mistakes founders make covers the product side: over-scoping, skipping QA, no analytics, no user feedback. How to validate your MVP covers what to test before any code exists. This post assumes you have validated the idea and are about to build. It is about the technical choices a founder often never sees being made, and which an agency or freelancer may make for you by default.

Why do engineering decisions matter so much in a first version?

Because an MVP is cheap to change in its features and expensive to change in its foundations. A button, a page or a pricing tier can be replaced in an afternoon. The shape of the database, where permissions are enforced and how money moves are load-bearing: every later feature is built on top of them, so changing them means touching everything at once.

The test for each decision below is simple. Ask: "If this turns out wrong, is it a one-file fix or a migration of live data?" The first kind you can safely postpone. The second kind deserves an hour of thought now, even in an MVP.

The nine decisions at a glance

#The decision that sinks youWhat it costs laterThe cheaper default
1Splitting into microservices on day oneCross-service changes, distributed bugs, extra hostingOne deployable app with clear internal modules
2Letting the screens design the databaseDuplicated data that drifts; painful reportingModel the business entities first, then build screens on them
3Money as floats, times without time zonesRounding errors in invoices; bookings off by hoursInteger minor units or numeric; timestamptz in UTC
4Checking permissions only in the UIAny user can read or change others' data via the APICheck on the server for every request; add row-level security for tenants
5Writing your own login systemPassword resets, sessions and lockouts become your security problemA maintained auth library or provider
6Trusting the payment success redirectPaid customers with no access, or access with no paymentWebhooks as the source of truth, processed idempotently
7Editing the production database by handEnvironments drift; nobody can rebuild the schemaVersioned migrations in the repository, run by the deploy
8Doing slow work inside the requestTimeouts, duplicate emails, half-finished jobsA simple job queue with retries
9Shipping with no error tracking or tested backupsUsers find bugs first; a bad restore finds you lastError tracking, structured logs and one rehearsed restore before launch

1. Should an MVP use microservices?

Almost never. A new product does not yet know where its natural boundaries are, and services freeze boundaries early. Martin Fowler's Monolith First makes the case plainly: almost all the successful microservice stories he had heard of started with a monolith that grew too big and was broken up, and systems built as microservices from scratch often ended up in serious trouble. He adds that moving functionality between services is much harder than moving it inside a monolith.

The cheaper default is a modular monolith: one codebase and one deploy, organised into modules such as billing, accounts and orders, which only talk to each other through small, deliberate functions. If one module later needs to become a service, the seam is already there.

2. What goes wrong when the screens design the database?

Screen-first schemas copy data wherever a page needs it. The customer's address lives on the order, the invoice and the profile, each edited separately, and within months they disagree. Reporting then becomes an archaeology project.

Start from the nouns of the business instead: who the customers are, what they buy, what states an order moves through. Give each fact one home, link records with foreign keys, and let the database reject bad data with constraints rather than hoping every screen validates it. This is an hour of whiteboard work, and it is the decision that is hardest to reverse once real data exists.

3. How should an MVP store money and time?

Store money as an integer number of minor units, such as cents, or as a fixed-precision numeric, never as a floating-point number. Floats cannot represent most decimal amounts exactly, so totals drift by fractions of a cent and invoices stop matching payments. Store the currency beside every amount.

Store time as timestamptz in UTC and convert to the user's time zone only for display. A booking stored as a local time without a zone will be wrong for someone the day your first overseas customer signs up. A minimal PostgreSQL table that gets both right, and lets the database enforce the rules:

-- One fact, one home: prices in minor units, times in UTC, rules in the schema.
create table orders (
  id           bigint generated always as identity primary key,
  customer_id  bigint not null references customers (id),
  status       text   not null default 'pending'
               check (status in ('pending', 'paid', 'refunded', 'cancelled')),
  total_minor  bigint not null check (total_minor >= 0),  -- 1999 = 19.99
  currency     char(3) not null,                           -- ISO 4217, e.g. 'USD'
  created_at   timestamptz not null default now(),
  paid_at      timestamptz
);

create index orders_customer_id_idx on orders (customer_id);

Every line in that table is a bug that cannot happen: no negative totals, no unknown statuses, no order pointing at a customer who does not exist.

4. Is it enough to hide buttons users should not press?

No. Hiding a button hides nothing from someone who calls your API directly, and every browser shows them how. Every request must be checked on the server: is this user signed in, and are they allowed to touch this specific record? The common failure is an endpoint like /api/orders/123 that checks the first question and forgets the second.

If the product serves several companies from one database, add a second layer in the database itself. PostgreSQL's row-level security lets you write policies that filter every query by tenant, and when it is enabled with no policy, the default is to deny access to every row. RAITHub's row-level security guide shows the pattern with tests; it is how Sundor Skin, a B2B wholesale platform RAITHub built, isolates data across 146 PostgreSQL tables with 12 staff roles.

5. Should you write your own login system?

Not for an MVP. Login looks like two fields and a button. Underneath are password hashing, reset tokens that expire, session revocation, email verification, rate limits on guessing, and account-recovery edge cases. Each is a security problem you then own.

Use a maintained library or a hosted provider, keep your own users table linked to it, and spend the saved week on the feature customers are paying for. This site, for example, uses NextAuth rather than a home-grown session system, and still needed rate limiting on its forms, which RAITHub built without adding Redis.

6. Why can't you trust the payment success page?

Because the customer's browser may never reach it: they close the tab, lose signal or the redirect fails. If access is granted on the redirect, some paying customers get nothing. If it is granted on a URL anyone can visit, some non-paying visitors get everything.

Grant access from the payment provider's webhook, verify its signature, and process each event exactly once, because providers retry. For calls you make to the provider, Stripe supports idempotency keys on every POST request, so a retried request after a network error does not charge twice; Stripe suggests V4 UUIDs and notes keys can be pruned after 24 hours. When a webhook returns 200 but the subscription still looks wrong, this checklist walks through out-of-order events. PropDesk, a property management platform RAITHub built, collects rent through Stripe behind a suite of 1,024 tests.

7. What is wrong with editing the production database directly?

It works once and breaks the second time. A column added by hand in production does not exist in staging or on a new developer's laptop, so the next deploy fails, or worse, succeeds against a different schema. After a few months nobody can rebuild the database from scratch, which also means nobody can prove a backup will restore.

The default is versioned migration files in the repository, applied by the deploy in the same order everywhere. If your first version came from an AI builder, stop editing production by hand explains how to move a Supabase project onto migrations without losing data.

8. What counts as "slow work" in a request?

Anything that talks to a slow or unreliable outside service, or does more than a moment of computation: sending email, generating PDFs, resizing images, calling an AI model, syncing to a CRM. Done inside the request, it makes pages slow, hits hosting timeouts, and leaves work half done when it fails. A user who clicks again sends the email twice.

Put a row in a jobs table, return to the user immediately, and let a worker process the job with retries. An MVP does not need a message broker for this; a PostgreSQL table and a scheduled worker are enough until volume proves otherwise.

9. What does "shipping blind" mean, and how do you avoid it?

It means launching without a way to see what fails. The first sign of a broken checkout is a customer email, days later. Before launch, an MVP needs three things: error tracking that alerts someone, logs that include a request or user ID so a complaint can be traced, and a backup that has actually been restored once into a scratch database. An untested backup is a hope.

This is different from product analytics, which the product-side post covers. Analytics tells you what users do. Error tracking tells you what the product did to them.

Which of these can an MVP safely skip?

Most of the rest. Scale work is the classic premature investment: caching layers, read replicas, Kubernetes, multi-region hosting and a design system can all wait for evidence. So can most admin tooling beyond what support needs on day one. The rule from the top of this post applies: postpone anything that is a one-file fix later, and decide now on anything that becomes a migration of live data. For how these choices affect the schedule, see how long an MVP takes to build.

Why RAITHub for an MVP build?

  • The foundations are decided with you, in writing. The data model, permission model and payment flow are agreed before the build, as part of the fixed written quote.
  • Tests from the first commit. PropDesk runs 1,024 tests; Sundor Skin 530+; TheSkinProof, the founder's own marketplace venture rather than a client project, 750+ across 217 API endpoints.
  • Speed without skipping the load-bearing parts. BlockEstate, a multi-tenant listing platform, went from start to MVP in 6 weeks.
  • You own it. Your repository, your cloud account and IP assigned to you, with an NDA as standard. See the MVP development service for what a build includes.

When you don't need us

  • You have not validated the idea yet. None of the nine decisions matters for a landing page or a clickable prototype. Run the experiments first.
  • A no-code tool fits the job. For internal tools and early demand tests, a hosted builder avoids most of these decisions entirely.
  • You have a capable technical co-founder. Hand them this list as a checklist and keep the money.

For a first budget range before you talk to anyone, try the MVP cost estimator.

Sources checked on 29 September 2026.

If you are about to start a build, or suspect one of these decisions is already baked into yours, book the free 15-minute technical audit.

Frequently asked questions

What is the most expensive engineering mistake in an MVP?

Usually the data model, because every later feature is built on it and fixing it means migrating live data. Permissions enforced only in the UI come a close second, because the fix touches every endpoint at once.

Should an MVP be built as a monolith?

In most cases, yes: one codebase and one deploy, organised into clear internal modules. Service boundaries are hard to move once drawn, and a new product rarely knows where they belong. Split a module out when evidence says it needs to scale or deploy on its own.

Do I need automated tests in an MVP?

For the critical paths, yes: sign-up and login, permissions, payments and the core workflow. They are what lets you change the product quickly after launch without breaking the parts that earn money.

Is it fine to use floats for prices if I round them?

No. Rounding hides the error in one place and it reappears in totals, refunds and tax. Store integer minor units such as cents, or a fixed-precision numeric type, with the currency beside every amount.

What should an MVP have before launch besides features?

Error tracking that alerts someone, logs you can trace a complaint through, versioned database migrations, and a backup you have restored at least once. Together they take days, not weeks, to set up.

How do I know whether my agency is making these decisions well?

Ask to see the database schema, where permissions are checked, how payment webhooks are handled and how migrations run. A good team can answer each in a few minutes and show you the code.

how not to build an mvpmvp architecturemvp engineering mistakesdata modeldatabase migrationsstartup tech decisions

Ready to discuss your project?

Book a free 15-minute technical audit with our engineering team.