mtx2s’s blog

エンジニアリングをエンジニアリングする。

技術的負債は開発者体験を悪化させる

ソフトウェアエンジニアにとって、技術的負債が増え続けるソフトウェアプロダクト開発現場に身を置くことがどれほど苦痛なことであるか。エンジニアリング組織のマネジメントを長年担ってきて、それは強く感じるところだ。

中途採用の選考プロセスに面接官として参加し、これまで数多くの退職理由を見聞きしてきた。その中で、レガシーシステムリファクタリング・リアーキテクティング・リライトできないことへの不満を理由として挙げるエンジニアは多かったように思う。裏を返せば、自社のソフトウェアプロダクトが技術的負債にまみれたまま放置されているなら、優秀な人材が他社に流出するリスクがあると認識すべきだ。

本稿では、技術的負債と開発者体験の関係について紐解くとともに、それに対してソフトウェアエンジニアリング組織を預かるマネージャーが取るべき行動について考えてみたい。

※これは、Engineering Manager Advent Calendar 2021 の21日目の記事です。

バックログアイテムの分類にみる不可視領域と開発者体験の関係

下の図は、バックログに含まれるアイテムをカテゴライズし、4色に色分けしたものだ。フィリップ・クルーシュテン(Philippe Kruchten)によって2009年頃に考案され、"What Colo(u)r is Your Backlog?" というタイトルで発表された。ここでは2013年に彼のブログ記事で紹介されたバージョンに基づいている。技術的負債(technical debt)を返済するためのアイテムは、右下の黒いエリアにカテゴライズされている。

Four Colors in a Backlog

四象限の左側にカテゴライズされたバックログアイテムは、ソフトウェアプロダクトの「振る舞い(behavior)」を変える・正すものだ。振る舞いは目に見える(visible)。「振る舞いを変える/正す」目的は、「ユーザー体験(UX, User eXperience)」を向上・改善することだろう。

対となる右側にカテゴライズされたバックログアイテムは、ソフトウェアプロダクトの「構造(structure)」を変える・正すものだ。構造は目に見えない(invisible)。「構造を変える/正す」目的は、振る舞いの「変更容易性(modifiability)」を向上・改善することだろう。

「振る舞いを変える/正す」のはエンジニアの役割なのだから、ソフトウェアプロダクトの変更容易性の良し悪しは、エンジニアの体験に影響を与える。「構造を変える/正す」意思決定が適切になされないと、「開発者体験(DX, Developer eXperience)」が悪化することが想像できる。

振る舞いと構造、ユーザー体験と開発者体験

フィリップ・クルーシュテンの同ブログ記事にも書かれているように、右上の黄色いエリアである「アーキテクチャ(architectural, structural features)」にカテゴライズされるアイテムに手を付けない選択は、右下の黒いエリアである「技術的負債」にカテゴライズされるアイテムの増加につながってしまう点も留意すべきだろう。

Failure Demand という利子の支払いに追われるエンジニアの疲弊

変更容易性の悪化が、ソフトウェアデリバリのリードタイム(lead time for changes)を悪化させることは言うまでもない。マーティン・ファウラー(Martin Fowler)によれば、コード品質を高く保つコストを削ることで生産性を上げる試みが通用する期間、いわゆる "design payoff line" に到達するまでの時間は、せいぜい数週間以内だと言う

それに加え、複雑化しすぎたコードを変更することは、多くのバグを生み出しやすい。それがリリース後の障害となり、変更失敗率(change failure rate)を押し上げる。こうしてエンジニアは度重なる障害対応に追われ、精神をすり減らし、疲弊していく。

このような、本来やるべきことをやらなかったために後から強いられる労力のことを "failure demand" と言う。技術的負債のメタファーで言えば、「利子を支払う」といったところだろう。この利子の支払いは、エンジニアが新たな価値を生み出す "value demand" のための時間を奪い、開発現場は益々混乱していく。書籍『LeanとDevOpsの科学』によれば、ローパフォーマーに分類される組織は、ハイパフォーマーに対して「予定外の作業や修正作業」にかかる時間比率が6ポイントも高い。

