Today, I met with the CTO of Unbounce, Carl Schmidt, who told me…
When I look at a solution like this, the first thing I think is, “does it actually solve a problem I have”? Which is followed immediately by “how”? What’s going on under the hood? Will it work with our platform? Is it engineered to be reliable, or is inserting it in the mix going to cause my application to fall over? Product web sites that do a good job of answering those questions immediately and clearly tend to have a better shot at getting me to open my wallet, whereas sites that only sell features leave me pretty cold.
In this blog post I’m going to go into some of the technical details behind FluidFeatures. As a user, this is something you do not need to know, but you might want to know, if you are going to deploy it with your Rails application in production.
The Rails Gems
There are actually two Ruby gems that make up your Rails integration with FluidFeatures. There is a Ruby gem, that can be used in any Ruby environment, and a Rails gem, which specifically integrates with your Rails application. In your Gemfile you only need to specify the Rails gem, fluidfeatures-rails, since it depends on the Ruby gem, fluidfeatures.
Ruby Gem
The Ruby gem manages application state and user transactions.
Application state is equivalent to what you will see on the FluidFeatures dashboard. This state includes which features there are and which users should see each version of each feature.
A single user transaction encapsulates a single HTTP request-response in the context of your Rails application. At the being of the transaction a snap-shot of the user’s visible features is taken, so that we can guarantee consistency for this user until the request-response is complete. We do this to ensure we do not have a feature enabled, or disabled, part-way through a request, which would result in a broken or half rendered feature.
Zero latency to your application is very important to us, which is why application state is pushed to your Rails application asynchronously from our HTTP API. The application state is stored in memory and when you make changes on the dashboard, we push that updated state to each instance of your running Rails application. The is done in a separate thread, so that it does not block your web application serving user requests. This is also done using HTTP long-polling, so that your Rails application initiates the request out over HTTPS port 443.
The FluidFeatures Ruby gem is resilient against outages of the FluidFeatures service, since any user request to your website is decoupled from updates being received from the FluidFeatures service. The gem will just use the last known state. You can also configure the Ruby gem to persist the state to disk, so that any restarts of your Rails application also handle outage of the FluidFeatures service. Even though we build this resilience into the client-side of our service, we take down-time with the FluidFeatures service very seriously and strive for zero down-time. We are simply building resilience at both ends, because your uptime is extremely important to us.
Transaction logs are sent back to the FluidFeatures service in a similar way to how application state is retrieved, which is over HTTPS in a separate thread, so as not to block your Rails application serving user requests. We bucket transactions to reduce the number HTTP requests back to the FluidFeatures service. If there are network issues or there is any reason that transactions cannot be sent to the FluidFeatures service we will spool them in memory, before discarding them. You can also configure disk spooling instead of discarding them and you can set limits on how much you spool to disk.
Rails Gem
As I mentioned, the Rails gem simply wraps the Ruby gem and adds the necessary integration into your Rails application.
A Rails controller before hook creates a user transaction for the currently logged in user at the beginning of each user request to your website.
A Rails controller after hook terminates this user transaction which queues the transaction log to be sent back to the FluidFeatures service.
You are required to define a method called fluidfeatures_current_user in your ApplicationController, which FluidFeatures calls during the before hook to determine who the current user is. This is also a chance to share any other details you want to expose in the FluidFeatures dashboard. This can be the user’s name, which is great for searching for users, or a group, such as “admin”, which can be used to segment your users.
Anonymous users are assumed if nil is returned from your fluidfeatures_current_user method. For anonymous users, we set a cookie in that user’s browser so that their experience is consistent. It is possible to manage anonymous users yourself, if you prefer.