<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/callbacks-concerns-and-current-avoiding-hidden-dependencies-in-rails-4tagk9vws" -->

---
title: Callbacks, Concerns, and Current: Avoiding Hidden...
description: An in-depth examination of three convenient Rails tools—Active Record callbacks, concerns, and CurrentAttributes—that each hide something important: callbacks...
canonical: https://daily.dev/posts/callbacks-concerns-and-current-avoiding-hidden-dependencies-in-rails-4tagk9vws
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: Callbacks, Concerns, and Current: Avoiding Hidden Dependencies in Rails | daily.dev
og:description: An in-depth examination of three convenient Rails tools—Active Record callbacks, concerns, and CurrentAttributes—that each hide something important: callbacks...
og:url: https://daily.dev/posts/callbacks-concerns-and-current-avoiding-hidden-dependencies-in-rails-4tagk9vws
og:image: https://api.daily.dev/og/posts/4TaGK9vWS.png
og:image:alt: Callbacks, Concerns, and Current: Avoiding Hidden Dependencies in Rails
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Callbacks, Concerns, and Current: Avoiding Hidden Dependencies in Rails

**[Ruby Flow](https://daily.dev/sources/rubyflow)** · 13 min read · 0 upvotes · 1 comments

## Summary

An in-depth examination of three convenient Rails tools—Active Record callbacks, concerns, and CurrentAttributes—that each hide something important: callbacks hide side effects, concerns hide ownership of responsibilities, and Current hides a method's real dependencies. Using an evolving e-commerce Order model (inventory, email, payments, loyalty discounts), it walks through concrete failure scenarios (a bulk import triggering 12,000 stray emails, discount logic silently breaking for phone orders and background jobs, callback ordering bugs) and proposes a guideline: keep callbacks only for data the record itself owns, extract concerns only when they have a small explicit contract, promote business concepts with their own vocabulary into dedicated objects like a PaymentLedger, and pass context explicitly as arguments instead of reading global state.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://develclan.com/callbacks-concerns-current-rails>

## Questions this post answers

### Why did a bulk import of historical orders in Rails send confirmation emails to 12,000 customers?

Using Order.create! for a bulk import triggers every Active Record callback registered on the model, including after_create hooks that send confirmation emails and reduce inventory. Because create callbacks run regardless of entry point (console, jobs, imports, tests), an import script with no mention of email or inventory can still trigger both, since those effects were implicitly attached to record creation rather than called explicitly by the importing code.

_Developers debugging surprise side effects from Active Record callbacks can find similar real-world breakdowns on daily.dev._

### Why does ActiveSupport::CurrentAttributes silently break discount logic for background jobs in Rails?

CurrentAttributes resets at the end of each request or job and is isolated per thread, so a monthly job that creates subscription orders never has Current.user set unless something explicitly assigns it. Code like Current.user&.loyal? then evaluates to nil, and the safe navigation operator makes the loyalty discount disappear silently with no error, because the method's dependency on the logged-in user is never visible in its signature.

_Anyone tracing silent discount or pricing bugs tied to global request state can dig into patterns like this on daily.dev._

### What is the main problem with a Rails concern like HasPayments that mixes calculations, persistence, and calls to accounting?

Its problem is that it has no clear contract: it depends on log_change from a separate Auditable module and on three Order columns (total, payment_reference, number) that it never declares, so understanding it requires reading both modules' bodies. Moving code into a concern does not separate responsibilities since Order's public interface still exposes every method the concern adds; a concept this complex deserves a dedicated object like a PaymentLedger instead.

_Rails developers deciding between concerns and dedicated objects can compare approaches like this on daily.dev._

## Community discussion

Top comments from developers on daily.dev.

**@nark3d** · 0 upvotes

> Fine, let me take the narrow case.  A callback that touches something the record genuinely owns, but only after the record is persisted: I think that is often the right place for it.  Passing context explicitly fixes the dependency.  The Order model is where ordering problems show up.  If a callback has to run after the row exists, does that make it a domain event, or is a callback with a guard the honest answer?

## Similar posts on daily.dev

- [ActiveRecord: Understanding CurrentAttributes](https://daily.dev/posts/activerecord-understanding-currentattributes-z8tu9gdrs) · RUBYLAND · 0 upvotes · 0 comments
- [The Rails Way in 2026](https://daily.dev/posts/the-rails-way-in-2026-lwaqok4v6) · arkency · 0 upvotes · 0 comments
- [Railsmaxxing, or hidden Rails gems](https://daily.dev/posts/railsmaxxing-or-hidden-rails-gems-f58azip3g) · Ruby Flow · 0 upvotes · 0 comments
- [Dynamic Ruby And Hidden Maintenance Costs](https://daily.dev/posts/dynamic-ruby-and-hidden-maintenance-costs-jnxrbecmr) · RUBYLAND · 0 upvotes · 0 comments
- [What Rails Actually Wants: Tidying Controllers and Views Without Service Object Explosion](https://daily.dev/posts/what-rails-actually-wants-tidying-controllers-and-views-without-service-object-explosion-z7ssr3alm) · Ruby Flow · 2 upvotes · 0 comments

---

Tags: [#testing](https://daily.dev/tags/testing), [#architecture](https://daily.dev/tags/architecture), [#ruby](https://daily.dev/tags/ruby), [#rails](https://daily.dev/tags/rails)

[View this post on daily.dev](https://daily.dev/posts/callbacks-concerns-and-current-avoiding-hidden-dependencies-in-rails-4tagk9vws)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"Callbacks, Concerns, and Current: Avoiding Hidden Dependencies in Rails","url":"https://daily.dev/posts/callbacks-concerns-and-current-avoiding-hidden-dependencies-in-rails-4tagk9vws","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/callbacks-concerns-and-current-avoiding-hidden-dependencies-in-rails-4tagk9vws"},"datePublished":"2026-10-09T21:36:55.740Z","dateModified":"2026-10-09T22:02:44.478Z","description":"An in-depth examination of three convenient Rails tools—Active Record callbacks, concerns, and CurrentAttributes—that each hide something important: callbacks...","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/713f66ccb9e4013cb3bbc6a1abf43412?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/713f66ccb9e4013cb3bbc6a1abf43412?_a=AQAEuop","isAccessibleForFree":true,"articleSection":"Ruby Flow","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"Ruby Flow","logo":"https://media.daily.dev/image/upload/t_logo,f_auto/v1/logos/a21aaa8bcbbf448193a016895c90a0e1","url":"https://daily.dev/sources/rubyflow"},"commentCount":1,"discussionUrl":"https://daily.dev/posts/callbacks-concerns-and-current-avoiding-hidden-dependencies-in-rails-4tagk9vws","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":0},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":1}],"keywords":"testing,architecture,ruby,rails","timeRequired":"PT13M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"Ruby Flow","item":"https://daily.dev/sources/rubyflow"},{"@type":"ListItem","position":3,"name":"Callbacks, Concerns, and Current: Avoiding Hidden Dependencies in Rails"}]}
{"@context":"https://schema.org","@type":"WebPage","@id":"https://daily.dev/posts/callbacks-concerns-and-current-avoiding-hidden-dependencies-in-rails-4tagk9vws","comment":[{"@type":"Comment","text":"Fine, let me take the narrow case.  A callback that touches something the record genuinely owns, but only after the record is persisted: I think that is often the right place for it.  Passing context explicitly fixes the dependency.  The Order model is where ordering problems show up.  If a callback has to run after the row exists, does that make it a domain event, or is a callback with a guard the honest answer?","datePublished":"2026-10-11T10:24:24.312Z","url":"https://daily.dev/posts/4TaGK9vWS#c-Ouxtbc5m0","author":{"@type":"Person","name":"Adam","url":"https://daily.dev/nark3d","image":"https://avatars.githubusercontent.com/u/2162621?v=4"}}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/callbacks-concerns-and-current-avoiding-hidden-dependencies-in-rails-4tagk9vws#faq","mainEntity":[{"@type":"Question","name":"Why did a bulk import of historical orders in Rails send confirmation emails to 12,000 customers?","acceptedAnswer":{"@type":"Answer","text":"Using Order.create! for a bulk import triggers every Active Record callback registered on the model, including after_create hooks that send confirmation emails and reduce inventory. Because create callbacks run regardless of entry point (console, jobs, imports, tests), an import script with no mention of email or inventory can still trigger both, since those effects were implicitly attached to record creation rather than called explicitly by the importing code. Developers debugging surprise side effects from Active Record callbacks can find similar real-world breakdowns on daily.dev."}},{"@type":"Question","name":"Why does ActiveSupport::CurrentAttributes silently break discount logic for background jobs in Rails?","acceptedAnswer":{"@type":"Answer","text":"CurrentAttributes resets at the end of each request or job and is isolated per thread, so a monthly job that creates subscription orders never has Current.user set unless something explicitly assigns it. Code like Current.user&.loyal? then evaluates to nil, and the safe navigation operator makes the loyalty discount disappear silently with no error, because the method's dependency on the logged-in user is never visible in its signature. Anyone tracing silent discount or pricing bugs tied to global request state can dig into patterns like this on daily.dev."}},{"@type":"Question","name":"What is the main problem with a Rails concern like HasPayments that mixes calculations, persistence, and calls to accounting?","acceptedAnswer":{"@type":"Answer","text":"Its problem is that it has no clear contract: it depends on log_change from a separate Auditable module and on three Order columns (total, payment_reference, number) that it never declares, so understanding it requires reading both modules' bodies. Moving code into a concern does not separate responsibilities since Order's public interface still exposes every method the concern adds; a concept this complex deserves a dedicated object like a PaymentLedger instead. Rails developers deciding between concerns and dedicated objects can compare approaches like this on daily.dev."}}]}
```

