- 経験豊富な開発者たちが、新人開発者とソフトウェア開発の哲学を共有した内容
- 全面書き直し(ground-up rewrite)を避けること、スケジュール管理とコード品質、自動化、エッジケース対応など、さまざまなテーマに関する助言を含んでいる
どんな代償を払ってでも、全面書き直し(ground-up rewrite)が魅力的に見える状況を避けること
- 全面書き直しの誘惑が生まれる時点とは、すでに蓄積した技術的負債のために既存コードの保守が難しくなった瞬間である
- コードの複雑さが積み上がる段階で、警告サイン(単純な修正すら難しい、コメントや文書化が困難、コアコードを理解している人が減る、など)を早めに捉え、積極的に解決策を探るべきである
- 拡張が終わった後は、必ず複雑さを減らし、品質を整える統合段階を経るべきである
- 全面書き直しが避けられなくなったなら、プロジェクトはすでに危険な段階に入っていることを意味する
- リスクを減らすには、継続的に技術的負債を管理し、コード品質を注意深く監視しなければならない
使える時間の半分で、作業全体の90%を完成させること
- まずコードを書いて動くようにすることは、作業全体の半分程度にすぎない
- その後の段階(エッジケース処理、テスト、デプロイ、文書化、性能、保守性など)をきちんと終えるには、思っている以上に多くの時間が必要になる
- 十分なバッファを取り、予想外の問題や仕上げ作業(ポリッシュ)に対応すべきである
- 結局のところ、「最初にコードをとりあえず動かすこと」と「完成した機能にすること」の間のギャップを認識し、それをスケジュールに反映しなければならない
ベストプラクティスを自動化すること
- 開発者に対して繰り返し「こうすべきだ」と口頭や文書で強調するだけでは、ミスが起きやすい
- 可能な場合は、自動テストやスクリプトによって「ルール違反時はビルド失敗」のような形で強制するほうが効果的である
- 新たに導入されたルール(禁止するAPI、必ず付けるべきコメントなど)についても、段階的に自動化することでエラーや漏れを減らせる
- ただし、すべてを自動化できるわけではなく、厳しすぎるルールは開発の流れを妨げることもあるため、バランスが必要である
極端な(病理的 / Pathological)データも考慮すること
- 正常入力(ゴールデンパス)だけでコードを判断するのは危険である
- リクエストが遅延したり中断されたりする場合、膨大なデータ(数百万〜数億行)、奇妙な文字列(長すぎるもの、スラッシュや空白を含むもの)などの問題状況を想定すべきである
- エッジケースに徹底して備えることが、最終的なコード品質を左右する
たいていは、もっと単純に書ける方法がある
- 最初にコードを動かした後、時間的な余裕を持って、より単純で明確になるよう改めて改善するのがよい
- 「よさそうに見える解法」を見つけたとしても、さらに良い解法がないか見直す余裕を持つ姿勢が重要である
- 長く複雑なコードを簡潔にリファクタリングする過程が、最終的な品質を高めてくれる
テストしやすいようにコードを書くこと
- インターフェースと副作用を最小限にすれば、自動テストを書くのははるかに容易になる
- テストしにくい構造なら、カプセル化が適切にできていない可能性が高い
- 実際にテストが円滑に進められる形でコード構造を設計すれば、バグを早期に発見できる
コードが「証明上」問題ないからといって終わりではない
- 構造的に安全に見えるコードであっても、周辺環境の変化や一部の呼び出し方の変更によって問題が生じることがある
- セキュリティ関連コードなら、現在の呼び出し元が安全であっても、将来変更される可能性を考慮して設計すべきである
- コードは「明らかに安全で、変更されても問題が起きないように」書かれなければならない
コメント
- 「この手紙を短く書けなかったのは、短く書く時間がなかったからだ」という文の出典はPascalである
- 常に off-by-one error に注意すること
- Wikiに新しいガイドラインを追加する
- 社内の文書化・知識共有の仕組みがきちんと整っていないと、情報が散在して混乱した状況が生まれる
- 公式文書が承認プロセスを経るうちに旧版化したり、複数のWikiが同時に存在してどれが正式な資料なのかわからなくなったりする問題が発生する
- Wikiを一か所に定めてすべての資料を集約し、不足している部分があれば開発者が直接書くか、リバースエンジニアリング(reverse-engineering)によって埋めていく方式が有効である
- 文書化が他の場所(ソース管理、コードコメントなど)とうまく連携していれば、最新情報を保ちやすい
- 自動テストやビルド環境が複雑だったり、開発者に公開されていなかったりする場合、実際には全体テストを回した経験がないという状況が生じうる
- Jenkinsのビルドスクリプトは単純に保ち、「cd project; ./build-it」の形で構成し、このスクリプト自体もソース管理に含めるほうが望ましい
- チーム全体が同じ環境(例: 仮想マシンイメージ、ビルド設定)でビルドとテストを実行できるよう共有すれば、問題の発生を事前に減らせる
- エッジケースを開発段階から考慮し、
destroy_object(foo) のように foo が NULL でも安全に動作するよう処理したり、create_object() が失敗した場合に destroy_object() を内部的に呼び出すようにしたりする方法が有益である
- 究極的には、すべての開発者が文書とビルド/テスト環境に簡単にアクセスし、参加できるようにすることが重要である
- 文書化と自動テスト: 知識共有のための文書やWikiの重要性、そしてJenkinsのようなCI/CD環境の設定が共有可能であるべきだという実務的提案が言及されている
- 人々が望ましい行動を覚えるまで「不便を与えること」以上に効果的な行動変容の道具はない
- チェスの格言に「よさそうな手が見えたら、まず指せ」というものがあるが、それに反対意見があるように、プログラミングも同じで両面性がある。
6件のコメント
> たいていは、よりシンプルに書ける方法がある
より簡潔な解決策を考えることが、コードやポリシーの複雑さを下げると信じています。
> 「可能な時間の半分で、作業全体の90%を完成させること」
これは本当にその通りだと思います。一度で完璧に実装するというより、素早く2回見直すほうがミスも減りますし、時間管理もうまくできるようになります。
> 常に off-by-one error に注意すること
off-by-one error とは何だろうと思っていたのですが、
for (int i = 1; i < n; ++i) { ... }、for (int i = 0; i <= n; ++i) { ... }のような境界条件に関連するエラーなのですね。> どんな代償を払ってでも、全面的な書き直し(ground-up rewrite)が魅力的に見える状況は避けるべきだ
多くのIT企業のビジネスが成長できなくなる落とし穴のようなものだと思いますし、技術的負債が多く積み上がった状態でそれを解消できない(あるいは解消したくない)エンジニアリング組織ほど、全面的な書き直しを魅力的に考えがちなように思います。
本当に納得できる理由がない限り、書き直しはできるだけ避けるべきだという著者たちの考えに深く共感します.
Hacker Newsの意見
ソフトウェア開発は試行と学習を繰り返すプロセスである。経験豊富な開発者はコードを書いてテストすることで理解を深め、多くを学ぶが、それは会議や計画では見えにくい。ジュニア開発者は本番投入可能なコードを提供することに苦労し、破棄される作業を嫌がる。マネージャーに開発経験が不足していると、こうした問題は悪化しうる
「長い手紙を書いたことをお詫びします。短く書く時間がなかったので」という引用は、フランスの数学者であり哲学者でもあるBlaise Pascalの言葉であり、短く書くほうが難しいことを意味している
90/50ルールは、コードの品質を高めるためにテストと例外処理を重視すべきことを強調している。自動テストの設定は、コードベースにおける明確な期待値を定めるのに役立つ
コンピュータサイエンス教育では複雑なコードを書くよう促されがちだが、読みやすいコードを書くことが重要である。変数名や関数名の付け方、一貫したフォーマット、モジュール化された設計などが必要だ
Ratchetというメカニズムは、将来に向けた完全な方法を提供する
経験やドメイン認識を一般化しようとする試みは、誤った一般化につながる可能性がある。開発は複雑さを管理する技術であり、失敗を通じて学ぶことが重要だ
「作業の最初の90%が時間の90%を占め、残りの10%がさらに別の90%の時間を占める」という引用は、開発の現実をよく表している。例外処理、エラー、ユーザビリティ、セキュリティなどを考慮すると、多くの予期しない作業が必要になる
Joel Spolskyの文章は書き直しの危険性に警鐘を鳴らし、DevOpsの知恵を強調している
コードの可読性を最適化し、バグが発見されるまでの時間が長くなるほどコストが増大することを認識すべきである。関数型プログラミングの原則を好み、強力な型システムを使うことは有益だ