/note/tech

現場で役立つ技術負債の効果的な返済方法

要約:

■ 1. 技術的負債の何が問題か

  • 技術的負債によって起きること:
    • どこに何が書いてあるかを理解するのがたいへんになる
    • 一つの修正のために、あちこちを書き直す必要が生じる
    • ちょっとした変更のはずが、本来はありえない場所にまで影響し、大幅なやりなおしになる
  • 現実的な問題:
    • 変更がやっかいで危険になる
    • 変更に時間がかかりすぎる
    • その場しのぎの対応がさらに状況を悪化させる
    • 変更が怖くなって変更を避け、さらに状況が悪化する

■ 2. やるべきことと、できていない理由

  • やるべきことは決まっている:
    • リファクタリングにより既存コードの設計改善を続ける
    • 機能追加を楽にするために設計改善を続ける
    • 不具合を修正するために設計改善を続ける
    • デッドコードを見つけたら削除する
    • タイポを見つけたら修正する
  • できていない理由:
    • 時間が足りない
    • 基本的なスキルが身に付いていない
    • 経験不足で練度が足りず、やるのが面倒かつ不安になる
  • 共有すべきメンタルモデル:
    • 負債を残したままの作業時間は、リファクタリングに使う時間と設計改善後の作業時間の合計より大きい
  • リファクタリングに使う時間:
    • 基本スキルを習得すれば時間はかからない
    • 練度が上がれば、もっと時間がかからない
    • スキル不足はやってみることで解消される
    • 練度不足はやり続けることで解消される
  • 二つの教訓:
    • 教訓①はやってみること、教訓②はやり続けること
    • やらないから基本のスキルが身に付かない
    • 続けないから練度があがらない

■ 3. 限られた時間の中での取り組み方

  • それでも時間は足りない:
    • リファクタリングの効果がありそうな場所はいくらでもある
    • 現実のソフトウェア開発ですべてに手を出すことは無理である
    • 効果が大きい場所に絞り込んでリファクタリングする
    • 一定の方向性を保ちつつ、少ない時間の回数を増やす
  • 管理者の承認は不要:
    • リファクタリングをやる理由は、機能追加や不具合修正の準備作業である
    • 準備作業だけを切り分けて承認を得るようなものではない
    • スキルを習得し練度を上げる取り組みに承認は要らない
  • 非効率で効果が小さいやり方:
    • すべての機能追加、不具合修正に一律に一定の時間を使う設計改善は悪手である
    • どこも十分な時間が使えず、本格的な改善に取り組めず、改善効果が小さくなる
  • 効率的で効果の大きいやり方:
    • 技術的負債の中でも諸悪の根源になっているところを見極め、そこを集中的に改善する
    • 諸悪の根源はあちこちに悪影響が及んでいるため、いちどに解決するのは無理筋である
    • 改善の方向性を見定め、実際の改善は機能追加時や不具合修正時に直接関係する場所に特化して進める
  • AIの活用:
    • AIにコード状態の下調べや設計改善の提案をさせるのは役に立つことが多い
    • その情報を参考に、自分たちでコードを理解して実際のコード変更を行う
    • 数行の変更程度であれば、良いと判断できればAIの提案をそのまま受け入れてもよい
    • より広い範囲や複数箇所に変更が及ぶ場合、AIによる一括修正はやらせない

■ 4. 準備編: 初期投資

  • 基本的な知識とスキルの習得:
    • tidy first 本とリファクタリング本を手元に置く
    • リファクタリング本の最初の例を使い、基本のリファクタリングの一日研修を行う
    • 基本のリファクタリングとは、チャンキング、名前の変更、説明用変数、メソッドの抽出、クラスの抽出である
  • 実験環境の構築:
    • 基本のリファクタリングを実コードで体験学習する
    • ブランチを作って破壊的なリファクタリングを実験する
    • テストは不要で、コンパイルのOK/NGで十分である

