In this guide
Somewhere along the way, "monolith" became an insult and "microservices" became a badge of modernity. Students now ask whether their final-year project should be microservices — as if the architecture were a feature.
It is not. Architecture is a trade-off, and for most student projects the monolith wins comfortably. This guide explains both honestly so you can choose with reasons, not fashion — and answer the inevitable viva question well.
What a monolith actually is
A monolith is an application where all components — UI, business logic, data access — are built and deployed as a single unit. One codebase, one deployment, one database (usually).
This is not a dirty word. Your MERN project, your Django app, your Laravel site: all monoliths, and all perfectly respectable. A modular monolith — one codebase with clean internal boundaries between modules — is what most successful startups run for years.
Monolith strengths:
- Simple to develop, test, and debug — one codebase, one deployment pipeline
- No network calls between components — function calls are fast and reliable
- Transactions are easy — one database means ACID guarantees across your whole app
- Easy to reason about — stack traces stay in one process
Monolith weaknesses:
- Scaling is all-or-nothing — if one endpoint is hot, you replicate the entire app
- One bad deploy takes everything down
- Codebase can become tangled as teams grow (the "big ball of mud")
- Technology choices are coupled — migrating one module to a new language is painful
What microservices actually are
Microservices split the application into small, independently deployable services, each owning its own data and communicating over the network (usually HTTP or message queues).
Monolith: Microservices:
[ One App ] [ Auth ] [ Orders ] [ Payments ] [ Search ]
| | | | |
[ One Database ] [DB] [DB] [DB] [DB]
Each service is developed, deployed, and scaled independently. The orders service can be rewritten in Go while auth stays in Node. The search service can scale to ten instances while payments stays at two.
Microservices strengths:
- Independent scaling — scale only the hot services
- Independent deployment — ship the search service without touching payments
- Technology freedom per service
- Team autonomy — small teams own services end to end (this is the real reason big companies use them)
Microservices weaknesses:
- Distributed systems problems everywhere — network failures, partial outages, eventual consistency, distributed tracing
- Operational complexity explodes — service discovery, API gateways, centralized logging, monitoring per service
- Data consistency gets hard — no more simple database transactions across services; you now design sagas and compensating actions
- Debugging crosses process boundaries — a slow request might span five services
- More infrastructure to build before you ship anything
Head-to-head comparison
| Monolith | Microservices | |
|---|---|---|
| Typical team size | 1–8 developers | 20+ developers (multiple teams) |
| Deployment | One unit, simple | Many units, needs CI/CD + orchestration |
| Scaling | Whole app at once | Per service |
| Data consistency | Easy (one DB, transactions) | Hard (distributed, eventual) |
| Failure modes | Whole app can go down | Partial failures, cascading failures |
| Debugging | Straightforward | Needs distributed tracing |
| Time to first deploy | Fast | Slow (infrastructure first) |
| Operational skill needed | Low–medium | High |
The honest decision framework
Ask in this order:
- How many developers? One person or a small team → monolith. Microservices solve team scaling problems; with one developer they only add overhead.
- Do parts of the system need wildly different scaling? A video-processing pipeline beside a CRUD API might justify splitting. A uniform CRUD app does not.
- Do you have the operations skill? Microservices need CI/CD, container orchestration, centralized logging, and monitoring from day one. Without them you get a distributed monolith — all the coupling, plus network failures.
- Is this a learning project with a deadline? → monolith. You will ship a complete, working system instead of half-built infrastructure.
Note: The industry pattern is "monolith first." Even companies famous for microservices started as monoliths and split only when specific pain (team size, scaling hotspots) forced it. Starting with microservices is optimizing for problems you do not have yet.
The middle paths (often the right answer)
- Modular monolith — one deployment, but strict internal module boundaries (separate folders/packages, no cross-module database access). You get most of the code-organization benefits with none of the distributed-systems pain. If you later need microservices, clean modules split cleanly.
- Monolith + extracted services — keep the core as a monolith; extract only the genuinely different workload (e.g. an ML inference service, a file-processing worker) as a separate service. This is pragmatic and common.
- Serverless functions for spikes — keep the monolith, add serverless functions for bursty side tasks (image resizing, webhook handling).
If you do choose microservices for learning
Do it deliberately as a learning exercise, and do it right:
- API gateway in front (single entry point, auth, rate limiting)
- Each service owns its database — no shared databases, or you have a distributed monolith
- Async communication between services via a message queue where possible; synchronous chains of HTTP calls create cascading failures
- Centralized logging and tracing from the start — you will be blind without them
- Docker Compose for local development so the whole system runs with one command (see the Docker Compose guide)
- CI/CD that can deploy services independently (see the GitHub Actions guide)
Finding service boundaries (if you split)
The hardest microservices question is not tooling — it is where to cut. The industry vocabulary for this is bounded contexts (from domain-driven design): split along business capabilities, not technical layers.
- Good boundaries: orders, payments, notifications, search — each owns its data and its business rules, and they interact through narrow, stable contracts.
- Bad boundaries: "frontend service," "database service," "utils service" — splitting by technical layer just distributes your monolith across the network with none of the autonomy benefits.
Warning signs your boundaries are wrong:
- Services chat constantly (dozens of calls per user request) — they are one service wearing two names.
- Two services share a database — the data boundary was never actually drawn.
- You cannot deploy one service without deploying another — they are coupled; merge them back.
The pragmatic path remains: start as a modular monolith with clean module boundaries, and extract a service only when a module genuinely needs independent scaling, deployment, or technology.
Common mistakes
- Choosing microservices for a solo final-year project. The viva question "why microservices?" needs a better answer than "it is modern." Interviewers know the trade-offs.
- Shared database across "microservices." This is a distributed monolith — you kept the coupling and added network latency.
- Synchronous chains. Service A calls B calls C calls D; when D is slow, everything is slow. Prefer events and queues.
- No handling of partial failure. In a monolith, a function either works or throws. Across services, the payment can succeed while the notification fails — design for it (retries, idempotency, sagas).
- Skipping the modular monolith step. Jumping straight to microservices skips the cheaper architecture that solves 80% of the organizational problem.
Where to go from here
- Running multiple services locally: Docker Compose for Full-Stack Projects.
- Deploying services independently: GitHub Actions: Build a CI/CD Pipeline.
- Services talking asynchronously: Message Queues: RabbitMQ and SQS Explained.
- Coordinating the team building it: Git branching workflows for project teams.
- More architecture topics in the Web Development branch hub.