ソフトウェアの高速化と同期ずれ:「コードを書くのが速くなった」のに速くならないのはなぜか
ソフトウェアの高速化はむしろ認知のボトルネックを移動させただけかもしれず、コードレビューがAI開発プロセスを遅らせる元凶と誤解されているが、問題はただ場所を移したにすぎないのかもしれない。
日本語
コピー

ソフトウェアの加速と同期外れ
1か月ほど前、SNSにこんな投稿をした。
コードレビューがAIによる高速な開発フローを妨げる元凶だとする報告や業界関係者の声が増えている。誰かが問いかけているだろうか——これは「何が起きているのかを突き止める」という認知上のボトルネックを、ただ別の場所に移しただけではないのか、と。「レビューにもAIを」が、彼らの思い描く終着点のように見える。
返信はそれなりに来た。「ひどい話だ」という声もあれば、「実は悪くないアイデアだ」という声もあった。当時はまだ、この考えをうまく言い表す概念を持っていなかった。その後、より明晰でシステム中心の言い方を見つけた。この記事は明らかにAI(とりわけLLM)をめぐる議論から生まれているが、内容としてはむしろ構造的な論証だ。こうした技術の採用がどの種類の変化を引き起こすのか、そして業界がこれまで他の技術やプロセスで経験してきた、より広範な加速のパターンについて論じている。なので以下では、個別の技術には触れない。
ここで提示するモデルは、Hartmut Rosaが『社会加速』で示したモデルに着想を得たもの(というより、その危険なほど乱暴な簡略化)で、1 自分の観察に合わせて歪めてある。まずは一つのパターン、循環あるいは周期から始めよう。
循環、周期、活動
まず、ソフトウェアを書くという行為をめぐる線形的な表現を見てほしい。

これは簡略化だ。もっと細かく分解できる。たとえばDORAレポートのバリューストリームマッピングならこうなる。2

とはいえ、ステップを簡略化したまま、別の複雑さを示すために、もっと非線形的に描くこともできる。

上のどのステップも、より前のタスクへ後退することを意味しうるし、緊急事態は前へのジャンプを意味しうる。このモデルが論証にとって十分に細かいのか、それとも粗い見積もりにすぎないのかは重要ではない。もっと精密にもできるし、もっと粗くもできる(「write tests」のノードは軽く本一冊に膨らむ)。ここでは主に問題を示すためのものだ。
いずれにせよ、どのバージョンでも、タスクの目標は許容できる品質で、できるだけ速く始点から終点まで進むことにある。開発を加速させるという心構えでは、個々のノードに注目して(コードを書く、デバッグする、コードをレビューする)どこを速くできるかを見ることもできるし、ワークフロー全体から入って循環そのものに働きかけることもできる。
たとえばコードレビューは、自動フォーマットとlintingで速くできる。自動化されたルールチェックで規約を強制したり、ある種のやり方を止めたりする——本来なら人手でやっていたことだ。時間が節約できるうえ、より高次のレビュー内容に集中できる。こうした自動化ルールを開発環境に持ち込めば、循環全体も速くなり、フィードバックループが締まる。書いているそばから直すのであって、欠陥を積み上げてから後で時間をかけて土台を剥がして直すのではない。
並行する循環
ここまでは順調だ。問題は、この孤立した一つの循環だけでは、ほとんどのソフトウェア仕事を正確に表せないことにある。複数の人間が複数のタスクを並行して進めるだけでなく、各タスクがそれぞれ独立した循環をなし、各人も同時に複数の循環の中にいる。たとえば、あるコードベースのチケットを処理しながら、これから始まるプロジェクトの設計ドキュメントを書き、サポートチケットを手伝い、会議に出て、メンタリングで自分のキャリアを育て、リリースやステータスレポートを読んで組織の動きを追う、といった具合だ。
以下は、関連はあるが簡略化された循環のいくつかで、視覚的な補助として示す。

