Skip to content

Latest commit

 

History

History
251 lines (243 loc) · 18.4 KB

レガシーコードからの脱却.md

File metadata and controls

251 lines (243 loc) · 18.4 KB

レガシーコードからの脱却〜ソフトウェアの寿命を延ばし価値を高める9つのプラクティス〜 2020/06/20

レガシーコード

  • ほとんどの人は、ソフトウェアがどのように作られ、どのようにレガシーコードになるのか、そして、それを防ぐ方法についてほとんど知らない。
  • 従来のウォーターフォールプロセスは、レガシーコードを作り出し拡散する
    • 機能をまとめてリリースすることは非効率である
      • 変更不可能なものを作ることを強いられる
      • 途中で新しい機能追加に対応しにくい
    • 建物を作るときはうまくいくが、ソフトウェアを作る時にはうまくいかない
    • 長いリリースサイクルの中でソフトウェアを作っていると、コードが動くのを開発者が見るのは書いてから何ヶ月も後になる
    • 未知を見積もることはできない
  • ソフトウェアエンジニアリングは、すべてのソフトウェア開発者が知らなければならない基本原則や共通の知識体系をまだ確立していない
    • 従来のマネジメント手法は、ソフトウェアには適用できないし、開発者は共有できるような知識体系をまだ手に入れていない

賢人による新しいアイデア

  • アジャイル開発手法は従来のウォーターフォール開発に代わる選択肢で、ソフトウェアを反復的に作るもの。
    • 開発コストの削減に役立つ
  • アジャイルやスクラムの目的は、小さなかたまりで仕事をすること。
    • いかにスコープを箱に収めるか
    • その箱に収めることになれるために、タイムボックスを設ける
  • ソフトウェア開発者はソフトウェアを作るという客観的な技能と、ソフトウェア開発独自の要求が求める主観的な芸術のバランスについて学ぶ必要がある
  • ソフトウェア開発者とマネージャーは技術的卓越性を求め、意図的に質の良いソフトウェアを作る必要がある。

9つのプラクティス

  • 第一原理
    • Ex. 「自分にしてもいいように人に対してせよ」
    • Ex. 単一責務の原則
      • クラスを変更する理由は一つでなければならない
    • Ex. オープン・クローズドの原則
      • ソフトウェアのエンティティ(クラス、モジュール、関数など)は、拡張に対して開いており、変更に対して閉じていなければならない。
      • 既存のコードを変更することなく、振る舞いを変更することができる
    • 多くの原則が導かれる原則は第一原理である
    • ソフトウェアにおいて原則は、包括的なアドバイスであり、開発者が良いソフトウェアを作る助けになるもの。
  • プラクティス
    • 実際の状況において原則を実現するための方法のこと。
    • 定義
      • ほとんどの場合に価値があるものである
      • 学ぶのが容易である。教えるのが容易である。
      • 実施がシンプルである。考えなくてもやれるくらいシンプルであること。
    • 原則がプラクティスをガイドする
  • 予測か対応か
    • 未来を正確に予測することはできない。
    • 開発者は変更に対応するための標準的な作法やプラクティスを身につけるべき
  • 「良い」ソフトウェアとは
    • 内部品質が高いこと
      • シンプルで、保守や拡張がしやすいこと
      • コードが変更可能な状態であること
    • よく外部指標で特徴付けられることが多い
      • Ex. ユーザビリティ、バグがない、適切なタイミングでの更新
    • 外部品質は、内部品質が原因で現れる結果に過ぎない。
  • 9つのプラクティス(XPの12つのプラクティスから抽出)
    • ①やり方より先に目的、理由、誰のためかを伝える
    • ②小さなバッチで作る
    • ③継続的に統合する
    • ④協力し合う
    • ⑤「CLEAN」コードを作る
    • ⑥まずテストを書く
    • ⑦テストで振る舞いを明示する
    • ⑧設計は最後に行う
    • ⑨レガシーコードをリファクタリングする

