Model or dataset
Envek/after_commit_everywhere avatar
Envek/after_commit_everywhere

after_commit_everywhere moves transactional callbacks out of the model

Use ActiveRecord transactional callbacks outside of models, literally everywhere in your application.

790 stars23 forksRubyMIT

At a glance

What is it?
The after_commit_everywhere gem lets Rails service objects, jobs and plain modules register after_commit, before_commit and after_rollback callbacks, with the callback firing after the outermost transaction commits, plus helpers that open a transaction only if one is not already open. It suits codebases where the work that should happen after a commit lives outside ActiveRecord models.
Who is it for?
Adopt after_commit_everywhere when your post-commit work lives in service objects, plain modules or background job wrappers rather than in ActiveRecord models, since the gem gives those places the same transactional callback semantics ActiveRecord provides inside a model. Do not adopt it as a replacement for model callbacks, and never call the class level `after_commit` on a model, because that registers the callback for every instance of that class including future ones.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 93 days ago.
What is it written in?
Mainly Ruby, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The callback you need is in the wrong place

ActiveRecord gives you transactional callbacks, and they are bound to models. That binding is the problem this gem addresses. The description is precise about it, allowing ActiveRecord transactional callbacks outside of ActiveRecord models, literally everywhere in your application. In a well factored Rails codebase, the work that must happen after a commit often does not live in a model at all. It lives in a service object, a job wrapper, a PORO, or a plain method on some other class, and those places have no access to the model callback API. The gem is by Evil Martians, an American design and engineering consultancy for developer tools, AI and cybersecurity startups, and it is inspired by two articles, one on Rails after_commit everywhere from Evil Martians and one from Arkency on running something in a background job after commit.

Three ways to reach the module

The integration is a mixin, and the README shows the three ways you might use it. The recommended usage is to include it in your base service class or anything similar:

ruby
class ServiceObjectBtw
  include AfterCommitEverywhere

  def call
    ActiveRecord::Base.transaction do
      after_commit { puts "We're all done!" }
    end
  end
end

You can also extend it wherever you need it, which gives you the method on that object's singleton:

ruby
extend AfterCommitEverywhere

ActiveRecord::Base.transaction do
  after_commit { puts "We're all done!" }
end

And you can call it directly on the module, which is the form to reach for inside a model class method:

ruby
AfterCommitEverywhere.after_commit { puts "We're all done!" }

The last form is not a stylistic preference. The README is explicit that while it is convenient to have an `after_commit` method at class level so you can call it from anywhere, you should take care not to call it on models.

The callback waits for the outermost transaction

The behaviour that matters is the timing, and the README is explicit that the main benefit is that it works with nested `transaction` blocks, which may even be spread across many files in the codebase. The example nests a transaction inside a transaction and registers the callback inside the inner one, and the output shows the order:

code
We're in transaction now
More transactions
Still in transaction…
We're all done!

The callback runs last, after the outer transaction has finished. For an `after_commit` that is the documented behaviour, executed right after the outermost transaction has been successfully committed and the data becomes available to other DBMS clients. The consequence for a design is that a callback registered deep inside nested transactions does not run when the inner block closes, and a service object that assumes otherwise will fire side effects earlier than the outermost commit if it is not careful about where it registers.

Three callbacks, and the edge cases around them

The available callbacks are documented with their behaviour outside a transaction, and those edge cases are the part worth memorising. `after_commit` runs right after the outermost transaction has been successfully committed, and if it is called outside a transaction it executes immediately. `before_commit` runs right before the outermost transaction is committed, and the README says it cannot imagine a use case for it but invites a pull request or an issue if you have one. It is supported only starting from ActiveRecord 5.0, so on older versions it is not available at all. `after_rollback` runs right after the transaction in which it was declared was rolled back, which might be a nested savepoint transaction block with `requires_new: true`, and if it is called outside a transaction it will raise an exception. The README also points at ActiveRecord's own limitations for rolling back nested transactions and offers `in_transaction` as a workaround.