繰り返すが、チームの各メンバーは一日のうちに、こうした循環をさまざまな種類で複数並行して回している可能性がある。
しかしそれだけではない。複数の循環が一部のステップを共有することもある。システムのある部分でコードを書くことが、さまざまなタスクへの備えになり、あるいはそれらのタスクへの貢献度を高める様子は想像できるだろう。その部分とやり取りするコードを書く、変更する、変更をレビューする、ドキュメントを書く・レビューする、インシデントで現れうる境界ケースに敏感になる、将来のタスクをより正確に見積もる、などだ。
新しいプロダクト変更に向けてコードをどう整理するのが最善かを計画するには、コードの現在の構造についての経験がものを言うだろうし、これから向かおうとしているプロダクトの野望を把握していることも同じくらい重要だ。同様に、現在のシステム運用の課題に詳しい人の意見は、変更の優先順位を決めるときに役に立つ。こうした関心の共有が DevOps のような考え方を生んだ。その背景にあるのは、良いフィードバックと統合(物を壁の向こうへ放り投げることなく)がソフトウェアデリバリーを助けるという信念だ。
基本的に、ひとつのループは同じ共有アクティビティ群に選択的に貢献できるが、逆にいくつかのアクティビティが複数のループに貢献することもある。それらのループは異なるタイムスケール上に存在しうる:

ここでコードレビューは、コーディングループが直接レビューを受ける場でありながら、個人のキャリア成長計画を実行に移す場でもある。規範に影響を与えたり押し通したりしようとする場であり、長期的なアーキテクチャや成長パターンに関心を持つ人が、絶え間ない技術変化を認識する場でもある。
こうした共有ステップはボトルネックにもなりうるし、速度向上を相殺してしまうこともある。たとえばこうだ。毎日自転車で通勤していて片道30分かかるとする。公共交通機関や車に変えて速度が2倍になれば、週に2時間30分を節約できる。しかし体力維持のために運動が依然として必要だと考えるなら、その時間は本当に「節約」できたわけではない。節約した大量の時間を通勤以外の運動に充てるか、あるいは気づかないうちに通勤時間と引き換えに長期的な健康要因を失うかのどちらかだ。
ソフトウェアに置き換えると、こんなパターンが見えてくる。「コードは以前より速く書けるようになったが、コードレビューが新しいボトルネックになった」。明白な対処は、コードを書く速度の向上に合わせてコードレビューを速めることだ。ある程度までなら、コードレビューの_一部_は確かに最適化できる。改善によって、ある種のエラーをより確実に、より速く検出できるようになるかもしれない。linting や型チェックと同じように、理想的にはこれらはレビュー時まで残さず開発段階に前倒しすべきだ。
しかしコードレビューはエラー探しだけではない。保守性や運用上の懸念を議論し、知識と認識を広め、外部の視点を取り入れ、より広い当事者意識を育てる場でもある。こうした目的は、自動化や高速化が可能だとしても、人々がどうしても維持しなければならない別のループが存在しうることを示している。
同期と非同期
仕事の一部を最適化すると決めたなら、次のいずれかを満たせば大きな速度向上が期待できる:
- あるタスクが持ちうる複数の目的を継続的かつ深く理解し、有用でよく統合された変更を加える
- ループを分離し、共有された目的のいくつかを手放す
1つ目は難易度が高い。調査と反復、そして仕事の進め方を見る鋭い目が要ることが多い。そうでなければ、すぐに「速く走っているのに速度は変わらない」状況に陥る。ツールも手法も入れ替えたのに、実際に足を引っ張っているボトルネックはほとんど変わっていない、という状態だ。これを正しくやるということは、速度を上げようとする時点で、その変更が仕事の進め方に構造的な影響を与えると分かっていて、それに伴う揺さぶりを受け止める準備ができているということだ。
2つ目は、気づかないうちに他のループを遅らせたり壊したりしかねない。それらの用途が依然として存在するなら、新しい活動が古い活動に取って代わらなければならない。そしてそれが元のループに跳ね返ることがある(たとえばコードレビューは今のコーディングを止めるかもしれないが、後のコーディングへの入力にもなっている)。結果としてどちらも弱まるか、分離した途端にそれぞれ別のペースで進み始める。後者の効果を「非同期化」と呼ぶ。非同期化のリスクのひとつは、あるループの中で有用な、あるいは不可欠なフィードバックが、もう一方のループに届かなくなることだ。
これに対処するため(根絶するのではなく)、最適化の層には3つ目の選択肢がある:
- 「規範」や慣行を前倒しで採用し、関係者の足並みを揃え、同期への依存を減らす。
「ベストプラクティス」やプラットフォームが提供しようとするのは、おおよそこれだ。標準に従えば、コミュニケーションと意味のすり合わせを減らせる。たいていは安定した土台を提供し、複数の活動を加速させる。だが非同期化を完全に防ぐことはできない。先送りにするだけだ。
非同期化を説明するために、互いにフィードバックしあういくつかのループを見てみよう:

