The only acceptable use for callbacks in Rails ever

Jonathan Wallace

2 min read

Aug 6, 2012

The only acceptable use for callbacks in Rails ever

About three years ago, I worked at a product company where the central functionality in our app consisted of five or six domain models with excessive callbacks. I often found myself attacking the knotty nastiness for days at a time, trying to track down stubborn bugs.

Some time later, I stood up in front of the crowd at the Atlanta Ruby User’s Group and said, “I hate callbacks! What good are they?”

Today, I have the answer.

Let’s try an example

To demonstrate where callbacks are acceptable, let’s use the canonical Rails example, an online store.

As usual, there are Products, Orders, and LineItems, but we’ll be focusing on Orders. An Order instance has many LineItem instances. For optimization purposes, an Order keeps a copy of the total price, which is the sum of all line item prices.

On our sign-up page, we have a check box where a customer can indicate that they’re a returning customer. Returning customers get 10% off of the total price! Order#returning_customer will represent this information in the database.

Two options

So far, we’ve detailed two possible candidates for callbacks necessary to maintain the correct state of an Order:

  1. Order#total_price (based on the sum of line items)
  2. Order#total_price (to be updated based on whether we have a returning customer)

A naive approach would be to add callback logic to the Order model to sum all associated line items and apply the returning customer discount. However, adding a callback for #1 would violate the Single Responsibility Principle, as an Order would now be required to know about attributes of line items and how to sum them.

Here’s the correct approach.

The simple rule

An Order may have a callback for #2 because there are no external dependencies in the callback logic! That’s it. That’s the simple rule. Use a callback only when the logic refers to state internal to the object.

You may now be asking, but how does an Order then determine the total_price? That’s the responsibility of a higher-level service object that manages the interaction between Orders and LineItems, perhaps a controller action or, preferably, a plain old Ruby object.

When do you use callbacks?

Angie Terrell

Reviewer Big Nerd Ranch

Angie joined BNR in 2014 as a senior UX/UI designer. Just over a year later she became director of design and instruction leading a team of user experience and interface designers. All told, Angie has over 15 years of experience designing a wide array of user experiences

Speak with a Nerd

Schedule a call today! Our team of Nerds are ready to help

Let's Talk

Related Posts

We are ready to discuss your needs.

Not applicable? Click here to schedule a call.

Stay in Touch WITH Big Nerd Ranch News