REPO LAYOUT VS. RUNTIME ARCHITECTURE
title: "Monorepo is not Monolith — and the difference decides your CVE response time" published: false description: "Repo layout and runtime topology are orthogonal axes. Here's what each one actually costs you in versio
title: "Monorepo is not Monolith — and the difference decides your CVE response time"
published: false
description: "Repo layout and runtime topology are orthogonal axes. Here's what each one actually costs you in version control, updates, scaling, and scanning."
tags: architecture, devops, security, kubernetes
cover_image:
canonical_url:
Repo layout and deployment topology are orthogonal. Most orgs conflate them, split their runtime to solve a version-control problem, and end up with a distributed monolith that fails together, deploys together, and now also drops packets.
The decision that actually matters is where you enforce a boundary — compiler, or network — and you make that choice once per boundary, not once per company.
The two axes
Fig. 1 — The four real options. "Monorepo vs. microservices" is a category error.| Axis | Question it answers | Reversible? |
|---|---|---|
| Source layout (mono/poly-repo) | How do teams land code? | Yes — painful but mechanical |
| Deployment unit (monolith/services) | What ships and scales independently? |
No — data migrations aren't git revert
|
Asymmetry of regret. Repo layout is a two-way door. Runtime topology is a one-way door the moment each service owns its data. Sequence accordingly: change the repo layout first, and only split the runtime once module boundaries have proven stable in a monolith.
Because it arrives by accretion, not decision. Someone extracts a shared auth library into its own repo for "reuse," and now a one-line change is two PRs, a release, and a version bump — with none of the deploy independence that would justify it. The tell: your internal libraries have semantic versions but only one consumer.
What actually runs
Fig. 2 — Same four capabilities. The difference is where the boundary is enforced.A network call is a function call that acquired five new failure modes and lost its type checker. That's the entire trade:
| You gain | You pay |
|---|---|
| Independent deploy | Timeouts, retries, backoff, idempotency keys |
| Independent scale | mTLS certs + rotation, SPIFFE identity |
| Independent failure | Distributed tracing, or you debug blind |
| Per-workload least privilege | NetworkPolicy per pair, contract versioning |
| Team autonomy | Eventual consistency where you had a transaction |
The availability math nobody runs first
Services in a synchronous request path multiply, they don't average. For n services each at availability a :
At a respectable a=0.999 per service:
| Services in path | Path availability | Annual downtime |
|---|---|---|
| 1 (monolith) | 99.90% | ~8.8 h |
| 5 | 99.50% | ~43 h |
| 20 | 98.02% | ~174 h |
The corollary. Microservices only improve availability if calls are asynchronous or optional. A synchronous fan-out of 20 services is strictly less available than the monolith it replaced. If reporting being down can 500 your checkout, you didn't isolate failure — you multiplied it.
- Do two services have to be released in a fixed order? → not independent
- Do they share a database schema? → not independent
- Does one service's deploy require the other's integration suite? → not independent
- Does a single user story routinely touch 3+ repos? → boundaries follow the org chart, not the domain
- Is there a "shared-models" library every service pins? → you have a monolith with extra latency and a build step
Any of these = the costs of both architectures, the benefits of neither. Merge them back.
Version control and updates
An engineering drawing has one revision block. A drawing set has a rev per sheet plus an index saying which revs go together. That index is the whole problem with polyrepo microservices.
| Concern | Monolith | Microservices | Monorepo mitigates? |
|---|---|---|---|
| Unit of change | Caller + callee in one commit; the compiler catches the mismatch | Two commits, two repos; nothing catches it until prod — unless you run consumer-driven contract tests | ✅ Atomic commit restores the invariant |
| Breaking a contract | Rename, fix call sites, merge | Expand → migrate → contract: 3 releases, maybe 3 teams | ✅ Partially — still need runtime bake |
| "What was running at 03:14?" | git tag |
Deploy manifest / cluster state. Git cannot tell you. | ❌ Orthogonal — that's GitOps |
| Bisecting a regression |
git bisect, works |
Bisect across the deploy manifest, by hand | ✅ Meaningfully |
| Update blast radius | Full-fleet restart for a one-line fix | One deployment — the real win | ❌ |
| Base-image bump | 1 Dockerfile, 1 PR | N Dockerfiles, N repos, N reviews, N releases | ✅ The killer argument |
| Dependency confusion risk | One lockfile, one registry config | N lockfiles, N registry configs, one misconfigured | ✅ |
DORA's actual finding: deployment frequency correlates with loosely coupled architecture and independent testability — not with the number of repos or containers. A well-modularized monolith on trunk-based development beats a badly-split microservice estate on every DORA metric. The architecture is a means; the coupling is the thing.
Scalability
Fig. 3 — Granularity of scaling is the real dividing line, not code size.The only scaling argument that survives contact is divergent resource profiles. If your resizer is CPU-bound and spiky while auth is memory-flat and steady, you're sizing every replica for the resizer and paying for idle auth 24/7. That's a real bill.
If every module has the same profile, splitting buys you a network hop and a Grafana dashboard.
| Concern | Monolith | Microservices |
|---|---|---|
| Horizontal scale | Add replicas behind an LB. Stateless monoliths go much further than folklore suggests. | Per-service HPA/KEDA. Efficient only when profiles diverge. |
| Vertical waste | Pod sized for the hungriest module | Right-size per service |
| Failure isolation | Reporting's leak OOM-kills auth. Shared fate. | Reporting dies alone — iff callers have timeouts and breakers |
| The database | Usually the actual bottleneck | Splitting app code does not split the DB. Real split means outbox and sagas where you had BEGIN TRANSACTION. |
| Team scale | Merge contention past ~20–30 engineers — solvable with a merge queue | Conway's law made explicit: independent cadence per team |
If your monolith is slow because of an unindexed query, microservices will give you the same unindexed query plus a service mesh.
Supply chain and scanning
Fig. 4 — Microservices shrink each blast radius and multiply the number of blast radii.This is where the two architectures invert, and where most security programs get it backwards.
| Concern | Monolith | Microservices |
|---|---|---|
| SAST | One scan, whole call graph. Taint analysis works end to end. | Per-service scans. The scanner cannot follow taint across a network hop — cross-service injection is structurally invisible. Under-priced regression. |
| SCA | One lockfile, one fix. But one bad transitive dep taints everything. | N lockfiles. Smaller each; drift guaranteed; inventory mandatory. |
| Findings noise | Big image → many findings, most in code paths never executed. Kill this with VEX, not with an architecture change. | Slim images, genuinely fewer findings. Real benefit. |
| Fleet CVE response | Hours. Bump, rebuild, roll. | Days to weeks — unless monorepo or golden-base auto-rebuild. Ask your worst team, not your best one. |
| Runtime attack surface | One identity, one NetworkPolicy. Compromise = full app + full DB creds. | Per-workload SPIFFE identity, per-pair NetworkPolicy, minimal filesystem. The genuine security win. |
| Policy enforcement | One gate | Enforce centrally at admission, never per-repo. Per-repo policy rots to the level of the least-staffed team. |
| Provenance / SLSA | One attestation, one control mapping | N attestations. SLSA L3 needs a hardened build platform — N times, or one shared platform. |
The pattern that actually works, regardless of which side you land on:
golden base image (hardened UBI/distroless)
└─> bump triggers rebuild of ALL dependents
└─> SBOM + in-toto provenance attestation
└─> cosign sign
└─> admission control verifies signature (Kyverno/Gatekeeper)
└─> Dependency-Track fleet inventory
The synthesis. Monolith: fast, complete remediation + wide blast radius. Microservices: narrow blast radius + slow, incomplete remediation. Monorepo + microservices + golden base is the only combination that buys both. If you're splitting the runtime and staying polyrepo, you're choosing the slow remediation on purpose — write that down in the ADR.
The gate
Split a service out only if you can say yes to at least one trigger and yes to all the mitigations.
Triggers — at least one:
- Materially different resource profile (CPU vs memory, spiky vs flat)
- Materially different failure domain (this must survive when that dies)
- Materially different compliance boundary (this touches PII and that doesn't)
- A team that needs independent cadence and owns the whole domain
- Different scaling ceiling or language/runtime, with a real justification
Mitigations — all of them:
- Callers have timeouts, jittered retries, circuit breakers
- The service owns its data; no shared schema
- Contract tests in CI on both sides
- Distributed tracing before the first split, not after
- Golden base image with rebuild-on-bump
- Centralized admission policy and signed images
Not on the list, and never a valid trigger: "the codebase is big." Big is what modules are for.
Verdict
Start monorepo + modular monolith. Enforce module boundaries in CI (import linting, architecture tests) so the seams are real before they're physical. Split one service at a time, strangler-fig style, only against the gate above. Stay monorepo through the split — it's the cheapest insurance you'll ever buy against base-image drift.
| Axis | Winner | Confidence |
|---|---|---|
| Version control | Monorepo | High — few real counterarguments below ~1000 engineers |
| Updates | Monolith | High |
| Availability | Monolith, unless calls are async | High |
| Scalability | Microservices, iff profiles diverge | Conditional |
| Supply chain | Monorepo + microservices + golden base | High |
Which axis does your org get wrong? I'd guess the one nobody wrote an ADR for.
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.



