LoginSignup
1
2

「きのこ本:プログラマが知るべき97のこと」を読んでまとめてみた

Last updated at Posted at 2024-03-17

1. はじめに

通称「きのこ本」と呼ばれている書籍はご存じでしょうか?
世界のプログラマ達によるエッセイ集のようなものであり、数種類の書籍でまとめられています。その中で「プログラマが知るべき97のこと」を読んでみて自分なりに備忘録としてまとめたものになります。(いくつかの章では章の名前だけを記載しています)

ありがたいことに、有志の方がこれらの本をGithubで公開しており、閲覧することが可能です。

2. 目次

3. 本編

[01] 分別のある行動

  • 先送りされていく修正作業のことを技術的負債と呼ぶ
  • 技術的負債はできるだけ早く返済する。分別のある人ならそうするはず

[02] 関数型プログラミングを学ぶことの重要性

  • 小さな関数を数多く作り、個々の役割を限定するほうが得策
  • 個々の関数は自ら渡される引数にのみ作用するようにする
  • そうすれば不具合は減り、デバッグも簡単になる

[03] ユーザが何をするかを観察する(あなたはユーザではない)

  • 観察していて最初に気づくのは、おそらく「ユーザは基本的にだいたい同じようなことをする」ということ
  • 作業を進める順序もどこで間違えるかも、ほぼ同じである
  • つまりソフトウェアは、こういうユーザの基本的なふるまいを踏まえて設計する必要がある
  • ユーザを直接観察すればそれを防ぐことが可能
  • ユーザの視野は狭くなるので、ヘルプメニューよりもツールチップの方が有用

[04] コーディング規約を自動化する

  • コーディングに規約を設ける目的の1つは「誰も、自分の書いたコードを『私物化』できないようにする」ということ
  • コーディング規約は可能な限り、自動的、かつ強制的に守られるようにすべき
    • コードの整形処理をビルドプロセスに含めてしまう。コードのコンパイルをするたびに誰もが必ず自動的に整形する
    • 静的なコード解析ツールを使用してコードを解析し、望ましくないアンチパターンがないか探す
  • コーディング規約は固定的でなく、変化していくべき
  • プロジェクトが進行していけば、そのプロジェクトで求められるものも変わっていく

[05] 美はシンプルさに宿る

  • プログラマがコードを書くときに留意すべきこと
    • 可読性、保守性、開発効率、美しさ(シンプルさ)
  • オープンソースのソースコードを入手して、内容をよく見ること
  • できるだけ著名なプログラマが書いたソースコードの方が良い
  • 美しいコードとは、突き詰めれば、シンプルなコード
  • システムを構成する各部分がすべてシンプルであること
  • 個々の部分が担う責務も最小限に抑えられていて、部分同士の関連もシンプル
  • シンプルできれいなコードはテストもしやすく、開発速度を落とさずに長期間にわたる保守が可能

[06] リファクタリングの際に注意すべきこと

  • リファクタリングするにあたって初めにすべきことは既存のコードベースとそのコードに対して書かれたテストコードの洗い直し
    • 具体的に現状での良い点、悪い点、強味、弱みを1つずつ確認する
    • 良い点、強味を残しながら、悪い点、弱みを克服することにつながる
    • 既存のシステムに手を加えれば必ず元よりも良いものになるはずと考えがちです
    • 実はなにもよくならないことがあるし、もとより悪くなくこともあり得る
    • 既存のコード、テストを十分に検証しなければ過去の失敗に学ぶことができない
  • 一度に大幅な変更を加えるよりも少しずつの変更を数多くするべき
    • テストからのフィードバックを得ることができ、変更がシステムへ及ぼす影響を容易に知ることができる
    • 変更を加えたらテストが百個以上も失敗するのは非常につらいもの
    • いらだちと焦りから、誤った意思決定をしてしまう
    • 失敗するテストが2,3個ならば、冷静に確実な対処ができる

[07] 共有は慎重に

  • 再利用も重要だが、コンテキストが大事
  • システムを構成するコードの絶対的な行数は減らしたいが、依存しあう部分を増やしてしまうこともある
  • 依存関係が生じる場合、コンテキストが重要になる
  • コンテキストさえ適切なら再利用は有効な手段となる

[08] ボーイスカウト・ルール

  • モジュールをチェックインする際には、必ずチェックアウト時よりも美しくする
  • この簡単なルールを守ることで、今よりもシステムの品質が低下することを防げる
  • むしろ、時間がたつごとにシステムの品質は徐々に向上していくはず
  • 開発にかかわるチームの全員が自分の担当する小さな部分だけではなく、システム全体の質に目を向けることにつながる

