Claude Code活用 #4 特定のページだけ文字化けする──データ依存の二重エンコードを粘り強く突き止めた

Claude Code活用 #4 特定のページだけ文字化けする──データ依存の二重エンコードを粘り強く突き止めた

不具合調査でいちばん怖いのは、「もっともらしいけれど、少しズレている結論」で止まってしまうことです。今回は、弊社が保守を担当しているレガシーな業務システム(Perl 製の CMS 管理画面)で起きた文字化けを Claude Code と一緒に追いかけた記録です。最初の調査結論は半分外れていて、しかもローカルではどうしても再現できませんでした。それでも粘り強く切り分けを続けた結果、発見の難しい根本原因にたどり着いて修正・本番反映まで完了しました。「Claude Code はここまで粘れる」という一例として、実際の流れをご紹介します。

前回の Claude Code活用 #3 は「横断調査で一発でクリアに分かった」例でしたが、今回はその逆で、一度たどり着いた答えを自分で訂正しながら粘った話です。

起きていたこと

CMS の管理画面で、あるブロックの「変更する」ボタンを押すと、参照先を選ぶダイアログ(モーダル)が開きます。このダイアログの中身だけが、こんなふうに文字化けしていました。

  • ダイアログのタイトル(「変更する」)は正常に表示される
  • 一方で、本文の説明文・選択肢のラジオボタン・「キャンセル/戻る/次へ」のボタンがすべて化ける

「タイトルは出るのに本文だけ化ける」というのは、地味ですが厄介なパターンです。画面全体が壊れていれば原因の見当もつきますが、一部だけ化けるとなると「どこで何が起きているのか」が一気に見えにくくなります。

最初の調査結論は、半分外れていた

数日前に一度、この文字化けを調査していました。そのときの結論はこうでした。

出力時に UTF-8 を二重にかけている(二重エンコード)。このダイアログは開くページに関係なく構造的に必ず化ける。データ破損でもバージョン落ちでもなく、コードを直すしかない。

方向性としては当たっていました。「二重エンコード」というメカニズム自体は正しかったのです。でも「ページに関係なく必ず化ける」「データには依存しない」という部分が、あとから振り返ると外れていました。この思い込みが、次のステップで大きな壁になります。

壁:ローカルでどうしても再現しない

修正するには、まず手元で再現できる環境が要ります。そこでローカルに同じ CMS を立て、デモデータを入れて同じダイアログを開きました。

ところが、何度やっても化けないのです。

コードを読む限り「二重エンコードが起きるはず」なのに、ローカルでは正常に表示される。普通ならここで手が止まります。「コードは合っている、でも再現しない」――この矛盾こそが、実は最大のヒントでした。

突破口:「コードが正しく見えて本番だけ化けるなら、差はデータだ」

ここで Claude Code と立てた仮説が「環境の差ではなく、データの差ではないか」というものでした。お客様の画面では特定のページでだけ起きていて、ローカルのデモデータでは起きない。であれば、本番に入っている実データそのものが引き金になっている可能性が高い、という読みです。

そこで地道な土台づくりに切り替えました。

  1. 本番とまったく同じ構成のローカル環境を用意する(DB のバージョン・文字コード設定まで本番に合わせる)
  2. 本番 DB を読み取り専用でダンプし、その実データをローカルに取り込む
  3. その状態で同じダイアログを開く

すると、取り込んだ瞬間に、本番と同じバイト列でぴたりと再現しました。 クリーンなデモデータでは起きず、本番データを入れた途端に起きる――この時点で「データ依存」はほぼ確定です。

切り分けの過程では、化けたデータのバイトを実際に拾って分析しました。二重エンコードが起きていると、C2C3 で始まるバイトが不自然に増えるという特徴(signature)があります。実際にダイアログの応答を採取すると、その異常なバイトが大量に並んでいて、しかも Latin-1 → UTF-8 で一発デコードすると元の日本語にきれいに戻りました。 「正しい UTF-8 をもう一度 UTF-8 でエンコードしてしまった」ことが、これで動かぬ事実になりました。

ここは、AI を使った調査の強みがはっきり出た場面でした。文字化けは、人の目には「□□□」や「å\x8f\x82…」のような意味不明な記号の羅列にしか見えません。「なんか化けてるな」までは分かっても、そこから先、内部で何が起きているのかは人間には読み取れないのです。ところが Claude Code のようなプログラムから見ると、文字化けは「意味不明な記号」ではなく、C2C3 がいくつ並んでいる、Latin-1 として解釈すると何のバイトになる、という具体的な数値・構造として見えるものになります。「化けている」という曖昧な印象を、「どう化けているか」という事実に変換できる――これは、人が目視で粘るのとはまったく別レイヤーの調査で、AI と一緒に不具合を追う大きな利点だと感じました。

