The No-Spec Cost
Scaling Requirements in Industrial Monorepos
A six-part series on why industrial monorepos fail and how to fix them. Most 50-project .NET solutions grew organically -- ServiceProvider god-objects mediate everything, DLLs are the only boundaries, and nobody knows which code implements which business feature. The cost is measured in developer hours, production incidents, and failed audits.
The fix is structural: two new projects (.Requirements and .Specifications) that create compiler-enforced logical boundaries. Features become types. Acceptance criteria become abstract methods. The compiler refuses to build until every AC is specified, implemented, and tested. Migration is incremental -- one feature per sprint, no big-bang rewrite.
Table of Contents
Part 1: The Industrial Monorepo Nobody Planned
How a 50-project .NET solution grows organically over a decade. The ServiceProvider god-object. The 400-file grab bag. The new developer who asks "which code implements order processing?" and gets no answer. The dependency graph nobody drew.
Part 2: Physical Boundaries Are Not Architecture
DLLs are packaging, not design. Namespaces are naming, not access control. The two ServiceProvider anti-patterns dissected with full implementations and sequence diagrams. The contracts grab-bag. The testing illusion (95% code coverage, 0% requirement coverage). What's missing: logical boundaries.
Part 3: Requirements and Specifications ARE Projects
The core thesis -- the longest post. Two new projects create typed boundaries. OrderProcessingFeature with 8 ACs spanning 5 services. Complete specification interfaces, implementations, tests, and generated artifacts. The validator bridge. The CI pipeline. The structured dependency graph -- after.
Part 4: What Changes at 50+ Projects
The AC cascade: add one abstract method, the build breaks across 5 teams until everyone implements and tests it. Feature traceability across 15+ projects in 1 second (not 2.5 hours). The compiler as cross-team coordination mechanism. Scaling characteristics: build time, memory, IDE responsiveness.
Part 5: Migration -- One Feature at a Time
Space migration: project by project. Time migration: feature by feature. The hybrid Program.cs with old ServiceProvider and new typed chain coexisting. The adapter pattern for legacy code. Sprint-by-sprint roadmap from 100% ServiceLocator to 0%. Common challenges and solutions.
Part 6: ROI and Maintenance Costs
The business case. Cost of the status quo: $740K--$2.4M per year in developer waste, defects, and compliance. Cost of migration: $112K one-time. Payback period: 2 months. 3-year ROI: 500%--1900%. Metric trends: defect density, onboarding ramp, build time, AC coverage.
Part 7: Inverted Dependencies -- Production-Clean Requirement Tracking
Invert the dependency direction: production artifacts have zero knowledge of requirements. Requirements references production via typeof()/nameof() and InternalsVisibleTo. Fluent builder mappings, Roslyn analyzer in inverted mode, drift detection, cross-repo tracking, and the decision guide for choosing between original and inverted approaches.
How to Read This Series
Architects and tech leads should read all seven parts in order. Part 3 is the core design; Part 6 is the business case; Part 7 is the advanced variant for sensitive environments.
Senior developers should start with Part 1--2 (the problem), then Part 3 (the solution), then Part 5 (migration) for practical adoption. Read Part 7 if production DLL cleanliness matters.
Engineering managers should read Part 1 (the problem in business terms), Part 4 (multi-team coordination), Part 6 (ROI and costs), and Part 7 Section 14 (security implications).
QA engineers should focus on Part 3 (the traceability matrix, [Verifies] attributes, compiler diagnostics), Part 6 (coverage metrics), and Part 7 Section 7 (test mapping in inverted mode).
Prerequisites
- Familiarity with C# / .NET and large-scale solution architecture
- Experience with dependency injection (Microsoft.Extensions.DependencyInjection)
- Understanding of the ServiceLocator anti-pattern (the series explains it, but recognition helps)
- The Requirements as Code post provides the foundational design this series scales
Key Concepts
| Concept | Meaning |
|---|---|
| Physical boundary | DLL, project, folder, namespace -- answers "where does this code live?" |
| Logical boundary | Feature type, specification interface -- answers "what does this code implement?" |
| AC Cascade | Adding an acceptance criterion breaks the build across all implementing projects |
| ServiceLocator | The god-object anti-pattern where every class resolves dependencies at runtime |
| Specification interface | A typed contract that the compiler enforces -- replaces ServiceLocator as the mediation layer |
| Traceability matrix | Source-generated mapping from requirement to spec to implementation to test |
Nothing in this series is hypothetical. The architecture is designed for -- and stress-tested against -- the kind of 50-project, 15-team monorepo that exists in every enterprise. The compiler doesn't care how big the monorepo is. It cares whether the types are satisfied.