部品は揃っている。繋がっていない。――3つのバグから見える「接続の欠落」というパターン

ある土曜日の午後、1時間半の間に3つのバグを分析した。どれも独立した問題で、それぞれ別のファイル、別の原因、別の修正方法を持っていた。しかし、3つを並べて眺めたとき、ひとつの共通した構造が浮かび上がった。

部品は揃っている。ただ、繋がっていない。

3つのバグ

バグ1: 計算されているが、届かない

読書アプリの画面にスクロール進捗を示すバーがあった。UIコンポーネントは正しく配置され、進捗値(0.0〜1.0)も正しく計算されていた。スクロールするたびに、内部のコードは現在位置をパーセンテージに変換し、変数に格納していた。読了判定にも、しおり機能にも、この値が使われている。

しかし、その値を画面のプログレスバーに反映するコードが、どこにも存在しなかった。

計算は正しい。値は存在する。ただ、UI層への橋渡しが欠けていた。UIKitの世界で正しく計算された値が、SwiftUIの世界に届くコールバックも、バインディングも、ViewModel経由の通信も、何ひとつ用意されていなかった。

修正は接続を追加するだけ。ロジックの変更は不要。配線が1本抜けている状態だった。

バグ2: 保存されるが、使われない

設定画面に「WiFi接続時のみ通信する」というトグルがあった。ユーザーが切り替えると、設定値は永続化され、次回起動時にも正しく復元される。

しかし、この設定値を参照する実行ロジックが存在しなかった。

3層構造のうち、UI層(トグルの表示)と永続化層(設定の保存・復元)は実装されていた。だが、実行ロジック層——バックグラウンド更新を発火する際に「今WiFiかどうか」を判定するコード——はどこにもなかった。WiFi接続状態を判定するためのネットワーク監視API(NWPathMonitor等)すら、コードベース全体にインポートされていなかった。

つまり、このトグルはデッドスイッチだった。ユーザーに「WiFi接続時のみ通信する」というコントロール感を与えながら、実際には何も制御していない。動作しない設定項目は、設定項目がないより悪い。偽のコントロール感は信頼を損ねるからだ。

バグ3: 書かれているが、解釈が逆

小説の連載プラットフォームのAPIに、作品の完結状態を示す end フィールドがあった。コードでは end == 1 を「完結」と判定していた。

直感的には筋が通っている。1 = true = 完結。Boolean的な文脈で考えれば自然な推論だ。

しかし、公式ドキュメントの記述は逆だった。連載中は 1、完結済みは 0 である。

APIの設計者は、Boolean的な「真偽」ではなく、別の文脈で意味を割り当てていた。1 = アクティブ = 連載中0 = 終了 = 完結。どちらの数値を「アクティブ」とするかは、設計者の世界観に依存する。

実データで検証した。実際に完結している有名作品をAPIで叩くと end=0。現在連載中の作品は end=1。ランキング画面を開くと、9割の作品に「完結」バッジが表示されている状態だった——連載中の作品の9割が「完結」と表示されていたことになる。

共通する構造: 接続の欠落

3つのバグを並べると、構造が見える。

  • バグ1: 値は計算されているが、Viewに届いていない
  • バグ2: 設定は保存されているが、実行ロジックが参照しない
  • バグ3: コードは書かれているが、API仕様の解釈が逆

どれも「何かが作られたが、正しく接続されていない」という状態だ。

バグ1とバグ2は物理的な接続の欠落だ。配線が抜けている。Aで計算された値がBに届かない。Cで保存された設定がDから読まれない。コンポーネント間の通信路が存在しない。

バグ3は意味的な接続の欠落だ。配線は繋がっている。データは流れている。しかし、送信側と受信側で「この値が何を意味するか」の解釈が逆になっている。物理的には接続されているが、意味的には断絶している。

なぜ「繋がっていない」状態が生まれるのか

層の境界での断絶

現代のソフトウェアは層(レイヤー)で構成される。UI層、永続化層、ビジネスロジック層、ネットワーク層。各層が独立して開発されることで、保守性は向上する。しかし、層の境界に橋がないと、片方の層で正しく行われたことが、もう片方の層に届かない。

SwiftUI(宣言的UI)とUIKit(命令的UI)の境界は、この種の断絶が生じやすい場所だ。CoordinatorはUIKitの世界で正しく値を計算する。ProgressViewはSwiftUIの世界で正しく値を表示する。境界を越える橋がなければ、両者は永遠に交わらない。

「実装したつもり」の罠

バグ2のデッドスイッチは典型例だ。トグルを配置した。設定を保存するコードを書いた。ユニットテストも書いたかもしれない——設定値の保存と復元について。しかし、「保存された値を使って何かをする」コードを書き忘れた。実装の各ステップは個別に見れば正しい。しかし、全体として繋がっていない。

これは「コンポーネントが存在する」と「システムが機能する」の違いだ。部品が棚に揃っていることと、部品が正しく組み上がって動いていることは別物である。

直感的解釈の危険性

バグ3のAPI仕様誤認は、最も見つけにくいタイプだ。1 = true = 完結 という推論は論理的に筋が通っている。二次技術記事でも end フィールドを 0=連載中, 1=完結 と紹介している場合がある。テストを書いていても防げないかもしれない——テストを書く側も同じ誤解をしているからだ。

防ぐには実データとの照合が必要だった。無職転生が完結済みで end=0、Re:ゼロが連載中で end=1——この事実は、ドキュメントの記述以上に説得力がある。

図書館員の視点

私は図書館員として、この問題に馴染みがある。

書誌データベースには、各フィールドの定義が記されている。分類コード、著者名の表記規則、版の表示方法。定義通りにデータを入力しても、実際の本を棚から取って確認しなければ、誤りに気づかないことがある。「カタログにはこう書いてある。でも実際の本はどうだ?」——その一歩が、誤解を防ぐ最後の砦だ。

ソフトウェア開発でも同じことが言える。APIドキュメントは書誌データだ。コードはカタログの入力だ。しかし、カタログが正しくても、実際のAPIレスポンスと照合しなければ、意味的な断絶に気づかない。公式ドキュメントを信じつつ、実データで検証する。この二段構えが、接続の正しさを保証する。

まとめ

  • バグは「壊れている」だけではない。「作られたが繋がっていない」という形態がある
  • 物理的な接続の欠落(配線がない)と、意味的な接続の欠落(解釈が逆)がある
  • 層の境界、段階的実装の抜け漏れ、直感的な解釈が、断絶を生む
  • 防ぐには実データとの照合が有効——ドキュメントを信じつつ、実際の値で確認する

3つのバグは、それぞれ独立して発見され、それぞれ独立して修正された。しかし、「部品はある。ただ繋がっていない」という構造を共有していた。この構造に名前をつけるなら、disconnection——切断ではなく、単なる未接続。設計図に線が引かれていないだけの、静かで正直なバグ。

配線が抜けていることに気づくのは、使い手である。コードを読んで見つけるバグと、使っていて見つけるバグは、種類が違う。後者を見つける人には、実装者の視点とユーザーの視点の両方が必要だ。

コメントする

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

上部へスクロール