新たな作業と予定外の作業等の比率

経営者やビジネスマネージャーからすれば、少しの振る舞いの変更に、なぜこれほどまでに時間がかかるのか理解できない。その上、障害まで頻発する。こんなことが続けば彼らは、エンジニアに対して不満と不審感を抱くようになる。その感情はエンジニアにも伝わる。エンジニアはきっと、やり切れない気持ちになるだろう。

開発者体験の悪化が組織を単なる集団に変えていく

2012年に行われたGallupの調査では、従業員エンゲージメント(employee engagement)の高さで上位4分の1に入る企業は、下位4分の1の企業に対し、顧客評価で10%, 生産性で21%, 収益性で22%上回った。また、欠勤は37%, 離職率は25%から60%、品質上の欠陥は41%下回った。

従業員エンゲージメントによるパフォーマンスの比較

従業員体験(EX, Employee eXperience)は、従業員エンゲージメントに影響すると言われている。開発者体験も同様だろう。

先述したように、「構造を変える/正す」ことを軽視することは、リードタイムや変更失敗率といったソフトウェアデリバリのパフォーマンスを悪化させる。しかし、従業員エンゲージメントに関する調査結果からもわかるように、開発者体験が悪ければ、ソフト面からビジネス成果や組織の持続可能性を押し下げることになる。

これは、チェスター・バーナード(Chester I. Barnard)の言う組織の3要素のうちのひとつである「協働意思(willingness to serve)」が失われるからだろう。組織が組織たり得ず、単なる人の集まり、つまりは「集団」の状態に陥ってしまうのだ。

技術的負債が増え続ける背景

このような問題を生み出す背景のひとつには、「振る舞いを変える/正す」ためのバックログアイテムばかりが優先順位の上位に並んでいるという状況がある。「構造を変える/正す」ためのバックログアイテムに着手される日は、なかなかやってこない。

優先順位の決定権者にとっては、ユーザー体験を高め、プロダクトのユーザー価値を高めることこそが優先課題だ。それが、プロダクトのビジネス価値向上につながる道だからだ。ソフトウェアの「構造を変える/正す」ことに取り組んだところで、少なくとも短期的にはビジネスに良い影響を与えるように感じられない。そんなものは少しでも後回しにして、「振る舞いを変える/正す」ことに集中したい。

エンジニアの不満は主にここに集中しがちであるが、技術的負債が増え続ける主たる理由は本当にこれだけだろうか。

それを知るためには、技術的負債と呼ばれるコードを自分の目で確かめると良い。そうすると、別の根深い問題が潜んでいることに気づく。とんでもなく低い技術レベルで書かれたコードが数多く紛れ込んでいるからだ。

書籍『How Google Works』によれば、Google(Alphabet)社には、「採用の質を犠牲にしてまで埋めるべきポストはない」という不可侵な黄金率があるという。採用において、速さか質かという選択肢が迫られる場面では、必ず質を選ぶことで優秀なエンジニアの獲得にこだわる。

しかし多くの企業では、採用でそこまでの質を追求できないのが実情だろう。世界屈指のテック企業と比べたら、そもそもの応募者数が違う。結果、社内のエンジニアのスキルは玉石混交、優秀なエンジニアもいれば、プログラミング言語仕様やプログラミングパラダイムを十分に理解していないエンジニアも存在することになる。社内にエンジニアが増えていく企業フェーズでは特に、こういったことが起こりやすい。

そんな状況で、均一的に質の高いコードを維持することは難しい。エンジニア育成にも時間がかかる。数少ない優秀なエンジニアだけでこの状況をカバーすることは不可能だ。

つまり、技術的負債が増え続ける主な背景はふたつ。ひとつは、返済する意思のない無計画な借金を積み重ねること。もうひとつは、一度の借り入れ額が大きくなりやすい問題(技術スキルの低さ)を抱えていること。

マーティン・ファウラーは、前者を「意図的(deliverate)」かつ「無謀(reckless)」な負債と呼び、後者を「無自覚(inadvertent)」かつ「無謀(reckless)」な負債と呼んだ。下の四象限の左上と左下がそれにあたる。