[09] 他人よりもまず自分を疑う

  • デバッグに関して一般にすべきなことは、具体期には、問題個所を切り分け、呼び出し先をスタブに置き換えテストを書く
  • スレッド間の整合性にかかわるバグを一般的なデバッグ作業やユニットテストで見つけ出すことは非常に困難
  • 設計をシンプルにするということが何よりも大切になる

[10] ツールの選択は慎重に

  • インフラのコードよりも、ビジネスドメインのコードに時間と能力をできる限り振り向けるべき
  • 一般的に新しいものをゼロから作るよりすでに存在するものを買ったほうが安くつく
  • ツールの選定において、初期の段階で重視するのは、インフラ関連の低水準プログラミングの手間を省き、問題の発生を防ぐこと
  • 分散アプリケーションを開発するなら直接ソケットを扱うのではなく、ミドルウェアを利用するべき

[11] ドメインの言葉を使ったコード

  • プログラムに暗黙の了解の部分があると他人が見たとき、それがわからず苦労することになる
  • 暗黙の了解の部分は作らず、できるだけ明確にしておくべき

[12] コードは設計である

  • コードを書くことは設計をすることであるということを肝に銘じる必要がある
  • 作る人間の能力を超えるほどの高度な製品、複雑な設計が求められている
  • さらに製品を早く市場に出せという圧力が強い状況では、設計不十分なまま製品が作られることになる
  • 建設の世界では、「模型」をつくるが、ソフトウェアでは「自動テスト」がそれにあたると考える

[13] コードレイアウトの重要性

  • 読んだり探したりの時間のほうが長いため、効率化を考える必要がある
    • 目立たない部分を作る
      • 見た目が似ているコードは動きも似ているという原則を徹底する
    • レイアウトに語らせる
      • 見てすぐわかるコードを書くためにはレイアウトも重要
    • コンパクトにまとめる
      • 一度に画面表示できるものが多くなればスクロール量やファイルの切り替えが少なくて済む

[14] コードレビュー

  • コードレビューはコードの質をあげ、欠陥を減らすためのもの
  • コードレビューの目的は、コードの誤りを修正するだけではない
  • 重要なことは、チーム全員に同じ知識を共有させること、コーディングにおいて全員が守るべきガイドラインを確立すること
  • コードレビューを成功させるために最も有効な方法は、レビューを楽しいものにすること
  • レビューで最も大事なことは人です

[15] コードの論理的検証

  • 静的コード解析によって多くのプラクティスを自動的にチェックできる
    • オブジェクトは可能な限り、不変オブジェクトにする
    • 関数はできる限り短くし、機能は1つに絞り込む
    • 関数の大きさの目安は24行程度
    • 関数のパラメータはできるだけ減らす(最高でも4つまでにする)

[16] コメントについてのコメント

  • 「書くのに苦労したコードは読むのにも苦労する」という格言がある

[17] コードにかけないことのみをコメントにする

  • たとえコメントを入れても、それが不適切なものであれば価値はゼロ(もしくはマイナス)である
  • コメントの内容は、すぐに陳腐化することに注意が必要
  • コードに書いていないことやコードにかけないことをコメントに書いてあるのが読む人にとって価値があるもの

[18] 学び続ける姿勢

  • 常に自分よりもレベルの高い人と仕事をするようにする
    • 自分よりレベルの高い人がいないと、学ぶことは難しくなる
  • 自分の利用しているフレームワークやライブラリに対する知識を深める
    • その機能と構造を十分に理解すれば、よりうまく使いこなせるようになる
  • 少なくとも毎年1つは新しい技術、ツールについて学ぶ
    • 未知なものに触れることは新たな発想のもとになる
    • 必ずしもコンピュータ関連技術でなくてもかまわない
    • システムが応用されるドメインについても学ぶことも重要である

[19] 誰にとっての利便性か

  • メソッドを「実装する側にとっての利便性」ではなく「呼び出す側にとっての利便性」を考える必要がある

[20] すばやくデプロイ、こまめにデプロイ

  • インストール/デプロイ作業こそが顧客が初めて製品に触れる機会である
    • この作業自体は簡単なものですが、信頼できる本番環境を作り上げるための第一歩

[21] 技術的例外とビジネス例外を明確に区別する

  • プログラムの実行時に起きる問題には大きく分けて2つの原因がある
    • 技術的な原因:アプリケーションの実行そのものが続けられなくなるような問題のこと
    • ビジネスロジックな原因:ユーザがアプリケーションの使い方を誤らせないためにわざと発生させる問題のこと
  • 2つの例外を明確に区別して扱うこと
    • 技術的例外はアプリケーションのフレームワークに対応を任せる
    • ビジネス例外はクライアントにあらかじめ対処するコードを組み込む

