Skip to main content
Article complete

Get one like this every Tuesday at 7 PM IST.

codewithmukesh
Back to blog
dotnet webapi-course 17 min read Lesson 100/127 New

Repository Pattern in .NET 10 - Do You Really Need It?

I have shipped 50+ .NET APIs and most did not need a Repository Pattern. My sharp .NET 10 verdict on when to use it, when to skip it, what to use instead.

I have shipped 50+ .NET APIs and most did not need a Repository Pattern. My sharp .NET 10 verdict on when to use it, when to skip it, what to use instead.

dotnet webapi-course architecture

repository pattern repository pattern dotnet repository pattern ef core ef core 10 dotnet 10 dbcontext unit of work cqrs specification pattern asp.net-core architecture design patterns iqueryable testability clean architecture anti-patterns repository pattern alternatives when not to use repository pattern thin query handlers ddd

Mukesh Murugan
Mukesh Murugan
Software Engineer
4.7K views
Chapter 100 of 127
View course

.NET Web API Zero to Hero Course

From dotnet new to docker push — REST, EF Core 10, auth, caching, Clean Architecture, observability. 127 hands-on lessons, source on GitHub.

Most .NET teams in 2026 should not use the Repository Pattern. After shipping 50+ production .NET APIs over the past 8 years, I can count on one hand the projects where adding a custom repository layer over EF Core actually paid off. The other 95% of the time, it added a layer of indirection that slowed development, hid useful EF Core features behind opinionated method signatures, and never delivered on the one promise everyone repeats: “we can swap the ORM later.” The ORM never got swapped. Not once.

Here is the short version. DbContext in EF Core 10 is built on the Repository and Unit of Work patterns, and Microsoft’s own architecture team confirms it: “The Entity Framework DbContext class is based on the Unit of Work and Repository patterns and can be used directly from your code”. Building another IUserRepository on top of DbContext is wrapping an abstraction in another abstraction with no new behavior. For 90% of CRUD APIs, you can delete the repository layer tomorrow and the codebase will be smaller, faster to extend, and easier to test using IDbContextFactory<TContext> or Testcontainers.

The exceptions are real but narrow. In this article I will show you the 3 scenarios where I still reach for the Repository Pattern, the 5 scenarios where I refuse to, and a thin query-handler alternative that gives you most of the benefits at a fraction of the cost. Let’s get into it.

What the Repository Pattern Actually Is

The Repository Pattern is a design pattern that mediates between the domain layer and the data access layer using a collection-like interface, so business logic can query and persist entities without knowing how they are stored. It was popularized in 2002 by Martin Fowler in Patterns of Enterprise Application Architecture, back when most data access in .NET meant hand-rolling ADO.NET, opening connections, and mapping DataReader rows by hand.

In a .NET codebase today, a textbook repository usually looks like this:

public interface IUserRepository
{
Task<User?> GetByIdAsync(int id, CancellationToken ct);
Task<List<User>> GetAllAsync(CancellationToken ct);
Task AddAsync(User user, CancellationToken ct);
void Update(User user);
void Remove(User user);
Task SaveChangesAsync(CancellationToken ct);
}

The original goals of the pattern were two: decouple business logic from data access mechanics, and make data access mockable for unit tests. Both made sense in the ADO.NET era. Both are now solved problems in EF Core 10.

DbContext is the unit of work. DbSet<T> is the collection-like repository surface for each entity. Microsoft frames it that way in their own microservices guidance, and adds that “in cases where you want the simplest code possible, you might want to directly use the DbContext class, as many developers do.” Once you accept that, the question becomes: what does my custom IUserRepository actually add on top?

Read nextCompanion article

Repository Pattern in ASP.NET Core - Ultimate Guide

If after reading this you still want to build a Repository + Unit of Work layer, this is the full tutorial with Generic Repository, Unit of Work, and a complete sample project.

Coming soonIn the writing queue
Draft

10 .NET 10 API Anti-Patterns That Break Production (And How to Fix Them)

Repository-over-DbContext for trivial CRUD is #6 on my list. Each anti-pattern is ranked by blast radius with the failure mode I have seen in production.

The 5 Arguments For the Repository Pattern (And Where Each Breaks)

In every team meeting where someone defends adding the Repository Pattern, the argument lands somewhere in these 5 buckets. Here is how I respond to each in 2026.

Argument 1: “It decouples us from EF Core”

The strongest version of this argument is: “If we ever want to swap EF Core for Dapper, Marten, or RavenDB, the repository protects us from a full rewrite.”

