$ cat blog/

$ cat blog/entry_003.md

エラーの数だけ、強くなれた — 68歳が直面したWebの洗礼

Every Error Made Me Stronger — A 68-Year-Old's Baptism by Web

에러의 수만큼, 강해졌다 — 68세가 직면한 Web의 세례

2026-03-26 entry_003 ./challenge/

最初のエラーは、真っ白な画面だった

PHPファイルを書いた。ブラウザでアクセスした。画面は真っ白だった。

アセンブラの世界では、エラーは明確だった。キャリーフラグが立つ。ステータスレジスタに値が入る。何が間違っているかは、レジスタを見ればわかった。ところがWebの世界では、エラーが「何も表示されない」という形で現れる。セミコロンが1つ抜けただけで、画面が沈黙する。

php.ini の display_errors = On を知ったのは、3時間後だった。

// 3時間の空白の末に学んだ最初のルール
ini_set('display_errors', 1);
error_reporting(E_ALL);

// アセンブラなら、HLTで止まってくれた。
// PHPは、何も言わずに死ぬ。

この日、自分は悟った。Web開発のデバッグとは、「沈黙を聞く技術」だ。

CORSという名の、見えない壁

kitemir.jp のバーチャル試着機能を shop.kitemir.jp と連携させようとしたとき、最も理解に苦しんだのがCORSだった。

アセンブラの世界にドメインの概念はない。ポート番号を指定してデータを送れば、相手は受け取る。だがブラウザの世界では、「同じサーバーの持ち主なのに、ドメインが違うからデータを渡せない」という事態が起きる。

Access-Control-Allow-Origin: https://kitemir.jp

// ↑ この1行を .htaccess に書くのに、2日かかった。
// 「なぜ自分のサーバー同士で通信を拒否されるのか」
// という怒りと混乱の2日間だった。

CORSを理解した瞬間、Webのセキュリティモデルの設計思想が見えた。ブラウザはユーザーを守るために、サーバー間の信頼関係を要求する。通信プロトコルの設計者としては、「なるほど、そういうハンドシェイクか」と腑に落ちた。

怒りの2日間が、理解の1秒に変わった。CORSは敵ではなく、設計だった。

非同期という、パラダイムシフト

アセンブラは本質的に同期的だ。INT 13h を呼べば、ディスクの読み取りが終わるまでCPUは待つ。CALL すれば、RET が来るまで次の命令は実行されない。すべてが順番通りに進む。

JavaScriptの非同期処理に出会ったとき、世界が反転した。

// 自分が最初に書いた(間違った)コード
let result = fetch('/api/tryon');
console.log(result);  // → Promise { <pending> }

// 「なぜデータが取れない?」と叫んだ。
// fetchは"約束"を返すだけで、データはまだ届いていない。

// 正しいコード
const response = await fetch('/api/tryon');
const data = await response.json();
console.log(data);  // → { success: true, ... }

Promise。約束。「今はまだ結果がないけど、終わったら教えるよ」という概念。アセンブラの世界には存在しなかったものだ。

だが、考えてみれば似た仕組みはあった。割り込み処理だ。ハードウェアに処理を依頼し、完了したらIRQで通知を受ける。非同期の本質は同じだった。Webはそれを、より洗練された形で実装していた。

> INT 13h = synchronous disk read (CPU waits)
> fetch() = asynchronous HTTP request (Promise)
> IRQ = hardware callback
> .then() = software callback
>
> // 名前が違うだけで、本質は同じだった。

Stripe Webhookの署名検証 — 信頼の証明

決済システムにStripeを導入したとき、Webhookの署名検証でつまずいた。Stripeは決済完了を通知するとき、リクエストボディと秘密鍵からHMAC-SHA256の署名を生成して送ってくる。受信側はその署名を検証して、「これは本当にStripeから来たデータだ」と確認する。

署名検証を飛ばしても動く。テスト環境では問題ない。だが本番でそれをやると、誰でも偽のWebhookを送って「決済完了」を偽装できる。

// 署名検証(省略禁止)
$sig_header = $_SERVER['HTTP_STRIPE_SIGNATURE'];
$payload = file_get_contents('php://input');

// タイムスタンプと署名を分解
$elements = explode(',', $sig_header);
$timestamp = substr($elements[0], 2);
$signature = substr($elements[1], 5);

// 期待される署名を計算
$signed_payload = $timestamp . '.' . $payload;
$expected = hash_hmac('sha256', $signed_payload, $webhook_secret);

// 一致しなければ拒否
if (!hash_equals($expected, $signature)) {
    http_response_code(400);
    exit('Invalid signature');
}

これを理解したとき、通信プロトコルエンジニアとしての血が騒いだ。HMAC署名は、かつて自分が実装した通信パケットのチェックサム検証とまったく同じ構造だ。データの整合性を保証するために、共有秘密鍵でハッシュを計算する。40年前と同じ原理が、ここでも使われていた。

技術の表面は変わる。だが、その下にある「信頼の証明」の原理は、40年間変わっていない。

500 Internal Server Error — 最も恐ろしい5文字

開発中、何度この数字を見たかわからない。500。サーバー内部エラー。原因が表示されない。ログを漁り、1行ずつ var_dump を挟み、どこで処理が死んでいるかを特定する。

ある日、FASHN AI(バーチャル試着API)との連携で、レスポンスが返ってこなくなった。ステータスコード522。タイムアウト。API自体は正常。自分のサーバーも正常。なのに動かない。

原因は、APIのリクエストフォーマットが変更されていたことだった。mode パラメータの配置場所が inputs の中からトップレベルに変わっていた。たった1階層の違いで、サービスが止まる。

// ❌ 旧フォーマット(522エラー)
{
  "inputs": {
    "model_image": "...",
    "garment_image": "...",
    "category": "tops",
    "mode": "performance"    // ← ここに入れると死ぬ
  }
}

// ✅ 新フォーマット(正常動作)
{
  "model_name": "tryon-v1.6",
  "inputs": {
    "model_image": "...",
    "garment_image": "...",
    "category": "tops"
  },
  "mode": "performance"      // ← トップレベルに配置
}

外部APIとの連携では、自分のコードが完璧でも動かないことがある。仕様変更、ダウンタイム、レート制限。コントロールできない変数が常に存在する。これはハードウェア制御のときと同じだった。ICの仕様書が間違っていることも、ロットによって挙動が違うことも、現役時代に経験していた。

エラーログは、最高の教科書だった

振り返ると、自分がWeb開発で学んだことの大半はエラーから教わった。成功したコードからは「それが動く」ということしか学べない。だがエラーからは「なぜ動かないか」「何が足りないか」「Webの世界はどういうルールで動いているか」を学べる。

真っ白な画面が、PHPのエラーハンドリングを教えてくれた。CORSが、ブラウザのセキュリティモデルを教えてくれた。Promiseが、非同期プログラミングの思想を教えてくれた。522エラーが、外部APIとの付き合い方を教えてくれた。

68歳から始めたWeb開発。エラーの数は、たぶん数千を超えている。だが、そのひとつひとつが自分を少しずつ強くしてくれた。

> error_count++;
> experience_points += error_count;
> level_up();
>
> // 出力: Level 68 Engineer — Web Development Unlocked
$ git log --grep="fix" --oneline | wc -l
    89

// 89回の修正コミット。
// そのひとつひとつが、教科書の1ページだった。