The Thesis
Convention over Configuration was a revolution. It eliminated thousands of lines of XML and replaced them with naming rules and folder structures.
But Convention has a hidden cost: you must document every convention (wikis, ADRs, onboarding guides) and enforce every convention (ArchUnit, NetArchTest, custom analyzers, CI scripts). That is work done twice — and both artifacts drift independently from the codebase they describe.
What if the compiler could replace all of that? What if a single attribute — [AggregateRoot], [Injectable], [Validated] — drove a Source Generator that produced the correct code, and a Roslyn Analyzer that guarded the boundaries? No convention to document. No enforcement code to maintain. The attribute IS the documentation. The generated code IS the implementation.
This series calls that fourth era Contention — where the compiler contends with the developer, not through convention policing, but through type-safe code generation.
Part I: The Four Eras
Code, Configuration, Convention, Contention — four ways to tell a system what to do. Each era improves on the last. The Convention Tax: why documenting and enforcing invisible rules is double the cost of just generating the right code. The Rosetta Stone example: [AggregateRoot] across all four eras.
Part II: Dependency Injection
From new UserService(new UserRepository(...)) to XML containers to Scrutor assembly scanning to [Injectable(Lifetime.Scoped)]. The convention era requires a naming doc, a scanning config, and an integration test. Contention: one attribute, zero enforcement code.
Part III: Validation
From manual if-checks to DataAnnotations to FluentValidation to [Validated] source-generated validators. Convention says "every command must have a validator" — but who checks? Contention: the SG generates the validator from required properties. Nothing to forget.
Part IV: API Contracts and Serialization
From hand-built JSON responses to Swagger annotations to Minimal APIs to [TypedEndpoint] that generates the endpoint, OpenAPI spec, and typed client in one pass. Convention says "all endpoints return Result
Part V: Database Mapping and EF Core
From ADO.NET to NHibernate XML to EF Core conventions to [AggregateRoot] / [ValueObject] driving EF configuration, repository interfaces, and factory generation. The flagship example from the CMF DDD DSL — where Convention required folder structure docs, EF convention docs, and NetArchTest rules. Contention: one attribute per entity.
Part VI: Testing and Requirements
From random tests to [Trait] metadata to folder naming conventions to [ForRequirement(typeof(Feature))] with a source-generated compliance matrix. Convention required a test plan document, a naming convention wiki, and a custom compliance scanner. Contention: the type system IS the traceability matrix.
Part VII: Architecture Enforcement
From wiki pages to NDepend XML to NetArchTest to [Layer("Domain")] with source-generated project constraints. Convention: an ADR, a NetArchTest suite, a CI check. Contention: one attribute, and the SG generates InternalsVisibleTo restrictions that make illegal dependencies a compile error.
Part VIII: Configuration and Options
From AppSettings["key"] to appsettings.json to IOptions<T> to [StronglyTypedOptions("Smtp")] with source-generated binders and validators. Convention: an options documentation page, a validation convention doc, and a startup integration test. Contention: one attribute.
Part IX: Error Handling and Result Types
From try/catch to exception hierarchies to Result<T> to [MustHandle] analyzers with source-generated exhaustive Match() methods. Convention says "always pattern-match your Results" — but code review is the only enforcement. Contention: the analyzer refuses to compile unmatched Results.
Part X: Logging and Security
From Console.WriteLine to NLog XML to ILogger<T> to [LoggerMessage] source generation. From if (user.Role == "Admin") to [Authorize(Policy)] to [RequiresPermission(Permission.ManageUsers)] with source-generated policy registration. Two domains, one pattern: Convention documents and polices. Contention generates and guards.
Part XI: The Compiler as Architect
The Grand Convention Tax Table across all 10 domains. The meta-pattern: Attribute → Source Generator → Generated Code → Analyzer → Diagnostic. When contention goes too far. The thesis restated: Convention is an honor system with expensive policing. Contention is a type system with free generation.
How to Read This Series
Architects evaluating the approach should read Parts I, V, VII, and XI — the thesis, the DDD flagship, architecture enforcement, and the synthesis.
Developers adopting Source Generators should read Parts I-III for the fundamentals, then jump to whichever domain matches their current work.
Tech leads frustrated with convention drift should read Parts I and XI, then the domain that hurts most on their team.
Anyone in a hurry should read Part I (the framework) and Part XI (the verdict).
Prerequisites
- Familiarity with C# and .NET (the examples use .NET 8+)
- Basic understanding of Source Generators (or willingness to learn — Part I covers the essentials)
- Experience with at least one "Convention over Configuration" framework (ASP.NET Core, Rails, Spring Boot)
Related Posts
This series builds on and references:
- Don't Put the Burden on Developers — the philosophical anchor: structural fixes, not discipline
- Requirements as Code — the C# typed requirements chain
- Requirements as Code (TypeScript) — the TypeScript equivalent
- Onboarding Typed Specifications — the seven-part onboarding series
- Builder Pattern — Source Generator patterns in practice
- DDD — Domain-Driven Design with the type system
- Meta-Metamodeling — M2/M3 theory behind attribute-driven generation
- Quality to Its Finest — multi-layer quality gates
- From Mud to DDD — brownfield DDD migration