はじめまして。エンタメ領域のDXを推進するブロックチェーンスタートアップ、Gaudiyでエンジニアをしている@kaa_a_zuです。
早速ですが、質問です。みなさんは、所属している組織のコードを誇りに思い、この先数十年間に渡って使い続けることができると確信していますか?
私は、この質問をされた場合、顔を背ける自信があります。今回は、私と同じようにこの質問に対して "YES" と答えることが出来なかった人たち、そして 「リファクタリングをしたい」と思っているエンジニアが在籍する組織の偉い人たち に読んでもらいたい内容を書こうと思います。
テーマは、技術的負債の返済方法と技術的負債をためない方法についてです。(かなり長いので、HOWだけ知りたい方は4章から読んでいただけたらと思います。)
1. 一般的な技術的負債とは
「負債」という言葉を、日々の開発ではよく耳にするかと思います。改めて、技術的負債とはなんなのでしょうか。
技術的負債というものは、1992年にWikiの開発者であるウォード・カニンガム氏によって使われた表現です。彼の言葉を引用すると、技術的負債は「借金」と表現できます。それも開発が進めば進むほど、利息が高くなる悪魔のような借金です。
Shipping first time code is like going into debt. A little debt speeds development so long as it is paid back promptly with a rewrite. Objects make the cost of this transaction tolerable. The danger occurs when the debt is not repaid. Every minute spent on not-quite-right code counts as interest on that debt. Entire engineering organizations can be brought to a stand-still under the debt load of an unconsolidated implementation, object- oriented or otherwise.
初めてコードを出荷することは、借金をするようなものです。少しの借金は、書き直しですぐに返済される限り、開発を加速させます。オブジェクトは、この取引にかかるコストを許容範囲にしてくれます。危険なのは、借金を返せなくなったときです。正しいとは言えないコードに費やされた時間は、借金の利息としてカウントされる。オブジェクト指向であろうとなかろうと、統合されていない実装の負債によって、技術者組織全体が立ち行かなくなる可能性があるのです。
お金に余裕のない企業が国や金融機関から借金をして事業をするのと同じように、「余裕がない状態で、負債をしてでもサービスをつくり提供する」という意味が込められています。この意味を読み取ると、改めて技術的負債という表現は上手くできていると感じます。
しかし、エンジニアリングにおいて、技術的負債をためようと思ってためているチームは存在しないはずです。ではなぜ技術的負債がたまってしまうのか。技術的負債と借金が大きく異なる点は「借主が借りたことを知らない(気付かない)」ことだと私は思います。
もちろん、納期が近いなかでやっつけの開発をした場合には、負債がたまっていることを自覚しています。しかしながら、みなさんが既に経験をしているように、通常の開発においても技術的負債はたまっています。借りていたことに気がつくのは数日後かもしれないですし、数年後かもしれません。
そして非常に残念なことに、その負債に関連したコードを書けば書くほど利息がついてきます。気がついた時には、利子が複利になっており、返済額もとても大きくなっているかもしれません。取り立てをされることはないですが、寝れない日が続く可能性は高いはずです。
さまざまな技術的負債が存在しますが、まとめると以下4つに分類することができると思います。
- 影響範囲が大きく、目に見えるもの
- 影響範囲が小さく、目に見えるもの
- 影響範囲が大きく、目に見えないもの
- 影響範囲が小さく、目に見えないもの
次の章では、4種類の技術的負債がなぜ発生するのかについて、私の考えを示します。
2. なぜ技術的負債が発生するのか
4種類の技術的負債がたまる代表的な原因をそれぞれ挙げてみます。
※原因の大きさによって、生じる技術的負債の種類が変わることをご了承をいただきたく思います
1. 影響範囲が大きく、目に見えるもの
- ビジネスプレッシャー…ビジネス的な観点ですぐにリリースする必要があり、設計やコードにおいて妥協せざるを得なかった。
- 不十分な先行定義…時間短縮のために、要件がまだ定義されていない状況で開発を開始しなければならなかった。
- 誤ったテックリード…十分に考えられていない指示が指揮系統のもとに伝わってしまった。
2. 影響範囲が小さく、目に見えるもの
- 割れ窓理論…DRYでなかったり、直交性がなかったり、変数名が適当なコード、コード規約が守られていないコードが散乱していたため、同様のごみ(ひどいコード)が捨てられ、さらに汚くなってしまった。
- ビジネス的柔軟性…機能が適切な粒度のモジュールになっていないため、ビジネス要求の変化に柔軟な対応を取れない状態になってしまった。
3. 影響範囲が大きく、目に見えないもの
- 継続的な開発…プロジェクトの息が長い場合、利用しているコアなライブラリやソリューションが最適ではなくなってしまった。
- コラボレーション不足…知識や決定事項が組織内で共有されておらず、エンジニアと他の部署での認識のずれが生まれてしまった。
- 知識の欠如…その時点で在籍している開発者が、問題に対してのベストな解決策や綺麗なコードの書き方を知らなかった。
4. 影響範囲が小さく、目に見えないもの
- 偶発的プログラミング…コードがたまたま上手く動いているが、なぜ動いているのかを説明することができない。または、データ構造やアルゴリズムについて深く考えることができていなかった。
- 不十分なテスト…テストスイートがないため、迅速だがリスクの高い暫定的なバグ修正が行われた。
- 属人化…(小さな組織では特に)知識が属人的になってしまい、負債かどうかの判断もつけにくい状態になってしまった。
これらの原因のうち、心当たりがあるものはありましたか?もしあった場合、それは悪いことではなく改善するチャンスだと私は思います。
3. 技術的負債がもたらす問題点
返済方法について考える前に一度立ち止まって、技術的負債の問題について考えてみます。私は、技術的負債がもたらす問題には、大きく分けて3つあると思っています。
3-1. バグの温床になる
他の部分に影響が出ないと思って書いたコードが、実は様々な部分に影響を及ぼしていた場合にバグが生じます。バグは、サービスのユーザーをイライラさせます。そして悪い評判が立ったり、遅延やセキュリティ問題が発生することは、サービスだけではなく会社に悪影響を及ぼします。
また、CSチームやQAチームのやることも増えるため、社内の環境も悪くなります。当然、エンジニアはそのバグの対処に時間を割かなくてはいけなくなります。該当コードを書いた人はすでに組織を去っているかもしれず、バグの温床になってしまいます。
3-2. 新しい機能をユーザーに届けることが出来ない
よくない設計をしていたが故に、想定していなかった修正をしなくてはいけなくなる恐れがあります。そうなると見積もりで引いていたスケジュールの間にギャップが生まれるため、機能のリリースが遅れます。結果的に機能を渇望しているユーザーの期待を裏切ります。
スケジュールが数日ズレるだけならまだ良いですが、内容によっては「現在の設計だったらこの機能を作ることは難しい」という結論に至る可能性があります。新たな機能が加わらないために飽きがきたユーザーは、そのサービスから離れていってしまいます。
3-3. DXが損なわれる
思いもよらぬ様々なバグを生じさせた過去があったり、ノンリーダブルなコードを見ていると、コードを書く前に勇気をふり絞らないといけなくなります。また、新しいコードを書くために、多くの既存コードを読む必要がでてきます。
最終的に、離職率の上昇やエンジニア採用の成功率低下にもつながる可能性もあります。ひどい作業を好んで取り組みたい、引き継ぎたいと考えるエンジニアはかなり少数なはずです。
…いかがでしょうか?「技術的負債を甘くみていた」と思ってくれた方がいたら嬉しいです。
以上、技術的負債がどういう時に発生して、何が問題であるのかについて説明をしてきました。ここまで読んでくださった方は、おそらく技術的負債を抱えている人だと思います。大変お待たせしました。
次の章では、技術的負債の返済方法について、私の考えを示します。
4. 技術的負債の返済方法
もしも、読んでくださっている方の中に「海老で鯛を釣る」ことが好きな方がいたら、非常に残念なお知らせをしなくてはいけません。
人生と違って、宝くじや玉の輿に乗って大逆転をすることができないのがこの世界です。
また、目に見えない負債を返済することもできません。私たちにできることは、目に見える負債を減らすことと、目に見えない負債を少なくすることの2つだけです。
先ほど「2. なぜ技術的負債が発生するのか」の章で、4種類の技術的負債における代表的な発生原因を挙げました。それぞれの原因を潰すことができれば、返済をする、または返済額を減らすことが可能です。返済が簡単な順で見ていきます。
4-1. 影響範囲が小さく、目に見えるもの
まず、影響範囲が小さく、目に見えるものが最も簡単です。その発生原因としては「割れ窓理論」と「ビジネス的柔軟性」がありました。 それぞれの返済方法には、以下が挙げられると思います。
<割れ窓理論>
割れ窓理論…DRYでなかったり、直交性がなかったり、変数名が適当なコード、コード規約が守られていないコードが散乱していたため、同様のごみ(ひどいコード)が捨てられ、さらに汚くなってしまった。
- コード規約ツールの利用
- フォーマットの自動化
- コードレビュー
- CIで規制
- 割れた窓を見つけた時に直す、もしくはチケットを切る
<ビジネス的柔軟性>
ビジネス的柔軟性…機能が適切な粒度のモジュールになっていないため、ビジネス要求の変化に柔軟な対応を取れない状態になってしまった。
- デメテルの法則
- 直交性,SOLID原則の意識
- コードレビュー
- DDD
- ADR(Architecture Decision Records)
4-2. 影響範囲が小さく、目に見えないもの
次に、影響範囲は小さいけれど、目に見えないものです。その発生原因としては「偶発的プログラミング」「不十分なテスト」「属人化」がありました。それぞれの返済方法には、以下が挙げられると考えます。
<偶発的プログラミング>
偶発的プログラミング…コードがたまたま上手く動いているが、なぜ動いているのかを説明することができない。または、データ構造やアルゴリズムについて深く考えることができていなかった。
- 利用しているアルゴリズムとオーダー(O)を意識
- 劣化が起きていないかCIでモニタリングを行う
- TDD
- コードレビュー
<不十分なテスト>
不十分なテスト…テストスイートがないため、迅速だがリスクの高い暫定的なバグ修正が行われた。
- テストを書く
- テストカバレッジをCIで規制
<属人化>
属人化…(小さな組織では特に)知識が属人的になってしまい、負債かどうかの判断もつけにくい状態になってしまった。
- 社内勉強会
- ドキュメント作成の義務化
4-3. 影響範囲が大きく、目に見えるもの
次に、影響範囲は大きいけれど、目に見えるもの。この発生要因としては「ビジネスプレッシャー」や「不十分な先行定義」「誤ったテックリード」がありました。それぞれの返済方法には、以下が挙げられると思います。
<ビジネスプレッシャー>
ビジネスプレッシャー…ビジネス的な観点ですぐにリリースする必要があり、設計やコードにおいて妥協せざるを得なかった。
この原因はエンジニアだけでは絶対に解決できません。ビジネスサイドの技術的難易度や技術的負債への理解と、ビジネスサイドとエンジニアサイドの心理的安全性を高める日々のコミュニケーションが必要です。 その上で、以下の方法があります。
- ビジネス要件のうち落とせるものがないかを確認
- 生じた負債はすぐにチケットを切る
- アジャイル開発をしている場合は次スプリントに負債返済を入れる
<不十分な先行定義>
不十分な先行定義…時間短縮のために、要件がまだ定義されていない状況で開発を開始しなければならなかった。
- 曳光弾の利用
- 使い捨て前提のコードを利用するプロトタイプの作成
<誤ったテックリード>
誤ったテックリード…十分に考えられていない指示が指揮系統のもとに伝わってしまった。
- キックオフミーティングや定期的なミーティングでその指示について議論する
- (全開発者が意見を言えるように)開発者がただの作業員だと思わないような環境づくりを行う
4-4. 影響範囲が大きく、目に見えないもの
さいごに、影響範囲が大きく、目に見えないという返済が最も難しいもの。この発生原因には「継続的な開発」「コラボレーション不足」「知識の欠如」がありました。それぞれの返済方法としては、以下が挙げられると思います。
<継続的な開発>
継続的な開発…プロジェクトの息が長い場合、利用しているコアなライブラリやソリューションが最適ではなくなってしまった。
- 定期的にライブラリをアップデートする
- ライブラリアップデートの自動化
- 技術アンテナを高く持ち、日頃から新しい技術を試す機会を設ける
<コラボレーション不足>
コラボレーション不足…知識や決定事項が組織内で共有されておらず、エンジニアと他の部署での認識のずれが生まれてしまった。
- 勉強会を開く
- デザインコラボレーションの機会を設ける
<知識の欠如>
知識の欠如…その時点で在籍している開発者が、問題に対してのベストな解決策や綺麗なコードの書き方を知らなかった。
- 既存の類似サービスの真似をする
- 先に利用する技術についての勉強会を行う
- 知見を持つ技術顧問を雇う
上記のそれぞれに対する解決方法を用いることで、いくつかの原因を潰すことができると思います。次の章では、これらの解決策をふまえて私たちがどのような取り組みをしているのかについて紹介します。
5. Gaudiyの技術的負債に対する向き合い方
ここからは弊社が技術的負債とどのように向き合っているのかについて、いくつかの事例をご紹介しようと思います。
はじめに、弊社はWebアプリケーションを提供しています。メインのリポジトリは2つで、それぞれがモノレポの形を取っており、フロント, BFF, 複数のバックエンドサービスが管理されています。さらに興味がある方はこちらの記事をご覧ください。
5-1. 技術的負債を解消する取り組み
5-1-1. ソリューションの乗り換え
私たちのアプリケーションには、WindowAとWindowBでリアルタイムなやり取りをしなければならない仕様があります。これを実現するために、当初は window.postMessage()
を1年近く利用し、うまく動いていました。ところがある日、それが負債であることに気がつきました。
この負債は、前章までに挙げた「影響範囲が大きく、目に見えない技術的負債」に当たります。感覚としては、ある日郵便受けを見ると、数千万円と書かれた連帯保証請求書が届いている感じです。当然びっくりしますね。
この負債に気がついた時に、問題や影響箇所の詳細について調査し、今乗り換えるべきかどうかをプロダクトオーナーを含めて相談しました。そして返済をするべき負債と決定したものの、どのようすれば返済ができるのかがすぐにはわかりませんでした。
ここで用いた方法が「知識の欠如」で挙げた「既存の類似サービスの真似をする」でした。幸いなことに、私たちが実現をしようとしていたタブ間通信と非常によく似ている挙動をするサービスを見つけることができました。その挙動がどのように実現されているのかをDevToolsのパフォーマンスタブやネットワークタブを用いて徹底的に調べ、解決方法を見つけることができました。
旧アーキテクチャ
新アーキテクチャ
私たちのサービスは新たなアーキテクチャに乗り換えられ、(今この瞬間は)うまく動いています。
5-1-2. 戦略的に作った負債の回収
私たちのアプリケーションは一言でいうと「エンタメSNS」に当たります。機能として、投稿一覧で画像や動画などのリッチなコンテンツが見れたり、他にも様々なアニメーションや動的なコンテンツを提供しています。ユーザーを飽きさせないために、新しい機能を頻度高く提供をすることも多いです。
また、ブロックチェーンなどの不確実性の高い要素によってスケジュールの見積もりが難しい側面があり、時折、QCDでいうQualityを二の次にしDeliveryを優先した開発をすることもあります。そうして出来上がったコードは、当然ながら質が良いものではありません。
これは、前章までに挙げた「影響範囲が大きく、目に見える技術的負債」に当たります。ユーザーからは見えづらい負債であっても、後々すばやい機能開発やより良い体験の提供がしづらくなってしまうため、すぐに返済計画を立てる必要があります。実際に、弊社ではアジャイル開発をしているので、「次スプリントに負債返済を入れる」ことで解消しています。
また、このうちの一部は「影響範囲が小さく、目に見えない技術的負債」に当たります。その代表例になるのが、パフォーマンス劣化です。時間がない中で書いたコードは、吟味されたものではないため、その塵が積み重なると、いつのまにか目の前には大きな山ができあがっています。
この負債を返済をするためには、パフォーマンスチューニングという地道な作業をする必要があります。Gaudiyでは、この山を作らないようにするために「偶発的プログラミング」で挙げた「利用しているアルゴリズムとオーダー(O)を意識する」や「劣化が起きていないかCIのタイミングでLighthouseAPIを使いモニタリングを行う」を取り入れています。
5-2. 技術的負債を未然に防ぐ取り組み
最後に、そもそも借入をしないために、技術的負債を未然に防ぐ取り組みについてご紹介をします。
5-2-1. DDDの推進
DDDの目的は「影響範囲が小さく、目に見える技術的負債」を防ぐことです。
私たちのアプリケーションには複数のドメインが存在し、バックエンドではマイクロサービスの運用を行っています。ある時、追加開発を行う必要がでた時に、破壊的修正を要してしまうという問題を抱えていました。そこでDDDの導入を行いました。
結果的にドメインエキスパートの考え方やメンタルモデルをシステムに組み込むことができ、これによってレバレッジが効きつつも、柔軟性をもった開発ができるようになりました。これは「ビジネス的柔軟性」が原因で起こる負債を防ぐために機能しています。
5-2-2. ちょいコラ、ちょいデモ、マルチアイ
次に、パターン・ランゲージとして社内で使っている「ちょいコラ」「ちょいデモ」「マルチアイ」の取り組みです。この目的は「影響範囲が大きく、目に見えない技術的負債」を防ぐことにあります。
大前提として、人は誰しもがケアレスミスをしてしまいます。ですがこのミスは、他者とのコラボレーションをすれば防ぐことが可能です。それを日常的に意識するため、利用しているパターン・ランゲージが以下になります。
- ちょいコラ…デザインとビジネスの観点をエンジニアリングに取り込みたいときに、担当者がその人達に助言をもらいにいく。
- ちょいデモ…細かいサイクルでデモを実施して、実際に動くものを確認するタイミングを設けることで、ユーザー視点を最初から開発に取り入れる。
- マルチアイ…偏った視点ではなく、複数の視点を交えて、最適解を導き出す。
これらは「偶発的プログラミング」や「コラボレーション不足」が原因で起こる負債を防ぐために機能しています。
5-2-3. オールオーナー(ALL OWNER)の考え
この行動指針は「影響範囲が大きく、目に見える技術的負債」を防ぐために機能しています。
どれだけ経験を積んできた人でも、勘違いや誤ったリードをしてしまうことがあります。この問題は、言われたことを鵜呑みにしないことで防ぐことが可能です。
Gaudiyには16のクレド(行動指針)がありますが、そのひとつに「オールオーナー(ALL OWNER)」という考えがあります。自律分散的なブロックチェーンの考え方に基づいて運営されている弊組織では「相談した上で、自身で最終決定をする」という考え方が存在します。
この考え方が組織で当たり前となっているため、プロダクトオーナーや、開発経験が長い人、年上の人、その他すべての人の意見を脳死で受け入れることはありません。これは「ビジネスプレッシャー」や「誤ったテックリード」が原因で起こる負債を防ぐことができます。
5-2-4. ドキュメントをすぐに作る文化
最後に、ドキュメント文化は「影響範囲が小さく、目に見えない技術的負債」を防ぐために機能します。
私たちは、様々な技術や言語、プラットフォーム、さらにブロックチェーンという変化の激しい先進技術を利用して開発に取り組んでいます。十数人のエンジニアしかいない組織ですので、いちコンテキストに関わっているエンジニアは、1〜3人程度です。つまり、設計背景や技術知識が簡単に属人化してしまいます。この「属人化」を防ぐために、実施したことや知見をNotionに蓄積しています。これもひとりだと後回しにしがちなので、朝会や夕会のタイミングでドキュメント作成をタスク化しています。
6. まとめ
この記事では、技術的負債をテーマに、発生原因や返済方法などを弊社の具体例を添えて書かせていただきました。
正直、私は恵まれていると思っています。というのも、未熟さが故に発生させてしまった技術的負債を解消する機会を設けてもらえているからです。
よく「コードの改修(=リファクタリング)をしてビジネス的になんの旨味があるの?」と問われたり、「リファクタリングをするならこの機能を作ってほしい」と要求をされる組織があるという話を聞きます。この時に、エンジニアは、WHYをしっかりと答える責任がありますが、(上下関係や心理的安全性の低さが故に)そのWHYを答えることすらできない組織もあると聞きます。
冒頭で質問させていただいた「所属している組織のコードを誇りに思い、この先数十年間に渡って使い続けることができると確信していますか?」という問いに 「YES!」 と答えることが出来ないのは仕方がないことかもしれません。でもこれは、エンジニアだけが悪いという話ではないと思っています。サービスを提供する組織全体として技術的負債を認知し、その返済をサポートする必要があると思っています。
この記事を読んだみなさんが少しでも技術的負債への解像度を高め、上記のような働きかけができるようになれば、書いた甲斐があるなと感じています。
ここまで読んでくださり、ありがとうございました。 書ききれていないこともあるので、よければカジュアルにお話しさせてください。