3

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?

1

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).

3

I would propose an approach based on Dependency Injection and Null Objects. If you let your configuration define which object should be used for a specific task in each environment, then you can define specific behaviors within these objects. Then you use a Factory to initialize your objects depending on the environment.

I will use the mail as an example, but this approach can be applied in many other contexts: logs, error messages, communications with external API, etc... A null mailer will be an object that has the same interface as your usual mailer, and will simply ignore the send mail instructions. The objects that need to send emails will have a mailer parameter that is initialized at object instantiation: in your production environment, you mailer will be your usual mailer. In your test environment, your mailer will be you null ("do nothing") mailer.

To manage the instantiation of objects depending on the environment you work in, build a factory object that reads your configuration file and return the mailer (logger, api library...) depending on what is specified there. You may have a configuration file for each environment.

  • 1
    This should be the accepted answer because it is more general purpose. There are times when changing config files or databases doesn't solve the environment problem. What if you want the code to run on your PC and on the embedded hardware without having to recompile or use different builds? Config files won't solve this unless there's some code that can tell where the app is running. The idea above is to severely limit where the environment information is known. I do it slightly differently but with the same general concept in mind. – Dunk May 27 '15 at 20:19
1

Code should not be environment aware, that's why we have configs. Also, you usually do not want to ship your code with development secrets (DB connection string, etc.). Or sometimes it is not desirable to give developers access to the production secrets.

Although it's a typical task in programming, to have separate configurations for different environments, we have no common solutions, tools or processes how to solve it. I prefer config copying or transformation, which you have mentioned.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.