LogoTRUONG PHAM
Home
Projects
Blogs
YouTube
Contact

Newsletter

Stay updated with technical artifacts and engineering insights.

LogoTRUONG PHAM

Building scalable software and sharing insights on technology & life.

Sitemap

  • Home
  • Projects
  • Blogs
  • YouTube
  • Contact

Connect

  • GitHub
  • LinkedIn
  • Email
  • YouTube

© 2024 TRUONG PHAM. © All rights reserved.

Privacy PolicyTerms of Service
Back
Monorepo or Microservice? Lessons from a 2-Person Team
Microservice Journey: Lessons & Trade-offs

Monorepo or Microservice? Lessons from a 2-Person Team

This article was written after 2-3 months of building a microservice system from scratch while learning along the way.

TP
Truong PhamSoftware Engineer
PublishedMarch 20, 2024
Stack
microservice ·monorepo ·backend

This article was written after 2–3 months of building a microservice system from scratch—with a networking background, not pure backend. If you are in a similar situation, I hope what I share will help you avoid some of the traps I fell into.


Context

Team of 2. Deadline is tight. Requirement: build a microservice system.

The first question I had to answer wasn't "how to write a service"—but rather: how to organize the code properly?

Monorepo or multi-repo? This was the first architectural decision, and it affected everything that followed.


What is a Monorepo and Why I Chose It

A Monorepo simply means all the source code for all services is placed in a single repository. It might sound "un-microservice-like"—but it's actually not.

Monorepo ≠ Monolith. This is a common misconception.

You can still separate services, deploy independently, and scale independently—but they live together in one repo. Tech giants like Google, Meta, and Uber all use monorepos at a massive scale.

With a 2-person team, I chose a monorepo for very practical reasons:

Easier management of shared dependencies. Instead of each repo having to keep its own shared libraries, configs, or type definitions—a monorepo allows for immediate sharing. No need to publish packages, no need for version syncing across repos.

Easier cross-service refactoring. When an interface changes, you fix it in one place and immediately see all affected areas—instead of having to open multiple repos and sync manually.

Easier onboarding for new members. Clone one repo, run the entire system. Much simpler than cloning 5–10 repos and configuring each one.

Simpler CI/CD in the early stages. One pipeline, one place to look, one place to debug when issues arise.


But Monorepos Aren't Without Problems

As the system grows, the monorepo starts to show weaknesses:

Increased build time. Without incremental build tools (like Nx, Turborepo, Bazel), every push builds everything—even if you only changed a small file.

Access control is harder. Multi-repo allows limiting who can read/write which repo. Monorepos need additional tooling for the same.

"Contamination" between services. Without good discipline, devs might accidentally import code directly from another service instead of going through the API—breaking service boundaries.


Microservices for Small Teams: The Real Trade-off

This is the part I want to be most direct about.

Microservice is not a good or bad architecture in itself. It is a suitable or unsuitable architecture for your context.

For large teams (30–50+ people), microservices solve a real problem: multiple teams working in parallel, independent deployments, scaling different parts of the system. The operational overhead is "paid for" by flexibility and parallel development speed.

For a 2-person team, it's a completely different story. You pay the full operational cost of microservices—but you don't have enough people to "benefit" from it the way it was designed.

Specifically, what microservices demand that small teams often lack the resources to do right:

  • Service discovery & load balancing—requires infrastructure, not just code.
  • Distributed tracing—to understand how many services a request passes through and where time is spent.
  • Centralized logging—logs scattered across multiple services are a nightmare when debugging.
  • Circuit breaker & retry logic—to handle when one service dies without bringing down the whole system.
  • API versioning—so services can deploy independently without breaking each other.

If these are missing, you don't have microservices—you have a distributed monolith: as complex as microservices but without the benefits.


So, What Should You Do?

If I were to do it again, I would start with a modular monolith—a single application but with code organized into clear modules, with strictly enforced boundaries. When there is a real need to scale, and when the team is large enough to operate it—that's when you split into microservices.

Martin Fowler calls this "Monolith First"—and I think it's the most practical advice for small teams.

Of course, if the requirement is already handed down and you must do microservices—then do it. But be honest with each other about what is being skipped, and prioritize building the core essentials first: observability, CI/CD, and clear service boundaries.


Lessons Learned

Microservices aren't hard in the code. They are hard in the operations. And doing operations right takes time, tooling, and enough people.

Choosing monorepo or multi-repo is a smaller decision than you think. What matters more is: are the service boundaries clear? Are they communicating via API or sharing a database? When one service dies, does the system keep running?

If you can answer those questions, you are doing microservices right—whether the code lives in one repo or ten.

Series • Part 3 of 20

Microservice Journey: Lessons & Trade-offs

NextREST, gRPC, or NATS? Don't Let Choice Bury Progress
Microservices is Not Just About Splitting Services
01Trade-offs Under Pressure02Microservices is Not Just About Splitting Services03Monorepo or Microservice? Lessons from a 2-Person TeamReading04REST, gRPC, or NATS? Don't Let Choice Bury Progress05Distributed Transactions - Why 'Immediate Consistency' is a Luxury06Authentication & Authorization - When API Gateway Carries the Team07Docker Compose is Not Enough - The Local Development Story08Database Separation - A Painful but Necessary 'Divorce'
TP

Written by Truong Pham

Software Engineer passionate about building high-performance systems and meaningful experiences.

Read more articles