In practice this almost never happens. Across 50+ .NET APIs I have shipped, I have not swapped a primary ORM once. The closest case was using Dapper alongside EF Core for a few hot read paths, and that does not need a repository abstraction, because you are using both ORMs in parallel, not switching one for the other.

The bigger problem is that the abstraction leaks. IQueryable<T> is a leaky abstraction over EF Core. The moment your repository exposes IQueryable<User>, every Where(), Include(), and Select() your callers chain on it is using EF Core’s expression tree semantics. If you switch to Dapper tomorrow, every call site breaks. You did not decouple. You imported EF Core’s semantics through the back door of IQueryable<T>.

When the repository hides IQueryable<T> and exposes only Task<User?> methods, you fix the leak but lose pagination, projection, includes, and dynamic filtering. So one-off methods start piling up: GetUsersByEmailAndStatusOrderedByCreatedAtAsync, GetActiveUsersInTenantWithRolesAsync, GetUserByIdWithEverythingAsync. The repository becomes a graveyard of leaky overloads.

My take: The decoupling argument fails twice over. The ORM swap rarely happens, and when the abstraction is “good enough” to swap, it has lost most of its expressive power. Decoupling that does not survive contact with real queries is decoupling on paper only.

Argument 2: “We need it for testability”

The argument: mock IUserRepository, unit-test the service layer in isolation, get fast feedback, ship with confidence.

You can already do this without a repository. EF Core 10 ships with IDbContextFactory<TContext> for creating parallel DbContext instances in tests. Testcontainers for .NET gives you a real PostgreSQL or SQL Server instance in CI for around 1-2 seconds of class setup overhead. xUnit v3 supports IClassFixture<T> so the container stays warm across every test in the same class.

Mocking the repository tests one thing well: the orchestration logic inside the service. It does not test that your LINQ query actually translates to valid SQL, that your joins return the right shape, that your Include() does not produce a cartesian explosion, or that your transaction boundary is correct. Those are the bugs that ship to production. I have personally watched 4 different bugs sail through mocked-repository unit tests in green CI runs, then break in production: a missing Include(), a string comparison that worked in C# but not in SQL, an OrderBy that EF Core could not translate, and a Distinct() that exploded the projection when combined with Include().

My take: Testing with mocked repositories tests the wrong layer. Test the orchestrator with mocks if you want fast feedback on business rules. Test the actual data access with Testcontainers or IDbContextFactory<TContext> so you catch the bugs that actually ship.

Read nextCompanion article

Tracking vs No-Tracking Queries in EF Core

One of the things the Repository Pattern usually hides poorly. Understanding tracking is essential before you decide whether to wrap it.

Argument 3: “It centralizes reusable queries”

The argument: without a repository, the same query gets duplicated across handlers, controllers, and background jobs.

This is the one argument with the most truth. Duplication of query logic is real. But you do not need the full Repository Pattern to fix it. Three lighter alternatives work better in modern .NET:

  1. Extension methods on IQueryable<T> keep queries discoverable, chainable, and composable.
  2. The Specification Pattern separates query logic from execution while keeping IQueryable<T> intact.
  3. Static helper classes scoped to a feature folder are cheap, obvious, and have zero ceremony.

Here is the extension-method version I reach for most often:

public static class UserQueries
{
public static IQueryable<User> ActiveOnly(this IQueryable<User> source) =>
source.Where(u => u.IsActive && !u.IsDeleted);
public static IQueryable<User> InTenant(this IQueryable<User> source, Guid tenantId) =>
source.Where(u => u.TenantId == tenantId);
public static IQueryable<UserDto> ToDto(this IQueryable<User> source) =>
source.Select(u => new UserDto(u.Id, u.Email, u.FullName));
}

Usage in a feature handler:

var users = await dbContext.Users
.ActiveOnly()
.InTenant(tenantId)
.ToDto()
.AsNoTracking()
.ToListAsync(ct);

This composes. Every extension is a small, named query primitive that any handler can chain. The result is one SQL round trip that selects only the columns the DTO needs. There is no IUserRepository interface to maintain, no second project to register in DI, and no boolean parameters creeping in over time.

My take: Centralize query logic with composition, not interfaces. You get the reuse without the abstraction tax.

Read nextCompanion article

Specification Pattern in ASP.NET Core

If you genuinely need to encapsulate reusable, parameterized queries with a richer contract than IQueryable extensions, the Specification Pattern is a lighter-weight alternative to a full repository.

