Skip to content
This repository has been archived by the owner on Feb 12, 2022. It is now read-only.

Why does this gem exist? #3

Closed
mislav opened this issue Aug 11, 2013 · 20 comments
Closed

Why does this gem exist? #3

mislav opened this issue Aug 11, 2013 · 20 comments

Comments

@mislav
Copy link

mislav commented Aug 11, 2013

This gem does nothing but require two other gems that both set one line of Rails app configuration each. Heroku official documentation recommends using this gem for Rails 4 apps. For past Rails versions, Heroku automatically injected plugins that flip the same switches.

Why does Heroku insist jumping through such ridiculous hoops to save users from configuring 2 lines of their app code for Heroku deployment? Is the user's config/ directory sacred and must never be touched after being generated? Will Heroku keep releasing a separate gem for each line of configuration it needs its users to tweak? Do Heroku employees really believe this is the One True Way of setting deployment configuration, and that it's worth inflating our (already overly sized) Gemfile bundles and putting extra load to Rubygems.org?

With vendor/plugins/ injection, Heroku platform tried to have us keep pretending that we can still deploy freshly generated Rails apps to Heroku like we could in the early days, no tweaks necessary. With Rails 4, now we have to edit our Gemfile to add this ridiculous gem, so the illusion is broken. But the illusion was broken even way before, when Heroku started getting choked on "sqlite3" in the Gemfile, and when the ephemeral file system taught us the hard way that our Paperclip/Carrierwave uploads won't work like they do in development and that the default Rails.cache store will be reset on every deploy.

What Heroku could do instead is update their documentation to give us a Rails deploy checklist:

  1. You need to log to STDOUT
  2. You need to enable serving static assets
  3. You can't have "sqlite3", but should replace it with "pg"
  4. Everything you write to the filesystem is short-lived.

Additionally, the heroku command-line tool could have a subcommand that boots up the current Rails app in production mode and checks whether the above conditions have been satisfied.

/cc @schneems @hone

@adamyonk
Copy link

👍

@crazymykl
Copy link

Devil's advocate: if Heroku changes the deployment requirements, the user need only bundle update to get it going again.

Additionally, many small packages and a metapackage is a better method than one massive one.

@mislav
Copy link
Author

mislav commented Aug 11, 2013

Seriously, how hard is this?

diff --git a/Gemfile b/Gemfile
index 60b8e95..e10452d 100644
--- a/Gemfile
+++ b/Gemfile
@@ -3,8 +3,7 @@ source 'https://rubygems.org'
 # Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
 gem 'rails', '4.0.0'

-# Use sqlite3 as the database for Active Record
-gem 'sqlite3'
+gem 'pg'

 # Use SCSS for stylesheets
 gem 'sass-rails', '~> 4.0.0'
diff --git a/config/environments/production.rb b/config/environments/production.rb
index 041350d..ce5ecf9 100644
--- a/config/environments/production.rb
+++ b/config/environments/production.rb
@@ -19,8 +19,7 @@ HerokuTest::Application.configure do
   # For large-scale production use, consider using a caching reverse proxy like nginx, varnish or squid.
   # config.action_dispatch.rack_cache = true

-  # Disable Rails's static asset server (Apache or nginx will already do this).
-  config.serve_static_assets = false
+  config.serve_static_assets = true

   # Compress JavaScripts and CSS.
   config.assets.js_compressor = :uglifier
@@ -48,9 +47,6 @@ HerokuTest::Application.configure do
   # Prepend all log lines with the following tags.
   # config.log_tags = [ :subdomain, :uuid ]

-  # Use a different logger for distributed setups.
-  # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)
-
   # Use a different cache store in production.
   # config.cache_store = :mem_cache_store

@@ -77,4 +73,11 @@ HerokuTest::Application.configure do

   # Use default logging formatter so that PID and timestamp are not suppressed.
   config.log_formatter = ::Logger::Formatter.new
+
+  STDOUT.sync = config.autoflush_log
+  logger = ActiveSupport::Logger.new(STDOUT)
+  logger.formatter = config.log_formatter
+  logger.level = logger.class.const_get(config.log_level.to_s.upcase)
+
+  config.logger = ActiveSupport::TaggedLogging.new(logger)
 end

