Milan Jovanović

Milan Jovanović

These are the best posts from Milan Jovanović.

41 viral posts with 10,034 likes, 962 comments, and 873 shares.
36 image posts, 0 carousel posts, 0 video posts, 0 text posts.

👉 Go deeper on Milan Jovanović's LinkedIn with the ContentIn Chrome extension 👈

Best Posts by Milan Jovanović on LinkedIn

One small change = 400x FASTER query

Here's what I did to achieve this performance boost.

I was implementing cursor pagination with EF and Postgres.

But I need to make the query faster.

How do you make an SQL query faster?

Add an index, and you're good to go.

Right?

Not quite...

I created a composite index on the required columns to speed up the query.

BUT THIS MADE THE QUERY SLOWER!!!

This led me down a rabbit hole to figure out why the index wasn't used.

I knew about tuple comparison from before, and this solved the problem.

But I didn't know how to translate this into an EF Core query.

Luckily, the Postgres provider supports this with a custom function.

Here's everything you should know: https://lnkd.in/eD629U-B

---
Sign up for the .NET Weekly with 74K+ other engineers, and get a free Clean Architecture template: https://lnkd.in/e-XH7dM7
Multiple EF Core DbContexts in a single application?

Here's when it makes sense to do this:

- Working with multiple databases
- Separating concerns
- Modular monolith
- Read replicas

I did this when implementing a modular monolith application.

Each module had a dedicated schema in the database and a separate DbContext in the code.

The surprising part?

How EF Core deals with database migrations and different schemas.

Here's a breakdown of how I solved this: https://lnkd.in/e6JWgpNv

Did you need to use multiple DbContexts with EF?

---
Sign up for the .NET Weekly with 74K+ other engineers, and get a free Clean Architecture template: https://lnkd.in/eDqzAKsP
Post image by Milan Jovanović
Do you know all 3 ways to define Middleware in ASP .NET Core?

Middleware allows you to introduce additional logic before or after executing an HTTP request.

Middleware is an excellent choice for solving cross-cutting concerns in your application.

You are already using many of the built-in middleware available in the framework.

I'm going to show you three approaches to define custom middleware:

- Request Delegates
- Convention based
- Factory based

Here's everything you need to know: https://lnkd.in/eHYAh7Pc

How do you prefer to define middleware in .NET?
Post image by Milan Jovanović
If your .NET app runs background jobs or distributed workers, you need a way to prevent duplicate execution.

Otherwise, two instances can pick up the same task: sending duplicate emails, charging twice, or overwriting data.

That’s where distributed locking comes in.

In my latest video, I show how to implement production-ready distributed locks in .NET using a single clean library.

You’ll see how to:
- Use Postgres advisory locks and Redis for distributed coordination
- Build a reusable locking abstraction for any .NET app
- Decide when it actually makes sense to use one

If you care about reliability and data integrity, this one’s worth your time.

Learn more: https://lnkd.in/eWfVWmHk
Post image by Milan Jovanović
What's wrong with the first code snippet?

The second example is 43x faster to insert 10,000 records.

But what causes such a huge performance difference?

If you understand how EF works under the hood, the answer should be obvious. 👇

Every call to SaveChanges means one round-trip to the database.

In the first example, inserting each record means one call to the database.

This quickly adds up...

Here's why it's important to use the right tool for the job: https://lnkd.in/eStH9yku

Sending unnecessary queries to the database is one of the biggest performance killers in enterprise applications.

---
Sign up for the .NET Weekly with 74K+ other engineers, and get a free Clean Architecture template: https://lnkd.in/eDqzAKsP
Post image by Milan Jovanović
C# 15 is getting union types.

That’s a big deal.

It means patterns like Result and Option finally have real language support.

No more awkward wrappers.
No more hacks.
No more pretending exceptions are the best way to model failure.

I’ve advocated for the Result pattern for years, but lack of native support always made it clunky.

That changes with union types.

Don’t want to wait?

Here’s how to implement the Result pattern today: https://lnkd.in/dh8U8E2g

If you still prefer throwing exceptions, you can skip this.
Post image by Milan Jovanović
What's a breaking change for an API?

Here are a few examples:

- Removing or renaming APIs or API parameters
- Changing the behavior of existing APIs
- Changing the API error codes

A breaking change means you need to version your API.

Otherwise, you risk breaking existing consumers.

