Yuki's Tech Blog

仕事で得た知見や勉強した技術を書きます。

仕事を通して知った大事な考え方 Part1

目次

初めに

エンジニアの仕事を通して知った、大事な考え方をまとめます。自分への戒めとして記事にして残しておきます。 あくまで個人的に大事だなと思っているだけなので、参考程度に見ていただけると嬉しいです。

仕事を通して知った大事な考え方

自分の書いたコードに責任を持つ

自分の書いたコードに責任を持つとはつまり、どういう目的・理由があってこのコードを書いたのか、論理(ロジック)を人に説明できることです(つまり、自分自身の中に論理(ロジック)を持っている状態)。

この論理がなかったり、ぼやけていると、なんでこのコードを書いたのか、なんでこの実装方針にしたのかを説明できなくて激詰めされます(あくまで経験則ですが笑)。「この人は自分がやった作業を論理立てて説明できないんだな」と思われて、自分の評価を落としてしまいます。例えば、Qiitaに書いていあるコードをペーストして動いた。だけどなんで動くのかを理解していないと、自分の中に論理を持っていない状態です。また、コードレビューを受けている方にありがちなのですが、レビュアーの思考を想像して、レビュアーが書きそうなコードをあえて書くというのも自分の中に論理を持っていない状態です(自分もやりがちなので気をつけます)。

  • 自分で考えた上で、その状況に応じた自分がこうしたいと思える最適なコードを書いている。
  • 仕事をする上で、こういう理由・目的があったからこの行動をしたと説明できる。言語化している。

この2つがちゃんと守られていれば、論理を持っていると言えるでしょう。そもそも、レビュワーの思考を想像してコード書いてても面白くないです。自分がこうしたいからと思ったから、このようにコードを書くって方がおそらく面白いと思います(もちろん、レビューを通して大事だなって思ったことはちゃんと実践した方が良いですけど)。 自分もまだできていない時があるので気をつけようと思います。

問題を切り分けて、トリガーを見つける

自分はよくエラーが出ると、パニックになり「エラーでた。どうしよ。何でやねん。何もわからん」と冷静さを失ってしまいます。そして、エラーがなぜ起きたのかを説明する仮説を作って、仮説が真の場合の対処法がないかをネットで探します(もしくは対処法を思いついているなら自分で実装する。てか、書いてて思ったけど、せめて仮説の妥当性をまずは検証した方が良い)。そして対処法を実装してみてうまくいくなら、仮説が真であることが分かり終了。うまくいかないなら別の対処法を考えるか、別の仮説を考えます。

これはすごく非効率です。そして自分の中に論理を持っていません(こういう理由・目的があったからこの行動をしていると人に説明できない)。仮説が正しいのかをまず検証していないので、そもそも仮説とエラーは全く関係ない可能性があります(仮説Aという理由によってエラーBが引き起こされたという関係性ではない可能性がある)。全く関係のないことと今回のエラーを強引に関連づけているので、なぜ関連づけたのか人に説明できないです。仮説の対処法を見つけて実装する前に、まずは仮説が真か偽なのかを検証しましょう。

じゃあ仮説を検証して仮説が真か偽なのかを調べれば良いのでは?と思うでしょう。しかし、仮に仮説が偽の場合、また仮説を一から考えなくてはいけません。これはとても非効率です。

どのように行動すればエラーを効率的に解決できるのでしょうか。個人的に考えた答えとしては、エラーが出た時にまずやらねければならないことは、エラーログを見る or 問題を切り分けることです(問題の切り分けはエラーログを見ても原因がよく分からない時 or そもそもエラーログがない時にやります)。 上の何が良くないのかというと、エラーがそもそもどこの領域に存在するかを特定していないことです。そのせいでエラーと全く関係ないことを強引に関連づけて仮説を作っている可能性があります。そして、なぜ関連づけたのかを人に説明できていない状況です。なので自分の中に論理を持てていないことになります。

エラーログを読めばエラーを引き起こす原因(トリガー)が一瞬で分かります。あとは対処法を考えれば良いだけです。今回はエラーログを読んでも良くわかんない時 or エラーログがそもそもない時に有効な、問題の切り分けについて重点的に説明します。問題の切り分けは医者が問診して病気を特定するのと大体同じであり、エンジニアが問題の原因を特定するために必要不可欠です。

問題を切り分ける際にまずやらなければならないことは、全体の処理の流れを把握することです。目の前のエラーだけを見るミクロな視点ではなく、全体の処理の流れを見るマクロな視点を持つことです。そうすれば目の前のエラーに振り回されることを防ぐことができます。例えばログイン機能でなぜかログインできないってなった時に、まずやらなければならないことは、ログイン機能の全体の処理の流れを把握することです。以下に処理の流れを示します。

[ログイン機能の処理の流れ]
1. ログインページにアクセスする
2. ログインページにメールアドレスとパスワードを入力する
3. サブミットボタンを押す
4. メールアドレスとパスワードをリクエストボディに含めたHTTPリクエストがサーバーに送られる。
5. サーバー側でメールアドレスとパスワードをボディから取得して、ユーザーを特定する。
6. 特定できたらユーザー情報をレスポンスボディに含めてHTTPレスポンスを返す。特定できなかったら例外を発生させて、HTTPレスポンスで500エラーを返す。
7. 正常にログインできた場合、ログイン後のページが表示される。ログインできなかったら、ログインページにバリデーションメッセージを表示する。

そして、どこまで動いてて、どこから動いていないのかを手順1から手順7まで順番に確認していきます。手順5で動かなくなったことが分かった場合、手順5がどういう流れで処理が実行されているのかを把握します。

