The importance of good practices in .NET programming
02. September 2024
The .NET technology is a free, open-source development platform that allows you to create various types of applications, and is capable of running programmes written in various programming languages, C# being the most popular among them.
The .NET platform was designed with a focus on productivity, performance, security and reliability. In addition, this technology offers automatic memory management via a rubbish collector (GC), is type- and memory-safe and supports concurrency via the async/await and task primitives. Its libraries are optimised to work on different operating systems and chip architectures.
Following good practices in .NET is key to ensuring system efficiency and security
How to improve efficiency
Optimisation
Write code that is efficient in terms of execution time and use of resources. Avoid unnecessary loops and excessive memory allocations.
Algorithms and data structures
Choosing appropriate algorithms to solve specific problems using efficient data structures, such as linked lists, trees and hash tables.
How to increase security
Measure code performance using profiling tools. Identify points for improvement and optimise them.
How to increase security
Input validation
Validate user inputs to prevent attacks such as SQL injection or cross-site scripting (XSS).
Error handling
Handle errors appropriately by avoiding sensitive information in error messages.
Encryption
Protect confidential data with appropriate encryption.
Minimum privileges
Ensure that code runs with the minimum privileges required.
General good practices for programming in .NET
Descriptive names
Use descriptive and objective names for variables, functions and classes. Names should be clear enough to indicate their purpose without the need for additional comments.
Comments
Document the code with clear and concise comments. Use XML comments to generate automatic code documentation, making it accessible to other tools and developers.
Version management
Use version control systems to manage changes. Tools such as GitHub, GitLab and Bitbucket offer additional features for collaboration and continuous integration.
Unit tests
Writing tests to check the functionality of the code.
Code review
Asking other elements to review the code developed.
Code organisation in .NET
Naming conventions are essential for efficient and collaborative software development, as they make it easier for other programmers to read the code and avoid potential ambiguities by making it more understandable.
Collaboration in which good practices in .NET are ensured facilitates maintenance and guarantees continuous improvement of the code, increasing the productivity of the teams.
In C# we can use PascalCase or camelCase
PascalCase
PascalCase is used for type names (classes, structs, enums), public methods, properties and file names. The first letter of each word is capitalised, and the rest are lowercase.
Examples:
- - MyClass
- - CalculateSalary
- - FullName
camelCase
CamelCase is used for local variables, method parameters and private fields. The first letter is lower case, and the rest of the words start with a capital letter.
Examples:
- - age
- - nameFull
- - quantityItems
Structure of folders and files
The organisation of files and folders in .NET projects is fundamental to maintaining clarity, facilitating maintenance and improving collaboration between programmers.
Some good practices include:
Keeping projects separate
This approach involves keeping .NET framework projects and other existing projects in separate folders, avoiding the need to use Visual Studio to open old projects. Each project remains independent and there is no code variation between them. It's a good option for supporting programmers who work with older frameworks and don't have the latest version of Visual Studio.
Multi-platform .NET project
An alternative is to reorganise the repository to have just one ‘.csproj’ file targeting several frameworks, allowing you to compile the project for different platforms and deal with specific compilation options for each framework.
Main folders in ASP.NET Core MVC projects
In addition to the main folders, such as ‘Controllers’, ‘Models’ and ‘Views’, there are other important files and folders in an ASP.NET Core MVC project:
- - ‘appsettings.json’: contains application settings.
- - ‘Startup.cs’: configures the request pipeline and defines the application services.
- - ‘Program.cs’: starts the application.
It's important to adapt the organisation to the specific needs of each project and team. Maintaining consistency and documenting decisions will facilitate future collaboration.
Logically separating the different components is essential for keeping the code organised and facilitating maintenance. The best practices for this are:
Modules and classes
Divide the code into modules or classes that represent different functionalities or responsibilities. For example, in a web application, separate classes for database manipulation, authentication, business logic and user interface.
Architecture layers
Use a layered architecture, such as the MVC (Model-View-Controller) or MVVM (Model-View-ViewModel) architecture, where each layer has a specific function:
- - Model: represents the data and business logic.
- - View: handles the user interface.
- - Controller/ViewModel: manages communication between the Model and the View.
Interfaces and contracts
Define interfaces for components that need to communicate with each other, allowing different implementations to be used without affecting the rest of the code.
Dependency injection
Using dependency injection to provide external components (such as services or libraries) to your modules makes it easier to replace implementations and improves testability.
Folder organisation
Separate files into folders with meaningful names, such as:
- - ‘Controllers’: for controllers in a web application.
- - ‘Services’: for business services.
- - ‘Models’: for data model classes.
Writing clean code in .NET
SOLID principles
The SOLID principles are a set of guidelines for object-orientated software design that aim to improve the readability, maintainability and flexibility of code.
The acronym SOLID stands for the five essential principles
SRP (Single Responsibility Principle)
A class should have only one reason to change. Each class should have only one specific task.
OCP (Open/Closed Principle)
Modules should be open for extension but closed for modification, which makes it possible to add functionality without changing the existing code.
LSP (Liskov Substitution Principle)
Objects of a superclass should be replaceable by objects of a subclass without affecting the functioning of the programme.
ISP (Interface Segregation Principle)
Interfaces should not contain unnecessary methods to avoid cumbersome systems.
DIP (Dependency Inversion Principle)
Dependencies should be based on abstractions, not concrete implementations.
Continuous review
Code review in C# is a crucial practice for maintaining software quality and readability. Some of the reasons why code review is important are mainly due to ease of maintenance, elimination of redundancies, code reuse, performance optimisation and the search for bugs and vulnerabilities.
Common revision techniques
Extracting methods
This involves identifying repeated or complex pieces of code and creating a method to encapsulate this logic, which improves readability and makes the project easier to maintain.
Renaming variables and methods
Choosing descriptive names for variables and methods, avoiding abbreviations and generic names, makes the code more understandable.
Eliminate code duplication
Look for duplicates and extract them to methods or classes, ensuring that the same logic is not repeated in several places.
Simplify conditions
Reduce the complexity of conditional statements by using logical operators and simpler expressions.
Split large classes
Large classes are difficult to understand and maintain, so it's important to separate functionality into smaller, cohesive classes.
Improve organisation
To make it easier to navigate the code, you need to group related methods together and organise them logically.
Remove unused parameters
Identify and eliminate unnecessary parameters in methods to simplify the code.
Managing dependencies in .NET
Dependency injection is a design pattern used in software engineering to reduce coupling between classes in a system. By definition, dependency injection allows classes to depend on abstractions (such as interfaces or base classes) rather than directly on concrete implementations, structuring the code so that classes receive their dependencies externally rather than creating them internally.
Example of dependency management
In a hypothetical scenario, let's imagine a ‘Car’ class that has several dependencies, such as wheels, engine and so on. Without dependency injection, the ‘Car’ class would be responsible for creating all these dependencies. However, with dependency injection, we can change the wheels at runtime, injecting different wheel implementations without modifying the ‘Car’ class. This makes the code more flexible, testable and independent of the concrete implementations.
//Dependency Abstraction
public interface IMyDependency
{
void WriteMessage(string message);
}
//Service Registration
services.AddSingleton<IMyDependency, MyDependency>();
//Injection into Constructor
public class IndexController : Controller
{
private readonly IMyDependency _dependency;
public IndexController(IMyDependency dependency)
{
_dependency = dependency;
}
public IActionResult Index()
{
_dependency.WriteMessage("Call to Action Index");
return View();
}
}
Implementing tests in .NET
Unit testing
Unit tests serve to protect the code, helping to maintain quality and confidence throughout the development process, and are important for:
Early detection of problems
Unit tests check specific parts of the code, such as functions and methods in isolation, making it possible to identify errors and problems before they spread to other parts of the system.
Code maintenance and revision
When changes need to be made to the code, unit tests provide a safety net. If an error exists, it is known immediately. They also make it possible to rewrite code with greater confidence, knowing that the tests will verify that the system is still functional.
Proactive documentation
Unit tests serve as documentation for the project, showing how individual parts of the code should behave.
Facilitate collaboration
When several programmers contribute to a project, unit tests help ensure that everyone is on the same page, setting clear expectations about how the code should work, regardless of who wrote it.
Increase confidence
Unit tests increase confidence as the project is developed, letting you know that changes made to the timeline will not alter existing functionality.
Integration testing
Integration testing is an essential stage in software development, where modules or components are combined and tested as a group. The aim is to check the efficiency and security of communication between systems, ensuring that the software works without integration errors. This phase takes place after unit testing (where modules are tested individually) and before system testing (which evaluates the complete system in a simulated production environment).
Integration tests can be manual or automated, and are collaborative, involving programmers, testers and software engineers. They help to identify and correct interface problems, such as missing data, syntax or semantic errors in the data, guaranteeing the quality of the application.
Difference between unit tests and integration tests
Unit tests focus on individual parts of the code, such as functions, methods or classes, to check that each unit works correctly in isolation, simulating or overriding external dependencies. They are quick because they test small parts of the code. An example would be testing a function that calculates the average of a list of numbers. In contrast, integration tests evaluate the interaction between different parts of the system to ensure that the components communicate correctly with each other and integrate smoothly. These tests take place in an environment closer to the production environment and are usually slower, as they involve several parts of the system. An example would be testing the integration between a web service and a database.
Security in .NET development
When developing in .NET, it is essential to follow security practices to protect the code and guarantee the integrity of the system, such as:
- - Do not use CAS (Code Access Security).
- - Avoid partially reliable code.
- - Security-neutral code
- - Carry out static code analyses
- - Carry out code reviews, both manual and automated
- - Carry out requirements-based tests to verify code security.
Tools and techniques for writing secure code in .NET
SAST (Static Application Security Testing) analysis
SAST analyses code for vulnerabilities before compilation. SAST tools provide real-time feedback to developers, helping them to correct problems before moving on in the development cycle, highlighting vulnerabilities in the code and offering guidance on how to fix them.
OWASP (Open Web Application Security Project) Checklist
Offers a checklist of security practices at all layers of the application. Consulting the OWASP checklist helps ensure that aspects such as identity, authentication, access control, input validation, encryption, user management and error handling are taken into account.
Security tests
They validate that the code follows security standards by checking functions, methods and libraries before integrating them into the application. These tests ensure that the code changes required by security reviews are implemented correctly.
Authentication and authorisation management
Implementing secure authentication and authorisation in .NET applications is crucial to protecting systems and their data.
Authentication and authorisation in the ASP.NET Web API
Supports authentication and authorisation. Authorisation determines whether a user is allowed to perform a specific action. Authentication can be configured using built-in IIS modules or by writing a customised HTTP module. In addition, HTTP message handlers can be used for Web API-specific authentication.
Identity library in ASP.NET Core
This is a powerful library for authentication and authorisation, offering support for authentication based on external authentication (such as Google or Facebook logins) and two-factor authentication. With Identity, you can manage users, roles and permissions efficiently.
Good security practices in .NET applications
Input validation, protection against common attacks (such as SQL Injection and Cross-Site Scripting) and proper vulnerability management are good security practices to follow.
How to optimise the performance of .NET applications
Avoiding boxing and unboxing conversions
Avoid using value types in situations that require repeated boxing conversions, such as in non-generic collections (e.g. System.Collections.ArrayList). Boxing and unboxing conversions are computationally complex processes as they create objects, and it is preferable to opt for simple reference assignments whenever possible.
Using StringBuilder
To concatenate many string variables (for example, in loops), it is preferable to use System.Text.StringBuilder instead of the + operator in C# to improve efficiency.
Avoiding empty finalisers
When a class contains a finaliser, it creates an entry in the queue. If the finaliser is empty, this will result in a loss of performance, and it is beneficial to remove unnecessary finalisers.
Monitoring profiles
Profiling tools serve to identify performance constraints, which helps to identify points that can be optimised. Monitoring the use of memory, CPU and other resources allows you to identify areas in need of intervention.
Testing
Performance tests make it possible to check that optimisations are working as intended. If necessary, rewriting the code makes it possible to improve algorithms, reduce unnecessary loops and optimise database queries.
Code documentation in .NET
Code documentation is a fundamental and often underestimated practice, essential for the prosperity of technological projects. Documenting code is an important step for the long-term success of a software developer in the .NET area.
Benefits of documenting code
Facilitates maintenance
Documentation simplifies understanding of how code works, speeding up the process of maintenance and project evolution. Thus, programmers who need to make changes to existing code can quickly understand the structure and logic with the help of proper documentation.
Transmission of knowledge
When new programmers take on a role in a project, documentation serves as a tool for passing on knowledge, which in practice reduces the time needed for them to familiarise themselves with the code and the development environment.
Code reuse
Well-documented projects encourage code reuse, as developers can identify useful functions in previous projects and incorporate them into new projects with ease, saving resources.
Quality assurance
Documenting code also contributes to the quality of the project. When programmers comment on projects, they are encouraged to think about design and functionality, resulting in more efficient code.
Efficient collaboration
In collaborative development environments, code documentation promotes effective communication. This way, teams can understand what each part of the code does and coordinate their tasks better.
Tools for automatically documenting project code
PlantUML
This tool allows you to use written language to create diagrams. It is an efficient way of visualising the architecture and flows in the software.
JSDoc
Especially for JavaScript projects, JSDoc is useful for automatically generating documentation from source code. It creates documentation that is readable by both humans and tools.
Postman
Although it is primarily known as a tool for testing APIs, Postman can also generate documentation through your API, making it easy to describe endpoints, parameters and responses.
Regular .NET project maintenance and updates
Code updates are fundamental to ensuring that a system remains efficient over time, and some of the maintenance practices include:
Code review
Revision is a way of reorganising code, improving its internal structure without altering dynamic behaviour. This practice helps to keep the code clean and readable. It's important to evaluate the necessary tests and make any code additions gradually.
Intuitive names
Using descriptive names for variables and functions makes the code easier to understand and maintain.
Short lines
It's good practice to keep lines of code short and remove unnecessary or obsolete code, improving the readability and performance of the solution.
Comments
Recording comments in the project to explain what the code is producing and its logic helps other programmers understand the context.
Consistent indentation
Maintaining a spacing and indentation pattern makes the code easier to read and leads to standardisation in the development process.
Division into functions and modules
To improve organisation and facilitate maintenance, it is suggested that code be separated into well-defined functions and modules.
Version control systems
Using version controllers such as Git or TFS helps to keep changes to the code recorded and easy to retrieve or compare.
In short, adopting good practices in .NET programming is fundamental to developing technological solutions with greater performance. Optimisations in the project lead to improved readability and collaboration, while the use of tools ensures effective monitoring throughout development. Thus, implementing these practices results in more efficient projects, benefiting not only programmers but also end users.
Wilde Artikel
What is Outsystems and how is it transforming software development
Java - What makes this language so popular among programmers?
Linux: a versatile and powerful operating system
COBOL - Is this language relevant today?
Implementing a Business Intelligence system
Emerging Challenges in IT Security - Trends and Strategies to Protect Companies