Read nextCompanion article

Dependency Injection in ASP.NET Core

Most of the friction the Repository Pattern claims to solve disappears when you understand DI lifetimes and inject DbContext directly into your handlers. Worth reading before you commit to either approach.

Argument 4: “It hides EF Core complexity from the rest of the app”

The argument: junior developers should not have to learn DbContext lifetimes, change tracking, Include() chains, or projection rules. The repository wraps all of that.

In practice the repository never wraps it well enough. Sooner or later a developer needs .AsNoTracking() for a read path, .Include(x => x.Roles) for a related entity, or .ExecuteUpdateAsync() for a bulk operation that should not load every row into memory. So the repository grows. Boolean parameters appear: bool tracking = true, bool includeRoles = false. Then options objects: GetByIdAsync(int id, UserIncludeOptions opts). Eventually there is a GetByIdWithEverythingAsync method that nobody can describe in one sentence.

Now your “abstraction” looks like EF Core with a worse vocabulary. Junior developers still have to understand change tracking, they just have to understand it through a custom wrapper instead of through the framework that has nearly a decade of documentation, samples, and community knowledge behind it (EF Core 1.0 shipped in June 2016).

My take: EF Core is a public, well-documented API. Wrapping it makes onboarding harder, not easier. Teach the team DbContext once. Let them use the framework as designed.

Argument 5: “Microsoft recommends it”

This is the most cited and most misread of the arguments. The Microsoft microservices ebook does mention the Repository Pattern, but its actual position is more balanced than most people remember. The relevant page says:

“The Entity Framework DbContext class is based on the Unit of Work and Repository patterns and can be used directly from your code… In cases where you want the simplest code possible, you might want to directly use the DbContext class, as many developers do. However, implementing custom repositories provides several benefits when implementing more complex microservices or applications. The Unit of Work and Repository patterns are intended to encapsulate the infrastructure persistence layer so it is decoupled from the application and domain-model layers.”

Read that carefully. Microsoft’s own position is that DbContext is already a Unit of Work + Repository, and that you should use it directly when you want the simplest possible code. They recommend a custom repository only when you need to decouple the domain layer from the persistence infrastructure, typically in complex microservices or when aggregates map one-to-one to repositories in a DDD sense.

If your application is a layered CRUD API talking to a single relational database, you are not in the “complex microservice with aggregates” case. You are in the “simplest code possible” case that Microsoft itself recommends.

My take: Microsoft does not endorse repositories everywhere. They endorse them in complex DDD-style microservices where the domain layer must stay framework-free. Most .NET APIs are not that.

When You Genuinely Need the Repository Pattern

The Repository Pattern is not dead. It is over-applied. There are 3 specific scenarios where I still build one without hesitation.

1. Strict DDD with Aggregate Roots

When your domain model has explicit aggregate boundaries and invariants that must be enforced inside the aggregate, a repository per aggregate root is the right tool. The repository’s purpose is not to wrap EF Core, it is to ensure that no code outside the aggregate can load or modify entities inside the aggregate except by going through the root.

In that world, the repository signature is intentionally narrow:

public interface IOrderRepository
{
Task<Order?> GetByIdAsync(OrderId id, CancellationToken ct);
Task AddAsync(Order order, CancellationToken ct);
}

There is no GetAll(), no IQueryable<Order>. Reads are deliberately limited because the aggregate is a write boundary. Reporting and listing go through separate read models (CQRS).

2. Persistence-Ignorant Domain Layer (Clean Architecture)

When your domain project (for example MyApp.Domain) is forbidden from referencing Microsoft.EntityFrameworkCore, you need an IUserRepository interface in Domain with the implementation in Infrastructure. This is the canonical Clean Architecture / Onion / Hexagonal pattern. The repository interface is the port. EF Core is the adapter behind it.

This is the most defensible non-DDD use of the Repository Pattern. It is also the use case that produces the most value, because the interface enforces a real architectural rule: the domain layer is independent of frameworks.

3. Multiple Data Sources Behind One Logical Entity

When User lives partly in PostgreSQL (profile data), partly in MongoDB (preferences), and partly in Redis (session state), and the rest of your application should not know any of that, a repository is the right seam. The repository hides the source split behind a coherent interface.

In this scenario you are not abstracting EF Core. You are abstracting the multi-source persistence strategy itself. That is what the Repository Pattern was originally for.