Luckily, ASPNET Core makes it easy to introduce versioning.

Check out this article on API Versioning: https://lnkd.in/dc62ZQFV

Here's a hot take for you to ponder.

Did we make a mistake as an industry with API versioning?

P.S. If you want to learn how to build production-ready REST APIs using the latest ASPNET Core features and best practices, this is for you: https://lnkd.in/dU6Psx5t
Post image by Milan Jovanović
Does your application need input validation?

There's a 99% chance the answer is YES.

Don't reinvent the wheel - use FluentValidation.

FluentValidation gives you a simple API to build complex validation rules.

You can write rules to ensure values are:
- Not null
- Not empty
- Have a min/max length
- Are equal/not equal to some value
- Define your completely custom rules

It's easy to write asynchronous validation rules using the MustAsync method. Dependency injection also works as you'd expect.

Here's how you can use FluentValidation to build a global validation middleware in minutes: https://lnkd.in/eKa6XVrT

Have you used FluentValidation before?

---
Sign up for the .NET Weekly with 74K+ other engineers, and get a free Clean Architecture template: https://lnkd.in/e-XH7dM7
Yes!🔥🚀
Post image by Milan Jovanović
Group related concepts together = build better applications 💡

Another name for this is cohesion, where we want to maximize the "relatedness" of files for a single feature.

Layered architectures typically have low cohesion.

The problem?

They force you to make changes in many different layers to implement or update a feature.

Vertical slices take a different approach.

All the files for a single use case are grouped together, which increases their cohesion. It's easy to find all the relevant components for a feature.

You can learn more about VSA here: https://lnkd.in/e-TKtnay

What do you think about this approach?

---
Sign up for the .NET Weekly with 74K+ other engineers, and get a free Clean Architecture template: https://lnkd.in/eDqzAKsP
Post image by Milan Jovanović
When I need to add a real-time feature to my app, there's one trusted solution I always go back to.

→ SignalR

SignalR allows you to add real-time functionality to your .NET applications.

And it all starts from a Hub. The Hub is a central component in your application that manages client connections and messaging.

Clients connect to the Hub to receive and send messages. Which means the communication is two-way.

SignalR abstracts away the transport mechanism from you. Usually this will be WebSockets, but if they're not available, it can fall back to SSE or polling.

Here's how you can get started: https://lnkd.in/ecnHcStj

SignalR is easily one of the best tools we have in .NET.

---
Sign up for the .NET Weekly with 74K+ other engineers, and get a free Clean Architecture template: https://lnkd.in/e-XH7dM7
Post image by Milan Jovanović
Starting from an existing database doesn’t mean you can’t use EF Core effectively.

Most .NET tutorials focus on Code-First.

But in the real world, many teams inherit a schema, and you still need a clean, maintainable way to work with it.

In my latest video, I show how to scaffold a DbContext from an existing PostgreSQL database using EF Core and .NET Aspire.

You’ll learn how to:
- Reverse-engineer your database with Scaffold-DbContext
- Configure it with Aspire’s connection management
- Avoid pitfalls like returning entities directly
- And evolve your setup into a Code-First workflow when your app grows

If you’ve ever had to start from a database instead of code, this one’s for you.

Learn more: https://lnkd.in/edBy4a77
Post image by Milan Jovanović
I built a free Clean Architecture template.

And now I've made it even better by adding Aspire.

More than 40,000+ developers have already downloaded it.

Here's what's inside:

- Simple domain model
- Domain Events pattern
- CQRS + clean use cases
- Validation and logging
- Authentication with JWT
- Boilerplate for RBAC Authz
- Minimal APIs, health checks
- Run locally with Aspire in seconds

I also made sure not to use any commercial libraries.

Get the template here: https://lnkd.in/dC4C7NPa
Post image by Milan Jovanović
If you're still writing five separate interfaces and classes for every single command in .NET, you need to see this.

For years, many of us in the .NET community have relied on MediatR and MassTransit for command handling and messaging. But the boilerplate is real.

I finally took the plunge and explored Wolverine, a library that claims to be the "next generation" of .NET messaging by focusing on convention over configuration.

In my new video, I share my raw, first-take as a long-time user of other messaging tools: https://lnkd.in/eDm9Aa3p
Post image by Milan Jovanović
In-memory rate limiting works perfectly... right until you scale.

