Ruby
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Methods and Blocks
-
Section 4: Object-Oriented Ruby
-
Section 5: Metaprogramming
-
Section 6: Working with Data and Files
-
Section 7: Ruby Frameworks Overview
-
Section 8: Ecosystem and Testing
-
Section 9: Practical Projects
-
Section 10: Interview Practice
-
Section 11: Data Structures and Algorithms in Ruby
-
Section 12: More Practice Exercises
-
Section 13: Enumerable and Functional Style
-
Section 14: More OOP Practice
-
Section 15: More Testing
-
Section 16: Enumerable and Comparable Modules In Depth
-
Section 17: Ruby Standard Library: Core Utilities
-
Section 18: Ruby Standard Library: Data and Security
-
Section 19: Ruby Standard Library: CLI and Text
-
Section 20: Ruby Networking
-
Section 21: Ruby on Rails Deep Dive
-
Section 22: Ruby Metaprogramming Deep Dive
-
Section 23: Ruby Design Patterns
-
Section 24: Ruby Concurrency
-
Section 25: Ruby Testing Deep Dive
-
Section 26: Ruby Gems and Packaging
-
Section 27: Ruby Performance
-
Section 28: More Practice Exercises
-
Section 29: More Interview Practice
-
Section 30: Rails API Development
-
Section 31: Rails Authentication and Authorization
-
Section 32: Rails Testing Deep Dive
-
Section 33: Rails Performance
-
Section 34: Rails Deployment
-
Section 35: Sinatra and Lightweight Ruby Web Apps
-
Section 36: More Ruby Language Deep Dive
-
Section 37: Ruby 3.x Modern Features
-
Section 38: More Data Structures in Ruby
-
Section 39: More Practical Projects
-
Section 40: Ruby Ecosystem Tools
-
Section 41: More Practice and Review
-
Section 42: Final Practice and Mastery
-
Section 43: Ruby Interview Deep Dive
-
Section 44: Ruby Background Processing Deep Dive
-
Section 45: Ruby GraphQL
-
Section 46: Ruby Object Model Deep Dive
-
Section 47: Ruby Hanami Framework Overview
-
Section 48: Ruby gRPC and Protocol Buffers
-
Section 49: Ruby Data Processing
-
Section 50: Ruby Search Integration
-
Section 51: Ruby File Upload and Media
-
Section 52: Ruby Email and Notifications
-
Section 53: Ruby Admin Panels
-
Section 54: Ruby Feature Flags and Experimentation
-
Section 55: Ruby Monitoring and Observability
-
Section 56: Ruby Docker and Deployment Deep Dive
-
Section 57: Ruby Security Deep Dive
-
Section 58: More Advanced Metaprogramming
-
Section 59: More Final Projects
222: Implementing Feature Flags with Flipper
I've been staring at this new PremiumDashboardController for a while now. It's mostly finished, but I'm nervous about pushing it to production. If the new analytics queries I wrote are as slow as they felt in staging, I might bring the whole site to a crawl. My first instinct—the old-school way—was to just wrap the whole thing in a simple conditional.
def index
if ENV['ENABLE_PREMIUM_DASHBOARD'] == 'true'
@data = AnalyticsService.fetch_heavy_stats
else
redirect_to root_path, notice: "Coming soon!"
end
end
The "Deploy-to-Change" Headache
This works, sure. But here's the problem: if I notice the database spiking at 2:00 AM, I have to change an environment variable and trigger a full redeploy of the application just to turn the feature off. That's a huge amount of friction for a "kill switch." I want to be able to toggle this feature on the fly without touching the CI/CD pipeline. This is where I usually reach for the flipper gem.
First, I'll add gem 'flipper' to the Gemfile and run bundle. I'm using the ActiveRecord adapter because I don't want to manage a separate Redis instance just for flags. After running the generators to create the necessary tables, I can finally scrap that ENV variable.
def index
if Flipper.enabled?(:premium_dashboard)
@data = AnalyticsService.fetch_heavy_stats
else
redirect_to root_path, notice: "Coming soon!"
end
end
Now, I can go into the Flipper console (or use the UI) and flip :premium_dashboard to true. The feature is live. No deploy. No stress. But I've immediately hit a new wall: I don't want everyone to see this yet. I want my internal team and maybe a few "power users" to test it first.
Targeting Specific Users
If I use Flipper.enabled?(:feature_name), it's a global boolean. It's either on for everyone or off for everyone. To make this a true "canary release," I need to pass an "actor" to Flipper. In our case, the actor is the current_user.
Let's try adjusting the check:
def index
# Passing current_user tells Flipper to check for specific actor permissions
if Flipper.enabled?(:premium_dashboard, current_user)
@data = AnalyticsService.fetch_heavy_stats
else
redirect_to root_path, notice: "Coming soon!"
end
end
Now, the logic changes. If the feature is globally "on," everyone gets it. If it's globally "off," Flipper then checks if the current_user specifically has been granted access. I can now go into the console and run Flipper[:premium_dashboard].enable(User.find(1)). User 1 sees the dashboard; everyone else is still redirected. It's a much safer way to roll out risky code.
The Danger of the "Permanent Flag"
Here is something I've messed up in the past: leaving these flags in the code forever. I once found a feature flag in a legacy project from 2017 that was still wrapping a "New Login Page." The "New" page had been the default for five years.
When we use Flipper, we're essentially adding technical debt by design. We are creating multiple code paths. Once the premium_dashboard is fully rolled out to 100% of users and we're sure it's stable, the very next ticket in my sprint isn't a new feature—it's "Remove Flipper flag for Premium Dashboard."
You have to be disciplined. A feature flag is a bridge to a destination; once you've crossed the bridge, you should tear it down so you don't have to maintain it.
📋 Practical Task
Exercise: Implementing a Beta-Only Search Filter
You are working on an e-commerce app. You've built a complex AdvancedSearchFilter that you only want to enable for users with an email ending in @company.com (internal employees) and one specific beta tester with id: 42.
Your Goal:
- Modify the
SearchController#indexaction to use theflippergem to guard theAdvancedSearchFilterlogic. - The feature flag should be named
:advanced_search. - Ensure the code allows for both global toggling and per-user (actor) enabling.
- Write a small script or a set of console commands that would:
- Keep the feature globally OFF.
- Enable the feature specifically for the user with ID 42.
- Enable the feature for all users whose email matches the internal company domain.
There are no comments for now.