Web development with Ruby on Rails

thoughtbot (lower case) is one of the leading American consulting firms focused on web development with Ruby on Rails. thoughtbot exploits a common business model in this environment, and earns not only through consulting, but also through its large contributions to Open Source, active participation in the community (for example, the Giant Robots Smashing into Other Giant Robots podcast), educational activities ( workshops, mentoring), internal products and literature.

To date, they have had two full-fledged books to their credit: The Playbook, a comprehensive guide to the internal routine and work tricks of thoughtbot (free to study on their website), and Backbone.js on Rails, an equally comprehensive guide to using the Backbone JS framework along with Ruby on Rails.

The peculiarity of their approach to the publication of books is interesting. This is not a static work, written once, printed on paper and becoming obsolete over the years, but a product that supports versioning, develops under the influence of readers and is accompanied by various electronic goodies, including ready-to-use example code. The “sources” of the book are stored on github, which means that at any time the reader has the opportunity to get the latest release, report a problem, discuss any part of the text, etc.

Today they announced the start of work on a new book called Ruby Science. The reference for writing fantastic Rails applications”. Moreover, you can start reading the book and take part in its development right now.

thoughtbot’s love of exploring and documenting its own workflow has been an influence on me for a very long time, and it started with using a couple of their gems and reading Backbone.js on Rails. They are able to provide a dry extract from their daily work, allowing the reader not to step on various painful rakes. Therefore, I could not pass by this announcement just like that. I strongly recommend that all rubyists keep an eye on this reading material, and indeed on the activities of thoughtbot. By the way, until the end of January, a 20% discount will hang on the book.

People come to Ruby on Rails looking for a framework that will allow them to make really fast, interesting, and maintainable applications. But over time, any project is overgrown with a thick layer of the most diverse code, which becomes more and more difficult, expensive and painful to maintain.

The authors of the book offer you their view on the essence of such problems, as well as a collection of recipes for their detection, elimination and prevention.

One important topic here: data migration in Ruby-on-Rails

It is important to keep an eye on the idempotency of such transformations. It is extremely likely that a Rake task will be executed more than once on the prod.

Despite the simplicity and attractiveness of this solution, it still has a significant drawback. It does not automatically deploy any version of the product with a single command. It is suitable in a situation where product development occurs in infrequent increments, rarely rolls back, and can be done with manual actions during deployments. But for continuous delivery, this method is not suitable.

Removing data migrations into separate inner classes inside the migration

Mark Qualie suggests adding a nested class definition inside the schema migrations code with an up method that defines the data migration logic. In this way, “locality” of knowledge about schema changes and related changes in data is achieved. Here is the sample code from the article:

class AddLastSmiledAtColumnToUsers < ActiveRecord::Migration[5.1]
def change
add_column :users, :last_smiled_at, :datetime
add_index :users, :last_smiled_at
end
class Data
def up
User.all.find_in_batches(batch_size: 250).each do |group|
ActiveRecord::Base.transaction do
group.each do |user|
user.last_smiled_at = user.smiles.last.created_at
user.save if user.changed?
end
end
end
end
end
end

The author proposes to execute this logic in the following way:

Dir.glob(“#{Rails.root}/db/migrate/*.rb”).each { |file| require file }
AddLastSmiledAtColumnToUsers::Data.new.up

Moreover, the author proposes to place this code in an asynchronous Job, adding logging and tracking of completed migrations, like storing schema migration versions in the database.

Using full gems for schema migration style data migrations

When the team is large, the application is large, or data migrations occur every two or three releases, it can pay off to use a ready-made full-featured gem for data migrations in the style of schema migrations.

This solution already satisfies the requirements of continuous delivery, because version tracking of data migrations is done in the same way as for schema migrations.

There were quite a lot of similar gems, but there are no super popular ones among them. Apparently, because the scale of the problem is few who reach the desired size.

The data-migrate gem has the most stars (> 670), links from articles, and the most well-maintained Readme. It only works with Rails 5+.

Two more gems with a similar experience but support for Rails 4+:

rails-data-migrations (> 93 stars)

nonschema_migrations (> 53 stars)

The name of the latter is especially noteworthy. It screams about the contrast between schema migrations and NOT schema migrations.

I did not audit the code of all these gems, because on a project of my scale there is enough approach with Rake tasks. But their discovery was one of the incentives for me to write this article. For me, they are a sign of the seriousness of the problem that can be encountered with the growth of the application.

All of them allow you to generate a data migration class in the project’s db/data folder, which is located next to the traditional db/migrate with schema migrations:

Horizontally, there are decisions about the placement of data migration logic.

Vertically – qualities, namely:

  • Blending is the fact that a single schema migration mechanism is used for data migration;
  • Zero Downtime Deployment – the ability to minimize the run time of migrations by using only the most necessary and quick operations to change the schema during deployment;
  • Test First – the convenience of developing data migration logic through writing a test of adequate complexity;
  • Continuous delivery – the ability to roll out a product of any version in one action;

Belonging to the codebase – placing the code of data migrations inside the codebase, as opposed to one-time scripts in the ticket system;

Locality – Finding data migrations in a standardized location in the codebase, which can be found by navigating through the project rather than searching by keywords.

Conclusion

When data migrations occur once every few months, manually running Rake tasks is a pragmatic solution.

But when this happens more often, then it is worth looking at full-blown automated solutions through a ready-made gem in the style of schema migrations. This will ensure continuous delivery requirements.

Thus, the problem of data migration should be solved as the scale of the project grows – in an architectural style corresponding to this scale. It seems that this approach has every chance to make the development process adequate.

How do you most often deal with data migrations?

66.67% I use the data schema migration mechanism provided by the framework

0% Upload data migration scripts from the codebase to the ticket system

11.11% Move data migration code into inner classes inside schema migrations and run them outside of deployment

44.44% Upload data migration code to Rake tasks

11.11% Use full gem for data migrations

Leave a Reply

Your email address will not be published. Required fields are marked *