Environment-Aware Code – Best Practices

cdevelopment-environmentnetproductionrelease-management

There are situations where the deployed environment (development, test, or production, for example) might dictate the outcome of certain actions.

For example, perhaps a successful "user registration" process will send a notification email to the new user. Environment-specific actions:

  • Development: Do not actually send the email. Email logs will provide enough for developers.
  • Test: Send all emails to some testEnvironment@domain.com inbox and not to the user's address.
  • Production: Send the email to the user.

I have listed three possible solutions below.

CurrentEnvironment configuration value

One way of solving this, which I have seen a lot, is to have some configuration value (whether it be in some xml config file or in the database) such as CurrentEnvironment which specifies the current environment in which the system is deployed to. This would require case/if checks in code to determine the desired action:

if(CurrentEnvironment == Environment.Test)
{
    // Send all emails to some testEnvironment@domain.com inbox.
}
else if(CurrentEnvironment == Environment.Production)
{
    // Send the email to the user.
}

This is not a maintainable solution in my opinion.

Wipe all email addresses

Another method is to run a change script, once restoring a database, to do the following:

  • Remove all user email addresses (in development),
  • Replace all user email addresses with testEnvironment@domain.com (in test)

This is an extra step in the release process which, if missed, could have some dangerous results. Additionally, this solution only fixes the email problem. There are perhaps many other situations where the environment matters.

Config transformation

Another idea is to use web.config transformations. This way the config can be different for different environments. For example, we will have the following configs:

 web.config
   web.Development.config
   web.Test.config
   web.Production.config

The transformation can then use different "providers" or set certain attributes according to the environment. For example, an overrideDeliveryAddress can be set in the web.Test.config:

<EmailService>
  <providers>
    <add 
      name="EmailServiceProvider"
      type="CustomProviders.EmailServiceProvider, CustomProviders"
      smtp="smtp.domain.com" password="xyz"
      overrideDeliveryAddress="testEnvironment@domain.com"
      enabled="true"
    />
 </providers>
</EmailService>

This solution requires much more work, but is more maintainable and less invasive. Code is now environment-oblivious.

What other ways can the above be achieved? Should code EVER be environment aware?

Best Answer

First, you should not use your real production data (especially not person-related data) in development or test (you may use carefully choosen excerpts from production data, of course). So your data pools (maybe a database, maybe a pool of files, maybe both), should be separated between those environments. So the only thing you need to change is the config file where you switch between those 3 data pools (for example, the database connection string, or the path to the files). This means there will be no need for any "environment-aware code", the only single thing which is enironment-aware is that entry in your main configuration file.

For example, your development environment should have development database with a list of email adresses, and your test environment a different database with a different list, and your production environment a database with the real mail adresses.

The problem you are describing seems to be a situation where you try to load a copy of the production database into your development environment and replace your development database with that. This is most probably a scenario you should avoid, better provide import/export mechanisms to transfer small portions of data from one database into another, where personal data is normally not transferred. If you really need an original copy of the production DB in your dev environment (because otherwise you cannot reproduce a certain bug), go with solution 2 and delete or anonymize any personal data like mail adresses (in many scenarios you must do this either for reasons of privacy).

Related Topic