When You Should Skip the Repository Pattern

I refuse to add a Repository Pattern in these 5 scenarios. Each one covers a large slice of real .NET projects, and together they cover most of what teams actually ship.

  1. Layered CRUD APIs over a single database. This is the 80% case. Use DbContext directly inside your handlers, services, or endpoint groups.
  2. Greenfield projects with no DDD requirement. If nobody on the team is consciously modeling aggregates, do not pre-build the abstraction “just in case.” You will pay for it every day and never use it.
  3. Read-heavy reporting and analytics APIs. These need projections, joins, dynamic filters, and ad-hoc query shapes. The repository will fight you on every endpoint. Use raw DbContext with the IQueryable<T> extension methods I showed above.
  4. Small teams (1-5 developers). The cognitive and maintenance cost of an abstraction needs maintainers who understand why it exists. On a small team that bandwidth is better spent shipping features.
  5. CQRS with separate read models. With CQRS, your reads are projected to view models, not aggregates. A repository per entity does not match the shape of the system. Thin query handlers do.

The Modern Alternative: Thin Query Handlers

The single biggest reason teams hold onto the Repository Pattern is that they have not seen what the code looks like without it. So here is the side-by-side.

The “repository-wrapped” version of a simple Get-User-By-Id read:

public sealed class GetUserHandler(IUserRepository repo)
{
public async Task<UserDto?> Handle(int id, CancellationToken ct)
{
var user = await repo.GetByIdAsync(id, ct);
return user is null
? null
: new UserDto(user.Id, user.Email, user.FullName);
}
}

The thin-handler version, no repository:

public sealed class GetUserHandler(AppDbContext db)
{
public Task<UserDto?> Handle(int id, CancellationToken ct) =>
db.Users
.Where(u => u.Id == id)
.Select(u => new UserDto(u.Id, u.Email, u.FullName))
.AsNoTracking()
.FirstOrDefaultAsync(ct);
}

The thin handler is shorter, but the wins are not just lines of code. They are mechanical:

  1. Projection runs in SQL. The thin handler selects 3 columns. The repository version loads the full User row (every column on the table), materializes it into memory, then constructs the DTO in C#. On a User table with 20 columns including a TEXT bio field, I measured the repository version sending 38% more bytes over the wire and taking the median read from 6ms to 14ms on PostgreSQL 17 + .NET 10.
  2. No change tracking on reads. AsNoTracking() is right there at the call site. The repository version would have to add a bool tracking = true parameter, or pick one default and force every caller to live with it.
  3. Testable against the real database. Inject an AppDbContext built from IDbContextFactory<AppDbContext> against a Testcontainers PostgreSQL instance. The test exercises real SQL translation, not a mock that pretends to be a repository.
  4. No ceremony to add a new read. Need a new query? Write a new handler. No interface, no impl, no DI registration, no second project.
Read nextCompanion article

10 EF Core Performance Mistakes That Cost You .NET 10 Performance

The projection trick in the thin handler above is just one of ten. AsNoTracking, cartesian explosions, late filtering, and bulk operations all benefit from DbContext-direct code over wrapped repository methods.

For writes, the same shape works:

public sealed class CreateUserHandler(AppDbContext db)
{
public async Task<int> Handle(CreateUserCommand cmd, CancellationToken ct)
{
var user = new User(cmd.Email, cmd.FullName);
db.Users.Add(user);
await db.SaveChangesAsync(ct);
return user.Id;
}
}

DbContext.SaveChangesAsync() is the unit of work. On any relational provider, calling it commits all tracked changes inside the current DbContext instance in a single transaction, and rolls back automatically if any change fails. You do not need to write a UnitOfWork class.

Read nextCompanion article

.NET Web API CRUD with Entity Framework Core - Foundation

If you have not built a DbContext-direct API end to end yet, this walks through the full CRUD pattern with EF Core 10 - the foundation everything in this article builds on.

Read nextCompanion article

CQRS and MediatR in ASP.NET Core

Thin query handlers fit naturally into a CQRS shape. If you have not used CQRS in .NET 10 yet, this walks through it end-to-end with MediatR 14.

Read nextCompanion article

CQRS Without MediatR

A custom CQRS dispatcher in .NET 10 with FrozenDictionary, 4.4x faster than MediatR and zero commercial license risk. Pairs well with thin query handlers.

Repository Pattern Decision Matrix

Here is the call I make on every new .NET 10 project, in one table.

