How to (actually) become an expert in .NET
The influencers want you to think that you must learn a gazillion different technologies to become a successful .NET developer. These days, the internet is full of .NET “roadmaps” that include everything but the kitchen sink: Azure, AWS, Redis, Docker, Postgres, GraphQL, gRPC, Dapper, ELK stack, CQRS, you name it.
There are several problems with this trend. One is that it generates fear and anxiety that it’s impossible to become a good developer, because the list of things you “must” know is growing by the day. It might also lead you to focus on the wrong things and waste the time you could spend actually improving your .NET skills. The other problem is that these claims are objectively wrong. I’ve been using .NET for over 15 years, 6 of which I’ve spent working for Microsoft, where I’m currently a principal engineer. If I were to be judged by what influencers call minimal .NET knowledge, I wouldn’t even qualify as a junior developer.
I’m not being funny or anything, but shouldn’t you learn Redis only if you, like, work with Redis? You don’t have to learn GraphQL to be a good .NET developer, but you should absolutely learn how memory management works. Kafka and RabbitMQ are optional—knowing how async/await works is essential. Don’t get me wrong: these are all cool technologies and it’s great to be familiar with them. But most technologies can be easily learned when there is a real need. What truly matters is mastering the knowledge of .NET itself, and that’s what this post is all about.
How to read this guide
I’m going to share with you the list of resources that will help you to become a great .NET developer. Even though I tried to summarize only the essentials, it’s still a lot of information. Mastering every area I’m gonna talk about will take you months or even years, so don’t get discouraged early on. It’s impossible to learn everything immediately—what matters is expanding your expertise slowly, topic by topic.
You can learn the topics I presented in any order. For example, if your job requires you to write high-performance code, you could start your learning journey by focusing on the performance and tools sections. Or if you want to broaden your C# knowledge first, you could start with C# learning resources. If you are not sure, Exploring .NET internals is the best place to get started.
Before we start: if you are an absolute .NET beginner, this post is probably not for you. If you want to learn C#, I recommend watching the C# for Beginners YouTube video course first. Now let’s get started.
Books
If you had asked me ten years ago what’s the best way to learn .NET, I would have probably told you to read CLR via C#. Although it’s still one of my favorite technical books ever, .NET has changed so much since the book came out that I can no longer recommend it as the most efficient way to master .NET. In fact, I think that the quality of technical books has been decreasing over the years, so there are fewer and fewer books that I would consider must-reads.
Having said that, if you like reading books, there are still some excellent ones out there! If I had to recommend only one .NET book, it would be Framework Design Guidelines. Written by .NET architects, it’s a collection of conventions and best practices for writing idiomatic .NET code. What elevates this book from the rest is that it’s full of comments and annotations from .NET legends such as Jeffrey Richter, Joe Duffy, Rico Mariani, and Vance Morrison, in which they explain not only the best practices, but also the reasoning behind them.
Another book I loved is Writing High-Performance .NET Code. When it came out in 2018, it was the most comprehensive guide to .NET performance. The only thing that’s holding me back from giving it my seal of approval now is that I haven’t re-read it since, so I’m not sure if it still holds up today (my guess is that it’s still relevant).
Exploring .NET internals
I think the key to becoming a .NET expert is learning how things work under the hood. Gaining a deep understanding of async/await, string interpolation, spans, and garbage collection will give you superpowers and help you stand out from the crowd. The .NET team regularly publishes deep dives on their blog (well, it’s just Stephen Toub in most cases), and they are great, without exception. Here are the ones I consider the most important:
- How Async/Await Really Works in C#
- String Interpolation in C# 10 and .NET 6
- ConfigureAwait FAQ
- An Introduction to System.Threading.Channels
- Understanding the Whys, Whats, and Whens of ValueTask
- All About Span: Exploring a New .NET Mainstay
If you prefer watching videos, Deep .NET is hands down the best collection of .NET deep dives on the internet. These videos cover the same topics as the blog posts I mentioned and even more, so they are definitely worth your time.
C# learning resources
Assuming you already know the basics of C#, progressing further is fairly straightforward. Whether you want to learn about the new C# features or just fill the gaps in your knowledge, you should check out the following pages:
These two articles describe the new features in the latest versions of C# and .NET, but I also recommend reading about all previous versions. Even if you are an experienced .NET developer, I think you will still learn something new.
Staying up to date
First of all, you don’t need to know about every new runtime or language feature. You’ll be perfectly fine even
if you don’t start using readonly ref struct
, record types, or pattern matching right away. But I would still
advise you to periodically check what’s been going on in the .NET ecosystem. It’s not necessary to do this on a
weekly or even a monthly basis—doing this once a year should be more than enough.
Your main source of information should be the .NET blog (other blogs often cherry-pick random pieces of information from official .NET posts). Not everything on the .NET blog is required reading, though. For me, the most illuminating posts are the ones that showcase the improvements in the latest .NET versions. My personal highlights are the following series:
- Performance Improvements in .NET 9
- .NET 9 Networking Improvements
- What’s new in System.Text.Json in .NET 9
- File IO improvements in .NET 6
These are just the latest posts in the series—if you like them, I highly recommend reading the earlier posts in the series as well.
Mastering the key libraries
To be an effective .NET developer, it’s more important to be proficient with the standard library than to be familiar with any specific external library. It’s definitely worth learning the modern APIs for JSON serialization and deserialization. Learning how to use the HttpClient correctly is also essential—it seems obvious, but it’s surprisingly tricky to get right. Here are my recommended articles about writing reliable networking code and avoiding common pitfalls:
- Guidelines for using HttpClient
- IHttpClientFactory with .NET
- Build resilient HTTP apps: Key development patterns
Outside the standard library, Newtonsoft.Json is still so widely used that it’s very useful to at least know how to use it optimally for performance.
Writing high-performance code
Modern .NET is incredibly fast. In fact, I would say that high performance is one of its defining characteristics, so it pays off to understand how to take full advantage of the platform’s high-performance features.
Stephen Toub’s annual blog posts on performance improvements in .NET are among the most valuable resources for learning about .NET performance. Although they are published as blog posts, they really are books in disguise—the latest installment is more than 300 pages long. Unless you have unlimited free time, reading them end-to-end is next to impossible. My recommended way of consuming these posts is to scan through the topics and carefully read sections that pique your curiosity. What does that mean? If you are a compiler optimization geek, you might want to read sections about the latest JIT and PGO improvements. But if you are looking for practical knowledge you can apply immediately in your backend service, you could read sections about JSON and networking improvements.
Writing the fastest possible .NET code requires understanding how garbage collection works. The official documentation on garbage collection is great: it’s easy to read and very thorough. It will teach you how generations work, what is the large object heap, what are the differences between workstation and server garbage collection, and much more. But the definitive resource for mastering memory in .NET is the .NET Memory Performance Analysis document written by Maoni Stephens, the .NET GC architect. It’s the ultimate guide on how to approach memory performance analysis, and one of the best ways to deepen your understanding of how memory works in .NET.
Finally, it’s important to learn how to measure performance correctly. BenchmarkDotNet is the undisputed king of .NET benchmarking and one of the few libraries everyone should be using. However, knowing how to use BenchmarkDotNet is just half of the story. Writing good benchmarks is not trivial and you can easily end up measuring the wrong thing. That’s where the .NET team’s Microbenchmark Design Guidelines come into play. Think of this document as the missing BenchmarkDotNet manual—it goes beyond the syntax and teaches you how to design your benchmarks the right way. The .NET Performance GitHub repository also contains all benchmarks for the .NET standard library. If you ever need inspiration when designing benchmarks, it’s the best place to start.
Useful tools
I think everyone should have a decompiler in their toolbox. It’s useful not only for reverse engineering, but also if you want to learn how seemingly simple statements like string interpolation work under the hood. The choice of decompiler is up to you—I personally prefer ILSpy.
One more indispensable tool is the .NET Source Browser. I often use it to inspect how .NET classes are implemented and to find examples of good code design patterns. Even though the .NET source code is available in the .NET Runtime GitHub repository, the source browser makes navigation much easier.
.NET Framework’s tooling was pretty bare-bones. In contrast, .NET Core comes with some fantastic diagnostic tools. You most likely won’t use all of them in your day-to-day work, but in the right circumstances, they can be incredibly handy. At the very least, you should be aware of which tools exist. That way, if you ever need to collect a memory dump, you’ll know you can use dotnet-dump. Or if you need to debug runaway threads, dotnet-stack can capture the stacks of all threads in a .NET process.
Conclusion
I hope this post was not too overwhelming! But my main message is simple: focus on having rock-solid fundamentals and you’ll be golden. Hopefully, you also discovered some new and interesting learning resources. Let me know if I missed anything you consider essential!