技術的負債の発生理由に関する分類

コード品質にもSLOとエラーバジェットを

さて、まずは「意図的」かつ「無謀」な技術的負債への対処方法を考える。

いつまで経っても優先順位の上がらない「構造を変える/正す」ためのバックログアイテムに着手することを、関係者から合意を取り付けようとする開発チームの交渉は、なかなか上手くいかないことが多い。そうであればその大役は、マネージャーの責務であると考えるべきだろう。

しかし、その実践はなかなかに骨が折れる仕事だ。関係者との力関係も影響するし、強力で洗練された説得力も必要になる。その上、こういった交渉の必要性は一度だけでなく、度々発生するであろうことを考えると、対応コストが高すぎる。マネージャーが開発プロセス上でのボトルネックになりかねない。どこかに優先順位付けのための良いバランスツールはないのだろうか。

SRE(Site Reliability Engineering)に、「エラーバジェット(error budget)」というものがある。例えば、あるAPIへのリクエストの95%のレスポンスタイムが400ミリ秒以下であることをSLO(Service Level Objective)としていた場合、400ミリ秒を超えるリクエストが5%未満の間はサービスレベルとして許容できる。この許容幅をエラーバジェットと言う。この考え方が、技術的負債についても応用できそうだ。

エラーバジェットの優れている点は、システムの変更を担う開発サイドと、システムの信頼性を担う運用サイドの対立を、両者の力関係ではなく客観的なメトリクスで解決した点だ。エラーバジェットが枯渇するまではシステムを自由に変更できるが、許容値を超えると機能リリースを止め、信頼性回復に注力する。

このSLOやエラーバジェットのようなものを、コード品質にも取り入れたい。コード品質は静的コード解析ツールなどを利用してメトリクス化できる。そのメトリクスを使ってSLOを策定し、バックログの優先順位決定権者と握っておくのだ。そうすれば、「振る舞いを変える/正す」ことによって生じた技術的負債の量がバジェットを超えた時、開発チームは、「構造を変える/正す」ことに着手できるようになる。

なお、既存のソフトウェアプロダクトのコード品質をメトリクス化すると、最初はひどい結果になる。導入初期段階でのSLOは低めに設定し、徐々に理想的なラインに近づけていく運用にすると良いだろう。

さてこれで、我々マネージャーはコストの高い交渉業務から解放された。やるべきことはただ、この説得力の高いバランスツール導入への同意を関係者から取り付けるだけとなった。

負債ベースラインと負債上限によるポリシー策定

書籍『リーン開発の現場』の著者でもあるヘンリック・クニバーグ(Henrik Kniberg)が、技術的負債のマネジメント手法について、「負債ベースライン(debt baseline)」 と 「負債上限(debt ceiling)」による許容範囲を設けることをブログ記事で提案している。技術的負債がこの許容範囲内に入るようマネジメントしようというものだ。負債上限を超えたら全ての機能開発を止めて、クリーンアップに専念する。この手法が、コード品質のSLOやエラーバジェットの導入にちょうど合う。

負債上限と負債ベースライン

ベースラインがある理由は、技術的負債の最後のひとつひとつを取り除いてゼロにする労力が大きすぎるからだ。

また、グラフがノコギリ歯状になっている理由は、振る舞いを変更するバックログアイテム、つまり新機能が提供する価値を仮説だと考えているからだ。リリース後の検証結果によってはその機能の提供を中止するかもしれないし、大幅に方向性を変えるかもしれない。それを前提とすると、仮説である間はコード品質を求め過ぎるのも非効率だ。検証によって価値が証明されてからクリーンアップするのが効率的だろう。これをサイクルとして繰り返すことが、ノコギリ歯で表現されている。それでも取りこぼしがあるだろうから、少しずつ技術的負債が増えていくという構図だ。

ちなみにここでは、素早く仮説検証するために生じた、数日から一週間以内の技術的負債を「良い技術的負債(good technical debt)」と呼んでいる。「意図的」かつ「慎重」な負債にカテゴライズされるものだろう。

