
はじめに
ゼネットの土屋です。
Rails 8.1 が正式リリースされてから 2 か月ほど経ち、
実際に運用してみると様々な挙動の変化が見えてきました。
今回はその中でも、Rails 8.1 の変更によって実際に発生した
CSV 出力の列順が変わってしまう問題について紹介します。
db/schema.rb のカラム定義が アルファベット順 に並ぶようになった
この仕様変更は以下のコミットで導入されました:
何が起こるのか?
例えば users テーブルが次のように定義されていたとします:
従来(Rails 8.0 まで)
id name age created_at updated_at
Rails 8.1 以降
id age created_at name updated_at
ポイントは以下の2つです:
idは常に先頭に来るid以外のカラムがアルファベット順にソートされる
CSV 出力で問題が発生
以下のように select("*")(または select 省略)で CSV を生成しているコードは注意が必要です。
users = User.all CSV.generate do |csv| users.each do |user| csv << user.attributes.values # ← カラム順に依存 end end
このコードは、ActiveRecord が返す カラムの並び順に依存します。
Rails 8.1 にアップデートすると、
- 列の順番が変わる
- 社内ツールで読み込めなくなる
- 外部システム向け CSV のフォーマットが破壊される
といった実害が発生します。
対応策:reselect で従来の順番を維持する
Rails には、既存コードの動作を維持するための reselect メソッドが用意されています。
実装例
既存コードへの影響を最小限にするため、 カラム順を 1 行追加するだけで明示的に固定する方法を採りました。
# 旧来の出力順を明示(id は必ず先頭) USER_COLUMN_ORDER = %w[ id name age created_at updated_at ].freeze users = User.all.reselect(USER_COLUMN_ORDER) # ← この1行のみ追加 CSV.generate do |csv| users.each do |user| csv << user.attributes.values end end
この対応により、
- Rails 8.1 の schema.rb の並び順に依存しない
- 従来の CSV 列順をそのまま維持できる
- 既存コードの変更が最小限ですむ
というメリットがあります。
ベストプラクティス
select("*")を使わない- CSV のように 順番が仕様になる処理では必ずカラムを明示する
- レガシーコードは
reselectを使って段階的に移行する
最後に
select 句を明示していれば起こらない問題ではあるものの、
意外と select("*") のまま書かれたコードは少なくありません。
schema.rb のカラム順がアルファベット順に変わるだけだから
「影響はほぼないだろう」と私も油断していたのですが、
実際には CSV 出力のように順序に依存した処理で大きく影響が出ました。
今回は reselect を使って最小限の影響で対応しましたが、
アプリケーションの性質によっては別のアプローチも十分考えられます。
Rails 8.1 へのアップグレード時には、
ぜひ CSV 出力や API レスポンスなど、
「カラム順が仕様になっている部分」を見直しておくことをおすすめします。