If you're running a single API instance, storing rate limits in memory is easy and fast. But the second you scale out to multiple instances, that approach breaks down. Your services suddenly have no centralized way to agree on the correct rate limit values, leaving your API vulnerable.

That’s when you need a distributed rate limiter.

In my new video, we tackle this head-on. I'll show you exactly how to build a production-ready, distributed rate limiter in C# using Redis.

We walk through the code for:
🔹 Why native .NET memory limits fall short at scale
🔹 Building a Fixed Window algorithm using Redis counters
🔹 Implementing a more advanced Sliding Window algorithm with Redis sorted sets
🔹 Hooking it all up with custom middleware in .NET 10

If you want to protect your APIs from abuse and handle high traffic correctly, check out the full step-by-step breakdown here: https://lnkd.in/gWk4SaSM
Post image by Milan Jovanović
How do you create background jobs in .NET?

With Quartz, it's as simple as implementing an interface.

You define a job, register it, and Quartz takes care of scheduling and execution for you.

It integrates beautifully with dependency injection, so you can inject any services you need, from logging to your repositories.

Jobs are scoped, meaning you can even inject a DbContext safely without worrying about lifetime issues.

Once you start using Quartz, it’s hard to go back to manual background loops or timers.

I wrote an advanced guide for working with Quartz in .NET that I think you’ll enjoy.

You can read it here: https://lnkd.in/eNjF_jXE

Have you tried Quartz before, or do you use another library for background jobs?

---
Sign up for the .NET Weekly with 74K+ other engineers, and get a free Clean Architecture template: https://lnkd.in/eDqzAKsP
Post image by Milan Jovanović
System integration testing changed how I test my applications.

Have you heard about it?

This type of test gives you the highest level of confidence.

Let's take a modular monolith and test the integration between modules.

A modular monolith consists of multiple modules (duh), each with a distinct responsibility. Modules represent high-level components with well-defined boundaries.

The modules essentially group together related functionalities (use cases).

What's the best way to test module interactions?

System integration testing (SIT) is an approach to verifying the collaboration of these modules.

During system integration testing, we want to ensure that all these modules interact correctly and fulfill our system's business requirements.

Here's a hypothetical scenario:

- A user registers with our application through the Users Module
- The Users Module publishes an integration event to notify other modules
- The Ticketing Module handles the event and creates a customer record

How would you write a test for this scenario?

If you want to learn more about system integration testing, I wrote a high-level guide.

Read it here: https://lnkd.in/esyaQ6SG

What do you think about this testing approach?

---
Sign up for the .NET Weekly with 74K+ other engineers, and get a free Clean Architecture template: https://lnkd.in/eDqzAKsP
Async messaging is one of the best ways to make your .NET systems faster and more reliable.

Instead of services waiting on each other, you send messages through a broker.

In my new video, I'll show you how to build async messaging in .NET using Azure Service Bus step by step:

✅ Queues vs. Topics (when to use which)
✅ Message processing and dead-lettering
✅ Subscriptions and filters
✅ Scheduling and deduplication
✅ Full demo with .NET + Aspire

If you've been curious about event-driven design or scaling your services, this video will give you a solid foundation.

Watch here: https://lnkd.in/eN6FVAEc
Post image by Milan Jovanović
The .sln file as we know it is finally dead.

After nearly two decades of dealing with unreadable GUIDs and painful merge conflicts, Visual Studio 2026 has introduced the new .slnx format. It’s clean, it’s XML-based, and it actually makes sense.

But that’s not the only upgrade.

In my latest video, I show you how I moved my entire solution from .NET 9 to .NET 10 in just a few minutes.

The secret isn't upgrading every project manually, it’s using a "Smart Setup" with Directory.Build.props and Central Package Management.

In this video, I break down:

- How to change target frameworks for 20+ projects instantly
- Managing NuGet versions in one single file
- A deep dive into the new .slnx format (and how to migrate)
- Handling Dockerfile updates for .NET 10

Stop fighting with your solution files.

Here is the better way to handle the upgrade: https://lnkd.in/dFgdarZe
Post image by Milan Jovanović
Everyone wants to build a modular monolith these days.

But what's the value of a modular monolith?

Here are a few advantages:

- Fast development velocity
- Lower operational complexity
- Simplified deployment process
- Easier transition to microservices

Have you ever struggled to scale your application?