技術的負債やコード品質のメトリクス化は、先述の通り静的コード解析ツールを利用するのが良いと思うが、そのメトリクスも完全なものとはならない。ヘンリック・クニバーグの同ブログ記事にもあるように、現場エンジニアの意見を取り入れるのも良さそうだ。

高い保守性というコーディング指針の言語化

次に、「無自覚」かつ「無謀」な技術的負債への対処方法を考える。もちろんエンジニアの技術力の底上げも必要であるが、本稿の趣旨とは外れるためここでは触れないこととする。

コード品質に対するSLOやエラーバジェットの導入は、コード品質の可視化と基準を明確にする。しかし、どのようにしてその基準を達成するのかについては明らかにしていない。つまり、どのように「コーディング」すれば、求めるコード品質となるのかが不明瞭だ。

残念ながら、静的コード解析のメトリクスが良いスコアになったからといって、それが必ずしも「コード品質が高い」ことを表しているとは言いきれない。それでなくても数値目標は、自己目的化しやすい。数値を追いかけるだけになってしまっては、目的は達せられない。どういうコードが「品質が高い」と言えるのか、それはどのようにコーディングすれば到達できるのか、開発チームと共同で言語化し、指針を明確にすべきだろう。

技術的負債をクリーンアップする方法と言えば、「リファクタリング(refactoring)」が真っ先に思い浮かぶ。指針を検討する上で、これが糸口となりそうだ。

リファクタリングの目的は、コードの変更容易性の向上・改善に加え、「理解容易性(understandability)」を向上・改善することだ。コードを変更するには、コードを読み、変更対象となる箇所を特定する必要がある。理解容易性が高いコードは、この探索活動の難易度を下げてくれる。

そして、リファクタリングには自動テストが付きものだ。変更後のコードが仕様通り動くことを簡単に確認するためだ。ここで、「テスト容易性(testability)」が向上する。

このように、リファクタリングによって得られる効果、すなわち「品質の高いコード」とは、変更容易性・理解容易性・テスト容易性が高いコードに他ならない。バリー・ベーム(Barry Boehm)らは、これらの品質特性をまとめて、「保守性(maintainability)」と呼んだ

保守性と3つの品質特性

