Matthew Lindfield Seager

Matthew Lindfield Seager

An issue with Spring caching lead me on a journey of discovery

TLDR; I spent quite a while trying to figure out why ENV variables weren’t being loaded by dotenv-rails. Reloading spring with spring stop was the surprise fix. I learned a lot in the meantime, and since!


I decided to encrypt all personally identifying information (e.g. names and email addresses) in an app I’m hacking away on. It won’t protect them if the application server gets compromised but it adds some protection for the data at rest (you might be surprised how often data is compromised from logs or backups).

Encryption keys are part of an apps configuration and as such, I learned, they don’t belong in the code. In production I will manage the encryption keys through Heroku config vars but I wanted a simple way to manage environment variables in development so I chose dotenv (via the dotenv-rails gem).

Once I had the gem installed and my .env file populated, I fired up the Rails console and was surprised my variables weren’t in ENV. Manually running Dotenv.load worked so I knew the gem was installed and my .env file was able to be found.

After restarting the console a couple more times, the next thing I tried was adding Dotenv::Railtie.load to config/application.rb as per the instructions on making it load earlier. I fired up the console again and they STILL weren’t loaded.

I’d looked through the code on Github and felt like I had a pretty good understanding of what should be happening (and when) but it wasn’t behaving as documented.

At this point I felt like I needed to debug what was going on inside the gem so I figured out how to get the local path to the gem (bundle show dotenv-rails - thanks Stackoverflow!) and then opened the gem in my text editor. In fish shell that can be combined into a single command:
atom (bundle show dotenv-rails)

From there I did some caveman debugging and added puts statements to #load and #self.load to see if I could see them being called. I then restarted the console… still nothing. But now that I had access to the gem I could start testing with rails server rather than rails console. I restarted my dev server and straight away saw:

`self.load` got called
`load` got called
`self.load` got called
`load` got called
=> Booting Puma
=> Rails 6.0.2.1 application starting in development

Sure enough, it works when I start the server (twice, thanks to my addition of Dotenv::Railtie.load) and so the problem is only in Rails console.

After some more digging around in the Github issues I found some reference to a similar problem being caused by spring. As soon as I ran spring stop and restarted the console it worked.

To get to the bottom of this issue I started researching Spring but according to the README, changing the Gem file AND changing config/application.rb should both have caused Spring to restart.

I’ve opened an issue on Spring to see if anyone can help me figure it out but in the meantime I’m happy to have learned a fair bit…

Lessons Learned

  1. Config belongs in the environment, not in the code: https://12factor.net/config
  2. The dotenv gem makes managing environment variables simple (including support for different variables in different environments)
  3. You can find a Gem’s installation path with bundle show <gem-name>
  4. You can’t pipe the output of that command to open the gem in Atom (at least in fish shell) but you can run it as a sub-command using brackets: atom (bundle show dotenv-rails)
  5. Spring is a built-in rails mechanism for caching the application to speed up boot times in development (particularly console and tests)
  6. You can restart Spring with spring stop or by closing your terminal (the next time you launch something it will start again)
  7. You can tell Spring which files to watch by editing the config/spring.rb file