Why I Like Developing in C# and .NET
I have been a C#/.NET developer for nearly ten years. Before that, I was a Java/Spring Framework developer for a couple of years. As with every change in technology, it took a bit of time to catch up and get comfortable in the new environment. However, the move from Spring Framework to .NET was an upgrade for me personally in terms of developer satisfaction. For instance, I was using Java 7 or 8, and the corresponding version of C# felt slightly more modern to me. I enjoyed using C# delegates, which I don’t believe had an equivalent in Java at the time, to build reusable code.
In addition, it’s easier to get started with good design patterns like dependency injection in .NET, protect API endpoints using middleware, and start web apps. I specifically like how .NET uses a code-first approach to configure dependencies using code instead of annotations. In comparison, using Spring Framework at the time, I would need to decorate service classes to use them as dependencies. I also like how easy it is to add NuGet packages in .NET compared to adding libraries in Java.
Fast forward to today, I feel at home and comfortable with all the great features that .NET has provided. For instance, it’s easy for me to set up a new .NET project with clean architecture, Entity Framework, Microsoft Authentication Library (MSAL), MediatR, etc., and get a solid starter codebase to build great apps. I especially enjoy how the .NET team and Microsoft strive to enable .NET developers to go full-stack with .NET, with frameworks like Blazor — which I myself am using to build user interfaces.
As someone who cares about clean code, I like how .NET and C# allow developers to implement layered architecture or clean architecture by simply creating different projects for different layers and controlling dependencies between them more robustly than just using folders in the same project. This is especially useful if you want to implement clean architecture. For example, using different projects for each layer allows me to enforce that the domain layer does not have dependencies on other projects by simply not adding those projects as dependencies. If I have just one project and implement different layers in separate packages or directories, it’s harder to ensure that code in the domain layer does not reference code in other folders or layers.
Being an object-oriented language, I enjoy developing in C# and using its object-oriented features to implement well-established design patterns. For example, the Command Query Responsibility Segregation (CQRS) pattern is a good way to adhere to the Single Responsibility Principle. Essentially, a single responsibility can be encapsulated into a command or query, with its corresponding implementation in a handler. In C# or similar object-oriented languages, you can define the command or query in an interface or abstract class, and the handler in a concrete class. The MediatR library makes it seamless to register queries, commands, and their corresponding handlers so you can use them in service classes by simply executing commands or queries, with the library handling the appropriate lookup.
Regarding the open-closed principle, the idea is to allow extending the functionality of a class or module without modifying the class. One way C# helps to implement this pattern is via interfaces. For example, suppose you have a class that utilizes Redis for caching. Instead of injecting this class directly into other classes, you can create an interface that defines the necessary caching methods and have the service implement this interface. Then, you inject an instance of the interface, not the concrete class. This approach aligns with the open-closed principle because it allows you to extend caching behavior at runtime by injecting a different implementation of the interface, without changing the classes that depend on it.
Last but not least, I like the user interface in Visual Studio for managing packages. It’s easy to search for and add packages and libraries. Back when I was focusing on Java codebases a few years ago, I used Maven to manage Java libraries. To add a library, I had to know and specify the group ID, artifact ID, version, and scope. To me, dependency management in Java just felt less intuitive and harder to use compared to .NET, thanks to the available tooling and IDE support.
I’m pleased that Microsoft and the .NET team have been consistently releasing updates that continue to improve .NET and C#, as well as enhance the developer experience. At the same time, I’m also excited to build applications in other languages, particularly Python. In fact, I’ve recently been focusing on building apps in Python. I felt reluctant and awkward at first because I was so used to writing code in C# and enjoying all the benefits of .NET. But I’m getting better at it and plan to share more in upcoming posts — particularly about the techniques and tools I use to organize the codebase and write code that meets my standards of cleanliness and maintainability.