Context
Two extraction proposals arrived in the same quarter:
- Extract
listingsfor scaling reasons. The argument: traffic to listings was growing, and the monolith was "going to hit a ceiling". - Extract
notificationsfor team boundary reasons. The argument: the cross-team friction around the notifications module was slowing everyone down.
Both came from senior engineers I respected. Neither came with the data I needed.
What I asked for
For each proposal, before I'd say yes or no, I needed:
- The actual call graph. Which other modules call into this one? How tight is the coupling? How many shared types?
- The actual deploy-frequency conflicts. Are we genuinely blocked on shipping, or is it a feeling?
- The actual scaling pressure. What's the current p99? Where's the monolith's CPU and memory headroom? When does the curve hit the ceiling at the current growth rate?
- The team-boundary friction, named specifically. Which PRs triggered the friction? What did the conversation look like?
What we found
For the listings extraction:
- RPS was at 18% of monolith capacity. Growth rate put us at the ceiling in approximately six years at current trajectory.
- Listings touched seven other modules at the type level. Extraction would need a shared schema package, i.e., we'd be reinventing the monolith's boundary as a dependency edge.
For the notifications extraction:
- The deploy-conflict story didn't hold up. We ship four times a day already. The "conflict" was three Slack messages in the last six months.
- The team-boundary friction was real, but the cause was an unclear ownership boundary inside the module, fixable with a documented RACI and a CODEOWNERS update, not by extracting a service.
The decision
Stay monolithic. Both modules. Fix the team-boundary friction with
stricter module boundaries inside the monolith, eslint-plugin- boundaries, a CODEOWNERS rewrite, and a documented internal API for
the notifications module.
What played out
Eighteen months later both proposals returned, almost word-for-word. Same arguments. Same lack of data.
I had the original analysis on file, walked through it again with the proposers, and the answer held. The notifications boundary work had solved the actual friction; the listings ceiling hadn't moved.
What I'd do differently
I'd write the rejection criteria as a durable doc the first time, not as a Slack thread and a meeting summary. Re-litigating the same call a year later cost me half a day of archaeology, pulling Loom recordings, walking back through the data, reconstructing my own reasoning.
The rejection doc would have made the second round a five-minute link. And, more importantly, it would have made the criteria legible: future proposals could pre-empt the same questions, which is what you actually want a senior engineer doing.