■ 5. 既存コード改善: 区分への焦点

  • 諸悪の根源の見つけ方:
    • 「区分」に焦点を合わせる
    • どの区分か判断する区分判定ロジックが複雑な箇所に注目する
    • 区分ごとに適用するビジネスルールが複雑な箇所に注目する
    • 区分の要素数が多い箇所に注目する
  • 区分に焦点を合わせる理由:
    • 複雑なロジックの記述箇所を確実に特定できる
    • ロジック記述の重複、散在、不整合を検出できる
    • 負債の具体的な姿をコードレベルで認識合わせできる
    • 業務の理解度が上がる
  • 複雑なロジックの記述箇所の特定:
    • どの区分に該当するかを分類する区分判定ロジックが対象となる
    • 区分によって適用するビジネスルールを切り替える業務ロジックが対象となる
    • これらは入出力手続きに断片的に埋め込まれていることが多い
    • 区分判定ロジックと適用ルールの切り替えロジックがあちこちに散在し、重複や不整合が起きているのが技術的負債の中心である
    • ここを整理すれば確実に技術的負債は減る
  • 技術的負債の実際の姿の認識合わせ:
    • 時間経過とともに区分が肥大化する
    • どの区分にあてはまるかを判断する区分判定ロジックも肥大化する
    • 区分によってビジネスルールを切り替える業務ロジックも肥大化する
    • すでに意味がない区分、複数の関心事が無秩序に混在した区分、読み替えられた区分など、積み重なった負債を目に見える形で特定できる
  • 業務の理解度があがる:
    • 区分、判定ロジック、適用ルール切り替えロジックが肥大化するのは、そこがビジネスの関心が強い領域だからである
    • 肥大化はビジネス環境やビジネスのやり方の変化を反映している可能性が高い
    • なぜその区分が必要か、なぜそのロジックで区分を判定するか、なぜ区分ごとにルール適用を切り替えるかを分析し理解する効果は大きい
    • 理解によって、まちがいが減り、補完が効き、予測でき、納得感が得られる

■ 6. 区分整理の進化モデル

  • 第一形態: 暗黙の区分:
    • 無名の区分がif文で記述された分岐構造となる
    • 信号が赤なら止まり、それ以外は進む、といった記述になる
    • 黄色と緑、黄色の点滅と赤の点滅、点灯していない場合、歩行者用信号、自転車の車道走行か歩道走行かを追加していくと泥沼に陥る
  • 第二形態: 区分定数:
    • 区分を番号化する原始的な構造化である
    • 信号番号をswitchで分岐し、1を止まる、2を注意して進む、3を進むとし、defaultを番号が不正とする
    • 要素の追加が楽で安全であり、変更に強い設計と受け止められる段階である
  • 第三形態: 区分名:
    • 区分の意味を明示する、意味的構造化の第一歩である
    • enumで赤、黄、緑を定義し、区分名でswitch分岐する
    • データとロジックは分かれているが、区分定義への参照を手がかりにロジックの記述場所を特定できる大きな進歩である
  • 第四形態: カプセル化:
    • 区分名を列挙し、区分ごとの定数とロジックを一箇所に集める
    • enumの各区分にメッセージを持たせ、区分ごとのリテラルとロジックを抽象化する
    • ロジックの散在、重複、不整合を解消する特効薬であり、意味的構造化の出発点となる
  • 第五形態: 意味的構造化:
    • 区分名の並びの不整合、定数構造やロジック構造の不整合に対し、関心を分離して整合性を向上させる
    • 赤、赤の点滅、黄、黄の点滅、緑、緑の点滅、点灯していないを一つのenumに詰め込み、車両、歩行者、自転車を条件分岐する状態が出発点となる
    • 点灯していない場合はガード節で切り出し、一旦停止して周囲に十分注意して進む旨を返す
    • 車両信号、歩行者信号、自転車走行をそれぞれ別のenumに分離し、各々に定数とロジックを持たせる