[22] 1万時間の訓練

  • 1万時間は膨大な時間。週に20時間なら10年かかる
  • 過去20年間におよぶ調査でも、ある知識や技術が身につくかは、大部分が訓練に費やされた時間の長さで決まる結果が出ている
  • 集中的訓練は専門技術を見つけるうえでは欠かせないもの
    • ただ反復訓練をすればいいだけではなく、自分の現在の能力を少し超える課題に取り組むことが重要

[23] ドメイン特化言語

[24] 変更を恐れない

  • リファクタチングをすれば時間と労力はかかるが、プロジェクトが進む中でその投資は十分回収できる
    • リファクタリングの経験はプロジェクトメンバーにとって良い経験となる
    • システムは本来どうあるべきかを全員が理解するようになり、システムについてのエキスパートになる
  • システムの改良にあたってすべきことは、内部インタフェースの再定義、モジュールの再構築、リファクタリングなどが挙げられる

[25] 見られて恥ずかしいデータは使わないこと

[26] 言語だけでなく文化も学ぶ

[27] 死ぬはずのプログラムを無理に生かしておいてはいけない

  • try-catchブロックをコードベースに大量に入れれば、「例外が発生しても絶対に止まらない」アプリを作ることにつながる
    • これは死んでいる人を無理やり立った状態にしているようなもの

[28] 魔法に頼りすぎてはいけない

