Design
for .NET
The Foundry of Precision Code
One generator to rule them all, one generator to find them,
one generator to bring them all โ and at compile time bind them.
Describe your domain. One source generator writes the application around it โ plain C# you can read, checked at compile time, and yours.
[Endpoint(HttpVerb.Post, "/{id}/confirm")]
[EndpointGroup<ReservationsGroup>]
[RequirePermission(BookingPermissions.Reservation.Update)]
[Mutation(Mode = MutationMode.Update)]
[TransitionsTo<ReservationStatus>(ReservationStatus.Confirmed)]
public partial class ConfirmReservationMutation
: Mutation<Reservation, ConflictError>
{
public required Guid Id { get; init; }
}
// No body: load, permission, transition, commit
// and the 409 are all declared. // excerpt โ ordinary C#, in obj/
var builder = endpoints.MapPost("/{id}/confirm", ...
{
if (!RequestBinder.TryBind<Guid>(__raw_id, out var id))
{ /* 400, naming the field */ }
var invoker = services.GetRequiredService<
IMutationInvoker<ConfirmReservationMutation, Reservation>>();
// โฆ
});
builder.RequireAuthorization(policy => policy.AddRequirements(
new PragmaticPermissionRequirement(
new[] { "booking.reservation.update" }, PermissionMode.All)));
builder.WithMetadata(new ProducesResponseTypeMetadata(409, ...));
// โฆ 200, 400, 401, 403, 404, 422 and 500 likewise
Declare Intent.
The Compiler Writes the Plumbing.
What the generator can decide at compile time, it decides there, and emits the line that does it โ not a lookup that might find nothing at runtime.
One Generator
One incremental source generator, 31 feature pipelines: repositories, endpoints, validators, invokers, DI wiring, OpenAPI โ from the declarations you write.
Modular by Design
45 modules. Reference a package and its feature switches on; remove it and the code it generated disappears. Adopt one library or the whole stack.
Mistakes Are Diagnostics
Over 400 PRAG diagnostics, each with an ID and a location, catch at build time what would otherwise fail on the first request โ or in production.
Built for Agents Too
A declaration is a dozen typed lines an agent can read; a diagnostic is a loop it can close on its own. A Claude Code plugin with 35 skills teaches the framework.
AOT-First Design
The generated code never reflects, and a web host serving generated endpoints is published Native AOT by the gate. EF Core is not AOT-compatible yet, so a host with persistence is not an AOT application today.
The Module Declares, the Host Composes
Each module states facts about itself; the host, which sees every assembly, puts them together โ the same modules run as one host or as two.
The Shift.
For decades, C# developers fought verbosity with abstractions, magic strings, and reflection. Source generators change the equation.
The Old Approach
You write explicit, descriptive code โ but then you pay the price at runtime. The framework uses reflection to discover your types, scan assemblies, build registrations. Startup is slow, trimming breaks, and a missing registration is found by the first request.
- โ Reflection-based discovery at startup
- โ Convention-over-configuration hides intent
- โ Runtime errors for compile-time mistakes
- โ Hand-written plumbing, a little different every time
The Pragmatic Approach
You write the same explicit, descriptive code โ but now the compiler reads it before the runtime does. Attributes and types become instructions for the source generator. Verbosity is no longer a cost. It's the input.
- โ Compile-time code generation โ no runtime discovery
- โ Explicit declarations are the input, not the cost
- โ Build errors for declarations that do not fit together
- โ Plumbing written the same way every time, tested once
"The best code is not the shortest โ it's the most legible to both humans and machines. A source generator lets you be as descriptive as you want, at no runtime cost."
Your code speaks.
A declarative API is semantic by construction. Attributes like
[DomainAction],
[Mutation(Mode = MutationMode.Create)] and
[Query<T, TDto>]
carry meaning โ not just configuration.
Repetitive code is where agents are weakest: every copy is a little different, and every copy is code to review. A generator writes it the same way every time. What is left to review is the declaration and the domain logic โ a few dozen lines, not a few hundred.
- โ The context is small โ a declaration is a dozen typed lines
- โ The feedback is immediate โ a diagnostic at build time, with a location
- โ Patterns are consistent โ one way to do each thing
// A read with no body: filters, sorting and a
// projection computed in SQL.
[Query<Reservation, ReservationSummaryDto>]
[RequirePermission(BookingPermissions.Reservation.Read)]
[Endpoint(HttpVerb.Get, "api/reservations/search")]
public partial class SearchReservationsQuery
{
[Filter] public Guid? GuestId { get; init; }
[Filter] public ReservationStatus? Status { get; init; }
[Sort(DefaultDirection = SortDirection.Descending)]
public SortDirection? CheckInSort { get; init; }
}
Built for the
You still read what the agent wrote. There is just much less of it, and none of it is plumbing.
Docs for People and Agents
Every module documents its concepts, common mistakes and troubleshooting, and an
llms.txt indexes it all for an agent.
Claude Code Skills
A plugin that teaches the framework:
pragmatic-new-app interviews you about your domain and
scaffolds the solution;
pragmatic-use-persistence,
pragmatic-use-actions-endpoints and the others know
each module, with examples copied from tested code.
A Loop the Agent Closes
When two declarations do not fit together, the build says so, with an ID and a location โ feedback an agent can act on before a person looks at anything.
400+ diagnosticsAOT-First. Not Full-Stack Yet.
The code Pragmatic generates never reflects: serialization goes through generated
JsonTypeInfo, and endpoints are mapped as plain
request delegates rather than through ASP.NET's runtime delegate factory.
Three smoke applications โ a web host serving a generated endpoint among them โ are published
Native AOT and run by the gate.
What is not AOT yet. Entity Framework Core is not fully compatible with trimming and AOT, so a host with persistence is not a Native AOT application today. The generated HTTP client has no JSON context yet. And some runtime code is not trim-safe โ reflective fallbacks, configuration binding: it is annotated, and the gate counts it, with a budget that can only go down.
What's Forged.
The first public release is 1.0.0-alpha: a core set of modules is stable, the others are functional or in preview, and APIs can still change before 1.0.
45 Modules, One Generator
Entities, repositories and declarative queries; mutations and domain actions with a generated pipeline; minimal-API endpoints with OpenAPI; identity, roles, permissions and multi-tenancy โ composed by one source generator.
Messaging, Sagas, Jobs
A transactional outbox; transports over Channels, RabbitMQ, Kafka, Azure Service Bus or SQL; sagas with compensation; background jobs with retry, timeout and distributed locking.
Compliance
GDPR erasure, retention, consent, access and portability; an append-only audit trail that can be verified; NIS2 incident reporting clocks; personal-data redaction in logs and audit; encryption at rest with key rotation.
Reference Applications
The Showcase and four applications โ Time off, Invoicing, Casework, Warehouse โ each tested end to end against real databases and brokers, plus runnable samples for every module.
Entity Traits
[HasComments], [HasTags], [HasAttachments] and [HasNotes]: one attribute on an entity brings its child entity, actions and endpoints.
Documents & Media
PDF, DOCX, XLSX, CSV and e-mail from templates, in the recipient's language; native (Rust) image resizing, conversion and QR codes; file storage on disk and in the cloud.
Packages on nuget.org
Today the alpha builds are consumed from a local feed. Publishing them is the next step.
Settle the Functional Modules
The modules still marked functional or preview fix their names and shapes and become stable. Each module's README says where it stands.
Pragmatic Design is in public alpha. Explore the documentation to see what's already built.
Explore Documentation