こんにちは、越川です。 エンジニアをしていると日々、様々なエラーに直面するかと思います。僕自身もクラウドを取り扱っているのですが、日々、様々なエラーに直面します。そこで、今回は僕がエラー解決をする上でよく使う思考法をご紹介したいと思います。
エラー解消は理解を深めるチャンス
エラーが起きると、どうしてもネガティブな印象を持たれがちですが、個人的には理解を深めるビッグチャンスだと思っています。当然、本番環境などで発生することは避けたいですが、エラーに直面すると、あらゆることを自分で調査し整理する必要があります。その過程で自ずとサービスやシステムの仕様を深く知ることができます。エラーを解消した頃には、解消する前と比較して具体的なメカニズムがある程度、理解できるようになります。
fail fastという言葉があるように、速く沢山の失敗をすることが大切です。そうすることで、自ずと各コンポーネントに対する理解が深まります。開発段階でそういった失敗を沢山こなしていれば、本番環境では同じような失敗は未然に防ぐことができます。ITの変化が目まぐるしいということは、その分、不確実性・複雑性の要素が強まっているということですから、このfail fastという考え方は一層、重要になってくると考えています。
重要な思考法2選
エラー解消をする上で僕が多用してる思考法は大きく分けて以下の2つです。抽象的な概念なのでこのタイミングでは?と思うかもしれませんが具体例を交えて後述しますので今は何となくの理解で大丈夫です。
構造化思考
構造化思考は調べると様々な定義がされていますが、僕はエラー解決をする上で全ての事象を構造体として捉えると定義しています。一見、1つの物に見えても、実際は複数の要素で成り立つ構造体であると考えることです。加えて、重要なことはその構造体は流れを持っているという点です。インフラ的な言い方をするとトランザクションという表現することも可能です。
仮説力
前項の構造化思考をベースにこの仮説力を使って実際にトラブルシューティングをします。この仮説力とは、「原因がここにあるんじゃないかな?」と原因を推察する能力です。この仮説力をベースに実際のエラー解消を行います。重要な点はこの仮説力をベースにサイクルを回すことです。 具体的には、以下の3つです。
1.仮説を立てる
ここに原因があるのかな?と仮説を立てます。この仮説を立てる時のベースになるのは先述の構造化思考です。
2.仮説を検証する
立てた仮説が正しいか実際に検証します。
3.フィードバックをする
エラーが解消されればその仮説が正しかったことになります。逆に解消しない場合は、仮説検証の結果から再度仮説を立てます(1に戻る)
仮説が仮に外れたとしても、その可能性を排除できたことになるので、総体的に見ると下図にように原因に一歩ずつ近付いたことになります。そこから再度、仮説を立ててサイクルを回していけば何も問題ありません。ここでのアンチパターンは、闇雲に検証作業を実行することです。仮説と山勘は全くの別物です。仮になんとなくで特定ができても、根本的な理由は分からないので再発の恐れがあります。加えて、類似の事象が発生した際にも応用が効きません。ですから、外れても良いので先ずは自分なりに論拠に基づいた仮説を立てることが重要です。
所謂、勘とは仮説検証の総数だと思っています。仮説検証を沢山こなして膨大なデータをプールしていきます。そうすると、類似の事象が発生した際にそのデータを基に何となく勘所が働くようになります。類似の事象でなかったとしても、溜まったデータを基に類推することは可能です。この仮説力というのは数をこなせばこなすほど、自ずと向上していきます。
実例を交えた具体例
さて、それでは実際の具体例を交えて、どうやって先述の思考法を活用していくか解説します。下図のようなLINE Botを例に考えてみます。構成はいたってシンプルです。 ➀ユーザがLINEからメッセージを送信すると、➁それをトリガーにLambda関数が発火します➂ユーザがBotに入力したメッセージと返答のメッセージをDynamoDBに保存して➃パターン化されたメッセージをユーザに返します。
このLINE Botでユーザがメッセージを入力しても返答が返ってこなくなったケースを想定してみます。ここで先ほどの思考法を思い出して下さい。構造化思考と仮説力がありましたね。先ずは構造化思考をあてはめてみると下図のようになります。構造体として捉えるということは、即ち各要素を局地的に見てはいけないということです。つまり、木を見て森を見ずになってはいけないということです。次に、構造体は流れを持っているということは、各要素はただ繋がっているだけではなく、採番されている通り流れを持っており、即ち依存関係があるということです。
構造化思考をベースに仮説を立てていきます。 ➀メッセージを送信はユーザ側で既に実施済です。では、➁トリガーは起動しているのでしょうか?トリガーが起動していたのであれば、ログが出力されているはずです。
ログが出てない場合は、そもそもLambda関数が発火していないということになります。発火していないということはトリガーが起動していないということです。となると、LINEとAPI Gateway間で上手く疎通が取れてないのでは?と仮説が立ちます。この仮説に基づき実際に検証作業をします。つまり、LINE側の設定とAPI Gatewayの設定を再度確認してみます。闇雲に調査するより、このように構造化思考と仮説力を使うことである程度、原因のスコープを絞ることができます。
では、ログが出ていたらどうでしょうか?Lambda関数自体は問題なく発火してるということになります。したがって、見るべきポイントはLambdaより奥の世界です。そうなってくると考えられる原因は様々です。Lambdaの関数コード自体に何か誤りがある可能性もありますし、Lambda-DynamoDB間で上手く疎通ができていない場合も考えられます。もしくは、DynamoDBのテーブル定義が誤ってる可能性も拭えません。 この場合はログをベースにトレースしていくと原因のスコープを絞ることができます。SyntaxError等のログが出力されていたら、Lambda関数上のコードに問題がある可能性があります。または、Access Deniedのようなログが出ていたら、これはアクセス拒否を指すわけですから、Lambda-DynamoDB間の接続が上手くいってないのかな?という仮説が立てられます。
このように事象を大局的に捉え、流れをトレースすることでパターンに応じた仮説を立てることができます。先ほどの仮説力のイメージを再掲すると、このような流れで実際に仮説を立て⇒検証し⇒フィードバックをするサイクルを回していくと、徐々に原因のスコープが絞られていき最終的に点に辿り着きます。原因が不明だとモヤモヤしたまま終わってしまうので、今回のケースではログにAccess Deniedが出力されており、Lambda関数にDynamoDBにアクセスするための適切なIAMロールが付与されていなかったということにしておきます(笑)
さいごに
実際の案件でエラーを解消するとなると、もっと複雑で様々なサービスが絡み合っているかと思います。しかし、やることは同じなので、丁寧にひも解いていけば問題ないかと思います。また、具体例でお見せしたフロー図の様な物が存在しないケースも往々にしてあります。そういった場合はご自身の頭の中でイメージを膨らませることが大事ですね。ぜひ一人でも多くの方に参考になると嬉しいです。