これは DEV の Summer Bug Smash: Smash Stories (Sentry 提供) への投稿です。

ライブラリやフレームワークのバージョン番号をご存知ですか? 知らない方のために簡単に説明します。 [email protected] のような表記の場合:

  • x はメジャーリリースで、破壊的変更が許容されます。
  • y は後方互換性のある機能リリースです。
  • z はパッチで、通常は小さな修正やバグ修正が含まれます。

プロジェクトを健全に保つには、少なくともパッチバージョンは定期的に更新すべきです。実際、最近のパッケージマネージャーは、許可すれば自動的に更新してくれることが多いです。

しかし、ある日、3 番目の数字だけを上げただけで、アプリケーションが完全に壊れてしまいました。しかもフレームワークのせいではありませんでした。私たちのせいでした。

日常業務では多くのバグを修正しています。でもコンテストで語る価値のある 一つ を見つけるには、驚くほど昔の記憶を掘り起こす必要がありました…

それでは、2017 年 にタイムスリップしてみましょう。


2017 年にようこそ

Web アプリケーションが世界を席巻し始めてからまだ数年しか経っていません。ECMAScript 6 はすでに 2 年前に登場していましたが、ブラウザのサポートが十分ではないため、Promise、アロー関数、letconst といった革新的な機能はまだ慎重に使っていました。

市場は開発者を激しく求めていて、企業は次のようなコードを書ける人なら誰でも採用していました:

export class

全画面表示モードに入る 全画面表示モードを終了する

ミートアップでは 「Angular 入門」 のようなトークがあふれていました。当時の新しい Angular 自体もまだ 1 年に満たない頃です。少なくとも新規プロジェクトの作成は CLI コマンド 1 つで済みました。一方、React プロジェクトの作成はまだ npm の半分をダウンロードするような感覚でした。


私は野心的なミドルレベルの開発者として、Angular を黎明期から使い続けているチームで働いていました。

そのプロジェクトは、フェアトレード認証製品を監視するための大規模なエンタープライズアプリケーションでした。西欧から、数週間に一度しか公共図書館からインターネットに接続できないようなアフリカの小さな国々まで、世界中で動作する必要があります。

将来性は重要でした。


私たちはこのアプリケーションを構築しながら、現代のフロントエンド開発を学んでいました。Observable はどのように動作するのか? RxJS とは何なのか? サービスはいつ使うべきなのか?

Angular 自体もまだ成熟途上で、時々何かが動作しないのは… まあ… Angular にバグがあるからです。私たちは次々と issue を提出しました。

Angular のチームは非常に迅速に対応してくれました。issue テンプレートを書き終える前にバグが修正されることもありました。

当然、私たちはマイナーおよびパッチリリースも含めて定期的にアップグレードを続けていました。


Angular の初期 i18n の問題

前述のとおり、アプリケーションは多言語をサポートする必要があります。Angular にはすでに i18n システムの初期版がありましたが、現在のものとは全く異なっていました。実装の詳細には深入りしませんが、翻訳対象の要素には単純に i18n 属性を付けます:

<h1 i18n>Hello</h1>

全画面表示モードに入る 全画面表示モードを終了する

Angular CLI はアプリケーションをスキャンし、これらの要素から翻訳ファイルを生成します。

問題は、各言語ごとに独自のビルドが必要になることです。ランタイムでの言語切り替えは現実的ではありません。そして残念ながら、ランタイムでの言語切り替えはアプリケーションの必須要件 でした(なぜだったかはもう正直覚えていません 😄)。

Angular エコシステムはまだ非常に若く、この問題を解決する成熟したライブラリが存在しなかったため、私たちは独自のソリューションを構築することにしました。


私たちの実装は驚くほどシンプルでした。言語が変更されるたびに、ページをスキャンして i18n 属性を含むすべての要素を探し、その内容を適切な翻訳に置き換えるだけです。

エレガント。シンプル。おそらく 15 行程度のコードでした。


パッチアップデート

すべてが完璧に動作していました… 2017 年半ばまで。

Angular はバージョン 4 になっていました。(バージョン 3 をスキップした理由は完全に合理的だったと記憶していますが、それが何だったかはもう思い出せません。😄)

ある日、私たちはごく普通の Angular アップグレードを行いました。正確なバージョン番号はもう覚えていませんが、4.2.4 から 4.2.8 へのアップグレードのようなものでした。

ごく小さなパッチアップデートです。何も起こるはずがありませんでした。しかし… 実際には起こりました。私たちの翻訳、私たちの国際化システム全体が、すべて消えてしまったのです!

アプリケーションは正常に起動し、すべてが問題なく見えました。しかし言語を切り替えようとすると… 何も起こりません。英語のまま動かなくなってしまいました。


デジタル探偵作業の時間

ここからがプログラミングで一番好きな部分です:ソフトウェア・フォレンジック。

最初、私はこんな小さな Angular のアップデートが原因である可能性を完全に否定しました。私たちは常にパッチをアップグレードしています。何か別のことが起こったに違いありません。

当時は CI パイプラインや自動テストがまだ黎明期でした。テスターが単に 1〜2 週間言語を切り替えていなかっただけかもしれません?