[手順5の流れ]
1. リクストボディからメールアドレスとパスワードを取得する
2. 取得したメールアドレスとパスワードをORマッパーのライブラリに渡して、DBと通信する。
3. ユーザーが特定できるなら、取得できる。特定できないなら例外を発生させる。

手順は3つしかないので、あとは一つずつ動作を確認していけば、エラーの原因(トリガー)を発見することができます。トリガーさえみつけてしまえば、あとは対処法を考えるだけです。対処法を思いつくなら自分でコードを修正すれば良いし、思いつかないならネットで探すかChat GPTに聞くか、上司に聞けば終わりです。

このように全体の処理を把握した上で問題の切り分けをすることで、問題が存在する範囲をどんどん狭めていくことができます。全体の処理を把握しない&問題の切り分けを行わないと、全体の処理の中でエラーが起きそうな部分とエラーを強引に紐付けた仮説を死ぬほど考えないといけません。上のログイン機能の場合、手順1から手順7のエラーが起こりそうな部分とエラーを強引に紐づけた仮説を死ぬほど考えないといけません。このやり方だと時間がかかりすぎてやってられません。

長くなってきたのでエラーの対処法をまとめます。

[エラーの対処方法]
1. エラーログがあるならエラーログを読む
2. エラーログを読んでもよく分からない or そもそもエラーログがないなら、エラーがなぜ起きたのかについて問題の切り分けをする。
3. まず全体の処理の流れを把握する
4. どこまで動いてて、どこまで動いていないのかを特定する(何ができてて、何ができていないかを特定するとも言える)
5. 動いていない手順が、どのような処理の流れなのかを把握する
6. どこまで動いていて、どこまで動いていないのかを特定する
7. 3~6を繰り返して、最終的にはエラーを引き起こす原因(トリガー)を特定する(もし問題が存在しそうな領域は特定できたけど、トリガーがいまいちわからないって場合は仮説・検証を繰り返す。仮説が真だと証明できればその仮説で説明していることがトリガーだと分かる)
8. トリガーに対してどのように対処するかは、自分の考え or ネットで調べる or 上司に聞く or Chat GPTに聞く。

問題を切り分けることも大事なのですが、問題を切り分ける目的としてはトリガーを見つけることなので、その目的を見失わない様にしましょう(問題を切り分けていると、そもそも何がしたいんだっけとなりがちなので笑)。

自分もまだできていないなと思うので気をつけます。問題の切り分けを高速に行うためには根本の仕組みの理解(Webフレームワークはどうやって動いているのかコードを読んで理解する、ネットワークはどのように通信しているのか、プログラムはどの様な環境でどの様に実行されているか、インフラはどういう仕組みで動いているのか、ライブラリは内部でどの様な仕組みで動いているのかコードを読んで理解する)が必要だなと思いました(余裕があるなら実際に仕組みから実装してみる)。 こちらの記事がとても参考になるのでおすすめです。

ガチで超重要な考え方「問題の切り分け」について | Take off Rails

ある問題とある問題に関係ない別の問題を強引に関連づけない

これについては上でも説明してはいるのですが、仮説を作りがちな人ほど、全体の処理の流れを見ないで、エラー自体を見て仮説を考えているなという印象です。エラーログが丁寧に書かれていればそもそも仮説を立てる必要はなくて対処法を実装すれば良いだけです。エラー自体を見てもよく原因が分かんないからから仮説を立てていると思うのですが、仮説をいきなり立てると、関係ないことと問題を強引に関連付けている可能性があります。 仮説を作る理由としてはトリガーを見つけるためだと思うのですが、それならば、まずは問題の存在領域を特定した方が仮説を作る対象が減って効率的に作業ができます。そして、問題の切り分けがちゃんとできているなら仮説を作らなくても問題の原因が特定できる可能性があります。そのため、まずはログを見る、ログを見ても分からないなら問題を切り分けるということを習慣づける様にすると良いと思います。まず初めに仮説を作ってある物事と問題を強引に紐付けない様に気をつけましょう。

自分はできていないなと思うので気をつけます。

正確な情報を得るためにまずは、公式ドキュメントを読む

ネットの海は広いです。広いからこそ何が正しくて何が正しくないかの基準が必要です。公式ドキュメントに書いてあることは当たり前ですがめちゃくちゃ正確です。そしてOSSのコードも当たり前ですが、正確です。情報としての質が高いです。情報の正確性と質が高いので、自分が持つ論理の正確性が上がります。ある情報が欲しい時にネットの海から闇雲に探すのは、めちゃくちゃ時間がかかり現実的ではないです。そのため、まずは公式ドキュメントで探すのが一番効率が良いし書いてあることも正確で良いなと思いました。公式ドキュメントを見たけど意味がよく分からないなら、ネットで探すか、Chat GPTに聞くかです。ネットの情報がもしないならOSSをコードリーディングするのが良いなと思います。

終わり

今考えると当たり前っちゃ当たり前だなという感想しかないですが、エラーが出ると焦って思考が止まり、上司に聞けば良いやで思考を放棄していたのもあり、うまく論理的な思考ができていなかったんだと思います。ブログへのアウトプットを通して自分の課題が言語化できたのでよかったです。気をつけねば。。 まだまだ書きたいことはあるので、続きはPart2で書きます。

参考記事

ペアプログラミングして気がついた新人プログラマの成長を阻害する悪習 - Qiita

ガチで超重要な考え方「問題の切り分け」について | Take off Rails

マクロ視点とミクロ視点とは?わかりやすく解説 | 人生にワクワクする様な学びや遊びをプラス【キャリアコンサルタントプラス】

「言語化できない」はサボり。直感タイプってそういうことじゃねえぞ。

ロジックとは? 意味・使い方をわかりやすく解説! | マイナビニュース