プラクティス1:やり方より先に目的、理由、誰のためかを伝える

  • ソフトウェアがどう作れられるかよりも何をすべきかに注目することによって、開発者は自由に最良の実装を発見できる

  • 目的、理由、誰のためかを表現するために、実装の詳細を説明することを捨て、機能を定義するための極めて重要な会話に代えていくことで、開発が発見のプロセスになる

  • 敏腕プロダクトオーナーは受け入れ基準が明確に定義された、良いストーリーを書く

  • もっと効率的に機能を作って、開発に充てる時間を全体の1/3まで回復しよう。要求の記述を減らし、プロダクトオーナーと開発チームで協調性を生み出していこう。

    • 実装を詳細に説明して要求をドキュメント化するのはやめること
    • 代わりに機能の目的、理由、誰のためかを話し合うこと
  • ストーリーで目的、理由、誰のためかを語る

  • 受け入れ基準を明確に設定する

    • 何をするはずか
    • いつ動くのか
    • どうなったら私たちは次に進めるのか
  • プロダクトオーナーのための7つの戦略

    1. SMEになる
    • プロダクトオーナーはテーマごとの専門家(SME)でなければならない
    • システムを作るよりも前に、できるだけ全員が理解できるよう、システムを視覚化し、例示して見せることに時間を割くべき
    1. 開発を発見のために利用する
    • 反復型開発のフィードバックの機会を活用して、何百ものユーザーに開発途中の機能を投入し、開発が正しい方向に向かっていることを確認すべき
    1. なぜ誰のために、を開発者が理解できるようにする
    2. どうやって手に入れるのかではなく、何が欲しいのかを説明する
    3. 質問にはすばやく答える
    4. 依存性を取り除く
    • 依存性が誰かをかかりきりにしないようにしてやる
    • バックログを順序付けし、チーム内のどんな依存性もリードタイムが十分にあることを保証するべき
    1. リファクタリングを後押しする
  • よりよいストーリーを書くための7つの戦略

    1. プレースホルダーとして見る
    • ストーリーはそれだけで要求に代わるものではない
    • 要求に代わるのは会話であり、ストーリーは単なるプレースホルダーにすぎない
    • スプリントプランニングや今後の議論に持ち込みたいと思う話の本筋を掴むためにストーリーを活用する
    1. 目的に注目する
    • 開発者はコードを書きながら機能をどうやって作るか見つけ出すべき
    • そのためにまずその機能が何をするものか、どうやって利用するかについて理解するべき
    1. 「誰」を擬人化する
    • 誰のためにその機能があるのかを知ることで、開発者はその機能がどんなふうに使われそうか理解を深めることができる
    1. なぜ機能が必要とされたかを知る
    2. シンプルに始めて追加は後で行う
    • 漸進的な設計と開発は、ソフトウェア開発におけるいちばん効率的な方法であり、最良の結果も提供する
    1. エッジケースを考える
    • ストーリーはハッピーパス(正常な操作による挙動)を提示するが、代替パスや例外やエラーのハンドリングなど、他にも取り扱わなければいけないパスがあることがほとんど
    • エッジケースはストーリーカードの裏にさっと書き留めておき、あとでそのテストを書いて実装を駆動する
    1. 受け入れ基準を利用する
    • ストーリーの実装に取り掛かる前に、受け入れ基準を明確に定義しておくこと。
    • 一連の受け入れテストとして表す
    • 受け入れテストがあることで開発者はストーリーの実装がいつ終わるかわかる。オーバーエンジニアリングも防げる