in_transaction opens one only when there is none

The helper exists because the obvious code has the wrong shape. `in_transaction` makes sure the provided block is running in a transaction, and the README says the aim is to provide clearer intention than a typical `ActiveRecord::Base.transaction` block, since `in_transaction` only cares that some transaction is present rather than that a transaction is nested in any way. If a transaction is present it yields without taking any action, and the note about that is important, because it means `ActiveRecord::Rollback` errors are not trapped by `in_transaction` but propagate up to the nearest parent transaction block. If no transaction is present, the block opens a new one. The result is a service object that is safe to call in isolation and that reuses an existing transaction when it is called from inside one.

Callback options for the awkward cases

Two options adjust the behaviour. `without_tx` changes what happens when a callback is called with no transaction open, and there are three values. `:execute` runs the callback immediately, `:warn_and_execute` prints a warning and then runs it, and `:raise` raises an exception instead of running it. That last value is the useful one for a callback that would be wrong outside a transaction, since a silent immediate execution is exactly the bug this gem's existence is trying to prevent. The other option is `prepend`, which puts the callback at the head of the callback chain rather than at the end, which matters when the order of several callbacks is significant.

The trap with models, and the test story

The README has a section titled be aware of mental traps, and it is the most useful thing in the documentation. Calling the class level `after_commit` on a model adds a callback for all instances of that model, including future ones, so the README shows a `Post` class with a `bulk_ops` class method that calls `after_commit` inside `find_each` and marks it as something you should not do. The fix is to call `AfterCommitEverywhere.after_commit` directly in class level methods, or to include the module in the model for instance level methods, though the README adds that needing ad-hoc transactional callbacks in a model may indicate the model has too many responsibilities. On testing, the FAQ answers a single question: does it work with transactional_test or DatabaseCleaner, and the answer is yes. The licence is MIT, the release history runs 1.4.0 for prepending callbacks support, 1.5.0 for not executing other callbacks in a transaction after an exception, and 1.6.0 preserving `I18n.locale` in callbacks, and the repository is not archived with the last push on 2026-07-24.

Editorial conclusion

Adopt after_commit_everywhere when your post-commit work lives in service objects, plain modules or background job wrappers rather than in ActiveRecord models, since the gem gives those places the same transactional callback semantics ActiveRecord provides inside a model. Do not adopt it as a replacement for model callbacks, and never call the class level `after_commit` on a model, because that registers the callback for every instance of that class including future ones. Verify first which ActiveRecord version you are on, since `before_commit` is supported only from ActiveRecord 5.0, and remember that the callbacks fire after the outermost transaction commits rather than the innermost one, which changes what you can assume about data visibility.

Frequently asked questions

What does the after_commit_everywhere gem do?

It allows ActiveRecord transactional callbacks to be used outside of ActiveRecord models, so service objects, plain modules and other code can register `after_commit`, `before_commit` and `after_rollback` callbacks that fire around the outermost transaction.

When does the after_commit callback actually run?

Right after the outermost transaction has been successfully committed and the data becomes available to other database clients. If it is called outside a transaction it executes immediately, which is the behaviour the `without_tx` option exists to control.

What does in_transaction do differently from ActiveRecord::Base.transaction?

It only cares that some transaction is present rather than that one is nested. If a transaction is open it yields without taking any action, and if none is present it opens one, so the same service object can be called in isolation or from inside a larger transaction. Note that `ActiveRecord::Rollback` errors are not trapped by it and propagate to the nearest parent transaction block.

Can after_commit_everywhere be used inside ActiveRecord models?

You can include the module in a model for instance level methods, but the README warns against calling the class level `after_commit` on a model, since that registers a callback for all instances of that model including future ones. Inside class level methods, call `AfterCommitEverywhere.after_commit` directly instead.

Official sources

  1. Envek/after_commit_everywhere on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/envek-after-commit-everywhere.svg)](https://hysenlabs.com/projects/envek-after-commit-everywhere)

Was this useful?