.NET has quietly become one of the best platforms for building production web APIs. The performance numbers are exceptional — consistently near the top of the TechEmpower benchmarks — and the developer experience has improved dramatically. Here's how we structure .NET API projects for maintainability and scale.
Minimal APIs vs Controllers
Minimal APIs, introduced in .NET 6 and significantly improved since, offer a lean way to define endpoints without the ceremony of controllers. For microservices, internal APIs, and projects where the endpoint count is manageable, they're excellent. For larger APIs with complex filtering, multiple versions, and team-scale codebases, controllers with their conventions around routing, model binding, and attribute-based configuration remain more maintainable. We use both — Minimal APIs for lightweight service endpoints, controllers for feature-rich API surfaces.
Entity Framework Core Done Right
EF Core is a powerful ORM, but it's easy to generate N+1 queries and wonder why your API is slow. The key practices: always use AsNoTracking() for read-only queries, load related data with explicit Include calls rather than lazy loading in production, use projection (Select) to fetch only the columns you need, and log generated SQL in development to catch unexpected queries before they reach production.
The Repository Pattern: When and Whether
Repository pattern over EF Core is a contested topic. EF's DbContext is already a unit of work and its DbSet is already a repository. Adding another layer is often unnecessary abstraction. We skip the repository pattern on greenfield projects and add it only when we have a genuine need — multiple data sources, complex query logic that benefits from encapsulation, or testability requirements that make mocking the DbContext impractical.
Structured Logging and Observability
Serilog with structured logging is our default for .NET APIs. Every request gets a correlation ID injected into the log context; every response code, duration, and key parameter is captured. Paired with a log aggregation platform — Seq in development, Datadog or Grafana Loki in production — you have queryable structured logs that make debugging distributed systems tractable. Add the OpenTelemetry SDK for distributed tracing across service boundaries and you have a complete observability picture.