Back to blog

I regret how I split Prestonly's codebase

5 min read

I’ve built two products from scratch in the last few years. A language learning platform with around 10k users and paying customers. And a voice-based interview prep tool I’m still building. Both run on what most people would call a monolith. Neither has microservices.

Every time I mention this to other developers, I get the look. Like I just admitted I don’t write tests.

But the thing I actually regret isn’t that I skipped microservices. It’s that I split Prestonly across four separate repositories — frontend, backend, mobile, common packages — because it felt like the “professional” way to organize things. And that decision has been quietly draining my time for two years.

The multi-repo tax

Here’s what adding a feature looks like on Prestonly right now. I change something in the common package. I push it, wait for CI, publish. Then I update the dependency in the backend repo. Push, wait for CI. Then the frontend. Push, wait for CI. Then mobile. Three to four merge requests for one feature. If something doesn’t work right, I’m bouncing between repos trying to figure out which version of the common package each app is actually running.

It’s not dramatic. It’s just… slow. Every single time. And the slowness compounds because I’m the only engineer, so there’s nobody else to parallelize this across. I’m context-switching between repos for what should be a single coherent change.

When I started building Prepovo, I put everything in a monorepo from day one. Turborepo, pnpm workspaces, shared types via tRPC. Adding a field to an API response and using it in the frontend is one commit. One CI run. Done. The difference is embarrassing.

What I should’ve done differently

I should’ve started Prestonly as a monorepo. Full stop. Not microservices, not even separate repos — just one repo with clear directory boundaries. The common package would be a local workspace dependency instead of a published npm package. Changes would be atomic.

Would I have needed more discipline to keep the code organized? Sure. But I need that discipline anyway — the separate repos don’t actually enforce clean boundaries, they just make it painful when boundaries are crossed. Which is constantly, because that’s how product development works. Features don’t respect your repo structure.

I keep telling myself I’ll migrate Prestonly to a monorepo eventually. I’ve been saying that for about a year now. There’s always something more important to ship. So the four-repo setup persists, and every feature takes 30% longer than it should, and I just absorb it as normal. That’s the real cost of these decisions — you stop noticing the friction.

The microservices version of this mistake, but worse

If four repos are bad, imagine ten services with separate databases, deployment pipelines, and service discovery. I’ve seen this at previous jobs — onboarding a new developer took a full day just to get the local environment running. Docker Compose files with fifteen services, port conflicts, environment variables scattered across a dozen .env files.

And debugging. A bug in a monolith means looking at one codebase and one set of logs. A bug that spans three microservices means correlating distributed traces, understanding request flows, and hoping someone set up the observability correctly. Which they usually didn’t, because the team was too busy building features to instrument everything properly.

Data consistency is the one that really gets me though. The moment you split your database across services, you’re dealing with sagas and eventual consistency and compensating transactions. These are genuinely hard engineering problems. But they’re problems you created by splitting the services. You didn’t solve a business problem — you manufactured an infrastructure problem and then solved that instead.

I realize this isn’t a controversial take. “Microservices are overkill for most startups” is something 90% of developers would agree with. And yet startups keep doing it. So either the advice isn’t landing, or the social dynamics of engineering teams are stronger than rational architecture decisions. Senior engineers come from places where microservices were the norm. They bring that playbook because it’s what they know. And nobody gets questioned for choosing the “scalable” option — saying “let’s just build a monolith” feels like admitting you’re not ambitious enough.

The part I’m less sure about

Here’s where I contradict myself a little. Prestonly’s monolith backend has gotten messy in ways that a stricter service boundary might have prevented. There’s a notifications module that reaches directly into the lesson progress tables. There’s a payment webhook handler that knows way too much about the gamification system. I keep meaning to clean it up.

Would separate services have forced me to keep those boundaries clean? Maybe. Or maybe I would’ve just built the same tangled mess across service boundaries, which would be even harder to untangle. I genuinely don’t know. The discipline problem follows you regardless of the architecture.

What I do know is that for Prepovo, the monorepo monolith lets me ship fast. tRPC gives me end-to-end type safety. One deploy, one set of logs, one mental model. When it breaks, I know where to look. When I want to refactor, I refactor.

If Prepovo ever gets to the point where I need to extract a service — because a specific component has genuinely different scaling requirements, not because it seems like the right thing to do — I’ll do it then. That migration path is well-understood. Going the other direction, from premature microservices back to a monolith, never goes smoothly.

So what

The question I’d ask before splitting anything — services, repos, packages — is blunt: are you solving a problem you actually have right now, or a problem you want to have someday?

With Prestonly, I answered wrong. I split repos because it seemed professional. Now I live with the overhead. It’s not catastrophic, it’s just a tax on every feature I build, forever, until I fix it or stop building the product. And fixing it keeps losing priority to actual product work.

…which is probably how most architecture regrets play out.

Ready to practice?

Start explaining concepts out loud and get AI-powered feedback. 5 minutes a day builds real skill.

Start practicing for free