You're not alone.

Let's say you're running a sale, and your application is in high demand.

Imagine extracting the sales module to its own service, which you can scale dynamically based on demand. This is something you can easily do with a modular monolith.

If you're ready to learn more, I wrote a detailed introduction to modular monoliths.

Check it out here: https://lnkd.in/dgE7bUjR

Modular monoliths blend the simplicity and robustness of traditional monolithic applications with the flexibility and scalability of microservices.

I'm tempted to say they bring together the best of both worlds.
Post image by Milan Jovanović
The Outbox pattern gets all the attention.

But reliable publishing is only half the problem.

Your service can publish a message correctly.

The broker can deliver it correctly.

Your consumer can process it correctly.

And you can still process the same message twice.

That’s the reality of at-least-once delivery.

A timeout, crash, or network blip can happen after your consumer finishes the work but before the ACK reaches the broker.

So the broker does the safe thing:

It redelivers the message.

This is where the Inbox pattern helps.

Instead of processing the message directly, the consumer first writes it to an `inbox_messages` table.

Duplicates are ignored with `ON CONFLICT DO NOTHING`.

Then a background processor picks up unprocessed messages and handles them in batches.

This gives you:

- Idempotent message reception
- Independent retry control
- Horizontal scaling with `FOR UPDATE SKIP LOCKED`
- Fewer duplicate side effects leaking into your business logic

The Outbox makes publishing reliable.

The Inbox makes consumption reliable.

You usually need both if you want event-driven systems that behave well under failure.

I wrote a full breakdown of how to implement the Inbox pattern in .NET with PostgreSQL and RabbitMQ: https://lnkd.in/dWvkVtWb
Post image by Milan Jovanović
How do you enforce architecture rules in code?

Compilers won’t catch violations. Code reviews are hit or miss.

There’s a better way: architecture tests.

They’re automated checks that verify structure and design, written in C#.

Use them to:

• Enforce project dependency directions
• Restrict coupling between components
• Apply naming conventions

Here’s how to write your first one in minutes: https://lnkd.in/gnWuVAwY

And remember: architecture should speed you up, not box you in.
Post image by Milan Jovanović
Here's how I implement CQRS in my applications.

The CQRS pattern is actually simple.

I organize my application around use cases.

Use cases represent a feature (functionality) in your application:

- Get the current user's details
- Add an item to the shopping cart
- Refund the payment for an order

So you can think of a use case as a business capability.

I structure my code around a single use case (feature).

There are two types of use cases:

- Commands → business logic, database write, trigger side effects
- Queries → return the required data representation for the UI

If you think it should be more complex than this, you're overengineering it.

What do you think about applying CQRS?

P.S. No - you don't need event sourcing or a separate database.
Post image by Milan Jovanović
Are you using SignalR the right way?

Broadcasting to all users isn’t scalable (or smart).

In most cases, you should target specific users.

The good news? SignalR supports this out of the box.

It maps connections to user IDs using claims from the authentication context (JWT, cookie, etc.).

As long as the client is authenticated, SignalR can route messages to that user directly.

Want to dive deeper?

Here’s a quick guide to doing SignalR right: https://lnkd.in/dDn73TSu

How are you handling real-time communication in your app?
Post image by Milan Jovanović
6 things I do to set up my new .NET projects the right way

1. Enforce a consistent code style
2. Centralize build configuration
3. Centralize package management
4. Add static code analysis
5. Set up local orchestration
6. Automate builds with CI

I wrote a step-by-step guide on how to configure all this here: https://lnkd.in/eCpE6KM3

What else would you add to this project setup?

I already know a few people will say tests.

Definitely integration testing with something like Testcontainers.

---
Sign up for the .NET Weekly with 74K+ other engineers, and get a free Clean Architecture template: https://lnkd.in/ew5Adz89
Stop polling your database.

Let Postgres tell you when something changes.

In my latest video, I show how to stream Postgres Write-Ahead Log (WAL) changes directly into your .NET app using Npgsql.

You’ll learn how to:
- Set up logical replication in Postgres
- Stream changes in real time with Npgsql
- React to inserts, updates, and deletes
- Implement cache warming, cache invalidation, or even Outbox publishing

Watch it here: https://lnkd.in/e8iveVDD

