We as software developers have created several disciplines for coding: test-driven developments, clean code principles, and screaming architecture, to name a few.?However, we often find our web.config files can sprawl out of control.
We just don’t pay as much attention to our configuration as we do to our?classes and methods. The good news is that many mistakes are easily avoided, and with a little discipline we can put our configs on par with the rest of our codebase.
In this post, I’ll show you how. We’ll look at five of the most common configuration mistakes and how to avoid them.
Download a free trial of CodeIt.Right and get an instant, automated code review.
What Is Web.config and Its Sibling, Appsettings.json?
Web.config and its newer sibling, appsettings.json, is the chief method the .NET framework has for managing changes across environments. By storing things like URLs, feature toggles, and server information in parameters, we free the applications itself to only focus on business logic that is the same for all deployments.
It is a file stored in the root of an ASP.NET application with the .config extension and uses XML as its language. You can find even more information about it here.
Appsettings.json is the default configuration file that comes with the newer .NET Core framework. It’s a little more terse to manage than its XML counterpart since it uses JSON as its language.
I will share some common mistakes that are made with one or both of these files and how you can avoid them.
1. Pretending Constants Are Configuration
Often, we put constants we could hardcode in our application into our configs “just in case.” We have such an obsession with reusability and future-proofing that we’re inclined to make life harder on ourselves on the off chance something changes. I call YAGNI?on this.
We deal in code. That’s our language strength. We get a compiler (usually). We get Typeahead.
In a web.config, we get some with weaker IDE help that ultimately breaks at runtime. Additionally, many times the more extensible we attempt to make something, the harder it becomes to maintain.
I’ve so often seen configs like this:
<add key="work_days" value="Mon,Tues,Wed,Thurs,Fri" />
Do you really need to configure this in a config, instead of an enum? Is this really going to change? Probably not, and if it does you can change the code in one place and move on with your work. It can get even worse when it evolves:
<add key="work.days.names" value="Mon,Tues,Wed,Thurs,Fri" /> <add key="work.days.descriptions" value="Monday,Tuesday, Wednesday, Thursday, Friday" />
At this point, this is more easily defined as a data structure inside your application. It’s clearly asking to be a Work struct with a Name and Description. (I’ve seen even worse examples, but I hope you get the point.)
The problem is, the configuration was designed for?environment-specific values, like database connection info and URLs to external services.
We can avoid this by just hardcoding these property values as constants.
If you see a config, ask yourself: Does this differ across environments? If not, consider moving it to an application-wide constant.
I still advise using dependency injection in your components where appropriate?as opposed to directly referencing them. Let your composition root reference the constant.
It may also be a configuration?that a third-party library needs. I’d still check if this configuration varies across environments. Often, these libraries have a code version of the configuration you can use instead.
2. Organizing by Technical Facets Instead of Features
As developers, we have the tendency to keep our minds in the technical. We want to think in terms of components, layers, and tools.
However, we’re often paid to solve business problems. When we align our code around its business needs, we have a screaming architecture that saves us a lot of pain. It also makes it easy to split our system in the future.
The same guidance applies to our configurations. When looking through code we are more likely to think “I need to see my payment gateway settings to see what URL I am using” as opposed to “I need to see all URLs so I can figure out which one is for my payment gateway.”
Yet, all too often I see this code smell:
<add key="services.endpoints.admin" value="http://admin.site.com" /> <add key="services.endpoints.twitter" value="http://twitter.com" /> <add key="services.tags.twitter value="software" /> <add key="services.impersonation.admin" value="true" />
Note that the top-level information is about the technical nature of the endpoints: They’re service endpoints.
Instead, I prefer a setup like this:
<add key="admin.endpoint" value="http://admin.site.com" /> <add key="admin.can_impersonate" value="true" /> <add key="twitter.endpoint" value="http://twitter.com" /> <add key="twitter.tags" value="software" />
Everything is grouped by its purpose in the application, letting you easily see what’s needed in an environment to achieve a feature.
3. Leaving Compilation Debug to “True”
For many years, it’s been commonplace for a .NET project to start with a web.config that includes this:
<compilation debug="true"/>
This little config is useful for being able to see what’s going on while you’re testing an app and building new features.
However, it’s easy to forget to turn this off in higher environments. This can lead to a myriad of problems in your application:
- Compiling web pages takes longer.
- You can run out of memory.
- It overrides your request timeouts, which can cause your application to hang.
- It can hog your memory.
There are more issues you can read about here. You can avoid these problems by setting up your prod and certain pre-production config files ahead of time with debug=”false.” (This tip only applies to web.config. .NET Core no longer has “compilation” settings.)
4. Plainly Storing Sensitive Information
It’s very common, especially in the early stages of a project, to store our credentials and other sensitive information in our configs in plaintext.
This is a large security risk.
Forget someone getting access to your config?file while it’s running. Often, we have our code flying around left and right, which lets people look through it. We may open it up to other?members of the company. We may even show?off some bits at a demo.
In all these scenarios, we risk exposing our credentials and other?sensitive keys.
Securing your credentials and secrets may be difficult at times, but it will prevent all this pain. Here are a couple of methods.
First, we can encrypt sensitive data. It’s a bit of an effort, but we can use the ASP.NET IIS Registration tool (aspnet_regiis.exe) to encrypt sections of our web.config file. Here’s a detailed guide?on how to do that.
Another method is to store your sensitive information in an external config file that’s persisted outside your source control system and loaded in at deployment time. Configuring it looks like this:
<appSettings file="secrets.config" > <add key="safe_config" value="plaintext" /> </appSettings>
This way, you can keep your credentials in a more secure place and your harmless configs in the main file. The app won’t know the difference at runtime.
5. Using the Wrong Tool for the Job
While appsettings.json is our default option in .NET Core, it has a flexible configurations system that gives us a host of other options:
- Azure Key Vault
- Azure Deployment Slots
- Command-line arguments
- Custom providers (installed or created)
- Directory files
- Environment variables
- In-memory .NET objects
This article offers more details. It’s pertinent to understand the pros and cons of each of these so we can select the one that best suits the needs of our project.
If for some reason none of these satisfy your needs, you can also make your own custom configuration provider with the prevalence of containerization.
My preference for many projects is to use environment variables, but your constraints and preferences may differ.
Configure It Right
It may take some discipline to follow these guidelines and ensure your configuration is healthy.
We often neglect our configuration files, focusing only on our code. But they’re important, and neglecting them will only cause you pain. With just?a little discipline, you can avoid this pain and focus on the fun parts of your job. You can even automate some of these rules to make them easy to follow.
Learn more how CodeIt.Right can help you automate code reviews and improve the quality of your code.
2 Comments. Leave new
I think a lot of these “errors” are just personal preference. Some are plain wrong.
The very first example, workdays changing….there are already a few countries that are moving to a 4 day work week. So yes, it would not change very often, but it’s almost always better to have a configurable entry that having to do a code deployment just because you hard coded the values.
Organizing by facets (or areas): completely personal preference. I wouldn’t dare say openly that someone is doing it wrong just because they chose to organize it in a different way than I prefer. As long as they are organized and the organization is intuitive, it should be good.
Leaving compilation to true is a lot harder these days, considering that removing the debug attribute is built-in in all the default Release transformation templates.
Another solution to #4, if it’s an app deployed to Azure, is to just have the appSettings stored on the Azure App Service/slot.
Hi, Gabriel. Thanks for the feedback. The way I organize configuration is a result of the pain I have seen across many teams. I could have been clearer on why I feel strongly about that. Thanks for the tip on #4! That is another good option.