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.
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.
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.
It is sometimes required for the methods with optional arguments to be able to differentiate between its default value and the value passed from the caller. Passing
nil might initially sound like a good idea since it represents “nothingness”. However, it might turn out that
nil is a legit value and there might be cases where it is desirable for the caller to pass
nil. In such a case, we cannot use it as a default value if we want to implement a special logic for the case of not providing that value.
Fortunately, there is an easy way to deal with it – use a special constant:
1 2 3 4 5 6 7 8 9 10 11 12
call method, we allow to pass an optional argument with a default of
NO_VALUE_PROVIDED, which is a private constant defined in that class that is an instance of
By depending on the instance of
Object that is initialized inside that class, we can avoid cases where the equality check returns
true even if this is not an expected outcome, which could happen if we used strings or symbols. We could use some symbol that would be very unlikely to be passed from the caller, like
:__no_value_provided__, but it arguably looks more like a workaround than a dedicated solution for the problem.
Also, a private constant ensures it is not used anywhere outside the class, which minimizes the chances that the passed argument would the same as our placeholder for no-value-provided scenario even more.
Imagine that you are implementing some form object because you are fed up with treating ActiveRecord models as such, and you need some extra flexibility. You start with a straightforward implementation for a base class of a form object where you can just whitelist attributes. That could look like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
Since the base class is ready, you can create a first form object that would inherit from this class:
1 2 3
Initially, it does the job, but then it turns out that you might need a default value if
some_attribute turns out to be nil. So you try something like that:
1 2 3 4 5 6 7
After checking if the default value works, this is what you get:
Whoops! How did it happen? The method was defined in the superclass so it should be inheritable, right?
Well, this is not really true. However, the problem is easy to fix.
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.
Just to give you a real-world example of what validation in ActiveRecord model looks like (as impossible as it seems, it really did happen) – when updating the check-in time of the reservation, which is a simple attribute on Reservation model, the record turned out to be invalid because… the format of guest’s phone didn’t match some regexp.
There are multiple ways to bypass this problem: use
validate: false flag with
save(validate: false) or use
update_columns method, but this is definitely not something that can be applied in a “normal” use case. In a typical scenario, this will be the error message displayed in the UI/returned to API consumer, and it will be confusing.
However, this is the expected behavior of ActiveRecord (or in general, ActiveModel-style) validations, which is a validation of the state of the model. And judging from this example, it’s evident that it leads to problematic scenarios. What kind of design then would be the most appropriate to prevent such issues?
Optimizing database queries is arguably one of the fastest ways to improve the performance of the Rails applications. There are multiple ways how you can approach it, depending on the kind of a problem. N+1 queries seem to be a pretty common issue, which is, fortunately, easy to address. However, sometimes you have some relatively simple-looking queries that seem to take way longer than they should be, indicating that they might require some optimization. The best way to improve such queries is adding a proper index.
But what does “proper index” mean? How to figure out what kind of index is exactly needed for a given query? Here are some essential facts and tips that should cover a majority of the queries you may encounter and make your database no longer a bottleneck.
Would it be possible though to obtain the same result somehow in Ruby?
That makes a significant impact on how we approach this kind of features. However, in the past, such things were quite often not stored in a database at all – it just took some UI acceptance validation or maybe a validation of the virtual attribute on the backend to be on the safe side.
Let’s focus on the latter case where we don’t need to store anything in DB and see what the possible solutions to that problems are. As trivial as this problem initially sounds, it will get quite interesting ;).
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.