プラクティス2:小さなバッチで作る

  • 鉄の三角形
    • スコープ = 時間 × リソース
  • バッチが小さいことのメリット
    • 理解しやすい
    • 見積もりしやすい
    • 実装しやすい
    • テストしやすい
  • 分割統治
    • 未知のことを探る際にしたいこと
      • 未知のことを既知のこととする
        • 大きな未知のことがあった場合に、それを扱う方法を理解する
      • カプセル化する
        • 大きな未知のことがあって、それが隠せるようなものなら一旦隠しておいて、あとで対処する
    • 一度にとりかかる作業が多すぎると、WIPの作業が増えてたくさんの無駄が生まれる
      • 待ち行列の理論
        • サイクルタイム = 仕掛かり中の数 / スループット ??
        • 全ての作業を終わらせるのにかかった時間を、TODOリストの項目数で割ったものがサイクルタイム
  • フィードバックサイクルを短くする
    • 顧客との会話、コンパイラから、自動ビルド、etc...
    • とくにビルドの高速化は重要
  • フィードバックに対応する
  • バックログを作る
    • ユーザーストーリーのリストのこと
    • 最小市場化可能機能セット(MMF)を整理する
      • 開発が早く終われば、必須ではない機能をリリースに追加可能になる
      • 時間切れの場合は何を外さないといけないか、ユーザーに価値を届けるために何を含めなければいけないかもわかる
      • バックログは優先順位をつけるのではなく並べ替える
        • 開発効率を考慮に入れる
  • ストーリーをタスクに分解する
  • 実践
    • ソフトウェア開発を計測する7つの戦略
      • ベロシティの計測は間違った方向に誘導する可能性がある
        • マネジメント側が謝ったゴールを設定してしまう
        • ベロシティは品質を犠牲にすればあげられる
      • ①価値実現までの時間を計測する
      • ②コーディングに使った時間を計測する
      • ③欠陥密度を計測する
      • ④欠陥検出までの時間を計測する
        • 欠陥が発生してから時間が経過するにつれて、欠陥修正のコストは指数関数的に増加する
      • ⑤機能ごとの顧客価値を計測する
      • ⑥機能を提供しない場合のコストを計測する
      • ⑦フィードバックループの効率を計測する
    • ストーリーを分割する7つの戦略
      • ①複数のことが混じったストーリーを要素に分解する
        • コンポーネントの分割とシステムのモジュール化に役立つ
        • 分割した小さなストーリーは作業しやすい
      • ②複雑なストーリーを既知のことと未知のことで分離する
        • 本当に顧客が望んでいることやその実装方法がわからないかもしれない
      • ③未知のことをわかるまで繰り返す
        • 何が未知なのかが判別できたら、それをカプセル化する
          • インターフェイスを定義して抽象化する
        • 未知のことのうちリスクが高いものは先にやって、リスクが低いものはあとにするとよい
      • ④受け入れ基準をもとに分割する
      • ⑤依存関係を最小にする
        • ストーリーはほかのストーリーに依存しないほうがよい
      • ⑥意図を一つにする
      • ⑦ストーリーをテスト可能に保つ

プラクティス3:継続的に統合する

  • 継続的インテグレーション
    • リリース直前まで待つのではなく、ソフトウェアをビルドしながら統合すること。
    • バグを早期に直せるだけではなく、より簡単に統合できる良いコードを書く方法を学べる重要なプラクティスだ
    • 機能統合するまでは、システム内で機能することが保証されない
  • 完了の3つの定義
    • 完了
      • 従来のウォーターフォール開発では、機能を作った開発者が自分のマシンで実行し、何らかの結果を得たことを意味する
      • 十分ではない
    • 完了の完了
      • 自分のマシンで機能するだけでなく、ビルドにも統合されていることを意味する
    • 完了の完了の完了
      • コードは自分のマシンで動作し、ビルドに統合され、クリーンで保守可能になっていることを意味する
  • 継続的にデプロイ可能にする
    • 継続的インテグレーションとは、本番環境へ継続的にデプロイしなければならないという意味ではない。
    • ビジネス上の決定に基づき、必要に応じていつでもリリース可能であることを意味する。
  • アジャイルインフラストラクチャの7つの戦略
    • ①すべてをバージョン管理する
      • コード以外のファイルもバージョン管理する
      • 設定ファイル、スクリプト、ストアドプロシージャなど
    • ②ワンクリックでエンドツーエンドのビルドをする
      • ビルドプロセス全体を自動化する
    • ③継続的に統合する
    • ④タスクの受け入れ基準を定義する
    • ⑤テスト可能なコードを書く
    • ⑥必要な場所のテストカバレッジを維持する
    • ⑦壊れたビルドをすぐ直す
  • リスクを減らす7つの戦略
    • ①継続的に統合する
      • リスクを排除する唯一の方法
      • 統合するまで機能が正しく動くかわからない
    • ②ブランチを避ける
      • ブランチの代わりにフィーチャーフラグを使う。
      • 使う準備のできていない機能を無効にする
    • ③自動テストに投資する
      • 開発コストを削減するためには、テストが完全に自動化されるように、検証のための人手の介在をすべて排除することが必須
    • ④リスクのある場所を特定する
    • ⑤未知の中で働く
      • 未知のものを確認したら、短時間だけそれに取り組み、記録して進捗を確認する
      • 短いタイムボックスを設定してチェックインし、進捗を測りながら進める
    • ⑥価値がわかる最小のものを作る
      • 問題が小さいほど、理解、解決、証明、保守が容易になる。
    • ⑦頻繁に検証する
      • 顧客は実際に目にするまで、欲しいものがわからないかもしれない
      • 検証を早い段階で行うことで、より価値の高いプロダクトを作れるし、顧客のやりたいことをうまくやる方法が見つかるかもしれない
  • まとめ
    • コードを書くたびに統合することで、ソフトウェア開発に伴うリスクを軽減できる
    • 統合が苦痛になるため、ウォーターフォールでは統合を延期し、リスクと変更のコストが増大する
    • リリース候補の検証を自動化することで、リリース直前の変更にかかるコストを無視できるようにする
    • フィードバックサイクルを短くすることで、開発者の行動の結果がすぐに把握できるようになる
    • 継続的にデプロイできることの重要性を理解すれば、タスクの自動化方法を探し、機能の相互作用について即座にフィードバックを得るために継続的インテグレーションを活用することになる

プラクティス4:協力しあう

  • ペアプログラミング
    • メリット
      • チームに知識を速く広げることができる
      • 密に協働することで、アイデアを検証し、問題をとことん考え抜くことができる
      • 時間をかけずに相手のスピードをあげられる
      • 経験の少ない開発者を指導しやすくなる
      • 設計の時間、デバッグの時間、保守性向上のためのコードの掃除にも有効
      • コードの美的感覚が共有できる
      • プラクティスのセットとコードスタイルが共有されていれば、新たにチームへ参加したメンバーのスピードをあげる最善で効率的な方法である
      • ペアリングをしていると、開発者への割り込みが減る
      • ペアにすることで、ある意味お互いを監視することになり、しっかりと1日を過ごすことができる
    • どうやってペアを組むか
      • ドライバー:キーボードを操作する人
      • ナビゲーター:ドライバーの横に座って、ドライバーの書いたコードを見ながら議論する
      • 時間を区切って、ドライバーとナビゲーターを交代する
      • ピンポンペアリング
        • 1人がテストを書き、1人がテストを合格させてコードをクリーンにする
    • だれとペアを組むか
      • ①開発者の強みと弱みを踏まえて組み合わせる方法
        • お互いの得意なところを生かし、苦手なところを補うために協力し合う
        • 性格に基づいてペアを組む場合は、強い性格の人(外向的で自己主張の強いタイプ)と受動的な性格の人(内向的で静かなタイプ)を組み合わせる
      • ②一番経験を積んだ開発者と一番経験の少ない開発者を組み合わせる方法
        • 学ぶための最良の方法は教えること
      • ③ランダムに人を割り当てる方法
        • 意外な相手が相性が良いこともある
        • ストロングスタイルペアリングという手法がある
          • 自分のアイデアをコンピュータに伝えるまでの間に、必ず他人の手を経由しなければならないという方法
    • バディプログラミング
    • スパイク
    • スウォーミング
    • モブ