論理削除 vs 物理削除、チームで選んだのは?

技術

背景

とあるテーブルの削除処理を実装する際、私は自然と 物理削除(DELETE文で完全に削除) を選んでいた。

というのも、以前から論理削除の煩雑さに違和感を持っていたからである。

論理削除は、「削除」されたレコードに delete_flg = 1 のようなフラグを立てて残す手法だが、この論理削除について、以下のポッドキャストで @t_wada さんが言及する回があった。この回は秀逸で、私も何度も聞き返していて面白いので、ぜひ聞いてみてほしい。

https://open.spotify.com/episode/1eg5B9gvM2qsJbxF511eAC?si=WGXGrs8cTqK3mNYLP8syXw

私の中で、論理削除には以下の様なデメリットがあると理解している。

  • 毎回 delete_flg = 0 を条件に加えないといけない
  • 「削除されたように見えて、実は存在している」状態が発生する
  • クエリの可読性・メンテナンス性が下がる

これにより、曖昧さや複雑さを呼ぶリスクがあると感じていたため、私自身も、「論理削除アンチ」寄りのスタンスだった。

しかし、チームの方針は一貫して論理削除。すべてのテーブルに delete_flg カラムを持たせ、基本的にレコードは残すスタイルである。

PRレビューでも、「ここ、論理削除の方が一貫性あるのでは?」というフィードバックがあり、ちょっとした議論に発展した。

結論:論理削除に切り替えた理由

最終的には、論理削除に変更する判断をした。

その理由は、「チーム全体としての驚き最小化」という観点である。

たとえ一部で物理削除を行ったとしても、読む人が「なぜここだけ物理削除?」と一度でも疑問を感じてしまうと、コードリーディング時の認知負荷が高くなる。

「あえて物理削除にしている意味はあるのか?」と、余計な考慮が生まれるのは避けたいところだった。

また、論理削除前提のチームであれば、検索系・集計系のクエリも delete_flg = 0 を含めて設計されているはずなので、そこに乗っかる方が安全でもある。

それでも、物理削除が好きな理由

とはいえ、個人的には物理削除が好きだ。

  • レコードがなければ「ない」と即断できる明快さ
  • テーブル構造がシンプルで、WHERE句もスッキリ書ける

など、論理削除特有の「残ってるけど使えないデータ」がないのは、実装者としては心地よい。

ただし今回は、「個人の好みより、チームの整合性を優先」する判断を行った。

学び:INSERT ... ON DUPLICATE KEY UPDATE の活用

今回、論理削除に切り替える中で、便利なSQL構文を新たに学んだ。

例えば、以下の様なクエリで、user_id にユニーク制約があるような場合、通常のINSERTだと重複エラーになるが、ON DUPLICATE KEY UPDATE 句を加えることにより、重複した場合どのようなUPDATE処理を行うかを指定することができる。

INSERT INTO user_address (user_id, name, delete_flg) VALUES (1, 'Tokyo', 0) ON DUPLICATE KEY UPDATE delete_flg = 0;copy

このように、既に同一キーの削除済みレコードが存在する場合に、それを復活させるような動きが可能である。

MySQL :: MySQL 8.0 リファレンスマニュアル :: 13.2.6.2 INSERT ... ON DUPLICATE KEY UPDATE ステートメントdev.mysql.com

論理削除前提の設計でも、こういった書き方を知っておくと柔軟に対応できると感じた。


まとめ

論理削除 vs 物理削除については多数議論がなされているトピックだ。

個人の考えとチーム方針がズレることもあるが、「整合性・一貫性・驚き最小化」という観点で柔軟に判断していけると、プロジェクト全体としての健全性が保たれると思う。