
穏やかなソフトウェア:ちらつくトグルを葬り、チャットに「今どのモードか」を語らせる
GeekByeの小さなリリースが2本。目玉機能はなし。ただ、クリックのたびにちらついていた設定トグルを直し、会議モードで答えたのかコーディングモードで答えたのかをようやく教えてくれるチャットアシスタントを作った。「穏やかなソフトウェア」が実際にいくらのコストで成り立っているのか、その一つひとつのディテールの話だ。
すべてのリリースが武勇伝になるわけではない。中には、ひっそりと間違っていた小さくてほとんど目に見えないものを並べただけのものもある。何かをクラッシュさせるわけではないが、アプリを少しだけ信用できなく、少しだけ落ち着かなく、少しだけ「自分のものではない」と感じさせる、そういう類のものだ。GeekBye v2.0.3 と v2.0.5 はまさにそれだった。新機能はなし。あるのは穏やかさだけ。
「穏やかなソフトウェア」というと気分のことのように聞こえる。だが実際には、具体的な決断の積み重ねだ。ここではそのうちの3つを紹介する。
決めきれなかったトグル
ある設定トグル — オーディオ復旧を制御するもの — が、クリックすると点滅していた。オンにタップすると、コンマ数秒だけ「オン」を表示し、そのあと「オフ」に跳ね戻り、それから落ち着く。ごく小さな視覚的なつっかえだが、目はそれを捉えてしまう。そして「このアプリは自分が何をしているのか分かっていない」と読み取ってしまう。
原因は**楽観的更新(optimistic update)**と呼ばれる典型的なUIのレースだ。トグルをクリックしたとき、アプリには2つの選択肢がある。設定が実際に保存されるのを待ってからスイッチを更新する(正しいが、もたつく感じがする)か、スイッチを即座に切り替えてバックグラウンドで保存する(一瞬で反応する感じがする)か。GeekBye は後者を採っていた — 即座に切り替える — が、同時に保存済みの値が返ってきたときにそれを読み直してもいた。つまり順序はこうだ。あなたがクリックし、スイッチが楽観的にオンへ切り替わり、一拍遅れて本物の保存値が到着し、1フレームだけ両者が食い違ってスイッチが目に見えて跳ねる。
修正は、楽観的な値と確定した値をきれいに一致させることだ — スイッチは自分が選んだ状態にコミットし、保存が確定したときに自分自身を疑い直すのをやめる。それと並んで、v2.0.3 は地味なコピーの見直しも行った。オーディオ復旧の説明文は隣の行と揃うように刈り込まれ、設定の文言はより人間らしくなった。どれも機能ではない。だがそのすべてが、「作り込まれた」と感じる設定パネルと、「気を配られた」と感じる設定パネルの違いを生む。
立ち去らないツアー
新規ユーザー向けのツアーは、ちょうど一度だけ役に立つ。問題はその「一度きり」さだ。一度見てしまえば、消せないオンボーディングのオーバーレイは、「このアプリはまだあなたを新参者だと思っている」と告げる散らかりになる。v2.0.3 はその両端を直した — プロダクトツアーは非表示にできるようになり、もし本当にまた見たければツアーを再生するボタンがある。役に立つときは見せ、そうでないときは道を空け、ユーザーが自分の条件で呼び出せるようにする。ユーザーがもう新参者ではないと認めること自体が、穏やかさのささやかな一形態なのだ。
自分の考え方を言おうとしないアシスタント
これが実際に重要だったもので、私たちがずっと追いかけていた、より深い問題につながっている。
GeekBye のアシスタントは、文脈に応じて違う答え方をする。会議中なら「これについてどう思う?」には、会話的で会議を踏まえた答えが返るべきだ。コーディングセッションなら、同じ言葉には技術的な答えが返るべきだ。だからバックエンドは質問を適切なフレームに通す — それを会議モードあるいはコーディングモードと呼ぼう — そしてそのフレーム付けが、返ってくる答えを本当に変える。
やっかいなのは、アプリがどのモードで答えたのかを一度も教えてくれなかったことだ。だから、会話的な質問にコーディング寄りの返事が返ってきたとき — あるいはコードの質問に雑談しているかのように答えたとき — それは AI がただ間違っているように見えた。間違っていたのではない。もう一方のモードにいたのであり、あなたにはそれを見る術がなかった。隠れた状態が、正しい振る舞いを壊れているように見せていたのだ。(モードのロジックそのものはもっと大きな仕事だ — コーディングセッション中にコード以外の質問へどう答えるかを決めるオーケストレーターがそれだ — だが、下した判断を見られないなら、その一切があなたの役には立たない。)
v2.0.5 はチャットに読み取り専用のモードチップを追加した。アシスタントが会議モードで答えたのかコーディングモードで答えたのかを示す小さなラベルだ。読み取り専用であることこそが肝心だ — それはあなたが設定するコントロールではなく、システムがすでに下した判断をのぞく窓なのだ。なぜ答えがしっくりこないのかを当てずっぽうで探すのはもう終わり。その答えがどのフレームから来たのかを見ればいい。
チップを誠実にするディテール
ここから伝える価値のあるエンジニアリング上の選択がある。というのも、これは後付けの表示と信頼できる表示を分けるような類のものだからだ。
モード情報は、ストリームされる応答の一部としてバックエンドから来る — サーバー送信イベントストリームの中の小さな mode フレームだ。だが、この機能より前の古いクライアントは mode フレームが何なのか分からない。よくても無視し、悪ければ何か文字化けしたものを描画してしまう。そこでクライアントは自分の能力を宣言する。「私はモードチップの表示方法を知っている」と伝えるリクエストヘッダーを送り、バックエンドはそれを送ってきたクライアントにだけ mode フレームを出す。古いクライアントは扱えないシグナルを受け取ることは決してなく、新しいクライアントは求めたフレームをきっちり受け取る。
これはヘッダー1つのハンドシェイクであり、「機能を追加した」と「まだ更新していない誰も壊さずに機能を追加した」の違いそのものだ。同じレビューの中で、残りの振る舞いも引き締めた。チップはリセット時と会話コンテキストが変わったときに自分自身をクリアし(古びたモードラベルはそれ自体が小さな嘘だ)、それがふさわしいチャットビューでのみ表示される。
2本の「機能なし」リリースが教えてくれたこと
- ちらつきは信頼のシグナルだ。 点滅するトグル、跳ねるスイッチ、1フレーム長く居座るラベル — どれもクラッシュという意味でのバグではないが、そのどれもが「このアプリは完全には制御しきれていない」とささやく。穏やかさとはそのささやきの不在であり、それはレース条件を一つずつ潰して初めて手に入る。
- 隠れたモードは、正しい振る舞いをバグに見せる。 アシスタントは正しいことをしていたのに、そのせいで非難されていた。純粋に、ユーザーがそのフレームを見られなかったからだ。システムが出力を変える不可視の判断を下すなら、その判断を見せること — 読み取り専用のチップが、しばしば修正のすべてになる。
- 新機能は、使えないクライアントには見えないべきだ。 能力ヘッダーのハンドシェイクによって、モードシグナルはそれを描画できるクライアントにだけ届く。後方互換性とは、あとから付け足すバージョンチェックではない。クライアントが自分の理解できることを告げ、サーバーがその答えを尊重することだ。
これらは、誰も変更履歴の見出しには書かないリリースであり、GeekBye v2 が今のような感触である理由の大きな部分だ。この穏やかさが築かれた土台となるリリースについては、バージョン2が実際に何を要するか(v2.0.0)を参照してほしい。それと並行して行った信頼性の作業については、アプリが自分自身をDDoSした日(v2.0.1)とAI議事録が会議の途中で録音を止める理由(v2.0.9)を。