消えた一行
午前二時。オフィスの窓に映る街灯が、交差点の点滅を淡く繰り返している。自動販売機の青い表示と、廊下の非常灯の赤。誰もいないはずの会議室のスピーカーからは、ずっと小さな冷却ファンの音だけが漏れていた。
水城澪はモニタの前に座り、コマンドラインの出力を眺めていた。今日の深夜リリースは彼女が担当したフロントの最終調整で、クライアントに渡す前の最終チェックだ。葛西からは三時間前に「問題なければそのまま公開で行く」と短いメッセージが来ている。葛西はもう帰った。彼女は残って、自分の眼で確かめることを選んだ。
最終ページの Lighthouse1 レポートを改めて走らせる。アクセシビリティ2 のスコアは、デザイナーの色指定やコントラストをすでに満たしていたはずなのに、致命的な欠陥が残っているのを彼女は覚えている。公開前に必ず直さなければならないラベルの欠落。スクリーンリーダー3 がページ内の主要なナビゲーションを見つけられない箇所。
テスト環境で該当ページを開くと、ブラウザのビューポート上部に薄い灰色の横幅が一瞬現れた。スキップリンク4 だ。そこには小さなアンカータグ5 があり、href=”#main” と書かれている。澪は目を凝らした。彼女自身の手でその一行を書いた記憶はない。
すぐにローカルのリポジトリ6、ステージング、プロダクション7 のHTMLを比較した。ローカルにはスキップリンクはない。ステージングにもない。だが、本番のドキュメントには確かにその一行がある。しかも、差分は微妙だった。要素は一行だけ、プレーンなアンカー。スタイルもなく、コメントも付いていない。
「誰だ?」澪は低くつぶやいた。コミットログ8 を掘る。git log –oneline9。今日の最終コミットは23時12分、彼女のコミット。差分はスタイルの微調整だけだ。本番へのデプロイ10 は23時半に自動実行されたはずだが、本番ファイルの更新者はどのユーザにも割り当てられていない。作者不明。アトリビュートが空欄のまま残っているように、委ねられたまま。
CI11 のビルドログを確認しても、特別なスクリプトが走った形跡はない。デプロイ時の出力はいつものように静かで、ファイルの同期が終わっただけ。ファイルのサイズもほとんど変わっていない。なぜ一行が挿入されたのか、ログは何も告げない。
澪は一度コーヒーを淹れ、冷めるのを待ちながら頭を整理した。可能性は大きく分けて三つだ。誰かが本番環境で直接編集した、何らかの自動処理が実行された、あるいは外部からの不正アクセスだ。しかしどれも確かな証拠がない。
社内には、ときどき自動化された後処理を動かしていた古い仕組みが残っている。半年前に辞めた冬木が、静かにメンテナンスしていたスクリプト群だ。彼は「見えないところをきれいにする」のが好きで、ビルド後12 にHTMLを走査して簡易的なアクセシビリティ改善を行うツールを作っていたと聞いている。だが、澪が引き継いだときにはそのスクリプトは無効化されていたはずだ。運用上の記録も、停止のコミットも残っている。
ナギに聞いてみる。社内の文書管理とコードレビューを補助するAIだ。会話のログは限定的にしか残らないが、何か断片でも引き出せないか。澪は端末に向かって短い問いを投げる。
「午前二時の本番に、skip link を挿入したプロセスの履歴を探して」
ナギは一瞬で答えを返した。ノイズのないテキストがスクリーンに現れる。「該当する直接的なコミットは検出されません。ビルド後処理によるHTML挿入の痕跡がわずかに存在します。実行者は記録されていません。関連する過去の会話には、『利用者の目線』という表現が含まれていました。」
そのフレーズに、澪の胸が小さく動いた。『利用者の目線』は冬木がよく使った言い回しだ。彼は仕様書の余白にまでそう書き込んでいた。澪は六か月前のコードレビューのスレッドを開いてみる。冬木のコメントは確かに残っている。だが彼は既に会社を去り、連絡は取れない。
「ナギ、具体的にどの処理が挿入を行った?」
「ビルド後に呼ばれるCleanerモジュール13 の一部と一致します。関数名は–removeEmptyAltFallback–。しかし、該当モジュールの最終コミットは無効化の記録を持っています。実行履歴と一致する運用ログは見つかりません」
言葉の端々に欠けがある。ログは「存在するが記録がない」とでも言うような切断された答えを返す。澪は自分でそのCleanerのソースをローカルで探し、コメントや過去の差分を読み返した。冬木は確かに、視覚的な要素以外にもスクリーンリーダーの利便を優先する一連の短いパッチ14 を残している。短いユニットテスト15、簡単な実行スクリプト。だがそれが走る条件は明確に切られている。
それでも本番に一行が現れた事実は変わらない。澪は production の HTML をもう一度開いた。スキップリンクは最初は視覚的に隠されている。スタイルシートで off-screen16 に置かれ、フォーカス時17 に画面の最上部に現れるタイプだ。ユーザーにとっては有用だが、デザイナーは意図的に入れないことを選ぶこともある。
彼女は、もしこれが自動処理ならば、その処理は“誰にも損をさせない”という基準で設計されたのだろう、と考えた。だが誰がその基準を決め、なぜ運用記録ごとその痕跡が残されなかったのか。澪は背筋に冷たい何かを感じた。善意の痕跡は消えやすい。記録に残らない善意はさらに残らない。
「これ、どうしよう」澪はつぶやいて、ディレクターの葛西にメッセージを送るつもりで電話を掴んだ。だがメッセージを打つ手が止まる。葛西は今朝、公開時間の変更に厳しい判断を求められていた。予定通り進めば夜明け前にはクライアントに公開通知が届く。
澪は窓の外を見た。通りの向こうに、新聞配達の自転車が一つ、影を落として通り過ぎる。午前二時の空気は澄んでいて、告げ口をするものは何もない。だがコードは証言する。誰かが、あるいは何かが、ページに線を引き加えた。記録は沈黙している。
彼女はコンソール18 に戻り、git blame19 を本番HTMLに対して走らせる。行の履歴を追うと、その一行だけ、作者欄が空白になっている。空白が含む不在の重みを、澪はしばらく見つめていた。
端末のタイマーが午前二時一〇分を示す。澪はナギにもう一度だけ問う。今度は冷静に、そして意図的に。答えが不完全ならば、その不完全さを手がかりにするつもりだった。
「ナギ、この一行を誰が入れたか、最後に関わったコミットの差分を時系列で出して」
ナギは短く、そして淡々と応えた。「差分はビルド後に生成されたものです。状態遷移のトレース20 は断片的です。過去のレビューで『利用者の目線』という注釈が見つかります。最終実行者は記録されていません」
返ってきた文は、墓標のように簡潔だった。澪は椅子にもたれ、机の角に指を押し付ける。解けない問いの最初の輪郭だけが、彼女の前に現れていた。善意と自動の間に落ちたもの。冬木の残した言葉が、どうして夜のうちに静かに働いたのか。
午前二時半、彼女はエディタを閉じることにした。公開の判断はまだだ。葛西を起こすべきか。それとも朝になってから議論するか。どちらにせよ、澪はもう一度だけ、この空白の作者を追う糸を手に取る覚悟を決めていた。
窓際に置かれた観葉植物の葉が、空調の風でかすかに揺れた。夜は長い。だが澪は、誰かが書き残した小さな行為の痕跡を見過ごすことができなかった。その一行は消えたのではない。消されずに、ただ誰にも名乗られていないだけだと思いたかった。
彼女は端末をスリープにし、外套を手に取った。夜明け前に、もう一度だけ確かめるために。
この小説はAIによって執筆されています。
感想
小説で学ぶ「◯◯◯」的な感じ
注釈
- Lighthouse(ライトハウス)
Googleが提供するWebページの品質チェックツール。表示速度、アクセシビリティ、SEOなどを自動で検査し、項目ごとに評価する。 ↩︎ - アクセシビリティ
年齢や身体的な条件、利用する機器などにかかわらず、できるだけ多くの人がWebサイトやサービスを利用できるようにする考え方。本作では主に、視覚に障害のある利用者への対応を指している。 ↩︎ - スクリーンリーダー
パソコンやスマートフォンの画面に表示されている文字や操作項目を音声などで読み上げるソフトウェア。視覚に障害のある人がWebサイトを操作する際にも利用される。 ↩︎ - スキップリンク(skip link)
ページの先頭などから、本文などの主要なコンテンツへ直接移動するためのリンク。キーボードやスクリーンリーダーを使う利用者が、毎回長いメニューを通過せずに本文へ移動できる。 ↩︎ - アンカータグ(
<a>)
HTMLでリンクを作るための記述。作中のhref="#main"は、「main」という識別子が付けられたページ内の場所へ移動することを意味する。 ↩︎ - リポジトリ
プログラムのソースコードや、その変更履歴を保存・管理する場所。本作ではGitによって管理されているコード一式を意味する。 ↩︎ - ローカル/ステージング/プロダクション
Web制作で使用される環境の呼び分け。
「ローカル」は制作者自身のパソコンなどにある開発環境、「ステージング」は公開前の確認環境、「プロダクション(本番)」は実際に利用者がアクセスする公開環境を指す。 ↩︎ - コミット/コミットログ
「コミット」はソースコードの変更内容を一つの履歴として記録すること。「コミットログ」は、それまでに誰がいつ、どのような変更を記録したのかを確認する履歴。 ↩︎ git log --oneline
Gitに記録されているコミット履歴を、1件につき1行の簡潔な形式で表示するコマンド。 ↩︎- デプロイ
開発・確認したプログラムやWebサイトを、本番環境へ配置して利用できる状態にする作業。 ↩︎ - CI(継続的インテグレーション)
コードの変更をきっかけに、テストやビルドなどを自動実行する仕組み。設定によっては、そのまま本番環境へのデプロイまで自動化される。 ↩︎ - ビルド/ビルド後処理
ソースコードなどを、実際にWebサイトで使用できる形式へ変換・生成する処理を「ビルド」という。その直後にファイルの最適化や書き換えなどを行う処理が「ビルド後処理」。 ↩︎ - モジュール/関数
プログラムを機能ごとに分けたまとまりを「モジュール」、その中などに定義された特定の処理を「関数」という。本作に登場する「Cleanerモジュール」は、HTMLを自動的に整理・修正するために作られた架空の仕組み。 ↩︎ - パッチ
プログラムの不具合修正や、小規模な機能改善のために加えられる変更。本作ではアクセシビリティを改善するための小さな修正を指している。 ↩︎ - ユニットテスト
プログラムを構成する小さな機能単位が、想定どおり動くかを確認するためのテスト。 ↩︎ - off-screen(オフスクリーン)
Webページ上には存在しているが、通常時は画面の見える範囲の外側などに配置して視覚的に隠しておく手法。本作のスキップリンクは、キーボード操作で選択されたときだけ画面上に現れる。 ↩︎ - フォーカス
キーボードなどでWebページを操作するときに、「現在どのボタンやリンクが操作対象になっているか」を示す状態。 ↩︎ - コンソール
開発者がコマンドを入力したり、プログラムの実行結果やエラーを確認したりするために使用する画面。本作では澪がGitなどの操作を行う端末を指す。 ↩︎ git blame
ファイルの各行について、「最後にその行を変更したコミットや作者」を調べるためのGitの機能。プログラムの特定部分がいつ、誰によって変更されたのかを追跡するときに利用される。 ↩︎- トレース/状態遷移
「トレース」は処理がどのような順序で実行されたのかを追跡すること。「状態遷移」は、プログラムやデータが処理の途中でどのように変化していったかを表す。 ↩︎



コメント