Rails Monolith towards Engines spike - Our story
Tute Costa
This year my team started working on a new UI for our patient check-in application, with slightly modified rules for specific use cases and forms. It would reuse most of our backend software with few changes. We named it Bariloche.
Bariloche is a new UI for our patient check-in workflows, targeted for Urgent Care facilities. Given that the UI would start from scratch, we’d use it as a testbed to build our frontend entirely with React Components. Everything would be namespaced under the “Bariloche” name.
Such a clear boundary between the monolith and this product made it an ideal case to try an Engine implementation. Given Bariloche would share the data layer declared by the main app and not need database migrations, our first step towards Engines would be smaller than for other potential extractions we could foresee.
Backward-compatible database migrations
Tute Costa
My team has a policy of not applying destructive changes to our database. We don’t, for example, remove a column or table when deprecated, and we don’t rename either, as renaming is akin to removing a name the code uses.
Our deployment process performs database changes about a minute before the related code changes go live. During this time, Rails might run database queries using an out-of-date schema, erroring out. As the usage of our app continues to increase, even sub-second rename migrations bring down several in-flight requests.
Avoiding nil in Ruby programs (and NULL in databases)
Tute Costa
Most Ruby developers see the following exception in production, no matter how well taken care their codebase is:
1
NoMethodError: undefined method `name' for nil:NilClass
And the search begins. Was it a user? Or another object that would respond to
#name
if present? Was it a typo? Many different statements can return nil
:
Development/Production parity for Rails Internationalization
Mauro Otonelli
To support both English and Spanish in web apps, we can use Ruby’s I18n
library, which allows for easy translations through YAML files for each
language. If a translation is missing, ideally we’d like to see an error in our
tests, which makes the test suite fail. However, if our development environment
is not set up to raise exceptions, there could be bugs lurking, or waiting to
be introduced.
One such instance happened to us recently, because our I18n.backend
(yes,
that’s a thing!) in Rails was different in the development/testing and
production environments. Development used the “base” i18n backend, while
production was set up to use the “fallbacks” backend.
Keep It Simple (KISS coding principle)
Tute Costa
The app we maintain has a set of steps (currently two) a user may have to fill before moving on. One step is always shown, while the other can be disabled with a feature flag. We assume this list may grow, and even when it doesn’t, we know code is copied over to other parts of the codebase, so we better get it right early.
Our first implementation:
1
2
3
4
5
6
def supported_steps
{
current_medications: true,
allergies_review: Feature.enabled?(PREVISIT_ALLERGIES),
}.select { |_step_name, enabled| enabled }.keys
end
A suggestion during code review:
1
2
3
4
5
def supported_steps
steps = [:current_medications]
steps << :allergies_review if Feature.enabled?(PREVISIT_ALLERGIES)
steps
end
It’s quicker to get what’s happening in the second option, and while the methods stay short, they are practically equal. Our code reviews can get lengthy so we try not to chime in when either option is fine, but I suggested the first as a simpler option (even if not easier)[1]. Let’s see why.
Using logs to power up your Rails development workflow
Mauro Otonelli
When working on Rails applications, I find tailing the development log and watching it as I navigate through pages very useful. Some of the questions that should be in the back of your mind while looking at it are:
- Am I hitting the right URL, with the proper HTTP method?
- Am I sending the right parameters? (Are they all necessary?)
- Are there any warnings/exceptions that I should handle?
- Does the ORM generate the right SQL statements?
- Is my request taking a longer time than expected?
- Am I making unnecessary 3rd party API requests?
Reporting from ephemeral containers in production
Sebastian Armano
Reporting is not a particular task of business analysts anymore. Anyone at any department, at any point in time, needs a quick report based on the latest data to validate their decisions. Having the ability to create small custom reports in a couple of minutes, and in a format that can be consumed by any analysis software is a powerful resource to have in our belt.
strong_password v0.0.7 rubygem hijacked
Tute Costa
I recently updated minor and patch versions of the gems our Rails app uses. We want to keep dependencies fresh, bugs fixed, security vulnerabilities addressed while maintaining a high chance of backward compatibility with our codebase. In all, it was 25 gems we’d upgrade.
I went line by line linking to each library’s changeset. This due diligence never reported significant surprises to me, until this time.