真因:日本語タグが引き金の「データ依存の二重エンコード」

最終的に突き止めた根本原因は、Perl の文字列まわりではおなじみの「utf8 フラグの取り違え」でした。要点だけ噛みくだくと、こうなります。

  • このダイアログの HTML は、生のバイト列として組み立てられる部品(テンプレートやページ名)と、utf8 フラグ付きの文字列として扱われる部品(プログラム内のメッセージなど)が混在している
  • ブロックに保存された日本語のタグを取り込む処理が、その値を utf8 フラグ付きで返す
  • このフラグ付きの値が、生バイトで組まれた HTML に混ざり込むと、文字列全体が「フラグ付き」に昇格してしまう
  • すると、生バイトだった部分が Latin-1 として読み直され、最後の出力時にもう一度 UTF-8 エンコードされる=二重エンコードになる

つまり、ブロックに日本語タグが入っているページだけが化けるのです。タグが空だったり英数字だけだったりすると、フラグの昇格が起きないので化けません。これで、最初は説明できなかった謎がすべてつながりました。

  • なぜ特定のページだけ化けるのか → そのページのブロックに日本語タグが入っているから
  • なぜローカルのデモデータでは再現しないのか → デモデータにその日本語タグが無いから
  • なぜ「構造的に必ず化ける」という最初の結論が外れていたのか → 引き金はコード構造ではなくデータだったから

最初の調査での「必ず化ける/データに依存しない」という見立ては、ここではっきり訂正されました。粘って再現環境を作らなければ、おそらくこの訂正はできませんでした。

修正:条件付きの1行を、差し込む直前に

原因が「フラグ付きの値が生バイトに混ざること」なので、対策はシンプルでした。値を HTML に差し込む直前で、フラグが付いていたら UTF-8 バイトに揃える、という1行を加えるだけです。

# フラグが立っている時だけバイト列へ揃える(フラグ無しデータには触れない)
$value = Encode::encode('utf-8', $value) if utf8::is_utf8($value);

ポイントは if utf8::is_utf8(...) という条件を付けたことです。これにより、もともとフラグの付いていない(=化けていなかった)データには一切手を触れず、無回帰を担保できます。同じ箇所が複数のステップにあったので、該当する数箇所に同じ対策を入れました。

修正後は本番で検証し、先ほどの異常なバイト(二重エンコードの signature)がゼロになり、ダイアログの全文が正常に表示されることを確認。本番反映まで完了しました。

Claude Code がここで効いた理由

今回いちばん伝えたいのは「一発で当てた」話ではありません。むしろ 最初の答えが外れていて、それを自分で訂正しながら粘れた ことです。振り返ると、効いたのは次の点でした。

  • 自分の結論を疑える:前回の「必ず化ける」という結論を前提に固執せず、「再現しない」という事実のほうを重く見て、仮説を立て直せた
  • お客様の観察を仮説に変えられる:「特定のページでだけ起きる」というユーザー側の気づきを、「ならば引き金はデータだ」という検証可能な仮説に翻訳できた
  • 地道な土台づくりを厭わない:再現環境を本番同等に整え、実データを取り込むという手間のかかる作業を、対話の中で一気通貫で進められた
  • 推測で止めず、バイトで裏を取る:「たぶん二重エンコード」で終わらせず、実際のバイト列を採取して Latin-1 → UTF-8 で復元できることまで確認し、事実で結論を固めた

不具合調査の難しさは、コードを読む力そのものよりも、「もっともらしい答え」で満足せずに最後の一歩まで詰めきれるかにあります。Claude Code は、まさにその粘りの部分で頼れる相棒になってくれました。

おわりに

「原因はだいたい分かった」で止まっていたら、今回の文字化けは「特定のページだけ、ときどき化ける謎の不具合」として残り続けたはずです。最初の結論を訂正し、再現環境を作り込み、データ依存という真因まで詰めきれたことで、1行の安全な修正で根治できました。

弊社 Blue Leaf では、こうしたレガシーシステムの保守・調査でも Claude Code を実務に組み込み、「もっともらしい答え」の先にある本当の原因まで詰めきることを大切にしています。原因の見えない不具合や、長く付き合っているシステムの調査でお困りのことがあれば、お気軽にご相談ください。

\ 最新情報をチェック /

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です