What makes code "cleaner"? What makes the difference between readable code and very readable code?
It can be very painful when needing to modify a piece of code in an application that you never spec'd out or wrote. But it doesn't have to be this way. By following some of these better programming tips your code can be easily read and easily modified by any developer at any time.
Refactoring is all about making code easier to understand and cheaper to modify without changing its behavior.
You should generally be looking for ways to simplify your code (e.g. removing heavily-nested case statements). As a minimum, look for the most complicated method you have and check whether it needs simplifying.
In Visual Studio, there is built-in support for Cyclomatic Complexity analysis.
Too often, developers are writing a line of code, and they forget that little bit of syntax they need to do what they want. In that case, they usually end up googling it to find the documentation and then copy and paste it into their code. That process is a pain because it wastes valuable dev time. They might also ask another dev for help.
Not to worry, AI pair programming is here to save the day!
Code duplication is a big "code smell" that harms maintainability. You should keep an eye out for repeated code and make sure you refactor it into a single place.
One of the major issues people had back in the day with ASP (before ASP.NET) was the prevalence of "Spaghetti Code". This mixed Reponse.Write() with actual code.
When moving through the different stages of testing i.e. from internal testing, through to UAT, you should suffix the application name with the appropriate stage:
It is not a good idea to have spaces in a folder or file name as they don't translate to URLs very well and can even cause technical problems.
Instead of using spaces, we recommend:
Other not recommended options include:
For further information, read Do you know how to name documents?
Try to avoid problems in if-statements without curly brackets and just one statement which is written one line below the if-statement. Use just one line for such if-statements. If you want to add more statements later on and you could forget to add the curly brackets which may cause problems later on.
Try to avoid Double-Negative Conditionals in if-statements. Double negative conditionals are difficult to read because developers have to evaluate which is the positive state of two negatives. So always try to make a single positive when you write if-statement.
Strings should be @-quoted instead of using escape character for "\". The @ symbol specifies that escape characters and line breaks should be ignored when the string is created.
You should always add the application name to the connection string so that SQL Server will know which application is connecting, and which database is used by that application. This will also allow SQL Profiler to trace individual applications which helps you monitor performance or resolve conflicts.
There are 2 type of connection strings. The first contains only address type information without authorization secrets. These can use all of the simpler methods of storing configuration as none of this data is secret.
When deploying an Azure hosted application we can use Azure Managed Identities to avoid having to include a password or key inside our connection string. This means we really just need to keep the address or url to the service in our application configuration. Because our application has a Managed Identity, this can be treated in the same way as a user's Azure AD identity and specific roles can be assigned to grant the application access to required services.
This is the preferred method wherever possible, because it eliminates the need for any secrets to be stored. The other advantage is that for many services the level of access control available using Managed Identities is much more granular making it much easier to follow the Principle of Least Privilege.
If you have to use some sort of secret or key to login to the service being referenced, then some thought needs to be given to how those secrets can be secured. Take a look at Do you store your secrets securely to learn how to keep your secrets secure.
In .NET 5 we can use Azure Key Vault to securely store our connection strings away from prying eyes.
Azure Key Vault is great for keeping your secrets secret because you can control access to the vault via Access Policies. The access policies allows you to add Users and Applications with customized permissions. Make sure you enable the System assigned identity for your App Service, this is required for adding it to Key Vault via Access Policies.
You can integrate Key Vault directly into your ASP.NET Core application configuration. This allows you to access Key Vault secrets via IConfiguration.
public static IHostBuilder CreateHostBuilder(string[] args) =>Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(webBuilder =>{webBuilder.UseStartup<Startup>().ConfigureAppConfiguration((context, config) =>{// To run the "Production" app locally, modify your launchSettings.json file// -> set ASPNETCORE_ENVIRONMENT value as "Production"if (context.HostingEnvironment.IsProduction()){IConfigurationRoot builtConfig = config.Build();// ATTENTION://// If running the app from your local dev machine (not in Azure AppService),// -> use the AzureCliCredential provider.// -> This means you have to log in locally via `az login` before running the app on your local machine.//// If running the app from Azure AppService// -> use the DefaultAzureCredential provider//TokenCredential cred = context.HostingEnvironment.IsAzureAppService() ?new DefaultAzureCredential(false) : new AzureCliCredential();var keyvaultUri = new Uri($"https://{builtConfig["KeyVaultName"]}.vault.azure.net/");var secretClient = new SecretClient(keyvaultUri, cred);config.AddAzureKeyVault(secretClient, new KeyVaultSecretManager());}});});
✅ Good example - For a complete example, refer to this sample application
Tip: You can detect if your application is running on your local machine or on an Azure AppService by looking for the WEBSITE_SITE_NAME environment variable. If null or empty, then you are NOT running on an Azure AppService.
public static class IWebHostEnvironmentExtensions{public static bool IsAzureAppService(this IWebHostEnvironment env){var websiteName = Environment.GetEnvironmentVariable("WEBSITE_SITE_NAME");return string.IsNullOrEmpty(websiteName) is not true;}}
In order to access the secrets in Key Vault, you (as User) or an Application must have been granted permission via a Key Vault Access Policy.
Applications require at least the LIST and GET permissions, otherwise the Key Vault integration will fail to retrieve secrets.
Figure: Key Vault Access Policies - Setting permissions for Applications and/or Users
Azure Key Vault and App Services can easily trust each other by making use of System assigned Managed Identities. Azure takes care of all the complicated logic behind the scenes for these two services to communicate with each other - reducing the complexity for application developers.
So, make sure that your Azure App Service has the System assigned identity enabled.
Once enabled, you can create a Key Vault Access policy to give your App Service permission to retrieve secrets from the Key Vault.
Figure: Enabling the System assigned identity for your App Service - this is required for adding it to Key Vault via Access Policies
Adding secrets into Key Vault is easy.
Figure: Creating the SqlConnectionString secret in Key Vault.
Figure: SqlConnectionString stored in Key Vault
Note: The ApplicationSecrets section is indicated by "ApplicationSecrets--" instead of "ApplicationSecrets:".
As a result of storing secrets in Key Vault, your Azure App Service configuration (app settings) will be nice and clean. You should not see any fields that contain passwords or keys. Only basic configuration values.
Figure: Your WebApp Configuration - No passwords or secrets, just a name of the Key vault that it needs to access
In .NET 1.1 we used to store our connection string in a configuration file like this:
<configuration><appSettings><add key="ConnectionString" value ="integrated security=true;data source=(local);initial catalog=Northwind"/></appSettings></configuration>
...and access this connection string in code like this:
SqlConnection sqlConn =new SqlConnection(System.Configuration.ConfigurationSettings.AppSettings["ConnectionString"]);
❌ Historical example - Old ASP.NET 1.1 way, untyped and prone to error
In .NET 2.0 we used strongly typed settings classes:
Step 1: Setup your settings in your common project. E.g. Northwind.Common
Figure: Settings in Project Properties
Step 2: Open up the generated App.config under your common project. E.g. Northwind.Common/App.config
Step 3: Copy the content into your entry applications app.config. E.g. Northwind.WindowsUI/App.config The new setting has been updated to app.config automatically in .NET 2.0
<configuration><connectionStrings><add name="Common.Properties.Settings.NorthwindConnectionString"connectionString="Data Source=(local);Initial Catalog=Northwind;Integrated Security=True"providerName="System.Data.SqlClient" /></connectionStrings></configuration>
...then you can access the connection string like this in C#:
SqlConnection sqlConn =new SqlConnection(Common.Properties.Settings.Default.NorthwindConnectionString);
❌ Historical example - Access our connection string by strongly typed generated settings class...this is no longer the best way to do it
Most systems will have variables that need to be stored securely; OpenId shared secret keys, connection strings, and API tokens to name a few.
These secrets must not be stored in source control. It is insecure and means they are sitting out in the open, wherever code has been downloaded, for anyone to see.
You may be asking what's a secret for a development environment? A developer secret is any value that would be considered sensitive.
Most systems will have variables that need to be stored securely; OpenId shared secret keys, connection strings, and API tokens to name a few. These secrets must not be stored in source control. It's not secure and means they are sitting out in the open, wherever code has been downloaded, for anyone to see.
There are different ways to store your secrets securely. When you use .NET User Secrets, you can store your secrets in a JSON file on your local machine. This is great for development, but how do you share those secrets securely with other developers in your organization?
Clear text email addresses in web pages are very dangerous because it gives spam sender a chance to pick up your email address, which produces a lot of spam/traffic to your mail server, this will cost you money and time to fix.
Never put clear text email addresses on web pages.
One of our goals is to make the job of the developer as easy as possible. If you have to write a lot of code for something that you think you should not have to do, you should make a suggestion and add it to the relevant page.
If you have to add a suggestion, make sure that you put the link to that suggestion into the comments of your code.
Use casts only if: a. You know 100% that you get that type back b. You want to perform a user-defined conversion
Empty Visual C# .NET methods consume program resources unnecessarily. Put a comment in code block if its stub for future application. Don’t add empty C# methods to your code. If you are adding one as a placeholder for future development, add a comment with a TODO.
Also, to avoid unnecessary resource consumption, you should keep the entire method commented until it has been implemented.
If the class implements an inherited interface method, ensure the method throws NotImplementedException.
We see a lot of programmers doing this, they have two conditions - true and false - and they do not consider other possibilities - e.g. an empty string. Take a look at this example. We have an If statement that checks what backend database is being used.
Be sure you are aware of what is business logic and what isn't. Typically, looping code will be placed in the business layer. This ensures that no redundant code is written and other projects can reference this logic as well.