A familiar pattern in a Quarterly Business Review: harsh questions are being asked about why performance at peak was uneven, releases were risky, and that one noisy dependency kept rippling across teams. The debate starts again and again. Re-platform and rewrite to microservices or harden the good old monolith? As always - the right answer is not ideology. It is a clear read on your domain boundaries, deployment independence, and the cost of change.
Why this matters
Investors want scale without fragility, faster iteration without runaway cost, and a predictable path from here to the next 10x. They will ask the founders how architecture supports revenue goals, regulated customers, and uptime promises. Founders need a framework, not slogans.
Investor expectations: Dos and Don'ts
- Domain boundaries — Draw clear lines between parts of the system and show them on one page. Use context maps and bounded contexts so each area owns its data and decisions, and changes stay local. Don't let teams call each other at random or share code in ways that blur ownership. If boundaries are not written down, every change risks breaking something else.
- Coupling and cohesion — Keep each module focused on one purpose and list its allowed dependencies. Use code reviews and simple tools to prevent circular dependencies. Don't hide shared state or let services talk through the same database tables. Avoid grab bag utility libraries that everything depends on.
- Data ownership and integration — Assign a clear owner for every dataset. Version your schemas. Connect systems through APIs or events. If you use change data capture, say so in simple terms and document the contract. Don't allow multiple services to write to the same table. Never ship breaking changes without a version and a migration plan.
- Deployment independence — Make it possible to deploy one service at a time with blue green or canary releases. Keep interfaces backward compatible so others do not need to deploy with you. Don't force lockstep releases or shared maintenance windows just because systems are tightly coupled.
- Runtime performance and bottlenecks — Measure real user latency at P95 and P99. Profile hotspots. Size queues. Tune databases and caches based on evidence. Don't overprovision without data or chase CPU charts without flame graphs and load tests.
- Scalability mode — Be explicit about where you scale up a machine, where you scale out across many machines, and how state is partitioned. Test autoscaling under load. Don't just rely on a single stateful chokepoint or expect autoscaling to fix design limits.
- Resilience and failure isolation — Set service level objectives, use error budgets, add circuit breakers and bulkheads, and run disaster recovery and restore drills in a clean room. Don't accept failures that cascade across services or skip timed restore tests.
- Observability and SLOs — Ship metrics, logs, and traces with the same tags for service, environment, and team. Track simple health views like Requests, Errors, Duration and Utilization, Saturation. Review alerts often. Don't rely on ad hoc log searches or tolerate silent failures that users feel before you do.
- Team topology fit — Align teams to the slices of the system they build and run. Each team should own its service, its data, and its on-call. Don't organize by technical layers that create gaps in ownership and constant cross team coordination.
- Cost and capacity predictability — Track cost per service and per unit of work, for example per API call or job. Reserve steady capacity when it saves money. Watch egress fees between regions and providers. Don't treat list prices as a forecast or hide cross plane and cross service costs inside averages.
Monolith vs Microservices
Monolith is a better fit when
- Domain boundaries are still evolving and you refactor often.
- Team is small and benefits from shared context and in process changes.
- Strong consistency and low latency matter more than independent scaling.
- You can enforce modular boundaries, internal APIs, and feature flags inside one repo.
- Clean data ownership is not yet possible across teams.
- Platform capacity for contracts, tracing, and SRE is limited right now.
Red flag: if one module is the clear hotspot or release coordination is the main blocker, start planning an extraction seam.
Microservices are a better fit when
- Domains are stable and ownership is clear per team.
- You need independent deploys and scaling for different slices, for example batch vs real time.
- Workload shapes differ and one area is a proven hotspot for CPU, memory, IOPS, or special hardware like GPUs.
- You can staff platform basics such as identity, secrets, templates, contracts, tracing, and SLOs.
- Data can be owned per service without cross writes to the same tables.
- Cost needs to be tuned per slice, and egress and capacity can be managed per service.
Red flag: if traffic is uniform, transactions are tightly coupled, or boundaries are fuzzy, a modular monolith is simpler and cheaper for now.
Migration readiness process
- Map seams and ownership: produce a one page context map with dataset owners and contracts. If ownership is unclear, stay monolith and modularize.
- Measure delivery pain: track change failure rate, lead time, deploy frequency, MTTR for 4 to 6 weeks. If the main bottleneck is cross team coordination or blast radius, consider extracting a seam.
- Prove a hotspot: show one area driving a disproportionate share of CPU, memory, IOPS, GPU, or incident minutes. If yes, isolate it.
- Validate data boundaries: confirm the candidate service can own its writes without cross table edits. If not, keep it in the monolith while you refactor ownership.
- Test state partitioning: demonstrate sharding or queue based isolation in a sandbox. If you cannot partition state, microservices will add cost without benefit.
- Check platform readiness: confirm paved paths exist for identity, secrets, logging, tracing, contract tests, SLOs, and release strategies. If missing, invest first.
- Build a cost model: compare 12 to 36 month TCO for both options including egress, support tiers, and staffing. Choose the cheaper model for the same SLOs.
- Run a time boxed spike: implement one extraction with blue green or canary, backward compatible contracts, and a strangler pattern. Measure the payoff before scaling out.
- Set exit criteria: define in advance what success looks like, for example 30% latency drop or 50% less coordination for releases and stop if you do not hit it.
Stage and stake for monolith vs. microservices
Maturity is not "microservices by default." It is about conscious, evidence-based choices. Choose the simplest architecture that meets today's constraints and can evolve tomorrow. Revisit the choice with data, not fashion.
What good looks like at any stage:
- You can explain why each boundary exists, who owns it, and how it changes.
- Delivery, reliability, and cost are measured and guide decisions.
- Rollout, rollback, and restore are practiced.
- Contracts are versioned and backward compatible during change.
How to communicate maturity to investors:
- "Here is our current shape and why."
- "Here are the seams we are watching and what would trigger change."
- "Here is the evidence that the last change paid off."
- "Here is our rollback and restore plan if it does not."
Glossary
- Bounded context: The smallest coherent domain where a model and language stay consistent.
- Change Data Capture: a method that reads a database's commit log to stream row level inserts, updates, and deletes so other systems stay in sync in near real time.
- Contract: The explicit interface and schema a consumer relies on.
- Strangler pattern: Incrementally replacing a legacy capability by routing traffic to a new component at the seam.
- Bulkhead: A limit or partition that keeps one failure from cascading.
- Error budget: The agreed allowance of unreliability that guides release risk.
Your turn
Where did your architecture hurt the most under peak or during releases? What change made the biggest difference?
Originally published on the Tech Due Diligence Playbook newsletter on LinkedIn.