00:00:13.800
My name is Monika. I loved it when my first C program printed 'Hello, World!' on the screen. It gave me a kick every time I did it with either Java, Python, Hadoop, or Ruby on Rails. Recently, there’s one more thing I want you to know about me: I applied for a role, and I was damn sure I would get it. Unfortunately, they rejected my application. With this, I realized one thing: even after a lot of preparation and experience, sometimes things can go wrong. Even though I've been practicing speaking at Toastmasters, this is my first tech talk at a tech conference, so please don't shoot me if I stammer or freeze in between.
00:01:43.110
This talk is a series of incidents that made me go "WAT!" Definitely, this is not my style of presentation. The style I’m using for this talk is inspired by an awesome talk given by Grady Booch. If you’ve already watched it, please don’t judge me too harshly if I don’t do justice to the presentation. If you haven’t watched it yet, I definitely recommend you do so. You will have a lot of laughs.
Okay, how many of you know Rails? Please be humble and raise your hands. How many of you know ActiveRecord? How many of you know ActiveRecord callbacks? I was advised to ask questions to engage the audience, so I hope I did that. Now, there was a night when my friend called me. He asked, "Can you see any difference in this code?" I thought, "Dude, I know I have experience, but I still can’t pick it out." He explained that one was a CustomersController and the other was a CommentsController. It turned out he wanted me to identify which model was using callbacks and which was not.
00:02:40.360
I thought for a moment and gave him a thoughtful gap before I answered that it was difficult to identify just based on those pieces of code. The point is, callbacks can be unobtrusive, and often they’re something you just use and forget without realizing the implications. Forwarding along, I remember my first assignment and it being the springtime in my career. I was proud to have written my first production code.
The requirement was to activate an account only when the password matches. Like any good programmer, I was excited to test it. The idea was to add a method that returns either true or false, checks the password match, and there it was, the moment of adding the callback to the code. I believed I was savvy—activating the account when the password matches was straightforward, right? I ran the test, and bingo! It ran successfully, which fed my confidence. But of course, I added more tests to check that the account should not activate when the password doesn’t match. I was expecting the test to pass, but oh no—the code did not behave as expected!
00:04:28.120
I thought, what could possibly go wrong with this three or four lines of code? It must be some setup error. After spending hours on it, I finally realized it was the callback that was causing the test to fail. If any of you already know why this was failing, please help me out because I had a terrible time figuring it out. The reason it failed had to do with how Ruby methods return the value of the last statement. I had ignored the point that before callbacks can exit the transaction if they return false. Thus, storing the failed result meant my code would fail! Failure in this was quite the shock.
00:06:34.440
Fast forward to summer, and I was on another project. There was a spike in test execution time by ten minutes, and frankly speaking, we had no idea why. For an entire month, we ignored it, simply because we didn’t know where to look. Waiting ten extra minutes for your tests to run can be quite irritating. Eventually, we traced the issue back to a callback causing this madness. We were astonished—it was such an innocent-looking bit of code that tracked down the time spike. My colleague explained that the user object, being most frequently created, triggered the callback and created a delayed job. If you create a user object multiple times, causing numerous delayed jobs, the time adds up.
00:08:05.450
Despite knowing we could create jobs asynchronously, we didn’t realize how much overhead the callbacks added. After this experience, I decided to be more disciplined with my coding. I dove into the documentation and learned that the after_commit callback suppresses exceptions. I was quite intrigued by the prospect of writing code that doesn’t throw exceptions, but the truth is that if exceptions occur in your after_commit, they won’t be updated in your user’s cache either. You won’t be notified about it until it’s too late, which can be quite dangerous.
00:10:01.500
The silver lining is, in Rails 4.2, the after_commit and after_rollback callbacks began throwing exceptions instead of suppressing them, which is definitely a good change! As winter approached, I hoped to master callbacks fully and be that person who smiled all the time. However, I faced a dilemma—the callbacks troubled me everywhere! After engaging with many people and researching extensively, I discovered the secret sauce for using callbacks correctly. But before I share that, let me ask: can you assign parameters to callbacks? Please don’t tell me to use the various high-key ways to send parameters because that wasn’t how callbacks were designed to work. The proper use of callbacks should be in line with the internal state of the object. If you’re using callbacks, they should only function based on the internal state of that object. That's where they excel!