目指すべき品質の高いコードとは、変更容易性・理解容易性・テスト容易性を兼ね備えた保守性の高いコードであることは分かった。これは、構造を変える・正すためにリファクタリングする時だけではなく、振る舞いを変える・正すためにコードを書く時にも言えることだ。そして同時に、お馴染みの手法や活動の目的も次のように明確になる。

  • 変更容易性・理解容易性・テスト容易性の高いコード設計を導く「TDD(Test-Driven Development, テスト駆動開発
  • テスト容易性を上げる「テスト自動化(test automation)
  • 保守性の高さを観点とした「コードレビュー」、または「ペアプログラミング」「モブプログラミング

特に、いくつかのチームリーダーやマネージャーにコードレビューの観点についてヒアリングしたところでは、明確な基準が存在していないケースが多かった。それでは一貫したコード品質は得られない。保守性の高いコードとはどういうものであるかをチームで具体化し、それを観点とするレビューが実施できる状態を目指したい。

なお、「どのようにコーディング」コーディング指針やモジュールの依存関係に関するルールなどをテストコードにすることもできる。こちらについては、記事『ArchUnitでアーキテクチャをテストする - mtx2s’s blog』に書いた。

mtx2s.hatenablog.com

継続的インスペクションによるコードレビュー負荷の軽減と手戻りの防止

ところで、コードレビューには2点ほど問題があると考えている。

ソフトウェアデリバリのリードタイム改善を進めていた時に気付いたのだが、コードレビュー待ちで止まってしまうワークアイテムがそれなりに多い。レビュアーが複数のレビューを抱えていたり、自身もコーディング中のワークアイテムを抱えていたりして忙しいからだ。

ある調査によると、コードレビューの効果を高めたいなら、「一度にレビューするのは400LOC未満」「一時間あたり500LOC以下」「一度に60分以上のレビューをしない」のが良いらしい。それなりに時間がかかる。忙しい身で品質の高いレビューは難しい。これが一つ目の問題だ。

二つ目の問題は、コードレビューがコーディングの最後に行われる点だ。大きな問題が見つかった時の手戻りが痛い。

これらの問題の対策として、ペアプロやモブプロも良いのだが、あわせて「継続的インスペクション(continuous inspection)」の導入も検討したい。せっかく静的コード解析ツールを導入したのなら、それをCIのパイプラインに組み込み、コーディング中、プルリク時、ビルド時に自動で検査すれば良い。そうすることで、レビューによる負荷も軽減されるし、コーディング段階からコード品質を検査することが可能になる。

技術的負債の原因の多くはアーキテクチャ選定の失敗

Software Engineering Institute(SEI)が、大企業3社(39ビジネスユニット)で働くソフトウェアエンジニアとアーキテクトを中心とする1,831名を対象に行った調査によると、技術的負債の原因として回答者に最も多く選択されたのが「アーキテクチャ選定の失敗」だった。

下のグラフは、14の選択肢をプロジェクトにおける負債の量でランク付けするよう求めた結果を、1位、2位、3位のいずれかに選んだ回答者の数で集計したものだ。

技術的負債の原因ランキング

アーキテクチャを入れ替えるには、大規模なリファクタリング、いや、おそらく「リアーキテクティング(rearchitecting)」が必要になる。

リアーキテクティングは時間がかかる上に、場合によってはその実施期間中、新機能開発を止めなければならないこともある。プロダクト開発に対してそのような厳しい制約を課すバックログアイテムの着手には、優先順位決定権者も簡単には許可を出せない。

しかしそれは、提案したリアーキテクティング計画の着手時期が早すぎるからだ。ビジネスには四半期や半期、通期ごとに目標がある。優先順位決定権者は、リアーキテクティングの実施が目標達成に影響を及ぼすことを危惧している。

だから、リアーキテクティングの実施を、目標達成に影響を及ぼさない時期にプロットすれば、意外とすんなり受け入れられる。次の目標策定時の計画に含められるからだ。そのためにもリアーキテクティング実施計画は十分に前もって準備を進めておきたい。

もちろん、リアーキテクティングを成功させるためには、十分な技術スキルとドメイン知識を持ったアーキテクトの存在と、その設計思想を適切にコードに変換できる能力がチームに求められることは、言うまでもないだろう。

最後に - エンジニアが組織やプロダクトに誇りを持つために

過去に、私がマネジメントすることになったばかりの組織のエンジニア数名に対して「もし、開発業務として自由な時間があるなら何がしたい?」と聞いたことがある。彼らの答えは概ね、担当する既存プロダクトのリファクタリング・リアーキテクティング・リライトのいずれかだった。これは、冒頭に書いた退職理由と同じだ。彼らは自分たちが作り上げたソフトウェアプロダクトやチームにも誇りを持っていなかった。むしろ否定的だった。

私は、自らが担当するエンジニアリング組織のミッションを、組織としての「プロダクト開発能力の差異化」だと定義している。市場においてプロダクトの優位性を獲得することに高い再現性のある組織を目指していると言えば伝わるだろうか。

mtx2s.hatenablog.com

その開発能力の源泉となるのは、チームやプロダクトに誇りを持って開発を続ける優秀なエンジニアたちだ。技術的負債の放置は、リードタイムや変更失敗率が悪化するというソフトウェアデリバリ面だけでなく、開発者体験、言い換えれば従業員エンゲージメントを悪化させる。これでは「プロダクト開発能力の差異化」の実現が遠のいてしまう。それだけに、技術的負債のマネジメントは、エンジニアリング組織を預かるマネージャーの重要な責務だと考えている。

追記:YouTube Liveでの登壇時のアーカイブ

本稿公開からちょうど1年後にあたる2022年12月21日に、本テーマをもとにイベント登壇した。タイミー社が主催するTech勉強会「技術的負債の返済から改善する開発者体験 - Techmee vol.5」でのゲスト公演枠で、その時の様子はYouTubeアーカイブされている。

www.youtube.com

timeedev.connpass.com

登壇時の資料はspeakerdeckに置いてある。

speakerdeck.com