ここに示されているのは、運用ループとコーディングループをまたいで、レビュー時に同期する共有点だ。運用から得た経験はプラットフォームループと規範ループに還流し、運用の入力を伴うコードレビューは、そうした経験が「強制執行」される場のひとつである。3 これらの同期点を取り除けば、より速く走れるかもしれないが、各ループはしばらく独立して動き、互いに離れていくかもしれない:

二つの図に大きな違いはないが、ここで指摘したいのは、開発段階の運用側の入力(コードレビュー中など)が欠けると、コードを段階的に本番へ出す過程で、同じような暫定修正を何度も抱え込んで適用し続けることになり、しかもその間に余計な手順が挟まる、ということだ。基盤プラットフォームや共有コンポーネントが変わると、その展開の同期が遅れることがある。変更を伝える機会そのものが減るからだ。開発速度が十分に速くなり、コードが適格かどうかを示す能力が それに応じて 上がらない(つまり、より長い本番環境での時間を待たずに示せない)なら、想定外の事態が起きる可能性は高まる。
これは、二つの高レベルなループの間で、共有された一つのタスクをめぐる同期の一形態にすぎないことを覚えておいてほしい。実際の仕事にはもっと多くのループ、もっと多くのノード、もっと多くの接続があり、チームや役割の内部と相互の間には、さらに細かな同期点が数多くある。実際のループはより堅牢かもしれないが、より予測しにくくもある。複数の同期点を持つループは、そのいくつかを外して速くなったように見える。だが、残った少数の同期点が遅くなる(追いつくために)か、取り消される(速く走るために)までの話だ。
同期点から得られるものは、関わる主体によっても違う。たとえば、あるエンジニアはそこから許可(と保護)を得て、別の誰かはそこから感知を得て、あるチームはそれを口実にコンプライアンスを強化し、経営層は自分たちがこの件に責任を負っていると宣言する。
スペクトルの両端は容易に想像できる。片端は、あらゆる想定外を避けるためだけに同期の手順に足を取られている組織。もう片端は、並行する仕様の網に絡まり、同じものの何世代もの版が一度も廃止されずに全部同時に保守されている組織だ。そもそも同期がまったく行われていないからだ。
ループをまたいで積み重なるドリフトは不整合を生む。メンタルモデルは遅れ、変化と圧力に追いつくために近道を強いられ、起きたと思っていることと実際に起きていることの差は広がっていく。⁴ それはサブシステムを引き裂き、弱らせ、事故——つまり想定外の急速な再同期点——を招く。
私は事故を急速な再同期点と見なしている。事故とはたいてい、同期が極限まで外れた場所だからだ。事故対応は、いつもの構造をいったん止め、優先順位を素早く組み替え、ロードマップをひっくり返し、(理想的には)複数チームの多くの人に、物事がどう動き、どう壊れるかについての理解を突然更新させる。いつものサイロはいつもどおりには機能しない。このこと自体が、同期が行き過ぎた後は強制的に修復するしかないと示している。
Rosa が著書で指摘するように、この加速はしばしば、土台にある安定したシステムが支えられる速度を超える。するとそれらのシステム自体が障害になる。インフラや制度が支えるシステムが停滞している、あるいは自分たちを縛っていると感じられ始め、代替を探し始めると、それらは捨てられるか解体される。
[加速は]制度的な一時停止と、背景条件の保障としての維持管理によって成り立っている。これは近代の加速史における基本原則の一つであり、その成功の根本的な理由でもある。[制度]自体は変化から免除されているため、信頼できる期待、安定した計画、予測可能性を生み出すのに役立つ。[……]こうした安定した期待の地平があって初めて、長期の計画と投資が理性的なものになり、それらは多くの近代化の過程にとって不可欠である。さらなる、いわば「境界のない」加速は、こうした制度と志向を侵食し[……]、それによって自らの前提と、後期近代社会全体の安定性を弱めかねない。こうして近代性(加速)の構想は、反近代的な減速運動よりも大きな危険に陥る。
同期を減らす必要性は、同期そのものがもう起きなくていいという意味ではない。ランニングマシンは決して遅くならない。システムの参加者はしなやかさを示し、要求に応えるよう実践と規範を作り替えていかねばならない。新しいリズムが新しい課題をもたらすとき、これはとりわけはっきりする。ここまで連れてきてくれたやり方では、この先を支えられない。またループをまとめて作り替える必要がある。
この観察で面白いのは、ある場所での減速が、別の場所での加速を戦略的に可能にするという点だ。
この特定の遅さは、良い遅さか、悪い遅さか
私の見るところ、ほぼ間違いなく、「コードを書く」ループを一周するほうが、「自分のアーキテクチャの帰結を受け止める」ループを一周するよりずっと速い。後者は通常、十分なフィードバックを得るまでに複数の開発サイクルをまたぐ。コードは毎時リリースできるが、あらゆるエッジケースが出そろうには軽く数週間かかる。
システム設計やソフトウェアアーキテクチャのレベル(「リージョン障害に耐える複式簿記が必要だ」)にいるときは、システムの過去を理解し、現状と限界をそれなりに把握し、将来の課題を見越す力が要る。それによって変更をどの方向へ押し出すかを決める。これは日々の変更(「この機能は台帳にトランザクションを一つ足す必要がある」)とは別のループであり、もっとも、両者は互いに関係している。
つまり、歴史のない新しいコードベースを相手にしていて、その未来がそもそも存在しないかもしれない(短期のプロトタイプや実験など)なら、互いに切り離された短いループを持てる可能性が高い。何千人ものユーザーを抱え、何年も積み重なったパターンとエッジケースがあり、さらに組織文化の全体と戦うか歩調を合わせるかしなければならない大きなプラットフォームを相手にしているなら、短いループを根拠づけるために長いループに頼ることになる。
ループ間の接続は時間とともに積み重なっていく。短いループを愛する人は、自分がこれほど遅くなり始めたことにひどく苛立つ。
しかし、不可逆な決定には、可逆な決定よりもはるかに周到な計画と十分な情報収集が必要であり、したがって不可避的に時間がかかる。実際、他の条件が同じなら、次のことが成り立つ。ある決定の時間的射程が長いほど、与えられた実質合理性の基準に照らしてその決定を下すのに必要な時間は長くなる。ここに現代の時間発展のパラドックスが現れる。私たちの決定の時間的射程は広がり続けているように見えるのに、それらの決定を下すのに必要な時間の資源は同じ幅で消えている。
したがって、ある人々は極めて速く進んでそこから利益を得て、別の人々は追いつかねばならず苦労している。これはある程度、私たちが同期と非同期の問題にまだうまく対処できていないことを示しているのかもしれない。だが、ある種の仕事の成果が、速く動く人々が依存する安定性を必要としたり提供したりするとき、人々が意図的にペースを落とさざるを得ないからでもあるだろう。背景レベルの速い反復——たいていはエコシステムの一部として当然視される——は、すべての参加者に加速の必要性をさらに強いる。
加速的思考モードでは、加速できるものはすべて加速しようとする。最適化、技術革新、プロセスの改善、規模の経済、あらゆる手段を使って。これはRosaの、加速が自己強化的に進むという全体的な主張と響き合う。⟦5⟧Rosaが示した多くの論点のひとつに、次のようなものがある。加速への要求と、そこから生じる圧迫感(すべてが速くなり、ついていくのは難しくなる。だから自分ももっとやらなければならない)を、システムの作動の仕方を形づくる時間の_構造_として捉える必要がある、というものだ。技術革新は加速の機会を提供し(多くの場合、経済的な力に駆動されて)、そうした革新が社会組織の構成の仕方を変え(多くの場合、専門分化を通じて)、それが今度は、達成可能なことへの期待の高まりと、世界が絶えず加速しているという感覚を通じて、さらなる加速への要求を呼び起こし、技術革新を促す。以下は彼が著書で示した図だ。