If you’ve ever built a cache layer or event-driven system in .NET, this one will spark ideas.
Post image by Milan Jovanović
Tired of writing the same unit test over and over with different inputs?

You don’t have to.

Parameterized tests (aka data-driven tests) let you write one test and run it with many inputs.

I use xUnit for this. You have a few options:

- Use TheoryData to define strongly-typed parameters
- Plug it into your test using [ClassData(typeof(...))]
- Or take a simpler approach using [InlineData(...)]

Each has trade-offs.

Want to see all the ways to write parameterized tests in xUnit with clear examples?

Start here: https://lnkd.in/dJKJgcPG

How do you write your test variations?
Post image by Milan Jovanović
The test pyramid was good advice for 2009.

I don’t think it’s good advice for modern .NET systems.

Back then, integration tests were expensive:
shared database servers, flaky CI, slow builds, painful setup.

So the advice made sense:
write tons of unit tests, mock everything, keep integration tests small.

But the economics changed.

With Testcontainers, I can spin up PostgreSQL, Redis, and RabbitMQ in seconds.

With Aspire, I can wire up the application graph and test the real system boundaries.

That changes what "fast feedback" means.

For me, the highest-confidence test suite looks more like this:

A thin layer of unit tests for pure domain logic.

A thick middle of integration tests against real infrastructure.

A few critical end-to-end tests for the flows that would really hurt if they broke.

And architecture/contract tests to keep boundaries from drifting over time.

I still write unit tests.

Just not for everything.

Handlers, endpoints, repositories, message consumers, and module APIs usually belong in integration tests.

That’s where the real bugs tend to hide.

I wrote a detailed breakdown of why I stopped taking the test pyramid seriously, and what I use instead: https://lnkd.in/gHpdN9tn
Post image by Milan Jovanović
[UPDATE]: THANK YOU! The X support team reached out, and I got my account back.

If my posts ever brought you something good, I ask for a small favor in return.

My X account got hacked, and I'm trying to get it back.

Stefan posted on X with some info on how to report the account.

If you could also help out by reporting the profile, I would really apprecaite it.
Post image by Milan Jovanović
Most .NET apps are one Redis crash away from teaching you that “it works on my machine” is not a monitoring strategy.

You do not need a big observability stack to get basic uptime monitoring in place.

Uptime Kuma gives you a simple way to monitor:

- Your websites
- Your APIs
- Your health check endpoints
- Your public status pages
- Incidents and scheduled maintenance

In my setup, I use it to monitor a .NET API health endpoint that also reflects the state of dependencies like Postgres and Redis.

That means you are not just checking whether the API responds.
You are checking whether the system is actually healthy.

A few practical things this setup gives you:

- A quick self-hosted monitoring dashboard
- Alerts when services go down
- Status pages you can share
- A simple way to validate your health check flow
- Better visibility into your app without another paid tool

I also cover one Docker networking detail that can easily trip you up when monitoring a local .NET app from inside a container.

If you want a lightweight and practical way to monitor your .NET APIs and microservices, I break the full setup down here: https://lnkd.in/dNV_FT_s
Post image by Milan Jovanović
What is the easiest way to generate PDF reports?

HTML-to-PDF conversion is my first choice.

I use IronPDF in most commercial projects.

Looking for a free option?

- Puppeteer Sharp
- Headless Chrome browser

But the core idea is what matters here.

With an HTML template, you have more control over formatting.

You can use modern CSS to style the HTML markup.

Here's the high-level implementation:

1. MVC views and the Razor syntax
2. Render dynamic HTML content
3. Pass HTML to PDF renderer

I've used this approach on a few projects with great results.

Want to see how to implement flexible PDF reporting?

Here's a simple guide: https://lnkd.in/dTd_CtGw

How do you implement PDF reporting?
Post image by Milan Jovanović
Clean code tip:

- Merge nested IF statements into one condition

I often make this simple change to improve code readability.

Clean code principles will make you a better developer.

But what is clean code in the first place?

Clean code is code that's:

- Easy to read
- Easy to maintain
- Easy to understand

Writing clean code is a skill.

It's a skill you can learn and improve with practice.

In the initial example:

- Nesting increases complexity
- Nesting makes the code harder to reason about

The solution is to combine the nested IF statements into a single IF.

Three ways you can do this:

- Write an inline boolean expression
- Expose a method with the expression
- Create a variable with the expression

But this is just the start.