■ 7. 一般の区分と企業独自の区分

  • 一般ルールに基づく区分:
    • 交通信号は一般ルールに基づく区分である
    • ロジックの複雑さはあるが、誰もが同じように判断し行動する
  • 企業独自の区分:
    • 競争優位を生み出すのは企業独自の区分である
    • 技術的負債返済のもっとも効果的な場所は、企業独自の区分に関する区分判定ロジックと、ビジネスルール適用を切り替える業務ロジックである
  • 業務領域の位置づけ:
    • 他社と同じか自社独自か、ロジックの複雑さが高いか低いかで業務領域を四つに分ける
    • 自社独自かつ複雑な領域が中核の業務領域であり、競合他社との差別化を生む
    • 他社と同じで複雑な領域は一般の業務領域、他社と同じで単純な領域は基本的な業務領域、自社独自で単純な領域は補完的な業務領域である
  • 効果的な返済方法の要点:
    • 区分判定や業務ロジックの切り替えが複雑な区分に焦点を合わせる
    • 区分名の列挙、区分ごとのリテラル、区分ごとのロジックを一箇所に集めて整理する
    • 競争優位を生み出す中核の区分とロジックの意味的な構造化に取り組み、事業目的適合性を向上させる

■ 8. データベースの設計改善

  • データベースは密結合の巣窟:
    • 大きな技術的負債であり、返済効果は大きい
    • 実際に変更するのは難しいかもしれない
    • 最大の障壁はメンタルモデルであり、結果として基本スキルと練度が不足する
  • 基本的なアプローチ:
    • 関心が分離できていないテーブルは諸悪の根源であることを理解する
    • 関心を分離するためのテーブル設計技法を知る
    • 小さく実験し、やり方を練習し、効果の感触を得て、問題の大きさを実測する
    • 効果を期待できるところを探してやってみる
    • やり続ける
  • 関心を分離できていないテーブル群の症状:
    • 目的、用途が混在しているのでテーブルやカラムの意図が不明になる
    • 間違った使い方、無理な使い方が発生する
    • 不必要な競合と性能劣化が起きる
    • 変更がやっかいで危険になる
    • その場しのぎの積み重ねで状況がますます悪化する
  • テーブル設計 関心の分離 三原則:
    • 発生時点が異なるデータは別テーブルに分ける
    • すべてのカラムは確定データのみにしてNOT NULLとする
    • 狭いデータ型やCHECK制約でデータ範囲を制限する
  • 事実の記録と状態保持の分離:
    • 起きた事実を正確に記録する
    • 事実を記録するテーブルとは別に、現在の状態を保持するテーブルを作ってもよい
    • 状態は事実の記録から導出可能である
    • 状態はキャッシュであり、あった方が便利なことは多い
  • 事実の記録と状態保持の対応例:
    • 増加した/減少したという記録に対し、状態は残高となる
    • 状態を遷移させたという記録に対し、状態は有効、無効、保留などの有限状態となる
    • ToDoが発生しDoneになったという記録に対し、状態は行動待ちの約束リストとなる
    • 関係の発生と終了という記録に対し、状態は担当や配属などの現在の関係となる
    • 地点の記録に対し、状態は現在地点となる
  • 着手すべき箇所:
    • 競争優位を生み出す中核の業務ロジックに関連するデータに焦点を合わせる
    • どの区分に該当するかを判断するためのデータが対象となる
    • 区分ごとに適用するビジネスルールを表現するデータが対象となる
    • 区分ごとのビジネスルールを適用した結果を表現するデータが対象となる
  • 大きなシナリオ:
    • 発生時点、事実の記録と状態保持の観点でテーブルを分割する
    • スキーマを分割して論理的なモジュール化を行う
    • データベースを分割して物理的なモジュール化を行う
    • 競争優位を生み出すためのデータを特定し、カプセル化する
  • とにかく、やってみること:
    • このやり方をやってみようという発想で既存のテーブル設計を見直すと、さまざまな選択肢を発見できる
    • 事実の記録と状態の保持を分けるとテーブルとその操作が単純になることを体験学習できる
    • このやり方に移行する問題点がより具体的になり、問題の大きさの見積もりと軽減策の検討が具体的になる
    • 調査、仮説立案、検討の過程で業務理解が深まる