Git の履歴を確認しました。何も疑わしい点はありません。最近のコミットは国際化に触れていません。

バックエンドが壊れているのかもしれません? 翻訳ファイルが消えてしまったのでしょうか?

いいえ。すべてが正しい場所にありました。

結局、気が進まないながらも、Angular のアップグレードをもう一度見直しました。

パッチを一つずつ遡っていきました。すると確かに… 2 つの小さなパッチバージョンの間で、翻訳が突然動作しなくなっていました。

つまり Angular に責任があるのでしょうか? それとも… そうではないのでしょうか?


謎の解決

Angular の変更履歴を掘り下げたところ、基本的に次のような内容の変更を見つけました:

生成された HTML に不要な i18n 属性を残しておく必要があるだろうか? コンパイラはすでに必要な処理を終えている。削除しよう。

突然すべてが理にかないました。私たちのランタイム翻訳システム全体が、Angular が保存を約束したことのない実装詳細に依存していたのです。

Angular にとって、その属性の削除は完全に合理的なクリーンアップでした。私たちにとっては… アプリケーション全体が壊れてしまったのです。


公式リファレンス(興味がある方へ)。私たちだけが影響を受けたわけではないことがわかります 🤣:


修正

幸い、問題の修正は特に難しくありませんでした。ただ… 面倒でした。Angular の内部的な i18n 属性に依存する代わりに、独自のディレクティブを作成しました。

その名前は?

fi18n

わかっています。エンジニアリングの創造性の頂点ですね。😂


私たちはひどいエンジニアだったのか?

この話は、私たちが何をやっているかわかっていない未熟な開発者だったということでしょうか?

全く逆です! 私たちは「それがクールになる」何年も前に、ランタイムでの言語切り替え を成功裏に実装していたのです 😉

私たちが犯した唯一の過ちは、偶然観察したものが Angular の公開契約の一部だと仮定したことでした。そうではありませんでした。私たちは Angular が変更する権利のある実装詳細の上にソリューションを構築していたのです。

そして最終的に… 実際に変更されました。

幸いなことに、アプリケーションは本番環境には程遠い状態でした 😅。私たちの若さゆえの楽観主義のおかげで、なんとか切り抜けることができました。


なぜこのバグなのか?

10 年以上のプロのプログラミング経験を経て、なぜ数多くの大きな本番障害ではなく、この バグを選んだのでしょうか? なぜなら、この教訓は今でもさらに重要になっているからです。

2026 年、フロントエンド開発は全く異なる様相を呈しています。もはや 「Angular 入門」 というタイトルのトークは行われていません。React、Angular、Vue はすべて成熟したエコシステムです。直面している問題が何であれ、誰かがすでに解決している可能性が高いです。

そして、すでに驚くほどの量のルーチンワークを処理できる AI もあります。

しかし今日でも同じようなことは起こり得るでしょうか? もちろんです。ただ… おそらくフロントエンドではもう起こらないでしょう。

今日、私たちは信じられないほどのペースで成長している全く新しいエコシステムを持っています:AI、そして特に AI エージェント。

今日の最大のエンジニアリング上の疑問の多くはここにあります。誰もまだすべての答えを持っていないため、カンファレンスやミートアップが次々と開催されています。私たちはまだエージェントループの仕組みを説明する記事を書いていますが、それらは初心者向けのトピックではありません。MCP のようなプロトコルは急速に進化しており、最新の情報を追い続けるには本当の努力が必要です。

それは 2017 年当時のフロントエンド開発を非常によく思い出させます。

そして当時と同じように、開発中に危険な仮定をしてしまうのは非常に簡単です。

誰かが構造化出力を使わずに、モデルの自由形式の応答を正規表現で解析する。

誰かがモデルが常に JSON を全く同じ Markdown ブロックでラップすると仮定する。

誰かがプロバイダーが返す未記載のフィールドにビジネスロジックを構築する。

誰かが全く同じプロンプトが常に全く同じツールをトリガーすると仮定する。

今日はすべてが動作する。明日もおそらく動作するだろう。来月、些細に見える詳細が小さなアップデートで変更される…

…そして突然すべてが崩れ落ちる。

ちょうど私たちの小さな i18n 属性がそうだったように。


本当の教訓

振り返ってみると、この話は本当に Angular についてのものではないと思います。もっと普遍的なことについてです。

エンジニアとして、私たちはしばしば 観察可能な動作保証された契約 と誤解します。今日存在するからといって、作者がそれを頼りにすることを意図していたとは限りません。

もしあなたのソリューションが未記載の動作に依存しているなら、それは確かな基盤の上に構築されているわけではありません。ただ運が良かっただけです。

しかし!

それは、私たちがミスを責め続けるべきだという意味ではありません。誰もがすべてを正しくできるわけではありません。大切なのは、そこから学ぶことです。

そしてこの特定のプロジェクトは? 幸せな結末を迎えました。私は何年も前にこのプロジェクトを離れました。でもアプリケーションは今も生きていて、うまくいっています。私が書いたコードのほとんどはもう消えてしまったでしょう… しかし、この記事を書く直前に確認しました。

ログインページは、ほぼ 10 年前に私がデザインした通りの見た目でした。それを見て私は微笑みました。なんて素晴らしいんだ 🤣❤️