ハコヅミを作る #61:プランを足して使えるようにする(試したら、全員のチェックが消えるマイグレーションだった)
前回(#60)の続きです。今シーズン最後のキャンプに出かける前に、来年のための機能を1つ作りました。
- 組み合わせの数だけ、プランが要る
- 足し引きしても、チェックを残す
- 列を1つなくすだけ、のはずだった
- プランの欄の形を、2回変える
- チェックを付けると、欄が閉じる
- 3回続けて、書き忘れる
組み合わせの数だけ、プランが要る
キャンプに、ロードバイクを積んでいくことがあります。キャンプ場のまわりを走るためです。ライト、空気入れ、ヘルメット。自転車の荷物も、忘れたくありません。
ハコヅミでは、キャンプを登録するときに「プラン」を1つ選びます。プランは、持っていくギアの組み合わせです。ソロ、デュオ、ファミリー。選んだプランの中身が、持ち物チェックに並びます。
ここに自転車を足そうとすると、こうなります。
- ソロ
- ソロ+自転車
- ファミリー
- ファミリー+自転車
組み合わせの数だけ、プランが要ります。ライトを1つ買い足したら、自転車入りのプランを全部直すことになります。
これは、ハコヅミを作る前に Notion で困っていたことと、同じ形です。Notion では、「人数」と「泊まりか日帰りか」の2つの軸で、持ち物を管理していました。軸が増えるたびに、組み合わせが掛け算で増えます。
そこで、プランを足して使えるようにします。「ソロ」と「自転車」を、両方選ぶ。自転車の荷物を直すのは、「自転車」のプランの1か所で済みます。

プランを2つ選んだキャンプ(架空のデータ)
実戦で試せるのは、春
これまでは、「使ってみてから決める」を大事にしてきました。ところが、今回はそれができません。今シーズンのキャンプは、これで終わりです。次に自転車を積むのは、春になります。
待っても、決める材料は増えません。机の上で決めて、冬のあいだに作っておくことにしました。春の最初のキャンプで、そのまま実戦に使えます。
「使ってから決める」は、使える見込みがあるときの話です。しばらく使えないと分かっているなら、考えが新しいうちに決めてしまうほうが、あとで楽です。
足し引きしても、チェックを残す
プランをいくつも選べるようにすると、今までの決まりと、ぶつかるところがあります。
今までは、キャンプのプランを変えると、持ち物チェックを最初から作り直して、チェックを全部外していました。ソロからファミリーに変えたなら、荷物は大きく変わります。「最初から見直してください」という考えです。
ところが、プランを足せるようになると、こういう場面が出ます。
前の晩に、荷物を半分積んだ。やっぱり、自転車も持っていこう。
ここで「自転車」のプランを足したとたんに、積んだ分のチェックが全部消えたら、困ります。
決まりを変えました。
- プランを足すと、その中身のうち、まだないものだけが足される
- プランを外すと、そのプランにしかないものだけが外れる
- ほかのチェックは、そのまま残る
「ソロからファミリーに入れ替える」は、「ソロを外して、ファミリーを足す」になります。両方に入っているテントやバーナーのチェックは、残ります。
料理(#43)を作ったときに、同じ考え方を入れていました。料理を選び直すと、食材だけが足し引きされます。プランも、それに合わせた形です。
やり直したいときのボタン
チェックが消えなくなると、逆に、「最初からやり直したい」ときの手段がなくなります。
最初は、「キャンプを作り直せばよい」と考えました。Claude Code に聞くと、それで失うものを並べてくれました。キャンプ場の情報、選んだ料理、あとから足した項目。やり直したいのはチェックだけなのに、関係のないものまで入れ直すことになります。
持ち物チェックの下に、「最初の状態に戻す」のボタンを置きました。

押すと、確認が出る
この確認の文は、2回直しました。
最初の文は、「チェックと「今回不要」を、すべて外しますか?」でした。これを見たわたしは、「今回不要も一緒に戻すように、変えてほしい」と頼みました。もう、そう動いていたのに、です。書いてあるのに、伝わっていませんでした。
次の案には、「並んでいるものは、そのまま残ります」とありました。「並んでいるものとは、何のこと?」と聞き返しました。
作った側は、画面の中身を知っています。「並んでいるもの」と書けば、持ち物チェックの項目のことだと分かります。使う側は、分かりません。「ギアや消耗品は、消えません」と、ものの名前で書き直しました。
確認の文は、「何が起きるか」だけでなく、「何が起きないか」も書きます。「最初の状態」と聞くと、あとから足したものまで消える、と読めるからです。
列を1つなくすだけ、のはずだった
ここからが、今回いちばん書いておきたい話です。
今までは、キャンプのテーブルに「プランの id」の列が1つありました。プランをいくつも選べるようにするので、キャンプとプランの組を持つ、新しいテーブルを作ります。
古い列は、どうするか。残しておくと、プランの持ち場所が2つになって、「どちらが正しいのか」で迷います。Issue には、こう書きました。
camps.plan_idの列はなくす(テーブルの作り直しになる。手元の D1 で、データが消えずに移ることを確かめてから出す)
SQLite(D1 の中身)では、ほかのテーブルを指している列は、そのままでは消せません。新しいテーブルを作って、中身を写して、古いテーブルを消して、名前を付け替えます。「テーブルの作り直し」です。マイグレーションの道具が、その SQL を自動で作ってくれます。
ハコヅミでは、前にも作り直しのマイグレーションを出したことがあります。そのときに決めた注意(外部キーの確かめを、あと回しにする書き方に変える)も、ちゃんと守りました。
そのうえで、Claude Code が、手元のデータベースで試しました。テストで溜まったデータが入った状態です。
| 前 | 後 | |
|---|---|---|
| キャンプ | 71 | 71 |
| 持ち物チェックの項目(ギア) | 58 | 0 |
| 持ち物チェックの項目(消耗品) | 55 | 0 |
| キャンプで作る料理 | 36 | 0 |
| 移したばかりのプラン | — | 0 |
キャンプは残っています。その中身が、全部消えました。
なぜ消えたか
持ち物チェックの項目には、「キャンプを消したら、その項目も一緒に消す」という決まりを付けてあります。持ち主のいない項目が残らないようにするための、ふつうの決まりです。
テーブルの作り直しでは、途中で古いテーブルを消します。データベースは、このとき、「中の行を全部消してから、テーブルを消す」という動きをします。
つまり、データベースから見ると、「全部のキャンプが消された」ことになります。決まりのとおりに、全部のキャンプの項目が、一緒に消されます。そのあとで新しいテーブルに名前を付け替えても、消えた項目は戻りません。
前に決めた注意は、「消したあとに、つじつまが合っているか」の確かめを、あと回しにするだけでした。「一緒に消す」動きそのものは、止まりません。
なぜ、今まで気づかなかったか
前に作り直しをしたときは、作り直したテーブルに、こういう「一緒に消える」子のテーブルが、ぶら下がっていませんでした。たまたま、踏まなかっただけです。
そして、マイグレーションは、空のデータベースでは、何の問題もなく通ります。消えるものが、ないからです。自動テストも、毎回まっさらなデータベースから始まるので、通ります。
データを入れた状態で試したから、分かりました。 そのまま本番に出していたら、全員の持ち物チェックが、消えていました。テスタがまだいないので、消えたのは、わたしのデータだけだったでしょう。それでも、これまでのキャンプの記録が、全部なくなるところでした。
列は、残すことにした
列をなくすのを、やめました。
- マイグレーションは、新しいテーブルを足して、今のプランを写すだけ。作り直しは、しない
- 古い列は、データベースに残る。コードからは、読まないし、書かない
- コードには、「もう使わない列」と、残した理由を書いておく
「持ち場所を1つにする」という目的は、コードの上では守れています。使わない列が1つ残るのは、きれいではありません。でも、きれいにするために、データを危ない目にあわせる理由は、ありません。
決まりも、2つ足しました。
- ほかのテーブルがぶら下がっているテーブルは、作り直さない
- マイグレーションの確かめは、空ではなく、データを入れたデータベースで行う
Issue に「確かめてから出す」と一言書いてあったことが、効きました。書いていなければ、「道具が作った SQL だから」と、そのまま出していたかもしれません。
プランの欄の形を、2回変える
プランを選ぶ欄は、形が2回変わりました。
1回目:チェックを並べる
最初は、プランを全部並べて、チェックで選ぶ形でした。作ってもらった画面を見て、すぐに気づきました。プランが増えると、画面が長くなります。フリーでも、プランは 20 件まで作れます。
2回目:リストボックス
そこで、「リストボックスで、複数選べるようにしたい」と頼みました。5 行ぶんの枠の中で、スクロールして選ぶ形です。
Claude Code は、言われたとおりに作ったうえで、気になる点を2つ挙げてきました。
- パソコンでは、2つ目を選ぶときに、Ctrl(Mac は ⌘)を押しながら押す必要がある。押さずにクリックすると、ほかの選択が全部外れる
- iPhone では、見た目がまったく変わる(1行の欄になり、押すと選ぶ画面が出る)
1つ目は、こわい動きです。気づかずに保存すると、外れたプランにしかない荷物が、持ち物チェックから消えます。
3回目:選んだものだけ出して、残りは畳む
Claude Code が出した別の案は、料理の食材を選ぶ欄(#55)と、同じ形でした。

選んであるプランだけが出る

開くと、残りのプランが出る
選んであるプランだけが出て、残りは畳まれています。プランが 20 件あっても、欄の高さは、選んだ数で決まります。
自分で「リストボックス」と言い出したので、最初は、それが答えだと思っていました。欲しかったのは、「プランが増えても、画面が長くならないこと」です。リストボックスは、そのための手段の1つでした。
頼むときは、やり方ではなく、困っていることを伝えるほうが、よい案が返ってきます。今回は、やり方で頼んだのに、困っていることのほうを汲んで、別の案を出してくれました。
一覧のほうは、すんなり決まりました。リスト表示は、プランの札を選んだ数だけ並べます。カード表示は幅が狭いので、「ソロ ほか1」と省略します。

リスト表示。札が、選んだ数だけ並ぶ

カード表示。2つ以上は「ほか1」
チェックを付けると、欄が閉じる
畳む形にしたあとで、E2E テストが、不具合を1つ見つけました。
「ほかのプランから選ぶ」を開いて、プランにチェックを付ける。すると、その瞬間に、開いていた欄が閉じます。2つ目を選ぼうとすると、もう一度開かないといけません。
原因は、「初めから開いておくか」の書き方でした。まだ1つも選んでいないとき(登録の画面)は、欄を開いておきたい。そこで、「開くかどうか」を、値で渡していました。
Svelte は、画面の中の何かが変わると、関係するところを描き直します。チェックを付けると、見出しの「1 件 選択中」が変わります。そのついでに、「開くかどうか」も、初めの値(閉じる)に戻されていました。人が手で開いたことは、Svelte は知りません。
「開いておく」ほうと「閉じておく」ほうを、別々に、決まった形で書くように直しました。
この不具合は、画面イメージの絵を撮ったときに、もう出ていました。「開いて、チェックを付けて、撮る」の順で撮った絵が、なぜか閉じた状態で写っていたのです。そのときは、撮る順番をまちがえたと思って、流しました。
「あれ?」と思ったことを、「自分のまちがいだろう」で片づけると、不具合を見逃します。テストに「チェックを付けても、欄は開いたまま」と書いていたので、拾えました。
3回続けて、書き忘れる
前回、リリースの PR に書く「マージの直前の時刻」を、2回続けて書き忘れた話を書きました。そのときに決めたのは、「Claude Code のほうから、マージの前に一言たしかめる」でした。
今回のリリースにも、マイグレーションが入っています。Claude Code は、PR の本文の一番上に、警告の印つきで欄を置きました。チャットでも、「書いたら、一言ください」と念を押しました。
わたしは、PR を開いて、そのままマージしました。3回目です。
言い訳をすると、PR を開いたときには、もう「マージする」つもりになっています。本文は、読んでいません。どれだけ目立つところに書いてあっても、読まないものは、目に入りません。
Claude Code の提案は、こうでした。
人が書く形をやめる案を出す。リリースの PR を作るときに、私がその時点の時刻を本文に書いておく。
PR を作った時刻でも、マイグレーションが当たる前であることは、変わりません。戻す先として、使えます。マージのあとで、GitHub に残っているマージの時刻に、書き換えてもらいます。
人が忘れても、困らない形です。
| やり方 | 結果 | |
|---|---|---|
| 1回目 | PR の本文に、書く欄を置く | 書き忘れた |
| 2回目 | 同じ | 書き忘れた。「次は、Claude Code がたしかめる」と決めた |
| 3回目 | 欄を一番上に置き、チャットでも念を押す | 書き忘れた |
| これから | Claude Code が書く | 人は、何もしなくてよい |
前回は、「同じ失敗を2回したら、手順を変える」と書きました。変えたつもりの手順(一言たしかめる)は、まだ、人が何かをすることに頼っていました。
手順を変えるなら、「人が、もっと気をつける」ほうへではなく、「人が、何もしなくて済む」ほうへ変えます。
おわりに
- 組み合わせで増えるものは、部品にして、足して使えるようにする
- 足せるようにしたら、「変えたら最初から」の決まりを見直す。代わりに、やり直す手段を別に用意する
- 確認の文は、作った側の言葉ではなく、ものの名前で書く。「何が起きないか」も書く
- テーブルの作り直しは、ぶら下がっているテーブルの中身を、全部消すことがある。ほかのテーブルがぶら下がっているテーブルは、作り直さない
- マイグレーションは、データを入れたデータベースで試す。空のデータベースでは、消えるものがないので、何も起きない
- きれいにするために、データを危ない目にあわせない。使わない列は、残しておけばよい
- 頼むときは、やり方ではなく、困っていることを伝える
- 「あれ?」と思ったことを、自分のまちがいで片づけない
- 手順を変えるなら、人が何もしなくて済むほうへ変える
次回は、今シーズン最後のキャンプで、実際に使ってみた話を書く予定です。