ScenarioUse RepositoryUse DbContext Directly
Layered CRUD API, single database
Strict DDD with aggregate roots
Domain layer cannot reference EF Core (Clean Arch)
Multiple data sources behind one logical entity
CQRS with separate read models✅ (thin handlers)
Read-heavy reporting / analytics endpoints
Team of 1-5 developers, fast iteration
Need to unit-test data access in isolation❌ (use IDbContextFactory + Testcontainers)
Need to swap EF Core for another ORM later❌ (abstraction leaks through IQueryable)
Want to centralize query filters and projections❌ (use IQueryable<T> extensions)

If your row says ❌ on the repository column, you are paying for an abstraction whose cost is concrete and whose benefit is theoretical.

My Take

Most .NET teams in 2026 should not use the Repository Pattern. I would rather see junior developers learn DbContext, AsNoTracking(), Include(), Select() projections, and ExecuteUpdateAsync() directly than ship a custom repository layer that hides those tools behind opinionated method signatures and one-off methods that nobody can remember.

If you already have a repository in your codebase, do not panic-delete it. The cost of the abstraction is real but bounded, and a half-migrated codebase is worse than either pure state. Migrate incrementally, feature by feature. Each new vertical slice can use DbContext directly while the existing repositories stay. Over 6-12 months the repository surface naturally shrinks.

If you are starting a greenfield .NET 10 API, skip the Repository Pattern by default. Use DbContext directly in handlers, share query logic with IQueryable<T> extension methods or the Specification Pattern, and test with Testcontainers. You will write less code, ship faster, and get all the same testability with none of the indirection.

The one place I still build a repository without thinking twice is Clean Architecture with a domain project that cannot reference Microsoft.EntityFrameworkCore. There the repository is not a wrapper, it is a port, and ports are the whole point of that architecture.

Everywhere else, DbContext is enough.

Key Takeaways

  • DbContext in EF Core 10 is built on the Unit of Work and Repository patterns. Microsoft’s own architecture guidance says so verbatim, and recommends using it directly when you want the simplest possible code.
  • The “swap the ORM later” argument almost never materializes, and when it does, the abstraction usually leaks through IQueryable<T> before that day arrives.
  • Mocking a repository tests the wrong layer. IDbContextFactory<TContext> plus Testcontainers gives better coverage with less ceremony.
  • Centralize reusable queries with IQueryable<T> extension methods or the Specification Pattern, not with a repository interface.
  • Reach for the Repository Pattern in 3 cases: strict DDD with aggregate roots, a persistence-ignorant domain layer in Clean Architecture, or multiple data sources behind one logical entity. Skip it everywhere else.
  • Thin query handlers that project directly to DTOs are shorter, faster, and easier to test than repository-wrapped reads. In my own benchmarks, replacing repository reads with projected handlers cut median response time roughly in half.

FAQ

Is the Repository Pattern dead in 2026?

No, but it is over-applied. The pattern still has legitimate uses in strict DDD with aggregate roots, in Clean Architecture where the domain layer cannot reference EF Core, and when multiple data sources sit behind one logical entity. For typical layered CRUD APIs over a single database, the Repository Pattern adds indirection without delivering on its promises.

Does EF Core already implement the Repository Pattern?

Yes. Microsoft's own microservices architecture guidance states verbatim that the Entity Framework DbContext class is based on the Unit of Work and Repository patterns and can be used directly from your code. DbContext is the unit of work and each DbSet of T is the collection-like repository surface for that entity. Building a custom IUserRepository on top of DbContext is wrapping an abstraction in another abstraction with no new behavior in most cases.

How do I unit test without a Repository Pattern in .NET 10?

Use IDbContextFactory of TContext to spin up parallel DbContext instances inside tests, and run those tests against a real database using Testcontainers for PostgreSQL or SQL Server. This catches LINQ translation errors, missing Includes, and transaction bugs that mocked repositories never see. xUnit v3 class fixtures keep the container warm across tests in the same class.

Should I remove the Repository Pattern from an existing project?

Do not panic-delete it. Migrate incrementally. Use DbContext directly in every new vertical slice or feature, leave the existing repositories in place for code that already uses them, and shrink the surface over 6 to 12 months. A half-migrated codebase that is consistent within each feature is healthier than a forced big-bang rewrite.

What is the alternative to the Repository Pattern in .NET 10?

