2022
The inherent unreliability of after_commit callbacks and most service objects' implementation
Service objects and/or after_commit
callbacks are ubiquitous in most real-world Rails applications. Whether it’s a good idea or not (ActiveRecord callbacks - I’m looking at you) is a different story, but one thing that is notoriously overlooked in the application design is reliability. And yes, the service objects are equally bad as after_commit
callbacks in that regard.
2020
A Secret Weapon For Your Rails Apps - RPC with RabbitMQ and Hutch
Communication between two or more applications is often everyday stuff, and it might seem that there is not too much to add there as this subject has been covered pretty well in the last years. Thanks to that, multiple patterns and standards have emerged. You no longer need to think about how the response format should look like for your REST API (go with JSONAPI and stick to the conventions) or figure out the authentication/authorization protocol (go with OAuth and the security headaches won’t bother you).
Read on →2019
From ActiveRecord callbacks to Publish/Subscribe pattern and event-driven design
Imagine that you are working on a large legacy application that also contains the dreaded ActiveRecord callbacks in the models handling most of the business logic. At some point, and under a certain level complexity, the mess caused by that choice might become hard to keep under control, the risk of introducing bugs will increase and the teams(s) working on the application will be way less productive. That will most likely lead to an attempt to find a better way of designing the application. The problem, though, might be that the scope of the application is so huge that introducing any meaningful changes to the application will take weeks, if not months.
Read on →Messages on Rails Part 3: RabbitMQ
In the first part of this series, we were exploring some potential options for communication between services - what their advantages and disadvantages are, why HTTP API is not necessarily the best possible choice and suggesting that asynchronous messaging might be a better solution, using, e.g. RabbitMQ and Kafka. We’ve already covered Kafka in the part 2, now it’s the time for RabbitMQ.
Read on →Messages on Rails Part 2: Kafka
In the first part of this series, we were exploring some potential options for communication between services - what their advantages and disadvantages are, why HTTP API is not necessarily the best possible choice and suggesting that asynchronous messaging might be a better solution, using, e.g. RabbitMQ and Kafka. Let’s focus this time entirely on the latter.
Read on →Messages on Rails Part 1 - Introduction to Kafka and RabbitMQ
Microservices, Service-Oriented Architecture (SOA) and in general, distributed ecosystems, have been on hype in the last several years. And that’s for a good reason! At certain point, The Majestic Monolith “pattern” might start causing issues, both from the purely technical reasons like scalability, tight coupling of the code if you don’t follow Domain-Driven Design or some other practices improving modularity, maintenance overhead, and also from organizational perspective since working in smaller teams on smaller apps is more efficient than working with huge team on an even bigger monolith which suffers from tight coupling and low cohesion. However, this is only true if the overall architecture addresses the potential problems that are common in the micro/macro-services world. One of these problems I would like to focus on is communication between apps and how the data flows between them.
Read on →2018
The Problems With Validating ActiveRecord Models And Why State Validation Is a Bad Idea
In the typical Rails application, you can find the most of the validations in the ActiveRecord models, which is nothing surprising - ActiveRecord models are used for multiple things. Whether it is a good thing, or a bad thing (in most cases it’s the latter) deserves a separate book or at least blog post-series as it’s not a simple problem, there is one specific thing that can cause a lot of issues that are difficult to solve and go beyond design decisions and ease of maintenance of the application, something that impacts the behavior of the model - the validations.
Read on →Rails And Conditional Validations In Models
Adding consents for accepting Terms of Service/Privacy Policies must have been a top popular feature in the majority of the applications due to enforcement of GDPR in May ;). From the technical aspects that GDPR requires, there is a proof of consent for processing the personal information. In that case, you need to have some actual attributes in the database that would confirm the fact that some user has indeed accepted Terms of Service/Privacy Policy.
Read on →The Case for before_validation callback: complex state normalization
Few months ago I wrote a blog post about ActiveRecord before_validation
callback and how it is used for wrong reasons and concluded that in most cases this is not something we should be using routinely. However, I missed one appropriate use case for it which might be quite common in Rails apps, so this might be an excellent opportunity to get back to before_validation callback and show its other side.
2017
The Case Against Exotic Usage of :before_validate Callbacks
It’s nothing new that ActiveRecord callbacks are abused in many projects and used for the wrong reasons for many use cases where they can be easily avoided in favor of a much better alternative, like service objects. There is one callback though that is special and quite often used for pretty exotic reasons that have nothing to do with the process when it gets executed - it’s the before_validate
callback.
Do. Or do not. There is no try - Object#try considered harmful
Object#try
is quite a commonly used method in Rails applications to cover cases where there is a possibility of dealing with a nil
value or to provide flexible interface for handling cases where some kind of object doesn’t necessarily implement given method. Thanks to try
, we may avoid getting NoMethodError
. So it seems like it’s perfect, right? No NoMethodError
exception, no problem?
2016
Decoding Rails Magic: How Does ActiveJob work?
Executing background jobs is quite a common feature in many of the web applications. Switching between different background processing frameworks used to be quite painful as most of them had different API for enqueuing jobs, enqueuing mailers and scheduling jobs. One of the great addition in Rails 4.2 was a solution to this problem: ActiveJob, which provides extra layer on top of background jobs framework and unifies the API regardless of the queue adapter you use. But how exactly does it work? What are the requirements for adding new queue adapters? What kind of API does ActiveJob provide? Let’s dive deep into the codebase and answer these and some other questions.
Read on →Decoding Rails Magic: How Does Calling Class Methods On Mailers Work
Have you ever wondered how it is possible that calling class methods on mailers work in Rails, even though you only define some instance methods in those classes? It seems like it’s quite common question, especially when you see the mailers in action for the first time. Apparently, there is some Ruby "magic" involved here, so let’s try to decode it and check what happens under the hood.
Read on →Scaling Up Rails Applications With PostgreSQL Table Partitioning - Part 3
After publishing recent blog posts about table partitioning - its SQL basics part and how to use in in Rails application I was asked quite a few times what is the real performance gain when using table partitioning. This is a great question, so let's answer it by performing some benchmarks.
Read on →Scaling Up Rails Applications With PostgreSQL Table Partitioning - Part 2
In the previous blog post we learned some basics about table partitioning: how it works and what kind of problems it solves. So far we've been discussing mostly basic concepts with raw SQL examples. But the essential question in our case would be: how to make it work inside Rails application then? Let's see what we can do about it.
Read on →Scaling Up Rails Applications With PostgreSQL Table Partitioning - Part 1
You've probably heard many times that the database is the bottleneck of many web applications. This isn't necessarily true. Often it happens that some heavy queries can be substiantially optimized, making them really efficient and fast. As the time passes by, however, the data can remarkably grow in size, especially in several years time which can indeed make the database a bottleneck - the tables are huge and don't fit into memory any longer, the indexes are even bigger making queries much slower and you can't really optimize further any query. In many cases deleting old records that are no longer used is not an option - they still can have some value, e.g. for data analysis or statystical purposes. Fortunately, it's not a dead end. You can make your database performance great again with a new secret weapon: table partitioning.
Read on →How To Handle Non-CRUD Logic In Your API
If you happen to develop API for non-trivial app with complex business logic beyond CRUD directly mapped to the database tables (i.e. typical Active Record pattern) you were probably wondering many times how to handle these cases and what's the best way to do it. There are quite a few solutions to this problem: you could add another endpoint for handling given use case, add non-RESTful action to already existing endpoint or you can add a magic param to the payload that would force non-standard scenario in the API. Let's take a closer look at the these solutions and discuss some advantages and disadvantages of each of them.
Read on →Refactoring Tips: Trade Conditionals For Type Delegation
Having some kind of type
attribute in your models is a pretty common thing, especially in applications with more complex domain. How do you handle such cases? Is it with multiple conditionals / case
statements in every method dealing with the type attribute? If you've been struggling with organizing and maintaing code for such use cases then say hello to your new friend: type delegation.
2014
You don't (necessarily) need gem for that feature
You are working currently on that awesome app and just started thinking about implementing new feature, let's call it feature X. What's the first thing you do? Rolling your own solution or... maybe checking if there's a magical gem that can help you solve that problem? Ok, it turns out there's already a gem Y that does what you expect. Also, it does tons of other things and is really complex. After some time your app breaks, something is definitively not working and it seems that gem Y is responsible for that. So you read all the issues on Github, pull requests and even read the source code and finally find a little bug. You managed to do some monkeypatching first and then send pull request for a small fix and solved a problem, which took you a few hours. Looks like a problem is solved. And then, you try to update Rails to2 the current version. Seems like there's a dependency problem - gem Y depends on previous version of Rails...
Read on →