[29] DRY原則

  • DRY(Don't Repeat Yourself: 繰り返しを避けること)原則はプログラミングに関して最も重要な守るべき原則
    • 言い換えれば、「すべての知識はシステム内において、単一、かつ明確な、そして信頼できる表現になっていなければならない」ということ
    • DRY原則はアプリのソースコードだけでなく、開発作業にも適用すべき作業
  • SRP(Single Responsibility Principle: 単一責任原則)
    • クラスに変更を加える理由は2つ以上存在してはならない

[30] そのコードに触れてはならない!

  • Webシステム開発プロジェクトの環境は下記のアーキテクチャに分割される
    • 開発者マシン上のローカル開発/ユニットテスト環境
    • 統合テストを手動、あるいは自動で行う開発サーバ
    • 品質保証(QA)チームや顧客が受け入れテストを行うステージングサーバ
    • 本番環境

[31] 状態だけでなく「ふるまい」もカプセル化する

  • システム理論において、大規模で複雑な構造のシステムを扱う際に、特に重要とされるのが「封じ込め」
    • つまり、サブルーチン、関数、モジュール、パッケージ、クラスなどの要素
    • 技術的なカプセル化だけでなく、ビジネスロジックのカプセル化も重要

[32] 浮動小数点は実数ではない

  • 浮動小数点の精度は有限であり、実数というより整数に近い
  • 浮動小数点は実数の近似値
  • 金融関係のアプリケーションには浮動小数点を使うべきではない
  • 浮動小数点は元々、科学技術計算を効率的に行うことを目的としたもの
  • 金融関係のアプリケーションでは、通貨の計算には整数を使うべき
    • 正確さを欠いていては、効率がいくら良くても価値はない

[33] オープンソースプロジェクトで夢を実現する

  • 他人の作ったソフトウェアについて学ぶのに、テストコードを書くほど効果的な方法はない
  • 努力して良いテストコードを書き、バグを見つけ、修正を提案する

[34] API設計の黄金律

  • APIを提供する際は、API自身のテストだけでなく、必ずそのAPIを利用するコードのユニットテストも書く

[35] 超人の神話

[36] ハードワークは報われない

  • 自分の働く時間や労力を減らせば減らすほど、プロジェクトへの貢献は大きくなる
  • 神経を集中させる時間、製品を生み出すのに使う時間が週に30時間を超えるなら、自分は働きすぎだと考えるべき
    • 自分のかけている労力を減らすことを検討する必要がある
  • ソフトウェア開発プロジェクトは通常、オリエンテーリングをしながらマラソンをするようなもの
    • さらに暗闇で、頼みにするのは大雑把な地図だけがある状態で走るようなもの
  • プロの仕事には、入念な準備と効率化のための努力、そして日々の反省と絶え間ない変化が必要

[37] バグレポートの使い方

  • バグレポートを書く際には3つのことが必要
    • バグの再現方法と発生頻度
    • 本来の仕様(望ましい動作や、こうあるべきという自分の意見)
    • 実際の動作(完全でなくても、自分の記録した範囲で詳しく書く)
  • 1つ注意すべきなのは、「バグ数は何かの単位、基準ではない」ということ
    • コードの行数が労力を示していないのと同じこと

[38] 余分なコードは決して書かない

  • 「より少ないことは、より豊かなこと」
  • レビューやペア作業で見逃されがちなこと
    • おもしろそうだと思えた
      • 面白いというのはコードを書く理由にならない。コードベースに新たな価値を加え得るコードのみを書くこと
    • 将来必要になるかもしれないと思うもの、そのために今書くのがベストだと判断した
      • 今不要なら、今書くべきではない
    • 必要かどうかを判断に迷った。顧客に判断を仰ぐべきだが、実装してしまうほうが簡単だと思った
      • 余分なコードを書く分、手間と時間を要し、保守にも手間と時間を要することになる
      • 実際には顧客に確認を取ったほうが簡単なはず
      • 余分なコードは、保守にかかる手間と時間が「雪だるま式」に増えていく
    • 余分な機能を正当化するため、議論を経ておらず、ドキュメントにも書かれていない要件をプログラマがでっちあげる
      • 要件を決めるのは顧客であり、プログラマが要件を決めてはいけない

[39] 最初が肝心

[40] プロセス間通信とアプリケーションの応答時間の関係

  • 応答時間はソフトウェアの使いやすさを大きく左右する
    • 何かを操作して、その応答時間を長く待たされることほどストレスのたまることはない
  • アプリケーションでパフォーマンスが問題になったとき、データ構造やアルゴリズムを調べて改善を図るのは得策ではない
    • この種のアプリの場合、応答時間を大きく左右するのは、リモートプロセス間通信(IPC)の数
  • リップルローディング:オブジェクトグラフを作成する際に、グラフ作成に必要なデータを取得するために何度もデータベース呼び出しを繰り返すこと
  • リモートIPCの数を減らす方法の1つが「思考節約の法則」を応用すること
    • 具体的にはプロセス間のインタフェースを最適化し、本当にいま必要なデータだけを必要最低限のインタラクションで取得する方法
    • ほかには、IPCをできるだけ並列化するという方法やIPCの結果をキャッシュする方法
  • アプリの設計にあたっては、入力への応答に必要なIPCの数が多くならないように配慮すべき

[41] 無駄な警告を排除する

  • ビルドで余計な警告が出るたび、それを即座につぶすということを続ける
  • そうすれば「これは意味のある警告か否か」ということを逐一判断する必要がなくなる

[42] コマンドラインツールを使う

  • コマンドラインツールを使う利点は、ビルドプロセスについての理解が深まるということではない
  • 作業によっては、コマンドラインツールのほうがIDEより簡単に効率よくできるものもある
  • IDEが裏でしていることをよく見て理解するために、コマンドラインツールを使ってみるのが最善の方法

[43] プログラミング言語は複数取得すべき

  • パラダイム(考え方やルール、記述方法などの枠組みとなるも)の違う言語を学ぶ効用はたくさんある
  • 最も顕著な効用は、1つのパラダイムしか知らなければ思いつかないような表現を使用することができる

[44] IDEを知る

  • 私は新しいIDEについて学ぶときは最初にキーボードショートカットを覚えるようにする

[45] 限界を知る

項目 アクセス時間 容量
レジスタ < 1ns 64bit
キャッシュライン 64Byte
L1キャッシュ 1ns 64KB
L2キャッシュ 4ns 8MB
RAM 20ns 32GB
ディスク 10ms 10TB
LAN 20ms > 1PT
インターネット 100ms > 1ZB
  • ハードディスクのすべてのバイトをランダムに調べていく処理をすると、完了までに32年かかる
  • RAMのすべてのバイトをランダムに調べる処理は11分かかる

[46] すべきことは常に明確に

  • 明確な目的もわからないのに闇雲に先を急ぐよりも、少し遠回りのようでも、確実に有効とわかる作業を探したほうがよい
  • 結局は最終目標に向かって着実に前に進むこととなる

[47] 大量のデータはデータベースで

  • アプリで扱うデータの量がシステムのRAM容量を超えている場合、インデックス付きのRDBMSテーブルを使うとよい
    • ライブラリに用意されているマップなどのコレクション型を使うよりも処理がけた違いに速くなる
  • RDBMSを利用するメリットとして大きいのはデータエレメント同士を関係つけることができること
    • このデータとこのデータは常に一致していなければならないというような制約条件を明確に宣言できる
    • 一方のデータだけを更新して、もう一方のデータを更新し忘れる「ダングリングポンタ」という問題の発生を防止できる

[48] いろんな言葉を学ぶ

  • 優秀なプログラマはプログラミング言語を巧みに操るだけでなく、自然言語も非常にうまく使う
    • 話す力が大事なのは、他人とのコミュニケーションが円滑にできるからだけではない
    • 自分の思考を明確にするためにも、話す能力は重要である
    • この能力は問題を抽象化する際には欠かせません
    • 抽象化こそ、プログラミングの核心

[49] 見積とは何か

  • 見積、ターゲット、コミットメントの3つの定義を明確にする
    • 見積:あくまで概算、大まかな判断なので、ある程度以上の正確さは期待できない
    • ターゲット:実現したいビジネス上の目標を明文化したもの
    • コミットメント:ある機能をある期日までに、一定以上の品質で提供すると約束すること
  • 見積を求められた時には必ず相手が、「見積」「ターゲット」「コミットメント」のどれを求めているのかを確認すること

[50] Hello,Worldから始めよう

[51] プロジェクト自身にしゃべらせる

[52] 「その場しのぎ」が長生きしてしまう

  • 暫定ソリューションの数があまりに増えればシステムのエントロピーは増大する
    • その結果、複雑になってしまい、保守が難しくなる
  • 暫定ソリューションに対する対応は大きく3つに分けられる
    • 暫定ソリューションをはじめから一切作らない
      • 現実には非常に難しい。厳密に守ると間に合わないことが現実には起きるから
    • 暫定ソリューション修正の優先度が上がる体制つくりをする
      • 文化を意図的に変えるのは容易でない
    • 現状のまま何も変えない
      • 消去法で、この方法を選ぶことになる
  • その状況を変える最善の方法は暫定ソリューションに修正を加えるのではなく、不要にすること
  • 後から改めて、より優れたソリューション、より有効性の高いソリューションを作ること

[53] 正しい使い方を簡単に、誤った使い方を困難に

  • 良いインタフェースとは次の2つの条件を満たすこと
    • 正しく使用する方が操作ミスをするより簡単
    • 誤った使い方をすることが困難
  • 使い方を間違えにくいインタフェースを作るには2つの方法が有効である
    • 1つはユーザがしそうな間違いを事前に予測し、それを防止する策を講じること
    • もう1つは、リリース後の早い時期に実際にインタフェースがどのように誤用されるかを観察し、改良を加える
    • 誤用を防ぐ最良の方法は、そもそも誤用を不可能にしてしまうこと

[54] 見えないものを見えるように

  • ソフト開発プロジェクトを進める際はいつでも、身に見える証拠がたくさんあるという状態を維持すべき
  • 目に見える証拠があれば進捗状況も正確に把握できる
    • ユニットテストを書くことによって、対象コードのテストが容易か困難かがわかる
    • ユニットテストを実行すれば、コードの動きがわかる
    • 掲示板やカードを使えばプロジェクトの進捗が可視化できる

[55] 並行処理に有効なメッセージパッシング

  • 並行処理に関しては、非常に解決の難しい問題が数多くある
  • 問題の根本は結局、「共有メモリ」である
  • 共有メモリをやめる方法として、プロセスやメッセージパッシングを使う方法がある
    • ここでの「プロセス」はOSのプロセスではなく、「他から保護された独立した状態の実行コード」という意味
  • 共有メモリを使わずメッセージパッシングを使ってプログラミングすることが、現在のコンピュータにとって有効な方法

[56] 未来へのメッセージ

[57] ポリモーフィズムの利用機会を見逃さない

[58] テスト担当者はプログラマの友人

[59] バイナリは常に1つ

  • 問題の発生を減らすために「みんなが同一のバイナリを使用するようにする」ことが重要
    • 開発を通じたどのステージにおいても、バイナリを常に1つという状態を維持する
    • 提供先の環境毎に違うバイナリを作るのではなく、どの環境でも同じバイナリを使い、細かい調整は環境側で行う
  • ビルド中にコードを書き換えたり、ターゲットごとに違うコードを使うというのは賢明でない
    • こういうことをするのは、開発チームの中に設計チームについて真剣に考える人がいない証拠
  • 環境に関する情報もバージョン管理の対象とする

[60] 真実を語るはコードのみ

  • プログラムの動作を完全に正確に知るには、結局はソースコードを見るしかない
  • 読んでわかりやすいコードを書くには、重要なのは、名前の付け方
    • システムを構成する各部分に見てすぐに機能がわかる名前をつける
  • コードをわかりやすくするには、部分同士ができるだけ依存関係を持たないようにすること
  • 直交性を高めることが大切
  • 自動テストを書いて「プログラムがどのような挙動をするはずか」を説明し、インタフェースのチェックをするというのも有効

[61] ビルドをおろそかにしない

  • ビルドスクリプトのコードは「コードではない」ということはおかしな話である
  • ビルドスクリプトを書く言語も言語の一種ならば、積極的に学んでもいいはず
  • コードはビルドされなければ何の役にも立たないし、アプリのコンポーネントアーキテクチャを定義するのもビルドである
  • ビルドは開発プロセスの中でも特に重要な部分といえる
  • ビルドスクリプトは、不適切な書き方をしてしまうと捕手が困難になるうえ、後で改善することも容易ではなくなる

[62] プリミティブ型よりドメイン固有の型を

  • 言語の提供するプリミティブ型を使うより、ドメイン固有の型を使うほうが好ましいという場合は珍しくない
  • ドメイン型の定義をすればコードの品質を高める次のような利点がある
    • コードが読みやすくなる
    • テストがしやすくなる
    • コードの再利用が容易

[63] ユーザの操作ミスを防止する

  • ある程度のフォーマットの違いは許容できるようにすべき
  • 入力してほしいのはあくまで情報であり、データではないということを考慮すべきである
  • デフォルト値を提供することもエラー防止の方法としては有効である

[64] プロのプログラマとは?

  • プロフェッショナルなプログラマの最大の特徴は「自分が責任をとる」という態度、責任感です
    • キャリアに責任を持つというのは、自分の力で自分の価値を高め、成長していくということ
    • プロのプログラマは自分の書いたコードに責任を持つ。間違いなく正しく動くと確認できるまではリリースしない
    • チームプレイヤーで、一人一人が自分の仕事ではなく、チーム全体のアウトプットに責任を持つ
    • バグリストが一定以上の規模にならないよう、常に注意を怠らない
    • 絶対に、間に合わせのいい加減な仕事はしない
  • プロであるということは責任を負うということ
    • 自分のキャリアにも製品の質にも責任を負う
    • 製品の質に責任を負うということは無駄がなく、動作も正しいコードを常に書き続けるということ
    • たとえ納期に追われていて余裕がなくなった時でも決して手を抜くことなく最善の努力を尽くして良い製品を作るということ

[65] バージョン管理システムを有効に使う

  • ソフトリリースにはタグ付けできる
    • リリース毎にタグを見てすぐにそれとわかるような名前を作ることができる
    • タグ付けをしておけばいつでも好きなバージョンのファイルをすぐに取り出すことができる
  • プロジェクトを構成する要素はとにかく何でもバージョン管理の対象にすべきである
    • ソースコードだけではなく、ドキュメントやツール、ビルドスクリプト、テスト、画像ファイル、ライブラリなど

[66] いったんコンピュータから離れてみる

[67] コードを読む

  • プログラミングの技術を本気で磨きたいと思っていた場合、とにかくコードを読むことである

[68] 「人間」を知る

  • ほとんどのソフトウェアは、誰かの目標達成を手助けするために書かれる
    • つまり、人は、人ともに、そして人のためにソフトウェアを書く
    • ソフトウェア開発は「人のビジネス」である
  • 私たちがお互いに理解し合うためには「定義の共有」ではなく「経験の共有」が必要
    • 生きていく中で得た経験に共通する部分がないと理解し合えない
  • プログラマがやろうとするものもやはり、アリストテレス的な「分類」である
    • ユーザの話を危機、求められていることを明確な定義をもとにカテゴリー分けをする
    • しかし、人間は元来、そのように世界を認識していない
    • 人間は「例」に基づいて物事を理解している
    • 例の中でも「プロトタイプ」と呼ばれるものは、他よりも優勢で大きな影響力を持つ

[69] 車輪の再発明の効用

  • 新たにコードを書くより、既存のコードを流用する方が安全でコストも少なくて済む
    • 既存のコードはその多くが「正しく動作するとすでに確認されたコード」である
    • 厳しいテストによって品質を高められ、製品として役立ってきた実績のあるコードが多い
    • 既存の製品やコードベースに時間と労力を投資したのに、同様のものを再度作ってまた時間と労力を投資するのは無駄という考えもある
    • あえて車輪の再発明をするのだとしたら、それなりの理由が必要になる
  • 本を読むなどして知識を頭に入れることも大切である
    • しかし、優れたプログラマになるためには、経験を積むことがどうしても必要
    • 現場で多くを見て、自分の手で何かを作ることが必要である
    • 車輪の再発明はプログラマが学び、技術を高めるうえで非常に重要なこと

[70] シングルトンパターンの誘惑に負けない

  • シングルトンパターンは魅力的であるが、利点よりも弊害の方が多いといえる
    • テストの妨げになること、そして保守性の点でも不利である
    • 「必要なインスタンスは1つだけ」という要望は、多くの場合推測に過ぎない
    • 理論的には独立しているはずのコード間に暗黙の依存関係を生むことになる
    • マルチスレッド環境での仕様は特に危険が大きい
      • シングルトンオブジェクトへのアクセスにはロックが必要になる
      • 単純なロックでは効率がいいとは言えないため、DCLPを使うことが増える
  • シングルトンパターンは必要なインスタンスが絶対に1つだけと確信できるクラス以外では使うべきではない
  • さらに、シングルトンをグローバルアクセスポイントとすることも避けるべき

[71] パフォーマンスへの道は地雷コードで敷き詰められている

  • パフォーマンスチューニングでは、多くの場合、コードの変更が必要になる
  • どんなコードにも必ず、複雑すぎる部分や依存性の高すぎる部分が含まれている
  • 「ソフトウェアメトリクス」という、コードの様々な特性を定量的に評価した指標がある
    • コードメトリクスの値は、コードの品質に大きく関係する
    • その中でもコードの依存性を図るのに使えるのが、「ファンイン」と「ファンアウト」である
    • ファンインは、あるメソッドが他のメソッドから呼び出される回数を表す
    • ファンアウトは、あるメソッドが他のメソッドを呼び出す回数を表す

[72] シンプルさは捨てることによって得られる

  • 質の悪いコードは容赦なく書き直す、場合によってはすべて消してしまう、というくらいの姿勢で取り組む方が確実に質の向上に繋がる

[73] 単一責任原則

  • 変更する理由が同じものは集める、変更する理由が異なるものは分ける

[74] 「イエス」から始める

  • 「ノー」ではなく、「イエス」という返答から始めるようにすればそれだけ物の見方は大きく変わる
  • 「イエス」から始めることはテクニカルリーダーには不可欠な態度である
  • 要望を出されたコンテキストが分かれば、新しい可能性に広がることが多い
  • 要望が出されたときに、相手に「なぜ」と問う代わりに、自分に「なぜ」と問いかけてみることも有効
  • 「イエス」と言って要望に答えるのは、顧客だけではなく、自分自身や開発チームにとっても役立つ
  • 「イエス」から始めれば、人との対立は生まれず、協力関係が生まれる

[75] 面倒でも自動化できることは自動化する

  • 同じことの繰り返し作業はプロジェクトのあちこちに見つかるはず
    • バージョン管理、コンパイル、ビルド、ドキュメント作成、デプロイ、レポート生成など
  • 自動化できる、自動化すべし、と思う作業が見つかるたびに、その自動化に必要な知識を身に着ける方法でよい

[76] コード分析ツールを利用する

  • コードの品質向上に当たってはテストに頼るのではなくて、解析ツールも積極的に利用するべき

[77] 偶然の仕様ではなく本物の仕様のためのテストを書く

  • テストの典型的な落とし穴は、実装の詳細にテストを強結合してしまうこと
  • 実装コードには元々の設計でそうしようと意図したわけではなく、実装の都合でたまたまそうしているという部分が少なくない
    • そのため、いわゆる「偶然の仕様」までテスト対象とするのは問題であり、確かめる意味はない
  • 単に現状のものを良しとするテストでは実施する意味があまりない
  • テストを効果的なものにするためには、実装で生じた偶然の仕様を確認するのではなく、元々の要求に合っているかを確認するべき

[78] テストは夜間と週末に

  • 金曜日の午後6時から月曜日の午前6時までは60時間もある
    • それだけの時間があれば、かなり価値のあるテストができるはず
    • 例えば、安定性テストや耐久テスト、パフォーマンステストなど

[79] テストのないソフトウェア開発はあり得ない

  • 土木建設の「橋」のように形のあるものをテストすることは容易でない
    • いったん、作ったものを壊したり修正したりするのも難しいので、おおよその検討で作ってテストすることはできない
  • ソフトウェアの場合、とりあえず作ってみるコストは圧倒的に安く済む
    • また、テストを容易に進めるための手法やツールもこれまでに数多く生み出されている
  • テストはソフトウェア開発の中でも特に難しく、そして重要な部分である

[80] 1人より2人

  • プログラマは技術が優れているだけでは十分でない
  • 他人との連携でより大きな成果が上げられるようでなくてはならない
  • 1人よりもペアの時のほうが、1つの仕事のために使える技術やテクニックが増える
    • その結果、問題領域についての知識も増えるため、ソフトウェア品質が向上する可能性は高くなる

[81] エラーがエラーを相殺してしまう

[82] 他社への思いやりを意識したコーディイング

  • 孤立して作業をしていると、このコードはいずれ他人によって使用され、実行されるのだということを、つい忘れがちになる
  • 誰かが書いたコードの質は、必ず他の誰かが書くコードの質に影響する
  • チームの同僚のことを考え、思いやりをもってコードを書けば、それは同僚たちにとって価値あるコードとなる
    • いずれ自分にもよい影響となって返ってくる

[83] UNIXツールを友にする

  • UNIXコマンドはパイプライン処理として動作するので、現在のマルチコアCPUの場合は、複数のコアに負荷が自然に分散される

[84] 正しいアルゴリズムとデータ構造を選ぶ

  • プログラミングにおける「再利用」を重視する日は多いが、いつ、何を、どのように再利用すべきが分からなければ良い結果にならない
    • それを分かるためには問題領域について、またアルゴリズムとデータ構造について十分な知識が必要である

[85] 冗長なログをは眠りを妨げる

  • ログの情報があまりに大量だと、ログがないのと同じくらい役に立たない
  • システムに何か問題がある場合、ログに残ったメッセージで最初に知ることとなる
    • 多くの場合、そのログはエラーログである
  • 参考程度に見た方がよいINFOレベルのメッセージなら重量なアプリケーションイベント1つにつき1つ出れば十分である

[86] WETなシステムはボトルネックが見つかりにくい

  • DRY原則に反しているシステムを「WETなシステム」と呼ぶことがある
  • WETなシステムには、同じ知識に対応する実装が複数存在する

[87] プログラマとテスターが協力してできること

  • テストの設計が悪いせいで自動化プロジェクトが失敗するということが少なくない
  • 設計が悪いテストとは、不要なテスト項目が多すぎるものやテスト項目の独立性が低いものをさす
  • テスターに技術的な知識がないと、独立性の低いテスト項目ができてしまうことがよくある

[88] コードは生涯サポートするつもりで書く

  • いつも「このコードは生涯、自分がサポートし続けなくてはならない」と思ってコードを書く
  • これまでに書いてきたコードは、どれほど前のものであっても、必ず今の自分の仕事に影響を与えている
  • コードを1行書くたびに、ユーザのこと、顧客のこと、そして自分のキャリアのことを考えるべき
    • このコードは自分んの今後の人生を決めるのだと思って書くべきでしょう

[89] 関数の「サイズ」を小さくする

[90] コードを見る人のためにテストを書く

  • テストはいつもコードを書き始める前に、書くようにしているのが理想的
  • 良いテストが書けているかを見極めるために「自分が誰のためにテストを書いているのか」と自らに問うてみるのが1つの方法
    • その答えが「自分のため、バグ修正の労力を少しでも減らすため」であるといいテストではない可能性が高い
    • その答えが「コードを見る人のため」であればよいテストである
  • いいテストの条件は次である
    • コンテキスト、出発点など満たすべき事前条件がわかる
    • ソフトウェアがどのように起動されるかがわかる
    • 期待される結果と確認すべき事後条件がわかる
  • テストに関しては何を見せるかだけでなく、「何を見せないか」も重要である
    • 例えば、テストのコードが多すぎれば、些細なことばかり囚われて肝心なことが理解できなくなる恐れがある

[91] 良いプログラマになるには

  • 良いプログラマとそうでないプログラマの違いは「取り組む姿勢」にある
    • 良いプログラマの姿勢は、プロフェッショナルという言葉にふさわしいものである
    • どんな場合でも「とりあえず動きそう」というだけのコードは決して書かない
    • 誰が見ても間違いなく正しいとわかる美しいコードを書くよう常に心がける

[92] 顧客の言葉はそのまま受け取らない

  • 顧客は決して自分の希望を正確に言葉にしているわけではないというのを常に忘れないようにすること
  • 同じ話を何度も聞いて初めて理解できると思った方が良いため、顧客と何度も話し合う必要がある

[93] エラーを無視するな

  • たとえどんなに問題の起きにくいコードでも、エラーチェックとエラーハンドリングは常に必要
    • 省略しても時間の節約には決してならない
  • コードからエラーを通知する方法
    • 戻り値を使用する
    • erronoを使用する
    • 例外を使用する

[94] リンカは魔法のプラグラムではない

[95] ペアプログラミングと「フロー」

ペアプログラミングのメリット

  • 不慮の事態の影響を最小限に抑えることができる
  • 問題解決が容易
  • 統合がスムーズ
  • 割り込みの影響を緩和できる
  • 新人は早くプロジェクトに馴染む

[96] テストは正確に、具体的に

[97] ステートに注目する

  • ステートに注目して考えると、コードをよりシンプル、かつ堅牢にすることへつながる

4. さいごに

これらのことは、一人でコードを書いたり、新しい技術のドキュメントを読むだけでは身につかない重要なことだと思いますので、たくさん学ぶことがあったなと思いました。次は「ソフトウェアアーキテクトが知るべき 97 のこと」について読んでみたいと考えています。

5. 参考サイト

1
2
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2