`supabase/migrations/`に`001_xxx.sql`のような連番でファイルを追加していく。多くのSupabaseプロジェクトで見かける、素朴なやり方だと思う。on-board typingでもそうしていた。ただ、複数の機能を並行して開発していると、この連番が思わぬところで壊れる。

CIが理由もわからず落ちる

Supabase CLIは、適用済みのマイグレーションを`supabase_migrations.schema_migrations`というテーブルに記録している。このテーブルのPRIMARY KEYは、ファイル名の先頭についている番号(バージョン)そのものだ。つまり、`016_a.sql`と`016_b.sql`のように同じ番号のファイルが2つ存在すると、`supabase test db`や`supabase db push`は「duplicate key value violates unique constraint "schema_migrations_pkey"」で必ず失敗する。

厄介なのは、このエラーメッセージだけを見ても、原因がすぐには分からないことだ。マイグレーションの中身自体には何の問題もないのに、CIだけが落ちる。

同じ問題を、3回作ってしまった

一度直せば終わりだと思っていたが、そうはならなかった。複数の機能をそれぞれ別のブランチで並行して作っていると、各ブランチの作業者は、自分のブランチの中でしか「次の番号」を把握できない。ブランチAが019を使い、ブランチBもほぼ同じタイミングで019を使う。それぞれ単体では問題なく通るので、マージするまで衝突に気づけない。

on-board typingでは、この衝突が016番、019/020番、021番と、形を変えて3回続けて起きた。3回目が起きたとき、ようやく「番号のつけ方そのものが並行開発と相性が悪い」と認めることになった。

対策1: 衝突したら、依存順序を保ったまま採番し直す

差し当たりの直し方はシンプルで、後からマージされた方のファイルを、空いている番号にリネームするだけだ。ただし番号は、ファイル名の順序であると同時に実行順序でもある。片方のマイグレーションがもう片方より後に実行される必要がある場合、採番し直すときにその依存関係を壊さないよう注意がいる。

対策2: 重複を機械的に検出できるようにする

その場しのぎの採番し直しを3回繰り返した後、`supabase/migrations/`配下のファイル名から先頭の数字だけを取り出し、重複がないかを確認するチェックスクリプトを作った。中身はシンプルで、ファイル名を数字プレフィックスでソートし、重複だけを抜き出すだけの数行だ。ただ正直に言うと、これをCIに正式に組み込むところまではまだできていない。今は手元で走らせる運用に留まっているので、次にこの手の衝突が起きる前に、CIの中で自動的に落とすところまで持っていくつもりでいる。

そもそも、Supabase CLIは連番を前提にしていない

振り返ってみると、根本的な選択ミスは番号の付け方にあった。Supabase CLIの`supabase migration new `は、本来`20260701123045_name.sql`のようなタイムスタンプ付きのファイル名を自動生成する。タイムスタンプなら、同じ秒に2つのブランチが同時にコマンドを叩かない限り衝突しない。on-board typingは可読性を優先して手動で`001_xxx.sql`という連番に統一していたが、それは同時に、Supabase CLIが標準で用意していた衝突回避の仕組みを自分から手放していた、ということでもあった。

見た目の綺麗さと、並行開発への耐性は、思っていたよりトレードオフの関係にあるようだ。「会社メールで登録するだけ」のようなシンプルな体験の裏側でも、こういう地味な運用の選択が後から効いてくる。on-board typingを支えるSupabase実装の詳細に興味があれば、他の技術記事もあわせて見てみてほしい。