README.md in strong_migrations-0.4.1 vs README.md in strong_migrations-0.4.2

- old
+ new

@@ -48,11 +48,11 @@ #### Bad ActiveRecord caches database columns at runtime, so if you drop a column, it can cause exceptions until your app reboots. ```ruby -class RemoveSomeColumnFromUsers < ActiveRecord::Migration[5.2] +class RemoveSomeColumnFromUsers < ActiveRecord::Migration[6.0] def change remove_column :users, :some_column end end ``` @@ -69,11 +69,11 @@ 2. Deploy code 3. Write a migration to remove the column (wrap in `safety_assured` block) ```ruby - class RemoveSomeColumnFromUsers < ActiveRecord::Migration[5.2] + class RemoveSomeColumnFromUsers < ActiveRecord::Migration[6.0] def change safety_assured { remove_column :users, :some_column } end end ``` @@ -85,11 +85,11 @@ #### Bad Adding a column with a default value to an existing table causes the entire table to be rewritten. ```ruby -class AddSomeColumnToUsers < ActiveRecord::Migration[5.2] +class AddSomeColumnToUsers < ActiveRecord::Migration[6.0] def change add_column :users, :some_column, :text, default: "default_value" end end ``` @@ -99,11 +99,11 @@ #### Good Instead, add the column without a default value, then change the default. ```ruby -class AddSomeColumnToUsers < ActiveRecord::Migration[5.2] +class AddSomeColumnToUsers < ActiveRecord::Migration[6.0] def up add_column :users, :some_column, :text change_column_default :users, :some_column, "default_value" end @@ -120,11 +120,11 @@ #### Bad Backfilling in the same transaction that alters a table locks the table for the [duration of the backfill](https://wework.github.io/data/2015/11/05/add-columns-with-default-values-to-large-tables-in-rails-postgres/). ```ruby -class AddSomeColumnToUsers < ActiveRecord::Migration[5.2] +class AddSomeColumnToUsers < ActiveRecord::Migration[6.0] def change add_column :users, :some_column, :text User.update_all some_column: "default_value" end end @@ -132,14 +132,14 @@ Also, running a single query to update data can cause issues for large tables. #### Good -There are three keys: batching, throttling, and running it outside a transaction. Use the Rails console or a separate migration with `disable_ddl_transaction!`. +There are three keys to backfilling safely: batching, throttling, and running it outside a transaction. Use the Rails console or a separate migration with `disable_ddl_transaction!`. ```ruby -class BackfillSomeColumn < ActiveRecord::Migration[5.2] +class BackfillSomeColumn < ActiveRecord::Migration[6.0] disable_ddl_transaction! def change User.unscoped.in_batches do |relation| relation.update_all some_column: "default_value" @@ -154,11 +154,11 @@ #### Bad In Postgres, adding a non-concurrent index locks the table. ```ruby -class AddSomeIndexToUsers < ActiveRecord::Migration[5.2] +class AddSomeIndexToUsers < ActiveRecord::Migration[6.0] def change add_index :users, :some_column end end ``` @@ -166,11 +166,11 @@ #### Good Add indexes concurrently. ```ruby -class AddSomeIndexToUsers < ActiveRecord::Migration[5.2] +class AddSomeIndexToUsers < ActiveRecord::Migration[6.0] disable_ddl_transaction! def change add_index :users, :some_column, algorithm: :concurrently end @@ -184,11 +184,11 @@ #### Bad Rails adds a non-concurrent index to references by default, which is problematic for Postgres. ```ruby -class AddReferenceToUsers < ActiveRecord::Migration[5.2] +class AddReferenceToUsers < ActiveRecord::Migration[6.0] def change add_reference :users, :city end end ``` @@ -196,16 +196,15 @@ #### Good Make sure the index is added concurrently. ```ruby -class AddReferenceToUsers < ActiveRecord::Migration[5.2] +class AddReferenceToUsers < ActiveRecord::Migration[6.0] disable_ddl_transaction! def change - add_reference :users, :city, index: false - add_index :users, :city_id, algorithm: :concurrently + add_reference :users, :city, index: {algorithm: :concurrently} end end ``` For polymorphic references, add a compound index on type and id. @@ -215,11 +214,11 @@ #### Bad In Postgres, new foreign keys are validated by default, which acquires an `AccessExclusiveLock` that can be [expensive on large tables](https://travisofthenorth.com/blog/2017/2/2/postgres-adding-foreign-keys-with-zero-downtime). ```ruby -class AddForeignKeyOnUsers < ActiveRecord::Migration[5.2] +class AddForeignKeyOnUsers < ActiveRecord::Migration[6.0] def change add_foreign_key :users, :orders end end ``` @@ -229,21 +228,21 @@ Instead, validate it in a separate migration with a more agreeable `RowShareLock`. This approach is documented by Postgres to have “[the least impact on other work](https://www.postgresql.org/docs/current/sql-altertable.html).” For Rails 5.2+, use: ```ruby -class AddForeignKeyOnUsers < ActiveRecord::Migration[5.2] +class AddForeignKeyOnUsers < ActiveRecord::Migration[6.0] def change add_foreign_key :users, :orders, validate: false end end ``` Then validate it in a separate migration. ```ruby -class ValidateForeignKeyOnUsers < ActiveRecord::Migration[5.2] +class ValidateForeignKeyOnUsers < ActiveRecord::Migration[6.0] def change validate_foreign_key :users, :orders end end ``` @@ -275,21 +274,21 @@ ### Renaming or changing the type of a column #### Bad ```ruby -class RenameSomeColumn < ActiveRecord::Migration[5.2] +class RenameSomeColumn < ActiveRecord::Migration[6.0] def change rename_column :users, :some_column, :new_name end end ``` or ```ruby -class ChangeSomeColumnType < ActiveRecord::Migration[5.2] +class ChangeSomeColumnType < ActiveRecord::Migration[6.0] def change change_column :users, :some_column, :new_type end end ``` @@ -310,11 +309,11 @@ ### Renaming a table #### Bad ```ruby -class RenameUsersToCustomers < ActiveRecord::Migration[5.2] +class RenameUsersToCustomers < ActiveRecord::Migration[6.0] def change rename_table :users, :customers end end ``` @@ -335,25 +334,25 @@ #### Bad The `force` option can drop an existing table. ```ruby -class CreateUsers < ActiveRecord::Migration[5.2] +class CreateUsers < ActiveRecord::Migration[6.0] def change create_table :users, force: true do |t| # ... end end end ``` #### Good -If you intend to drop a table, do it explicitly. Then create the new table without the `force` option: +Create tables without the `force` option. ```ruby -class CreateUsers < ActiveRecord::Migration[5.2] +class CreateUsers < ActiveRecord::Migration[6.0] def change create_table :users do |t| # ... end end @@ -365,11 +364,11 @@ #### Bad This generates a single `UPDATE` statement to set the default value. ```ruby -class ChangeSomeColumnNull < ActiveRecord::Migration[5.2] +class ChangeSomeColumnNull < ActiveRecord::Migration[6.0] def change change_column_null :users, :some_column, false, "default_value" end end ``` @@ -377,11 +376,11 @@ #### Good Backfill the column [safely](#backfilling-data). Then use: ```ruby -class ChangeSomeColumnNull < ActiveRecord::Migration[5.2] +class ChangeSomeColumnNull < ActiveRecord::Migration[6.0] def change change_column_null :users, :some_column, false end end ``` @@ -391,11 +390,11 @@ #### Bad In Postgres, there’s no equality operator for the `json` column type, which causes issues for `SELECT DISTINCT` queries. ```ruby -class AddPropertiesToUsers < ActiveRecord::Migration[5.2] +class AddPropertiesToUsers < ActiveRecord::Migration[6.0] def change add_column :users, :properties, :json end end ``` @@ -403,11 +402,11 @@ #### Good Use `jsonb` instead. ```ruby -class AddPropertiesToUsers < ActiveRecord::Migration[5.2] +class AddPropertiesToUsers < ActiveRecord::Migration[6.0] def change add_column :users, :properties, :jsonb end end ``` @@ -419,11 +418,11 @@ #### Bad Adding a non-unique index with more than three columns rarely improves performance. ```ruby -class AddSomeIndexToUsers < ActiveRecord::Migration[5.2] +class AddSomeIndexToUsers < ActiveRecord::Migration[6.0] def change add_index :users, [:a, :b, :c, :d] end end ``` @@ -431,11 +430,11 @@ #### Good Instead, start an index with columns that narrow down the results the most. ```ruby -class AddSomeIndexToUsers < ActiveRecord::Migration[5.2] +class AddSomeIndexToUsers < ActiveRecord::Migration[6.0] def change add_index :users, [:b, :d] end end ``` @@ -445,10 +444,10 @@ ## Assuring Safety To mark a step in the migration as safe, despite using a method that might otherwise be dangerous, wrap it in a `safety_assured` block. ```ruby -class MySafeMigration < ActiveRecord::Migration[5.2] +class MySafeMigration < ActiveRecord::Migration[6.0] def change safety_assured { remove_column :users, :some_column } end end ```