Here are 8 more clean code tips: https://lnkd.in/dt84mr9J

Which solution do you like better?

---
Sign up for the .NET Weekly with 75K+ other engineers, and get a free Clean Architecture template: https://lnkd.in/ej4nP9pY
Post image by Milan Jovanović
Some LINQ queries are much harder to read than the SQL they create.

Left joins are a great example.

You want a simple result:

→ Show all products
→ Include reviews when they exist
→ Keep products with no reviews

But in EF Core, writing that query used to take several steps.

It worked.

It was also easy to get wrong, hard to scan, and not fun to explain to the next person reading your code.

EF Core 10 fixes this with new LeftJoin and RightJoin operators.

Now your query can say what it is actually doing.

Why this matters:

→ Less noise in common queries
→ Clearer intent for your team
→ Fewer workarounds
→ The same SQL result underneath

This is useful for more than products and reviews.

Think:

→ Users with optional settings
→ Orders with optional shipping data
→ Customers with optional invoices

Anywhere related data may be missing, a clean join makes the code easier to trust.

I wrote a practical guide to the new join operators in EF Core 10, including how they compare to the old approach and when to use each one.

Read it here:
https://lnkd.in/dcK2Nvnq
Post image by Milan Jovanović
Injecting settings in .NET?

You have 3 main interfaces to choose from. But picking the wrong one means your app might completely ignore your config changes.

Here is the simple breakdown:

1️⃣ IOptions
• Read once at startup.
• Cached for the entire lifetime of the app.
• Perfect for static settings that never change.

2️⃣ IOptionsSnapshot
• Re-evaluated on every single request.
• Picks up appsettings.json changes without restarting your app.
• Perfect for web APIs (Scoped lifetime).

3️⃣ IOptionsMonitor
• Real-time updates.
• Triggers an event exactly when a setting changes.
• Perfect for background services (Singleton lifetime).

The Rule of Thumb: Use IOptions for static config. Upgrade to IOptionsSnapshot in web apps. Reach for IOptionsMonitor when you need change notifications in background workers.

Want a deeper dive into how the Options Pattern works under the hood?

Read my full breakdown here: https://lnkd.in/d4wfFdC9

Which of these three do you find yourself using the most?

---

Tired of writing the same boilerplate code for every new project? Skip the setup and start building features immediately with my Clean Architecture template: https://lnkd.in/dCddeyp7
Post image by Milan Jovanović
No - you aren't doing "integration" testing with an in-memory database.

At best it's a glorified unit test...

I've seen many examples using the EF Core in-memory provider.

This isn't an integration test because there's no real database.

Worse, this will fail to catch any LINQ or SQL bugs.

Here's a better approach:

- Use a real database or Docker container
- Connect to this database from your tests
- Write proper integration tests that have value

If you want to use Docker, I recommend exploring Testcontainers.

It lets you define throwaway containers in your tests.

What tools or methods do you use for integration testing?
Post image by Milan Jovanović
Most Keycloak setups you see online are not production-ready.

They use the embedded database and call it a day.

That works for a demo.
It is not the setup you want for a real system.

If you plan to run Keycloak seriously, the baseline is simple:

• Use a real database like Postgres
• Isolate Keycloak into its own schema
• Configure the DB connection explicitly
• Let Keycloak scaffold its own tables
• Verify the full auth flow end to end
• Inspect the database so you understand what it is actually doing

One thing that stands out very quickly is how much complexity Keycloak hides from you.

Realms, clients, users, credentials, sessions, mappings, and a surprisingly large schema behind it all.

Authentication is not a small feature.
It is a system.

I put together a practical walkthrough showing exactly how I set this up with Postgres, Docker Compose, Swagger, and a look at the schema under the hood.

Watch here: https://lnkd.in/dRjEvqxz
Post image by Milan Jovanović
Here are 5 underappreciated LINQ methods you should know:

- SequenceEqual
- Aggregate
- GroupJoin
- ToLookup
- Intersect

LINQ is full of gems that can make your code cleaner and more efficient.

What's one LINQ method that you think deserves more love?
Post image by Milan Jovanović
.NET 10 is officially released! 🎉

Here are the top updates 👇

First, .NET 10 is an LTS (long-term support) release. So we get support for the next 3 years.

🔥 Performance improvements