Admittedly, Rails doesn't allow us to easily log to STDOUT instead of a file (hence 5 lines of logger config) but explicitness is not a bad thing in configuration. The logger config API could be improved with a contribution to Rails.

Even though I configured my app correctly, Heroku complains:

-----> WARNINGS:
       Include "rails_12factor" gem to enable all platform features
       See https://devcenter.heroku.com/articles/rails-integration-gems for more information.

@adamyonk
Copy link

I'd much rather just have to do a little reading to figure out what's going on and what needs to be changed for Heroku-specific apps. Less ✨. Thanks for summing it up here.

@schneems
Copy link
Contributor

@mislav are you going to be at Rubyconf? Would love to chat at length about this and really anything else in person.

To understand why this gem exists you need to understand some values held by the Ruby Heroku team. In no particular order:

  • We aim to make deploying and running rails apps easy for all developers
  • If your app can run on Heroku it should be able to run anywhere else. We don't want vendor specific hacks.
  • We hope to one day be able to run rails apps out of the box with no "heroku specific" configuration (save maybe adding the PG gem to your Gemfile.)

Now for some gem specific info:

If you're going to tell someone to do something (http://schneems.com/ut-rails) you need to be prepared to tell them why. If you're telling them to add configuration options you need to explain what they do (https://github.com/heroku/rails_12factor#rails-4-serve-static-assets, https://github.com/heroku/rails_12factor#rails-4-logging). If you're telling them to add a gem you also need to explain what it does: rails_12factor makes your app compliant with a 12factor provider. Many developers don't need to know the specifics behind the configuration and exactly why it needs to be configured, and now they don't have to try to take in and parse that info. Those who want to dig deeper, are steered towards the readme with more information.

By putting all the config required to run on a 12factor provider in one place we can isolate the differences between "stock" Rails. While you're right we could do this in documentation rather than code, it's impossible to push upstream information to peoples minds while it's pretty easy to cut a new release of a gem to fix bugs or add additional edge case protection or default configurations.

Yes warnings are the bane of developers existence, and we're not happy having this one in there, but it's better than raising in this case. I've seen some devs who don't want the gem fork it, make a null branch and require that null branch from their Gemfile. As you mentioned you've got your app configured correctly, but because the standard config modes are all in ruby files there is no sane way for us to know that from a platform level. To support this Rails would need to have a platform standard way of exporting config (perhaps rake rails:config generates a json file that we can read in).

My personal goal for this gem is to make it obsolete. From Rails 2 to 3, deploying onto Heroku got harder, and again from 3 to 4 even on the same stack (cedar) which hasn't changed. We don't want to be in the Rails hacks business we want to be in the deploy and run your app the best we possibly can business. We spent literally weeks talking about the best way to deploy Rails4 on Heroku before the RC came out. All options had non-trivial side effects including cutting a gem.

Every hour I have to spend working around the framework is another hour I cannot be contributing to Rails itself, and that makes me sad.

@mislav
Copy link
Author

mislav commented Aug 14, 2013

Thanks for the thoughtful reply. No, I never attended any Rails/Rubyconf. But anyone who wants to get a hold of me virtually is welcome to initiate hangouts/Skypes/tmux sessions ;)

I get it that you tackled the issue, solved it and shipped it. No solution, especially fast ones could ever be perfect. However, the issues that I'm having with new Rails releases and Heroku deployment (I don't know what other 12factor hosts are and I don't care, since I only deal with Heroku for now in my life) is that on the surface it seems that Heroku adds new requirements for Rails apps to be "compatible" with their platform, but in reality nothing actually changed since Cedar debuted: Use Postgres, serve your assets yourself, log to stdout—that's it. But the Heroku instructions for different Rails versions are different. Now we're adding a gem which, from its description, seems to be doing something sophisticated and describes it as this higher-order "12 factor" philosophy, but behind the scenes it just flips a few switches. Something that we as developers should be in charge of. I fear that the new generations of developers are getting spoiled by "just add it to your Gemfile!" mentality.

