Skip to content

Design Principles

This page is Compono's current, evolving statement of what it believes — revised as the project's philosophy actually evolves, not a historical snapshot. It absorbs and retires docs/design-principles.md and docs/manifesto.md's original content, per ADR-0030 Amendment 2. For how these beliefs actually shape the running system today, see Current Architecture; for the sequence of decisions that got here, see the Historical Decision Log.

Why Compono exists

Test composition in .NET deserves a design grounded in the capabilities and expectations of modern .NET, not just automatic object creation. AutoFixture demonstrated the value of automatic object creation, declarative test data, shared instances, and test-framework integration — and enabled a style of testing many teams came to depend on. But the platform has changed: records, primary constructors, required members, nullable reference types, source generators, trimming, Native AOT, modern test frameworks, better compile-time analysis, and stronger expectations around determinism and diagnostics are all part of modern .NET in a way they weren't when AutoFixture's design took shape.

Compono asks a fresh question: if a test composition framework were designed for modern .NET today, what should it look like? It is not intended to reproduce AutoFixture feature-for-feature — it's a new product focused on composing complete test environments, not just generating objects. See Migrating from AutoFixture for what that means concretely for an existing test suite.

What test composition means

Object generation is only one part of preparing a test. A test may require a system under test, constructor dependencies, shared instances, test doubles, anonymous values, realistic semantic data, reusable project conventions, framework-specific parameter binding, and deterministic reproduction of failures. Compono treats those needs as parts of a single composition problem: tests declare what they require, and Compono determines how those requirements are satisfied. See The Composition Model for what this means concretely from a test author's point of view.

Guiding principles

  • Composition over object generation. Compono is not primarily a fake-data generator or object factory — its purpose is to coordinate every contributor involved in preparing a test (constructor dependencies, shared instances, test doubles, semantic data), not just fill in field values.
  • Predictability over magic. Convenience should not come at the cost of understanding. Resolution order is deterministic and documented (see The Provider Pipeline); when composition fails, Compono explains what was requested, why, which providers were considered, which was selected, where resolution failed, and how the failure can be reproduced.
  • Source-generated by default. Compono discovers object construction metadata at compile time and generates composition plans wherever possible (Source Generation, resolved by ADR-0001) — runtime execution focuses on executing known plans rather than repeatedly inspecting types through reflection. Runtime reflection is not part of the default architecture; if ever supported, it requires an explicit opt-in (a compiler-visible MSBuild property, or a dedicated compatibility package/mode) and must never silently become the fallback path — a still-open decision, tracked in Current Architecture.
  • Deterministic by design. A composition is reproducible from its seed and configuration; a failed test reports enough information to recreate the generated values and object graph (see Determinism and Seeding).
  • Modular architecture. The core Compono package must not depend on any test framework (xUnit, NUnit, MSTest) or test-double/data library (NSubstitute, Moq, FakeItEasy, Bogus) — those capabilities live in integration packages built on stable, public extension contracts (see Package Guides).
  • Modern .NET first. Compono prefers a small, coherent design for modern .NET over broad compatibility with legacy runtimes.
  • Performance is a feature. Test infrastructure runs constantly: composition startup time, allocations, generated-plan execution, and test discovery overhead all matter, and are measured and protected rather than treated as a later optimization (see Performance).
  • Diagnostics are a feature. Composition failures should be understandable without stepping through framework internals — a useful failure is better than a clever fallback.

What Compono should avoid becoming

  • An AutoFixture compatibility layer
  • A monolithic testing toolkit
  • A service locator hidden inside tests
  • A reflection-heavy runtime
  • A collection of unrelated convenience APIs
  • A system whose behavior depends on mutable global state
  • A framework where providers silently override one another
  • A source of random, irreproducible test failures
  • A feature-complete wrapper over every third-party integration

Product priorities

When design goals compete, Compono prioritizes, in order: a coherent test composition model; predictable behavior and diagnostics; performance through source-generated execution; broad compatibility and convenience. This order may be refined as the project evolves, but convenience should not override clarity or architectural integrity.

The north star

Test authors declare what a test needs. Compono satisfies those needs through explicit, deterministic, replaceable composition providers operating within a composition context.