Dear god....this has always been a point of confusion for me, in that if you look up best practices, you'll find a multitude of contradictory advice. Well, I guess all solutions could be equally safe and sound...but it was still confusing and something almost never covered in beginner how-tos. I'm glad I'm not the only one who was always mildly confused.
I think the issue is that it's highly environment-specific and there's no single best practice aside from general principles like "don't store passwords in your repo".
I'm not sure that I'd even go so far as to say no passwords in the repo. In some environments, the developer is the only one with access to DB and the app code, and there is no growth planned (no separation of responsibilities), in which case it is acceptable even if not optimal to have them in database.yml.
Yes, but the developers who know how to then remove such hard coded variables and then where to store them will, out of habit, go the extra step, even if out of paranoia, because such a step is likely forgotten when the repo suddenly needs to be opened up
The more common case I've come across is that devs who leave their passwords hard coded do so because they are inexperienced and know of no other way. I wish the best practice were the default in this situation
This is extremely useful. A TON of people learn Rails from tutorials and never hear about issues like this. Then they go and push to GitHub and expose their login information for something secure. It took me too long to figure this out, thankfully nobody really looks at my Rails projects on GitHub (I hope).
edit: I see the source of this misunderstanding. The author states:
> Rails applications should be “turnkey”; that is, deployed anywhere without modifying code
This is an ideological statement with no basis in reality. If you have to jump through all these hoops in order to trick your app into running as expected without modifying code - you're probably Doing Things Wrong™.
A better approach might be to make a private fork of the project you wish to deploy, modify the code as needed, deploy from that, and periodically merge from upstream.
I've been reading "The Twelve-Factor App" today, which goes even farther and suggests a strict separation of code and config[1]. In which case, presumably, all of these settings:
would be stored outside the codebase, not just the username and password. This seems to be accomplished with a tool like Foreman[2], mentioned at the end of OP under "Other Approaches".
It's more work to set up, but does seem like it would be more reliable than your method. I like the idea of deploying an app anywhere without modifying code (only config details, stored separately). I've yet to work on a large SaaS app, but am studying the 12-factor manifesto to try to implement as much of it as possible when I do.
I read the "Twelve Factor" article you linked to and .. well, I just disagree.
> A litmus test for whether an app has all config correctly factored out of the code is whether the codebase could be made open source at any moment, without compromising any credentials
This to me is a totally arbitrary and kind of silly "test". When was the last time you had to suddenly open-source an app you were working on? Never, right? Me neither. Why would I go to all this work to keep it "instant open-source ready"?
> As the project grows further, developers may add their own special environments like joes-staging, resulting in a combinatorial explosion of config
As a senior rails dev + team leader, the way I'd handle this is to tell "joe" not to commit his local junk to the repo. Otherwise I have never seen this "explosion" happen. I would certainly not go to all this effort - and introduce all this complexity - to work around what I consider to be an educational issue.
The whole 12-factor config thing just comes across as premature optimisation. If you actually find yourself having a problem that putting config in environment variables would fix - then fine, implement it. But no-one should be doing it up front. 99% of the time, you ain't gonna need it.
* There is a reason for autoloading: faster development. Even though you should not usually need to make app-specific configuration changes outside of the main environment.rb and each environment-specific config (development.rb, etc.), if you do, you might consider putting them into a module in an autoload path, and make sure that the fully-qualified path (A::B::C maps to something like app/models/a/b/c.rb) is correct, so that it autoloads without trouble.
* Some suggest putting constants for frequently used values in a file somewhere under config/initializers. That way Rails doesn't complain when reloading them. But, that doesn't autoload if you make changes in development.
* You can extend a Module that is autoloaded with methods that are "marked" with module_function :method_name to become private class methods on the class that extends them. This way you have non-inheriting methods that basically act like config variables.
* You can just use class_attribute in Rails to define inheritable attributes and then use self.the_class_attr_name = something to set them in the body of the class for configuration.
* attr_accessor :some_var_name, @@some_class_var, @some_instance_var, a hash, a Struct, or a OpenStruct are fine ways to hold config values and can be set inline for class vars, in the initialize method for instance vars, or methods called by other hooks, depending on what you need.
* Usually avoid $some_global_var as there is no way to tell whether it has been defined nor whether it has been set explicitly to nil.
author of rails_config here. rails_config is sort of nice because you can use ERB so mix your regular settings along with ENV settings
That said, it's not been a terribly well maintained project. Haven't had time, needs help! I still use 0.2.5 since that's the only one that works for me :)
Taylor Mock and I are the authors of the article. Here's why we wrote the article. And why we suggest an alternative to using the Unix shell to set local environment variables.
The rails-stripe-membership-saas example application [1] from the RailsApps project has become very popular. I've been getting lots of requests for help stemming from problems setting local environment variables. Personally, I prefer to use the Unix shell to set local environment variables such as email account credentials and API keys. Always has worked for me. Apparently some people have trouble (perhaps complications with rvm or just plain ignorance). Anyway, telling newbies to school up is not a solution. So Taylor Mock came up with a trick to use Ruby to set local environment variables from a Rails application without involving the Unix shell.
It has some advantages. If the variables are set in the shell, the code just works. If the shell environment is a mess, the developer can use a local_env.yml file to set the environment variables.
There are several other valid and appropriate approaches (a .env file, a dotenv gem, etc). Also, there is a whole mini-industry of clever gems for setting constants and configuration variables in Rails. This is different. This is just intended for variables such as email account credentials and API keys that shouldn't be hardcoded.
I describe the motivations for the article in a RailsApps blog post [2].
Totally agree on your thoughts with relying on env. variables - I hit the same issue with trying to write re-usable Chef code.
Another pro tip w/r/t environment variables: NEVER rely on $PATH - that should be considered a global variable that any arbitrary piece of code in your process can modify at will since everyone knows about it. I know Jruby use to rely on this for their Ant integration and it was annoying as hell before it was fixed.
I've wrestled with this pattern (anti-pattern?) myself. One key advantage is that I can run plain Ruby unit tests outside of Rails, as long as I make sure the right environment variables are set in my shell.
I'm conflicted about it though; I hate being out of the mainstream on something as fundamental for maintenance as this and I have to jump through hoops to get Apache to set my environment variables.
I've been really liking the foreman approach with Heroku. It's super easy to create a .env file that contains environment variables that can then be git-ignored.
Interesting article as I haven't gone down this path.
I tend to favor YAML files for configuration, and symlink them in the case of security concerns like private keys.
To roll up a standard access pattern in our app which has dozens of configuration files, I wrote this which allows local developer overrides via xxx_local.yml:
I like that they present solutions that let you load ENV vars from YAML, sort of like a best of both worlds. Maintaining ENV Shell across multiple servers is even harder so YAML works well.
That's kind of interesting. One would think the :null_store would only be useful in some environments, and not as something you may want, for example, in production. In what capacity are you using this?