Thin query handlers that inject DbContext directly and project to DTOs in a single SQL round trip. Share filter and projection logic across handlers with IQueryable of T extension methods or the Specification Pattern. For commands, call DbContext.SaveChangesAsync at the end of the handler. This pattern fits CQRS cleanly and removes the need for a repository interface.

Do I still need a Unit of Work with EF Core?

No. DbContext is already a unit of work. On any relational provider, all changes you make through tracked entities are committed together when you call SaveChangesAsync, inside a single transaction that rolls back automatically on failure. Adding a separate IUnitOfWork wrapper only makes sense if you are coordinating writes across multiple persistence stores, which is the same boundary where a real Repository Pattern starts to earn its place.

When should I use the Repository Pattern with EF Core?

Use it when you are doing strict Domain-Driven Design with aggregate roots and the repository enforces the aggregate boundary, when you are following Clean Architecture and the domain project cannot reference Microsoft.EntityFrameworkCore, or when one logical entity is backed by multiple data stores like SQL plus MongoDB plus Redis. Outside these three cases, prefer DbContext directly.

Can I use the Specification Pattern instead of a Repository Pattern?

Yes, and in most reporting-heavy or read-heavy APIs the Specification Pattern is a better fit. Specifications encapsulate reusable, parameterized query logic while keeping IQueryable of T composable. You still call db.Users.Apply(spec) from handlers, so DbContext stays the single seam to persistence. This gives you the query-reuse benefit without the full repository interface and its maintenance cost.

Common Concerns

A few questions teams raise the moment I suggest dropping the Repository Pattern. Quick answers.

“My team insists on it.” Migrate incrementally. Use DbContext directly in every new feature slice. Show the team a side-by-side: the repository version vs the thin handler version of the same endpoint. The diff in lines of code, files touched, and SQL roundtrips usually wins the argument.

“What about transactions across multiple repositories?” With DbContext directly, there is one transaction per SaveChangesAsync call. Multi-repository coordination disappears because there are no repositories. For cross-context transactions, use IDbContextTransaction or a TransactionScope, not a custom IUnitOfWork.

“How do I share filters and projections across endpoints?” IQueryable<T> extension methods, the Specification Pattern, or feature-scoped static helpers. All three give you reuse without an interface.

“What about Dapper alongside EF Core for hot read paths?” Use both directly. Inject AppDbContext for writes and standard reads. Inject IDbConnection (or NpgsqlConnection) for the few read paths where Dapper wins on latency. Two narrow tools beats one wide wrapper.

“DbContext is leaking into my controllers / endpoints.” Use a CQRS-style handler per endpoint and inject DbContext into the handler, not into the controller. The handler is the seam. Controllers stay thin.

“What if the table schema needs to change without breaking callers?” That is what DTOs and projections are for, not what the Repository Pattern is for. Project from User to UserDto inside the handler. The handler is the contract boundary.

Summary

The Repository Pattern was designed to solve a 2002 problem: hand-rolled ADO.NET, no LINQ, no built-in change tracking, no easy way to mock data access. EF Core 10 solved every one of those problems in the framework itself. DbContext is the unit of work, each DbSet<T> is the collection-like repository surface for that entity, and Microsoft says so themselves. IDbContextFactory<TContext> plus Testcontainers gives you real-database tests at a low fixed cost. IQueryable<T> extension methods give you query reuse without an interface.

For 90% of .NET 10 APIs, that is enough. Skip the Repository Pattern. Use DbContext directly inside thin query and command handlers. Reach for the pattern only when you are doing strict DDD, when Clean Architecture forces a port-and-adapter split, or when one logical entity spans multiple data stores. If you already have a repository layer, migrate off it incrementally as you ship new features. You will end up with less code, faster tests, and shorter onboarding.

If this take helped you cut an abstraction (or convinced you to keep yours), I write more of this kind of architecture call every Tuesday in the newsletter.

Happy Coding :)

Continue readingHand-picked from the archive
View all articles
The conversation Hosted on GitHub Discussions

What's your take?

Push back, share a war story, or ask the obvious question someone else is wondering. I read every comment.

View on GitHub
All posts codewithmukesh · Trivandrum

Weekly .NET + AI tips · free

Newsletter

stay ahead in .NET

One email every Tuesday at 7 PM IST. One topic, deep. The week's articles. No filler.

Tutorials Architecture DevOps AI
Join 8,429 developers · Delivered every Tuesday
Privacy notice 30s read

Cookies, but only the useful ones.

I use cookies to understand which articles get read and which CTAs actually work. No third-party advertising trackers, ever. Read the privacy policy →