Under the hood, .NET 10 delivers major performance boosts. The JIT compiler has been tuned with new optimizations such as de‑abstraction, smarter inlining heuristics and constant‑folding, allowing the runtime to do more work at compile‑time rather than at run‑time. There are many other optimizations across collections and LINQ, which translate to tangible speedups across everyday workloads.

🚀 Aspire goes polyglot

Formerly .NET Aspire, Aspire is now a polyglot, developer‑first platform for orchestrating front ends, APIs, containers and databases. Version 9.5 introduces a preview of a single‑file AppHost, letting you define and run your entire distributed application in a single file. The release also debuts a Generative AI visualizer to inspect and debug prompts and responses, plus expanded AI support with typed clients for GitHub Models and Azure AI Foundry and first‑class OpenAI integration.

🔗 EF Core’s new LeftJoin/RightJoin operators

LINQ’s lack of a built‑in outer join has long been a pain point. EF Core 10 fixes this by adding LeftJoin and RightJoin operators, so the code now reads exactly like the SQL you intend. For example, you can join products to reviews using LeftJoin() and EF will generate the correct LEFT JOIN statement under the hood. A corresponding RightJoin() keeps all rows from the right side while bringing in matches from the left. These new operators eliminate the verbose GroupJoin + DefaultIfEmpty pattern and make LINQ queries easier to read and maintain.

What's your favorite feature coming in .NET 10?

---
Sign up for the .NET Weekly with 74K+ other engineers, and get a free Clean Architecture template: https://lnkd.in/e3q7Nu4s
Refit - a game-changer API client

I've spent many hours working with external APIs.

It's a crucial part of modern software development.

But let's be honest - it can be a real pain sometimes.

We've all been there:

- Configuring HttpClient for each API
- Writing repetitive code
- Hoping we didn't miss a parameter somewhere

That's why I want to introduce you to Refit.

It's a library that will help you build API integrations faster.

It handles all the HTTP heavy lifting. You focus on what matters: your application logic.

Here's everything you need to know about Refit: https://lnkd.in/d8XrVMwS

Did you ever work with Refit in your applications?

---
Sign up for the .NET Weekly with 75K+ other engineers, and get a free Clean Architecture template: https://lnkd.in/edzd2tK5
Post image by Milan Jovanović
Rewrites feel clean at the start.

Then reality shows up.

Missed edge cases.
Broken behavior.
Delayed releases.
A second system nobody fully trusts.

A safer option is to migrate incrementally.

That’s where the Strangler Fig Pattern shines.

Instead of replacing the whole legacy API at once, you put a reverse proxy in front of it and start routing traffic endpoint by endpoint.

Old system keeps running.
New system takes over gradually.
Risk stays contained.

In my example, I start with a Node.js API, add YARP as a reverse proxy, and then migrate individual endpoints into a modern .NET 10 API.

The nice part is that this works just as well for old .NET Framework apps.

You don’t need a giant rewrite to modernize a legacy system.

You need a controlled migration path.

I break down the full implementation here: https://lnkd.in/dg_zf-MV
Post image by Milan Jovanović
Two-factor authentication is easy to get mostly right.

And dangerous to get slightly wrong.

The basic idea is simple:

- Generate a TOTP secret
- Show a QR code
- User scans it with an authenticator app
- User enters a 6-digit code
- Server validates the code

But the details matter.

The first mistake is enabling 2FA too early.

If you enable it before the user confirms their first code, you can lock them out of their own account.

The correct setup flow is:

1. Generate a pending secret
2. Show the QR code
3. Ask the user to enter the first code
4. Validate the code
5. Only then activate 2FA
6. Generate recovery codes

The second mistake is issuing a full access token after password login.

If the user has 2FA enabled, the password should only get them a short-lived limited token.

That token should only allow one thing:

Validate the 2FA code.

Only after the TOTP code is valid should you issue the real access token.

And then there are the security details people often skip:

- Encrypt TOTP secrets at rest
- Prevent code reuse within the verification window
- Rate limit failed attempts
- Hash recovery codes before storing them
- Show recovery codes only once

2FA is not just a QR code screen.

It’s a full authentication flow.

I wrote a detailed walkthrough for implementing 2FA in ASPNET Core with TOTP, QR codes, recovery codes, and the correct login/setup flow: https://lnkd.in/dAwkap2i
Post image by Milan Jovanović

Related Influencers