【書評】新人プログラマーのためのエラーが怖くなくなる本
はじめに(読もうとしたきっかけなど)
実務で、以下のスキルが不足していることを痛感していたため、本書を手に取った。
- エラー発生〜解決までに時間がかかる。
- 不具合が再現できない場合の対応方法の引き出しが少ない。
本の構成
- 第1章:エラーはどうして怖いのか?
- 1-1:エラーを読んでみよう
- 1-2:エラーを読まなくなってしまう理由
- 1-3:エラーに向き合う心構え
- 第2章:エラーの上手な読みかた
- 2-1:エラーの構成要素を知ろう
- 2-2:エラーの種類を知ろう
- 第3章:不具合の原因を効率的に見つけるには?
- 3-1:デバッグとは?
- 3-2:プリントデバッグをやってみよう
- 3-3:二部探索で効率的に探そう ほか
- 第4章:ツールを活用してデバッグを楽にしよう
- 4-1:デバッガは強力なツール
- 4-2:ブレークポイントを使ってみよう
- 4-3:いろいろなステップ実行 ほか
- 第5章:どうしても解決できないときは?
- 5-1:プログラマーのための情報収集テクニック
- 5-2:エラーが見つからないときは?
- 5-3:不具合が再現できないときは? ほか
- 第6章:デバッグしやすいコードを書こう
- 6-1:再代入は控えよう
- 6-2:スコープは可能な限り狭めよう
- 6-3:単一責任の原則を知ろう ほか
オススメ度:★★★★☆(4/5)
イラストや図も交えて解説されていてわかりやすく、字も大きめなため、とても読みやすかった。特にプログラミングを学び初めの方にオススメできる技術書であると感じた。
タイトルに「新人プログラマーのため」と書かれているが、エンジニア3年目となった自分でも知らない点がいくつかあったため、読んでおいてよかった。
また、いずれ自分が新人へ指導する機会ができた場合に教えるべき内容を整理することができた。
勉強(参考)になったこと
第2章:エラーの上手な読みかた
エラーの構成要素(エラーの種類、メッセージ、スタックトレース)を把握することが大切。
スタックトレース
エラーが発生するまでの処理の流れを表したもの。エラーの発生場所を教えてくれる。
最終地点でエラーが発生しているので、まずはそこから読んでいくのが効率的。
JavaScriptや多くのプログラミング言語は。スタックトレースは下から上への流れで記述されるが、Pythonは逆の順序で記述される。
第3章:不具合の原因を効率的に見つけるには?
二分探索で効率的に探そう
闇雲にプリントパックせず、全体を見渡して原因となりそうな箇所を少しずつ絞り込んでいく。システムは基本的に「INPUTから始まり、OUTPUTで出力」されるので、怪しいと感じるコード範囲の真ん中あたりに仕込むことで、その範囲を分割することができる。
時にはコードだけでなく、システム全体を分割して調査することも必要となる。(フロントエンドとバックエンド、サーバーとデータベース等)
gitを使った二部探索
bisect
(バイセクト)コマンドで過去のどの時点で不具合が発生したかを効率的に調査できる。
デバッグをすばやく進めるための考え方
- 事前に仮説を立てる
- 1度に1つのことだけを検証する(最初に立てた仮説が正しいかどうかを検証することだけに集中する)
- 小さな疑問に耳を傾ける(1つの考えに固執せず、ほんの少しでも疑問に感じる部分は、原因の可能性が低くても手を動かして確認する。)
- 手間を惜しまず手を動かして確認する
ラバーアダック・デバッグ
一人で行き詰まったときに、コードを1行ずつ誰かに説明しながらデバッグする手法。その過程でハッと解決策の糸口が見つかる可能性も。。。
第4章:ツールを活用してデバッグを楽にしよう
ブレークポイントをコード上で設定する
デベロッパーツール上だけでなく、ソースコード上でdebugger;
文を挿入することでブレークポイントを設定できる。
<script>
console.log('hoge');
debugger; // ブレークポイントを設定
console.log('hogehoge');
</script>
条件付きブレークポイント
以下のように、特定の条件になったらデバッガが起動する機能
・特定の式の結果が真値になったとき
・コードが指定回数実行されたとき
コード上に自分で条件式を書いても同じことができる。
<script>
for (let i = 0; i < 10; i++) {
if (i === 5) { // ブレークポイントを起動したい条件をコードじで表現する
debugger;
}
console.log(i);
}
</script>
ブラウザの便利な条件付きブレークポイント
種類 | 概要 |
---|---|
XHR/フェッチブレークポイント | ネットワーク通信をした際にデバッガを起動でき、ドメインを指定して条件の絞り込みもできる。ネットワーク通信に関するコードのデバッグ時に役立つ |
DOMブレークポイント | HTMLの要素の状態変化を条件に指定できる。属性が変更されたとき、要素が削除されたときなど。DOM操作をするコードのデバッグ時に役立つ |
イベントリスナーブレークポイント | イベントトリガーにデバッガを起動できる。不具合の原因となっているイベント(マウス/キーボード操作など)は特定できているが、該当のコード箇所がわからない場合に役立つ |
変数を監視する
指定した変数の中身を任意のタイミングで確認できるが、監視対象に設定できる変数はグローバルである必要がある。(関数やクラス内部のプライベートな変数にはアクセスできない。)
この場合は、一時的に監視したい変数をグローバル変数に代入することで監視ができる。
<script>
calcButton.addEventListener("click", () => {
const sumNum = sum(num1.value, num2.value);
window.sumNum = sumNum; // windowというグローバルなオブジェクトに変数を代入
result.textContent = sumNum;
});
</script>
第5章:どうしても解決できないときは?
プログラマーのための情報収集テクニック
検索テキストをダブルクォーテーションで囲む
Googleなどで検索する際には、検索するテキストをダブルクォーテーションで囲むことで「完全一致検索」ができる。
英語で検索する
用語 | 意味 |
---|---|
not working | 動かない |
how to use | 使い方 |
how to implement | 実装方法 |
GitHubを使った検索テクニック
「GitHub Code Search」の機能を活用することで、自分が使っているライブラリがうまく動かないときに参考になる場合がある。
正規表現を使った検索
Googleと同様に複数の単語を検索テキストに含めると、それぞれの単語を含むファイルがヒットするため、正規表現を使用するのがよい。
用途 | 記述方法 | 記述例 | 検索結果 |
---|---|---|---|
コード検索 | /正規表現/ |
/export function hello/ |
export function hello |
コード検索 | /正規表現/ |
/function say[a-z]{4}/ |
function sayName , function sayFile など |
ファイルパスの絞り込み | path:パス名 |
path:tailwind.config.js |
tailwind.config.js のファイル名 |
コミュニティで質問する
具体的で明確なタイトルをつける
例:「ReactのuseStateを使っているが変更が反映されない」
問題の詳細を網羅的に記述する
- エラーがある場合は全てを記述する。(長すぎる場合、適切な部分だけ記述する。)
- 言語、ライブラリ、動作環境の名称とバージョン
- 操作しや進行した作業手順を完結に箇条書きで示す。
- 期待する動作や目標を明記する。
エラーが見つからないときは?
エラーの出力設定を確認
PHPでは以下のようにエラー出力をOFFにする設定がある。
<?php
init_set('display_errors', 1); // これがあるとエラーが表示されなくなる。
$nickname = 'hoge'; // 正しくはecho $nickname;
echo nickname;
?>
不具合が再現できないときは?
まずは不具合が再現するかを確認する。再現できない場合は再現に必要な「条件」を見つけるため、以下のような情報を収集する。
- ユーザーの操作環境(OSやブラウザの種類・バージョン、回線状況)
- 不具合が発生した日時
- 該当日時にエラーログが記録されているか
- ログインなどユーザー固有のデータや処理が存在するか
本番環境のエラー収集方法
どうしても不具合が解消できない場合の回避術
「Work around(回避術)」という、根本的な解決にならないが別の方法で目的を達成するアプローチ。
問題を隠蔽する場合もあるため、ポジティブなアプローチではないが、不具合の解決は時間との闘いでもある中で、何もしないよりはマシという選択肢。
第6章:デバッグしやすいコードを書こう
純粋関数を利用する
純粋関数は以下条件を満たす関数。
- 引数が同じであれば、同じ戻り値となる。
- 副作用がない。
純粋関数は読みやすく、デバッグしやすいという特徴があるが、全ての関数を純粋関数で書くことは現実的ではなく、無理やり純粋関数にすべきではない。
Discussion