Releasing a Redesign using Feature Flags and Rails Variants

Written by Ari Summer·
·Last updated July 16, 2023

Redesigning an existing application can be a time consuming process, especially for a relatively large application. Performing a redesign behind a feature flag has a lot of benefits. It allows you to make incremental progress on the new design, deploying updates while your users are none the wiser - they continue to see the existing design while the redesign happens behind-the-scenes.

With a feature flag, your team can iterate and use the new design in production internally. You also have the option to allow users to opt-in to the new design to receive feedback before making it generally available. This reduces the risk of regressions when it comes time to unleash your new design on the world. You’ll see this approach used by companies like AWS and Github.

How would you do this with Rails? For feature flag support, there’s a ubiquitous gem called Flipper. Once you have Flipper (or some other way of toggling features) set up, you still need a way to switch between the current and new design. Template variants to the rescue! Action Pack Variants were added in Rails 4.1. They’re primarily used to render different templates for different devices, e.g. you may have a separate template for tablets, but they can also be used for this use case. Here’s an example:

app/controllers/application_controller.rb
class ApplicationController
  before_action :set_variant
 
  def set_variant
    if Flipper.enabled?(:new_design, current_user)
      request.variant = :new_design
    end
  end
end
app/views/layouts/application.html.erb
<html>
  <head>
    <%= javascript_include_tag "application", "data-turbo-track": "reload" %>
    <%= stylesheet_include_tag "application", "data-turbo-track": "reload" %>
  </head>
</html>
app/views/layouts/application.html+new_design.erb
<html>
  <head>
    <%= javascript_include_tag "application-new-design", "data-turbo-track": "reload" %>
    <%= stylesheet_include_tag "application-new-design", "data-turbo-track": "reload" %>
  </head>
</html>

In the example above, for each request we’re setting request.variant to :new_design if the new design is enabled for the current user. By doing this, any view or layout with the .html+new_design.erb suffix will take precedence over .html.erb for the current request.

With a setup like this, we can put all javascript and css for our new design in the head of the application.html+new_design.erb layout. If the user is enabled for the new design, Rails will render application.html+new_design.erb as the layout instead of application.html.erb.

Now let’s say we’re ready to start redesigning our application dashboard which is located at app/views/dashboards/show.html.erb. All we need to do is create a new view called app/views/dashboards/show.html+new_design.erb with the new markup.

app/views/dashboards/show.html.erb
<p>Users NOT enabled for the new design will see this.</p>
app/views/dashboards/show.html+new_design.erb
<p>Users enabled for the new design will see this.</p>

Once you’ve completed your redesign, “flipping” it on for everyone is easy with a feature flag. If something goes wrong, it’s easy to roll back to the old design. If all goes well, all that’s left is to remove the unneeded code from the old design and rename the *.html+new_design.erb files to *.html.erb , replacing the old design templates.