私たちは加速をふつう、技術進歩の_結果_として理解する。だがここでの見方は、_時間構造の加速_それ自体が社会を(そしてもちろん私たちの業界も)形づくるメカニズムになっている、というものだ。加速の時期には、さまざまな形の抵抗が伴うことも多い。そのいくつかは、状況を再び掌握しようとする反射的な反応にすぎない(これ以上適応サイクルを強いられたくないという思いから)。だが、有益な減速もある。安定性をもたらし、他の加速の取り組みの時間的な視野を広げてくれるものだ。
生産性が何を指すのかをはっきり説明できるテック企業はほとんどないが、それを絶えず改善しようとする衝動は確かに存在する。仕事がどのように行われているかをより深く理解しない限り、おそらく次のような状況が続くだろう。つまり、新しい技術が自分の仕事にどう影響するかについて人々の言うことはばらばらで、それはランダムな作業ループのランダムな部分をやたらに削ったり足したりしているだけのことだ。この全体的な力学は、ある種のタスクをはるかに速くこなせるのに、全体として生産性が上がったとは感じず、時間を節約できたどころか仕事が増えたと感じる人がいる理由を、うまく説明してくれると思う。人が主張しているのがどの種類の減速なのか、見分けるのが難しいこともある。だが、減速を求める声をすべて無益なラッダイトの愚痴として片づけるのはよくない。6 先を見据えるなら、より有用なのは、自分が代替品を用意しないまま自分の土台を蝕んでいないか点検することかもしれない。
これは何の役に立つのか?
システム思考は往々にして、コンポーネントそのものではなく、コンポーネント間の相互作用に注意を向けることを求める。ここで提示したモデルがするのは、そうした相互作用に時間の次元を加えることだ。仕事の中で完了するタスクや活動は、ソフトウェアを作るという営みの構成要素と見なせる。これらのループの間の、そして人と人の間の同期の要求とフィードバック経路が、それらの交差点を描き出す手段を与えてくれる。
結局のところ、ループモデルでさえ、粗い過度な単純化にすぎない。人は置かれた状況に影響され、同時に状況に影響を与え、その相互影響は絶え間なく続き、明確に定義されたタスクやシーケンスに押し込めることはできない。現実はずっと乱雑だ。このモデルは、どんな加速も孤立して起きることはないと気づかせてくれる道具として使える。あらゆる取り組みには、同期が外れる可能性が、そして関連するループがそれに伴って組み替えられる可能性が含まれている。ある意味で、目指すべきは具体的な問題を見つけることではなく、リズム上の潜在的なミスマッチ——適応し、ペースについていくうえでの困難を示すもの——を見つけることだ。
どの分析の立場を取るかは影響を持つ。タスクを孤立して最適化すると、単一のループ内ではそれなりの局所的な成果が得られることがあり、時にはより広い範囲でもうまくいく。だが、各ループを寄せ集め、その中のごちゃごちゃも一緒に見たほうが、何を加速する価値があるか(あるいは、別の部分を速くするために何を遅くするか!)、落とし穴がどこにありうるか、そして調整の必要性がどこへ波及し、最終的に何になるかが見えやすくなる。実験と速度向上の試みは、西洋社会に劇的な変化が起きない限り、誰かが必ずやるし、これからも続くだろう。実験するときに、結果について何を見るべきかをもっとはっきり分かっているのは悪いことではない。
1:この本の要約や抜粋はまだノートに公開していないが、読めば自分の枠組みの作り方に深く影響すると分かる類の本であることは間違いない。そして、これを読んでいるところを見た周りの全員に約束したとおり、読み終えたら私はとてもうるさくなる。さあ、来たぞ。『社会加速:近代性の新理論』を手に入れよう。Columbia University Press、2013年。
2:原典記事、図50は75ページ。
3:この例は、同期点が不可欠だとか、唯一の同期点だと言いたいわけではなく、それが実在し、影響を及ぼすと言いたいだけだ。これは私の個人的な経験から来ているが、コードレビューやRFCレビューでも同期点を何度も見てきた。仕事がチーム間のサイロの境界をまたぎ、プロジェクトが組織レベルで大きくなるたびに現れる。
4:これは技術的負債のような概念の成因のひとつとも見なせるのではないかと思う。技術的負債は、ある解決策を検証することと、それをエンジニアリングとして持続可能に維持することの間の乖離として理解できる。
5:これは認知システム工学におけるストレッチシステムの法則とも関係していると思う。概して、これは異なる学問分野が似た現象に対して似ているが異なる定式化を与えているケースのひとつなのだろう。
6:ラッダイト主義の話が出たので、慣例に従ってBrian MerchantのBlood in the Machineに触れておかなければならない。この本はラッダイト主義の歴史的な文脈を見事に復元している。産業革命初期に労働者が自分の仕事の主導権を守ろうとした運動だ。ラッダイトたちは新しい自動化技術すべてを組織的に抵抗したり破壊したりしたわけではなく、劣悪な労働条件の工場主を狙い撃ちし、他の工場主には手を出さなかった。