はじめに
昨日、社内ミーティングで改めて感じたこと、そしてXでもよく見かける意見に深く共感した話です。一度決めたデータ構造は、想像以上に後から変更が難しいものです。
データ構造は一度決めたら変えにくい
アジャイル開発では、ソースコードはリリース後もリファクタリングやリプレイスが可能です。しかし、データベースのテーブル定義だけは、一度データが蓄積されてしまうと、後からの変更が非常に困難になります。
技術的にはALTER TABLEで変更できますが、デッドロックの発生、既存データへの影響、データ整合性の確保など、事故のリスクが大きく、運用コストも高いため、極力避けたいのが実情です。
DB変更時に特に気をつけたいこと
スペルミスは絶対に避ける
これは絶対にNGです。後から見ると格好悪いだけでなく、影響範囲が広大です。開発のたびに過去の過ちを掘り返されるような感覚に陥ります。
AIによるスペルチェックなどを積極的に活用し、複数人でのレビューも行いましょう。
NG例(間違いがわかりますか?)
catagory → category
usrname → username
Refrence → Reference
命名規則とデータ型の統一
可能な限り一般的な基本ルールに忠実に揃えましょう。組織内に既存の命名規則がある場合は、それに統一するのが良いでしょう。特に日付系のカラムは、テーブルを跨いで統一されていると理解しやすいです。
推奨例:
作成日:created_at(datetime型)
更新日:updated_at(datetime型)
どのデータを格納するか(値の持たせ方)
statusカラムのように、Rubyのenumを使う場合は、DBには直接「エラー」「処理済み」といった文字列ではなく、数字を格納することが推奨されます。
また、別テーブルの変更内容を記録するようなケースでは、以下のように全く同じカラム構造を複製するのではなく、柔軟なデータ構造を検討するのも良いアイデアです。
従来の方式:
create_table "target_table" do |t|
t.string "fizz_column", limit: 1, default: "0", null: false, comment: "カラム1"
t.string "buzz_column", limit: 1, default: "0", null: false, comment: "カラム2"
end
create_table "other_table" do |t|
t.string "fizz_column", limit: 1, default: "0", null: false, comment: "カラム1"
t.string "buzz_column", limit: 1, default: "0", null: false, comment: "カラム2"
end
拡張性を考慮した方式:
create_table "other_table" do |t|
t.json "columns_hash", null: false, comment: "カラム情報をハッシュ形式で保持"
end
このようにハッシュで保存することで、target_tableのカラム変更やother_tableへのカラム追加が必要になった際も、プログラムの修正だけで対応できるようになります。
可能な限りDB変更をしない開発を意識する
ぶっちゃけ、SQLの実行自体は極力減らしたいものです。ridgepoleのようなツールがあるとはいえ、リリース時にDB変更を意識しなくて済むよう、拡張性のあるテーブル構造を心がけましょう。
物理削除と論理削除の適切な選択
delete_flagが必要なのか、本当に熟考すべき点です。顧客情報など、履歴保持が必須なデータ以外は、どちらが適切か十分に検討しましょう。
個人的には、論理削除(delete_flagやdeleted_atを使う)の場合、生きているデータを取得する際にSQLで毎回WHERE delete_flag = false(またはWHERE deleted_at IS NULL)といった条件が必要になり、スコープの設定忘れなどによる不具合のリスクも考慮すると、できれば物理削除が望ましいと考えています。
一見でわかるDBやカラム名
ソースコードや仕様書を見なくても、何のためのテーブルなのか、雰囲気で掴めるような名前が理想です。機能名をそのまま英訳するのではなく、なるべく一般的な命名規則や慣例に置き換えられないか、常に考える習慣をつけましょう。
created_atとupdated_atに作成日と更新日以上の役割を持たせない
例えば、ユーザーの会員登録日にcreated_atを使ったり、ユーザーの退会日にupdated_atを使うべきではなく、それぞれ専用のカラムを持たせてそこに値を格納し、参照するのが良いと思っています。
updated_atは特に、何かあってデータが書き換わると簡単に変わってしまうため、あまり当てになりません。
まとめ
データ構造は、一度決めたら長く残り、その後の開発に大きな影響を与えます。安易な決め方をすると、技術的負債として将来のエンジニアにのしかかります。妥協せず、じっくり考えてチーム内で議論を尽くし、納得のいく形で決定することが、何よりも大切だと改めて感じました。