---
title: Extract Mostly-Constant Data from the Database
teaser: |
  How moving some data out of the database and into your codebase
  as well-named constants can decouple your code and improve performance.
tags: web,rails,sql
author: Tute Costa
published_on: 2014-04-11
---

Using database-backed mostly-static models in our applications can cause
coupling and performance problems. Extracting that data to Ruby constants helps
to resolve those problems.

## Example

Consider the following database tables and their data:

* `subscription_plans` (subscriber free, subscriber paid, group free,
  group paid)
* `countries` (United States, India, Belgium, Uruguay, etc.)

These change so rarely that for the purpose of the application, it is
essentially constant. Representing the data in our database has the following
drawbacks:

* Code/database coupling
* Slower performance

## Code/database coupling

If we put constant data in the database, our code and the database will need to
exist in a very specific shape at a given time.

Every environment (development, staging, production) will need to know the
values and seed them into their respective databases.

When we need to deploy code changes, we may need to include a data migration to
change the data. Particularly in [environments that gradually roll out features](http://signalvnoise.com/posts/3535-beyond-the-default-rails-environments),
this may complicate our development, git branching, and deploy processes by
forcing us to consider questions such as:

* Do we run the migration before or after the code changes are deployed?
* During the roll out to a percentage of users and app servers, will both old
  and new code work on the migrated database or will one cohort get bugs?

## Slower performance

While [99% of our traffic patterns should be served from our database cache](http://podcasts.thoughtbot.com/giantrobots/77),
we will still be paying a small performance penalty for accessing the database
across a network.

In comparison, if we move our data to constants in our application, we will
already have it in memory when the application loads. There is essentially no
performance hit to pull it from memory when we need it.

## Solution: Constants and Plain Old Ruby Objects

Here is an example class that can replace the `subscription_plans` table:

```ruby
class SubscriptionPlan
  def initialize(slug, name, braintree_id = nil)
    @slug = slug.to_s
    @name = name
    @braintree_id = braintree_id
    raise 'plan slug not recognized' unless SLUGS.include?(@slug)
  end

  SLUGS = %w(subscriber_free subscriber_paid group_free group_paid).map(&:freeze).freeze
  SUBSCRIBER_FREE = new('subscriber_free', 'Free Subscription')
  SUBSCRIBER_PAID = new('subscriber_paid', 'Monthly Subscription', 'user_monthly')
  GROUP_FREE = new('group_free', 'Group Subscription')
  GROUP_PAID = new('group_paid', 'Group Subscription', 'group_monthly')

  attr_reader :name, :slug

  def braintree_plan?
    braintree_id.present?
  end

  def price
    price_in_cents.to_f / 100
  end

  def price_in_cents
    braintree_plan? ? 5000 : 0
  end
end
```

The application no longer needs to have up-to-date seeds, neither its test
suite to have factories in sync to be able to run.

We no longer need database migrations if a new subscription plan is added in
the future. Instead, the constantized data change alongside with the code
that consumes it, making deploys simpler.