@sobrinho
Copy link

What you guys think about heroku creating an initializer on applications at the slug compilation stage?

The initializer may configure everything needed to work (not sure if we can change the logging at the initializers stage) and the applications will not be changed to be deployed on heroku or any other environment.

An alternative may be creating a file like a preinitializer.rb, as we do on rails 2 to configure bundler but it's less elegant and may require more work on the heroku side.

@mislav
Copy link
Author

mislav commented Aug 14, 2013

I'm guessing Heroku team must have felt dirty injecting their code in our apps (when they did that to vendor/plugins) and that they don't want to go down that path again. I sure wouldn't.

@sobrinho
Copy link

There is a specific reason to not do that in this case? I never got a problem because of that.

@yfeldblum
Copy link

Initializers in config/initializers/ may run too late for various use-cases.

@marnen
Copy link

marnen commented Aug 26, 2013

@crazymykl That's worth a lot.

@schneems:

If your app can run on Heroku it should be able to run anywhere else. We don't want vendor specific hacks.

Sounds good, but isn't the addition of rails_12factor itself a vendor-specific hack? Perhaps Heroku should just inject the one line gem 'rails_12factor', group: :production into the Gemfile? (I don't like direct injection, but this seems like the least invasive solution.)

@mislav:
I like the gem approach, because I'd rather have this all encapsulated into one line of code that I need to change. I don't want to have to manually tweak config options for different hosts more than absolutely necessary

@EdJones
Copy link

EdJones commented Mar 9, 2014

It's seven months later and I spent a week trying do deploy a trivial (one model) app.

Now I must work to figure out why this gem is messing up my development mode.

How did we get to this point?

@marnen
Copy link

marnen commented Mar 9, 2014

Why do you think this gem is at fault? What problems are you having?

Marnen Laibow-Koser
marnen@marnen.org
http://www.marnen.org

Sent from Gmail Mobile

@nickmccurdy
Copy link
Contributor

👍 I agree with @mislav. While I appreciate that this gem is around for convience and can be updated in the future if needed, I think it's more important to force developers to update their config files for Heroku because of transparency.

@marnen
Copy link

marnen commented Mar 9, 2014

For the record, let me explicitly state my strong disagreement with that. I
think that it's important to have all the Heroku-specific configuration
options in one place, And that's why I like this gem. I don't want to have
to go through multiple configuration file to make my application run on
Heroku, and I don't want to have Heroku-specific options in a configuration
file with anything else.

Also, virtually every Rails application on Heroku is going to have to set
the same configuration options. To my mind, that mean that having the gem
so as to avoid repeated work is an excellent idea. I really don't
understand the opposition to this gem at all, because I think that any
other solution would make maintenance and deployment more difficult.

Marnen Laibow-Koser
marnen@marnen.org
http://www.marnen.org

Sent from Gmail Mobile

@csuhta
Copy link

csuhta commented Mar 12, 2014

At the very least, it would be nice if you stopped getting the warning when you've configured your app correctly without this gem's assistance.

@schneems
Copy link
Contributor

How should we detect a correctly configured application? Rails gives us no machine parseable config output. I recommend against trying to statically parse ruby code (where config is stored). Even if you 'rails c' the destination of the logs is not easily discoverable not to mention now we have to wait for the app to boot again which would make deploys slower platform wide.

Detecting gems on the other hand is straightforward. The logic is all in the gem name. If you fork the gem and strip out its guts we will still skip the injection. You can proceed to configure your app as you see fit.

Richard Schneeman

On Wed, Mar 12, 2014 at 10:06 AM, Corey Csuhta notifications@github.com
wrote:

At the very least, it would be nice if you stopped getting the warning if you've configured your app correctly without this gem's assistance.

Reply to this email directly or view it on GitHub:
#3 (comment)

@toothrot
Copy link

Hey folks, what about http://12factor.net/config ?

@muescha
Copy link

muescha commented Nov 11, 2014

it should be named as rails_12factor_for_heroku

@heroku heroku locked and limited conversation to collaborators Nov 11, 2014
@schneems
Copy link
Contributor

This gem is no longer needed with Rails 5

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests