Code Complete
基礎
ソフトウェアコンストラクションとは
- コンストラクションとはプログラミングのこと
- コーディング+デバッグ+ α(詳細設計やユニットテストなど)からなる
- 「コーディング」という言葉単体は、単なる機械的な作業を連想させるため、コンストラクションの説明としては適切ではない
- コンストラクション以外の作業は?
- プロジェクトマネジメント
- 要求開発(要件定義)
- 概要設計(アーキテクチャ)
- UI 設計
- システムテスト
- 保守
なぜコンストラクションが重要か
- 開発時間の大部分を占めるため
- 開発の中心であるため
- コンストラクションの改善は驚くべき効果をもたらすため
- コードが唯一のドキュメントになってしまうことがよくあるため
- どれだけ急いでいても絶対に省略できない工程であるため
メタファにより開発への理解を深める
メタファの重要性
- メタファを使った理解を「モデリング」という
- よく理解できないものを、既に理解しているものと照らし合わせることで、理解が深まること
- 概念全体を把握しやすくなる
メタファの使用方法
メタファはヒューリスティクスの意味合いが強い。どうすれば物事がうまくいくか、考えるために使うものである。
- アルゴリズム
- 厳密に定義された一連の命令
- ヒューリスティクス(発見的)
- 答えを見つけるために役立つテクニック
システム構築のメタファ
- BAD) コードを書く
- 手紙を書くイメージ
- 書いて終わりじゃねーのよ
- BAD) システムを育てる
- 作物を育てるイメージ
- コードはすくすく育たないし収穫もできないのよ
- SOSO) システムをインクリメンタルに開発する
- 真珠養殖のイメージ
- GOOD) システムを構築する
- 建築のイメージ
- 現代のシステム開発における用語の多くは建築由来。e.g. アーキテクチャ、ビルド、etc
話は逸れるけど、知識を常に収集してストックしておく「知的道具箱」というメタファもいいね
上流工程
準備の重要性
- 品質向上したいタイミングが
- プロジェクトの終わりの場合
- システムテストを強化する
- ただし、どれだけテストをしたところで、もとがクソなら意味がない
- プロジェクトの途中の場合
- コンストラクションのプラクティスを強化する
- プロジェクトの最初の場合
- 高品質な計画、要求、設計を行う
- 安い車として設計したものは、どれだけ後段で努力してもロールスロイスにはならないからね
- プロジェクトの終わりの場合
準備の最大の目標
リスクを減らすこと。
準備不足の原因
- 上流担当開発者が仕事をこなすだけの知識を持っていないから
- すぐにコードを書きたい衝動を押さえられない開発者がいるから
- 準備にかける時間を上司がよく思わないから。対処法:
- はっきり断る
- コーディングしているふりをする
- 準備を怠る危険を上司に叩き込む
- 転職する
準備に文句を言わせないためには
- 論理で訴える
- ユーザが求めていないものを作ると目も当てられないよ
- 変な構築をすると必要のないものに膨大な時間と資金が消えていくよ
- 例えで訴える
- プランクトンがぴょう期ならいずれクジラも死ぬ、みたいな食物連鎖の例とか
- データで訴える
- 問題の修正時期があとになるほど、コストは指数的に増えるよ
- これはウォーターフォール・アジャイルを問わない
ソフトウェアの種類
- ゆるいシステム
- アジャイル開発
- 反復型 の開発手法が適している(以下を交互に繰返し行う)
- 計画、要件定義、アーキテクチャの策定
- コンストラクション、システムテスト、品質保証
- ざっくりとした要求仕様の策定
- 設計とコーディングを同時にやってしまう
- 別チームでのテストや QA はなし
- かたいシステム
- ウォーターフォール開発
- 逐次的 な開発手法が適している
- ちゃんとした要求仕様の策定
- アーキテクチャの設計とレビュー
- 詳細設計とレビュー
- 別チームでのテスト
- QA
反復型手法
コストは以下の順に安くなる
- 準備なしの逐次型(高い)
- 準備なしの反復型
- 準備ありの逐次型
- 準備ありの反復型(安い)
どんなプロジェクトでも「最も重要な要求とアーキテクチャの要素を早期に洗い出すこと」が大事。目安として:
- 逐次型なら事前に 80%の要求を明らかにしておく
- 反復型なら事前に 20%の要求を明らかにしておく
反復型 or 逐次型
- 逐次型に向いているもの
- 要求が安定している
- 設計が単純で理解しやすい
- メンバーが分野に精通している
- プロジェクトのリスクが低い
- 長期的な予測が重要である
- 下流での変更が高く付く
- 反復型に向 いているもの
- 要求が不透明
- 設計が複雑で理解しにくい、手間がかかる
- メンバーが分野に明るくない
- プロジェクトのリスクが高い
- 長期的な予測が重要でない
- 下流での変更は安価である
以下、プロジェクトに適したコンストラクションの準備とは何かを考えてみる
準備:課題定義
Product vision statement / Mission statement / 課題定義
- システムが解決する課題が何であるかを定義するもの
- ソリューションについては一切言及しない
- ユーザの言葉で書く。コンピュータ用語は使わない。
- 以下を防ぐためのもの
- 誤った課題を解決しようとして時間を無駄にすること
- 本来の課題が解決されないこと
準備:要求
要求 / 要求開発 / 要求分析 / 要求定義 / 仕様 / 機能仕様 / スペック とは:
- システムが何をすべきかを定義するもの
要求が必要な理由
- システムの機能をユーザ 主導(not プログラマ)で決定するのに役立つ
- プログラマ同士の議論を減らせる
- 手戻りを抑制できる
要求は変わる
- 顧客は、開発の過程を通じて自分自身のニーズを理解していくものだから
- 平均して当初の仕様の 25% は変更される
要求変更への対処方法
- 要求がきちんと定義されているかチェックリストで確認する
- コストを関係者全員に認識させる
- 変更の管理手順を予め定めておく
- 変更に対応できる開発手法をとる
- プロジェクトを中止する
- その変更が本当にビジネスに価値をもたらすのか考えさせる
アーキテクチャ
- 最上位レベルの設計、概略設計のこと
- 最終的なシステムの品質を決定する
- 後で変更すると膨大なコストがかかる
アーキテクチャの構成要素
- 概要
- 全体構成をざっと説明するもの
- どんなパーツがあるのか列挙
- 各パーツの役割は1つに絞る
- パーツ間のやり取り規則は明確にする
- 主要なクラス
- システムの8割を司る2割のクラスを明記する感じ
- データ設計
- DB の大まかな構造と内容
- 業務ルール
- 例えば顧客情報は 30 日より古くなってはならない、などあれば
- UI 設計
- リソース管理
- セキュリティ
- パフォーマンス
- スケーラビリティ
- i18n, l10n
- エラー処理
- メッセージ規約など
- フォールトトレランス
- オーバーエンジニアリング
- どれくらい堅牢にすべきか
よいアーキテクチャとは
- 採用した理由、採用しなかった理由が明記されている
- 途中変更する場合は全体と調和するようにする
- 目的が明確である(パフォーマンス重視、柔軟な変更可能性重視、など)
- マシンや言語に依存していない
- 過不足がなく丁度いい
- リスクが明記されている
- 複数の角度からの見解が盛り込まれている
- 不安要素がない
上流工程にかける時間
スケジュール全体の2割から3割
コンストラクションの重要な決断
言語
- 使い慣れた言語だと生産性が高まる
- 高級言語の生産性は低級言語の 5 から 15 倍
- 考える能力は言葉(≒ プログラミング言語)を知っているかどうかで決まる
設計
設計の難しさ
設計の難しさは、設計が:
- wicked problem であるため
- wicked problem = やっかいな問題
- やってみて初めて気がつく問題が内包されていること
- ルーズなプロセスであるため
- 良い方法と悪い方法の違いがわずかであり、間違えやすい
- どれだけやれば十分かわからない、よくあるのは「時間がなくなったら」
- 妥協と優先順位付けの産物であるため
- 制限がつきものであるため
- 非決定論的であるため
- 無限にやり方がある
- ヒューリスティックなプロセスであるため
- 発見的、試行錯誤的
- 試してみないとわからない
- 創発的であるため
- 常に動き続け、レビュー、話し合い、経験を通じて改善されていくもの
重要な設計概念
鉄則:複雑さへの対応
- essential(本質的)問題と accidental(偶発的・付随的)問題
- なぜ複雑さへの対応が必要か?
- 人間の頭はプログラム全体を理解することはできないから
- 複雑な問題を 単純な問題に分割する ことが必要
- 方針
- 一度に対処しなければならない本質的な複雑さを最小限に抑える
- 偶発的な複雑さを必要以上に増やさない
高品質な設計とは
- 最小限の複雑さ 凝った設計にするな
- 保守性
- 疎結合
- 拡張性
- 再利用性
- 高いファンイン
- あるクラスが、他のたくさんのクラスで使われている
- 低いファンアウト
- 1 つのクラスが使用する他のクラスが少ない
- 移植性
- 無駄がない 足すものがなく削るものもない
- 階層化
- 汚いコードを覆うインターフェース層を追加する、など
- 標準化
- 独自のフレームワークをつくるより、一般的なデザインパターンを採用する、など
設計のレベル
- Lv1 ソフトウェア
- Lv2 サブシステム・パッケージ
- Lv3 クラス
- Lv4 ルーチン
- Lv5 ルーチン内部
Lv1 ソフトウェア
システム全体のこと。
Lv2 サブシステム・パッケージに分割
- 大きな単位でシステムを分割する よくあるサブシステムの分け方:
- 業務ルール 源泉徴収額の計算、など
- ユーザーインターフェース 画面描写
- DB アクセス
- システムへの依存部分 windows 用コード、Mac 用コードなど
- サブシステム間のやり取りを可能な限り少なくする => 複雑さへの対処
- ベスト サブシステムが別のサブシステムのルーチンを呼ぶ
- まあまあ サブシステムが別のサブシステムのクラスを含む
- だめ サブシステムが別のサブシステムのクラスを継承する
Lv3 クラスに分割
- パッケージ内で、機能をクラスに分割し、クラスを設計する
- クラスのインターフェース(パブリックなルーチン)も検討する
Lv4 ルーチンに分割
- 各クラスをルーチン(プライベートなルーチンを含む)に分割し、ルーチンを設計する
- このレベルで検討した結果、Lv3 に戻ってインターフェースを変更するのもアリ
- 担当エンジニアの頭の中で行われることが多い
Lv5 ルーチンの内部設計
- ルーチンの設計を行う
- 担当エンジニアの頭の中で行われることが多い
構成要素の設計:ヒューリスティクス
設計には、必ず正しい答えがあるわけではなく、常に発見的・試行錯誤的である。 このため「完璧ではないものの、概ね良い答えが出るであろう方法(≒ 経験則)」で設計は行われる。 この方法のことをヒューリスティクスと呼ぶ。
以下、いくつかのヒューリスティクスを説明する。
現実の世界をオブジェクトにする
- 属性を決める
- メソッドを決める
- 包含や継承の関係を決める
- 属性・メソッドのパブリック・プライベートの別を決める