事故の概要

「全記事を一括で更新しよう」。 そう思い立って実行したバッチ処理が、このブログのリポジトリを破壊しました。

処理完了後、サイトを確認すると全ての記事が「複製」され、同じ内容でURLだけが微妙に異なる記事が2つずつ並ぶという惨状が広がっていました。

これはバグではありません。スクリプトは正確に動作しました。 間違っていたのは、「柔軟性」に甘え、一貫性のない入力を与え続けた私の運用です。

なぜ起きたのか:Source of Truth の乖離

原因は、記事のURL(スラッグ)を決めるルールが「曖昧」だったことにあります。 当時の私のシステムには、URLを決めるための「正解(Source of Truth)」が2箇所に分散していました。

  1. ディレクトリ名(物理的実体): projects/00-why-i-left
  2. 実行時引数(オペレーターの意志): manager.py ... --slug why-i-left-blogger

「手抜き」が生んだ設計ミス

私はこれまで、ディレクトリ名を 00-why-i-left のように簡潔に付ける一方で、公開URLはSEOを意識して why-i-left-blogger のように長く具体的に設定していました。 この不一致を埋めるために、毎回コマンド実行時に --slug オプションでURLを手動指定する運用を行っていました。

つまり、「ファイルシステム上の名前」と「公開したい名前」が乖離している状態を、スクリプトのオプション引数で毎回補正していたのです。

これは極めて危険な設計でした。「正しいURL」の情報がどこにも記録されておらず、「オペレーターの記憶と指先」だけが正解を知っている状態だったからです。

自動化による破綻

今回、私は横着をして「コマンドライン引数なし」で全記事を一括処理しました。 「引数を省略したらディレクトリ名から自動生成する」という便利機能があったからです。

その結果、システムは素直に「ディレクトリ名(why-i-left)」を正解として採用しました。 しかし、それは私が過去に手動で作り上げてきた「本当の正解(why-i-left-blogger)」とは異なっていました。

結果として、古い正解ファイル(why-i-left-blogger.md)はそのままに、新しい誤ったファイル(why-i-left.md)が生成され、記事が二重に存在する状態になったのです。

解決策:Source of Truth の統一

この事故を防ぐために必要なのは、高度なチェック機能でもAIによる監視でもありません。 「正解を一つにする」ことです。

今回の修正で、私はスクリプトから「コマンドライン引数でのURL上書き機能」を削除しました。 その代わり、「ディレクトリ名こそがURLである」というルールを絶対化しました。

  • URLを変えたいなら、ディレクトリ名をリネームする。
  • ディレクトリ名以外からURLは決まらない。

ディレクトリ構造の大規模リファクタリング

このルールを適用するため、私は既存のディレクトリ名をすべてリネームし、公開済みURLと一致させました。

  • projects/00-why-i-left -> projects/00-why-i-left-blogger
  • projects/19-why-astro -> projects/19-technical-blog-astro-cloudflare

この変更によって、「ディレクトリを見ればURLがわかる」「ディレクトリ名さえ間違っていなければ事故は起きない」という状態が確立されました。

柔軟性はリスクである

エンジニアは往々にして、ツールに柔軟性を持たせたがります。 「デフォルトはこれだけど、オプションで上書きもできるようにしておこう」。その親切心が、将来の自動化を阻害し、事故を招く要因になります。

今回の事故は、「手動運用に頼ったズボラな設計」はいずれ自動化の波に飲まれて破綻するという良い教訓になりました。 不自由であること、選択肢がないことは、堅牢なシステムにおける最大の美徳なのです。