← All posts

Ensuring the Same Configuration Across Development, QA, and Production

One of the biggest deployment challenges is making sure the application behaves consistently across all environments while allowing environment-specific settings (like connection strings and API keys).

1. Keep Configuration Outside the Code (12-Factor App)

Never hardcode environment-specific values.

Bad

var connectionString = "Server=DEV-SQL;Database=MyApp;";

Good

builder.Configuration.GetConnectionString("DefaultConnection");

Store values in configuration files, environment variables, or a secret manager.


2. Use Environment-Specific Configuration Files

ASP.NET Core supports this automatically.

appsettings.json
appsettings.Development.json
appsettings.QA.json
appsettings.Production.json

Example:

appsettings.json

{
  "Logging": {
    "LogLevel": {
      "Default": "Information"
    }
  }
}

appsettings.Production.json

{
  "Logging": {
    "LogLevel": {
      "Default": "Warning"
    }
  }
}

3. Use Environment Variables for Sensitive Data

Never store:

  • Database passwords
  • API Keys
  • JWT Secret
  • Payment secrets

Instead:

ConnectionStrings__Default
JWT__Secret
Stripe__SecretKey

Environment variables override appsettings values.


4. Keep the Same Infrastructure as Much as Possible

Try to keep:

  • Same .NET version
  • Same OS
  • Same Docker image
  • Same IIS/Kestrel version
  • Same database version
  • Same Redis version

Only change what must differ (URLs, credentials, scaling).


5. Infrastructure as Code (IaC)

Provision environments using tools such as:

  • Terraform
  • Bicep
  • ARM Templates
  • AWS CloudFormation

This ensures infrastructure is reproducible and consistent.


6. Use Docker

Package the application once.

FROM mcr.microsoft.com/dotnet/aspnet:8.0

Deploy the same image to:

  • Development
  • QA
  • Production

Only inject different configuration values through environment variables.


7. CI/CD Pipeline

Build once.

Developer
      ↓
Build
      ↓
Run Tests
      ↓
Create Artifact
      ↓
Deploy to Dev
      ↓
Deploy Same Artifact to QA
      ↓
Deploy Same Artifact to Production

Don't rebuild for each environment. Promote the same artifact to reduce inconsistencies.


8. Database Migration Strategy

Use migration tools consistently.

dotnet ef database update

Or execute migrations during deployment.

Every environment should have:

  • Same schema
  • Same indexes
  • Same stored procedures

9. Validate Configuration at Startup

Fail fast if required settings are missing.

builder.Services
    .AddOptions<JwtOptions>()
    .BindConfiguration("JWT")
    .ValidateDataAnnotations()
    .ValidateOnStart();

This prevents the application from starting with invalid configuration.


10. Automated Testing

Before promoting to Production:

  • Unit Tests
  • Integration Tests
  • API Tests
  • Smoke Tests
  • Health Checks

These help verify that the application behaves consistently across environments.


11. Feature Flags

Instead of maintaining different codebases:

Production
    Feature A = ON
    Feature B = OFF

QA
    Feature A = ON
    Feature B = ON

The code remains the same; only feature toggles change.


12. Configuration Validation in CI/CD

The pipeline should verify:

  • Required environment variables exist
  • Secrets are available
  • Connection strings are valid
  • Configuration files are syntactically correct

This catches issues before deployment.