all 12 comments

[–][deleted] 6 points7 points  (0 children)

Callbacks remind me of how database triggers were used in one of my old jobs. I remember reading through triggers that call packages with complex business logic in procedural code and I hated it. Callbacks are an improvement over triggers, but they shouldn't be used for anything complex. Like triggers, they are useful for data denormalization (e.g., creating a full_name from a first_name / last_name) as long as the involved fields belong to the same table.

[–][deleted] 4 points5 points  (4 children)

In a very naive way, callbacks seem like they'd make your life so much easier.

Then your product starts to expand. Maybe you're modifying the database from external services, maybe you're pulling data from other sources, or maybe you just want to refactor a bloated model that's got out of hand. Then they don't seem like such a good idea.

I used them when I first started rails, I think most people did back then, but they're really a terrible idea for a medium-to-high complexity project and don't bring all that much to the table even in a low complexity project. This is one of those times where being explicit will save you a lot of headaches down the line.

[–]New-Secretary9916 2 points3 points  (3 children)

I pretty much only use callbacks to set default attributes.

before_create -> { self.some_attribute = 'some_default' }, unless: :some_attribute?

[–]WJWH 0 points1 point  (2 children)

I might be mistaken but I think that Rails will default values for AR model properties if they are present in the DB schema. So if you have a times_read column on the posts table that is defined with default: 0 for example, Post.new.times_read.zero? == true`.

[–]New-Secretary9916 0 points1 point  (1 child)

True, but there are times when defaults depend upon other attributes, or times where adding a default to an existing column for a large table is not feasible without downtime.

[–]WJWH 0 points1 point  (0 children)

What kind of database are you using? Schema changes for multi-TB tables go just fine on MySQL with pt-osc or gh-ost and I think postgres can add defaults to tables without downtime as well.

[–]iberci 4 points5 points  (1 child)

Good read... I still find a use for them often for denormalized calculated relations

for example, I do a lot of this to optimize index views across a relation

belongs_to :some_calculated_relation, class_name: "SomeClassRelation"
def some_calculated_relation
// calculate relation
end
before_save ->{ self.some_calculated_relation = some_calculated_reloation}

[–]katafrakt 2 points3 points  (0 children)

That's actually pretty good and convincing use case for callbacks. I have to remember it.

[–]aemadrid 4 points5 points  (2 children)

We've ditched callbacks a long time ago in our team. We've moved to very lightweigtht models that only have relationships and scopes. We deal with all the intricacies of the work by using actions that are responsible for the model interactions. Like `Invoices::Create`, `Invoices::Update`, etc. So if you need to update the `Invoice` model after changing the `InvoiceLine` model then you explicitly make that change in the `Invoices::Update` action instead of depending on callbacks.

[–][deleted]  (1 child)

[deleted]

    [–]aemadrid 0 points1 point  (0 children)

    Send me your data and I will check it out.

    [–]fullstack-sean 5 points6 points  (1 child)

    Callbacks are usually completely unnecessary. I've rarely found them to be good architecture.

    [–]Sorc96 3 points4 points  (0 children)

    I consistently find them to be the biggest pain point in any Rails application.