Rails.env and ENV
Rails.env and ENV look related but do different things. This post covers what each one actually does, when to use ENV.fetch over ENV[], and how to combine them in practice.
Rails.env
Rails.env returns the current environment as a string. Rails ships with three standard environments: development, test, and production.
Rails.env # => "development"
Rails.env.class # => ActiveSupport::StringInquirer
The return type is StringInquirer, which is an ActiveSupport wrapper around String. It lets you use predicate methods instead of string comparisons:
# Both work, but predicate methods are the Rails convention
Rails.env == "production" # string comparison
Rails.env.production? # preferred
All three environments have a predicate method:
Rails.env.development?
Rails.env.test?
Rails.env.production?
When to Use It
Use Rails.env when you need to run different code paths depending on the environment.
Conditional logging:
if Rails.env.development?
logger.debug("Response payload: #{response.body}")
end
Skip heavy setup in test:
unless Rails.env.test?
SomeService.configure do |config|
config.api_key = ENV.fetch("SOME_SERVICE_API_KEY")
end
end
Feature behavior per environment:
def send_notification(user)
if Rails.env.production?
ExternalMailer.deliver(user.email, message)
else
Rails.logger.info("[DEV] Would have sent email to #{user.email}")
end
end
ENV: Reading Environment Variables
ENV is a Ruby built-in, a hash-like object that maps to the environment variables of the current process.
ENV["DATABASE_URL"] # => "postgres://localhost/myapp_development"
ENV["MISSING_KEY"] # => nil
ENV[] vs ENV.fetch
The key difference is how they handle missing keys.
ENV["KEY"] returns nil silently when the key doesn't exist. This can cause nil related errors from where the variable was read, making the root cause hard to trace.
ENV.fetch("KEY") raises a KeyError immediately if the key is missing, with a clear message:
ENV.fetch("MISSING_KEY")
# => KeyError: key not found: "MISSING_KEY"
You can also provide a default value:
ENV.fetch("LOG_LEVEL", "info") # => "info" if not set
ENV.fetch("WORKERS") { |k| "2" } # => "2" via block
Use ENV.fetch for required variables (API keys, database URLs, credentials). Fail loudly at startup rather than silently returning nil somewhere deep in a request.
Use ENV[] with a fallback for optional variables where nil or a default is acceptable:
log_level = ENV["LOG_LEVEL"] || "info"
max_retries = (ENV["MAX_RETRIES"] || "3").to_i
ENV Values Are Always Strings
Every value from ENV is a string, even if it looks like a number or boolean. You need to convert explicitly:
ENV["MAX_CONNECTIONS"] # => "10" (String)
ENV["MAX_CONNECTIONS"].to_i # => 10 (Integer)
ENV["FEATURE_ENABLED"] # => "true" (String)
ENV["FEATURE_ENABLED"] == "true" # => true (Boolean comparison)
Combining Rails.env with ENV
A common pattern is to use Rails.env to guard whether a required ENV variable is needed:
if Rails.env.production?
api_key = ENV.fetch("PAYMENT_API_KEY")
end
Or setting up service configuration:
Analytics.configure do |config|
config.enabled = Rails.env.production?
config.api_key = ENV.fetch("ANALYTICS_KEY", nil)
end