はじめに
こんにちは、DIVXの高橋です。
アプリケーションでエラーが発生したとき、これまでは多くの方がGoogle検索を活用して解決策を探していたと思います。しかし、AIが登場してからは、AIの力を借りて問題を解決しようとする方も増えてきています。
あなたも次のような経験をしたことがあるかもしれません。
- AIの指示通りに作業を進めても、なかなかエラーが解消しない。
もしそのようなことがあれば、少しAIに頼り過ぎてしまっているのかもしれません。
AIは強力なサポートツールですが、その能力を最大限に引き出すためには、次の基本的な心構えが重要です。
- まずはエラーがどんなものなのかをしっかりと理解する。
- 必要に応じて関連するドキュメントを確認する。
この記事では、そんなエラー解決の方法を紹介したいと思います。
AIをどのように使うか?
AIを効果的に活用するには、解決策を求めるだけでなく、以下の使い方が良いと思います。
- エラーの理解:エラーの内容や原因をAIに聞き、理解を深める。
- 仮説検証:自分の仮説をAIと共有し、新たな視点やアドバイスをもらう。
このように、AIを問題解決のパートナーとして使うことで、効率的にエラーが解決できると思います。
NG行動
エラーが発生した際に、そのままエラーメッセージをAIに貼り付けて解決策を求めるのは下記の理由から避けた方が良いです。
- 正確な回答が得られないことがある: AIは常に正しい答えを返してくれるわけではありません。
- 自分の理解が浅くなる:エラーを十分に理解せず、AIの回答を無条件に受け入れてしまう可能性があります。
このような行動を避けることで、素早く問題解決にアプローチができます。
少し例を見てみます。
例
こちらのケースではタイプミスで@ticketのsを付けないでコードを記述してしまった場合で考えてみます。
@tickets = Ticket.filter_by_area(params[:area])
@tickets_count = @ticket.size
こちらを実行すると下記のようなエラーメッセージが表示されます。
NoMethodError in TicketsController
undefined method `size' for nil:NilClass
このエラーメッセージについて、AIに直接尋ねてみましょう。
◎質問
NoMethodError in TicketsController#index
undefined method `size' for nil:NilClass
def index
@tickets = Ticket.filter_by_area(params[:area])
@tickets_count = @ticket.size
上記のコードでこのようなエラーがでます。原因はなんでしょうか?
◎回答:1回目
NoMethodError: undefined method 'size' for nil:NilClassというエラーは、コード内でnilオブジェクトに対してsizeメソッドを呼び出そうとしていることが原因です。この場合、@tickets_count = @ticket.size行でエラーが発生しています。
原因はいくつか考えられます。
1、@ticketsがnilになっている: Ticket.filter_by_area(params[:area])がnilを返している可能性があります。filter_by_areaメソッド内で、nilを返さないように確認する必要があります。
2, メソッドの実装ミス: filter_by_areaメソッドが正しく定義されておらず、想定外の値を返しているかもしれません。このメソッドの実装を確認し、nilを返していないか確かめてください。
◎回答:2回目(原因を特定してくれている)
このエラーの原因は、@ticketがnilになっているため、sizeメソッドを呼び出そうとしたときにNoMethodErrorが発生していることです。@ticketがnilになる理由はいくつか考えられます。
1、タイプミス: 変数名のタイプミスです。@ticketではなく@ticketsを使うべきかもしれません。
2、フィルターメソッドの結果がnil: Ticket.filter_by_area(params[:area])がnilを返している可能性もあります。この場合、filter_by_areaメソッドが正しく指定された引数に対して結果を返すように確認する必要があります。
順番に確認して、問題を特定してください。
「@ticketのsが抜けているよ」という具体的な指摘をAIから期待していましたが、実際には1回目の質問ではタイプミスが指摘されず、2回目にタイプミスを指摘されました。このように、AIが毎回ピンポイントに誤りを指摘してくれるとは限りません。
そのため、AIの回答を無条件に受け入れてしまうと、エラーの解決に苦戦しがちです。エラー解決をより効果的にするには、AIの回答を参考にしつつも、自分自身で問題を細かく分析して、他のリソースやドキュメントも活用することが重要になります。
エラー解決で大切なこと
エラーを解決する際に、次のポイントを押さえておくことが重要だと思います。
- エラーの正確な理解:何が問題なのかをしっかり把握する。
- 仮説を立てる:自分なりの解決案を考えてみる。
- ドキュメントでの確認: 細かい言語仕様や関連情報をドキュメントなどで確認する。
これらを踏まえて、AIを活用しながらエラー解決を見ていきましょう!
例1
先ほどのNoMethodErrorで見ていきます。
@tickets = Ticket.filter_by_area(params[:area])
@tickets_count = @ticket.size
↓実行エラー
NoMethodError in TicketsController
undefined method `size' for nil:NilClass
まずはエラーメッセージを正しく理解してきましょう
◎質問
NoMethodErrorとはどのようなエラーでしょうか?
◎回答
NoMethodErrorは、Rubyのプログラムで発生するエラーの一種で、呼び出そうとしたメソッドが存在しない場合に発生します。
具体的な発生状況としては、以下のような場合にNoMethodErrorが起こります。
1. オブジェクトに存在しないメソッドを呼び出した場合
2. オブジェクトの参照がnilの場合
NoMethodErrorなので、こちらのインスタンスがsizeメソッドを呼び出せないことを言っています。
ではなぜ?呼び出せないのか?
◎質問
undefined method `size' for nil:NilClassとはなんでしょうか?
◎回答
undefined method 'size' for nil:NilClassは、Rubyのエラーメッセージで、nilオブジェクトに対して存在しないメソッド(この場合はsize)を呼び出そうとした場合に発生します。このエラーメッセージを分解すると、以下のようになります。
undefined method 'size': 'size'というメソッドが、呼び出されたオブジェクトに存在しないという意味です。
for nil:NilClass: メソッドが呼び出されたオブジェクトのクラスがNilClassであることを示しています。
nilは、RubyにおいてNilClassクラスの唯一のインスタンスで、つまりは何もない状態を表しています。
このエラーメッセージが出る一般的な理由としては、変数やメソッド呼び出しが期待どおりのオブジェクトを生成せず、nilになっていることが挙げられます。
nilに対しては基本的なメソッドしか存在しないため、sizeのようなメソッドを呼び出そうとするとエラーになります。
それはnilだからということが理解できます。
状況が正しく理解できたら「なぜnilになってしまうのか?」仮説を立てて行きましょう。
@tickets = Ticket.filter_by_area(params[:area])
今回は上記のコードが「正しくデータを取得できていない」と仮説を立てて見ましょう。
データ取得の確認はデバックなど実際にコード動かして確認します。
データは正しく取得できていたので、こちらのコードは間違っている様子はなさそうです。
次に、コードは正しいのに、インスタンス変数の中身がnilになってしまう理由をさがします。
仮説を立てて検証したとこも含めてAIに聞いてみましょう。
◎質問
@tickets = Ticket.filter_by_area(params[:area])
こちらのコードでデータが取得できることを確認しました。
しかしこちらのコードでは@ticket.sizeは変わらずundefined method `size' for nil:NilClassとなってしまいます。
原因はなんでしょうか?
◎回答
@ticketsが適切にデータを取得できていることを確認できたとのことですので、@ticket.sizeの部分ではなく、単に変数名のミスが原因である可能性が高いです。
あなたのコードでは、@ticketsという変数にデータを格納しているにもかかわらず、@ticket.sizeを呼び出しています。
自分の仮説結果を加えてAIに聞いてみると、今度はピンポイントに原因を回答を返してくれました。
ただ今の状態では変数名のミスでエラーになっていることはわかったのですが、なぜnilになっているか?はわからないままです。
次に、コード理解、細かい言語仕様を理解していきましょう。
◎質問
変数名のミスが原因でnilになってしまうのはなぜですか?
◎回答
変数名のミス自体が直接nilを引き起こすわけではありませんが、間違った変数名を使用すると、その変数に期待する値が格納されていないため、Rubyではその変数はnilのままになります。
以下はなぜミスがnilを引き起こすのかの詳細です:
初期化されていない変数: Rubyにおいて、宣言した後に値を明示的に代入しない限り、変数にはnilが格納されます。
実際にドキュメントを見て詳細を確認しましょう。
「初期化されていないインスタンス変数を参照したときの値はnilです」とあります。
引用:https://docs.ruby-lang.org/ja/latest/doc/spec=2fvariables.html
今回の問題の原因は、スペルミスにより初期化されていないインスタンス変数に対してsizeメソッドを呼び出してしまったためで、それがエラーの発生につながっていました。
という原因にたどり着くことができました。
例2
先ほどはエラーメッセージがありましたが、エラーメッセージがない場合は更にAIに頼りすぎと解決が難しくなります。
class User < ApplicationRecord
after_create_commit :log_user_saved_to_db
after_update_commit :log_user_saved_to_db
private
def log_user_saved_to_db
Rails.logger.info("ユーザーはデータベースに保存されました")
end
end
引用:https://railsguides.jp/active_record_callbacks.html#after-commitコールバックのエイリアス
上記のコードで考えてみます。
◎不具合
User.createが実行されユーザーがDBに保存されたのに、 「ユーザーはデータベースに保存されました」というログが記録されません。
上記の不具合を解決したいのですが、このままAIに頼むとどうでしょうか?
◎質問
class User < ApplicationRecord
after_create_commit :log_user_saved_to_db
after_update_commit :log_user_saved_to_db
private
def log_user_saved_to_db
Rails.logger.info("ユーザーはデータベースに保存されました")
end
end
とありますが、User.createのときにログが残りません。原因はなんでしょうか?
◎回答
User.createを使用してもログが残らない原因はいくつか考えられます。以下の点を確認してみてください
1、コールバックが呼ばれていない: コールバックが正しく設定されているか確認します。after_create_commitはトランザクションがコミットされた後に呼び出されるため、トランザクションが成功していない場合、コールバックが実行されません。例えば、バリデーションエラーが原因でレコードが保存されていない場合があります。User.createの結果を確認して、保存が成功しているかどうかをチェックしてください。
2、Rails.loggerの設定: Rails.loggerが正しく設定されているか確認します。特に開発環境やテスト環境でのログ出力設定が適切かどうかを確認してください。
3、標準出力: 標準出力(コンソールやログファイル)にログが出力されるよう、適切に設定されているか確認します。
4、コード実行確認: コールバックのメソッドが適切に呼び出されているか、メソッド内部でのエラーがないかを調査します。一時的にデバッグ用の出力(例: コンソールへの簡単なプリント)を追加して、メソッドが実行されていることを確認します。
これらのポイントを順番に確認することで、問題の原因を特定できる可能性があります。
◎質問
create メソッドは正しく動いており、正常にデータが保存されています。
ただログが記録されません。なぜでしょうか?
◎回答
データが正常に保存されているにもかかわらずログが記録されない場合、以下の点を確認してみてください
1、ログレベルの設定: Rails.loggerのログレベルがINFO以上になっているか確認します。デフォルトではdevelopment環境でDEBUGレベル以上のログが出力されますが、別の設定がされている場合、INFOレベルのログが無視されることがあります。config/environments/development.rbの中でconfig.log_levelが設定されているかを確認してください。
2、ログファイルの確認: ログが出力される場所を確認してください。特に、ログが想定しないファイルや場所に出力されていないか確認します。
3、ログの破損: ログファイルが破損している場合、ログが出力されないことがあります。ログファイルを削除、またはファイルの内容をクリアしてから再度確認してみてください。
4、コンソールへの出力: 開発環境では、サーバーコンソールにログが出力されるはずです。それが表示されていない場合、他の部分で問題が生じている可能性があります。
5、カスタムロガー: アプリケーションがRails.loggerではなく、他のロガーを使用している場合、そちらの設定を確認してください。
これらの要因を確認することで、ログが出力されない問題の特定につながるかもしれません。
◎質問
他のログは記録されるので、ログも正しく設定されています。
原因はなぜでしょうか?
◎回答
他のログが正常に記録されている場合、いくつか考えられる原因があります
1、トランザクションのコミット: after_create_commitとafter_update_commitコールバックは、データベーストランザクションがコミットされてから実行されます。もしUser.createがトランザクション内で実行され、その中でロールバックされるようなコードが他に存在する場合、コミットが発生せずコールバックが呼ばれない可能性があります。
2、別のエラーや例外: log_user_saved_to_dbメソッド内での他のコードの実行が失敗している可能性があります。例えば、例外が発生しているがキャッチされているためにサイレントに失敗している場合があります。
3、ActiveRecordの設定: 特定のバージョンや設定で、コールバックが正しく動作しない問題が発生している可能性もあります。使用しているRailsとActiveRecordのバージョンに関連する既知の問題がないか確認してください。
デバッグ時は、log_user_saved_to_dbメソッド内での動作を確認するために、メソッドに単純なデバッグ出力(例えば、puts "Executing log_user_saved_to_db")を追加して、コールバックが実際に実行されているかどうか調べてみるのが役立つでしょう。
このようにAI頼りすぎると直接答えにたどり着くことはできません。
なので、まずはコード理解や細かい言語仕様を理解していきましょう。
◎質問
after_create_commitとafter_update_commitの違いを教えて下さい
◎回答
after_create_commit: 新しいレコードがデータベースに作成され、そのトランザクションがコミットされた直後に実行されます。このコールバックは、新規作成(create)の際にのみ適用されます。
after_update_commit: 既存のレコードが更新され、そのトランザクションがコミットされた直後に実行されます。更新(update)の際にのみ適用されるコールバックです。
ざっくり理解できたので、もう少し詳しい内容をドキュメントを使って理解していきましょう。
細かい言語仕様に関しては、AIで確認するよりもドキュメントなどの一次情報を見るほうが確実なので、必ずドキュメントまで見ていきましょう。
ドキュメントを見るとこのような内容が書かれています。
after_commitコールバックは作成・更新・削除でのみ用いることが多いので、それぞれのエイリアスも用意されています。
場合によっては、createとupdateの両方に単一のコールバックを使わなければならなくなることもあります。
これらの操作の一般的なエイリアスを次に示します。
・after_destroy_commit
・after_create_commit
・after_update_commit
・after_save_commit
引用:https://railsguides.jp/active_record_callbacks.html#after-commitコールバックのエイリアス
こちらの内容から、「after_create_commit」と「after_update_commit」はafter_commitのエイリアスという事がわかりました。
またこのようにも書かれています。
ただし、after_create_commitコールバックとafter_update_commitコールバックに同じメソッド名を指定すると、
両方とも内部的にafter_commitにエイリアスされ、同じメソッド名で以前に定義されたコールバックをオーバーライドするため、
最後に定義されたコールバックだけが有効になってしまいます。
「after_create_commit」と「after_update_commit」もどちらもafter_commitのエイリアスなのでオーバーライドされてしまうようです。
ドキュメントの内容と合わせてAIに聞いて見ましょう。
◎質問
ただし、after_create_commitコールバックとafter_update_commitコールバックに同じメソッド名を指定すると、
両方とも内部的にafter_commitにエイリアスされ、同じメソッド名で以前に定義されたコールバックをオーバーライドするため、
最後に定義されたコールバックだけが有効になってしまいます。
とドキュメントに記載があります。
class User < ApplicationRecord
after_create_commit :log_user_saved_to_db
after_update_commit :log_user_saved_to_db
private
def log_user_saved_to_db
Rails.logger.info("ユーザーはデータベースに保存されました")
end
end
とありますが、User.createのときにログが残りません。原因はなんでしょうか?
◎回答
この問題は、after_create_commitとafter_update_commitに同じメソッド名を指定したことで、
最初に定義されたafter_create_commitコールバックが後で定義されたafter_update_commitコールバックによって上書きされてしまっているためです。
その結果、after_create_commitコールバックが実行されず、新しく作成されたユーザーのログが記録されていない可能性があります。
ドキュメントを使用して言語仕様を細かく理解し、その知識をもとにAIに相談することで、最終的に問題解決にたどり着くことができました。
効率良くAIを使ってエラー解決するコツ
エラー解決においては、まずエラーの理解や仮説立てが重要になります。AIの修正案に頼る前に、これらのプロセスをしっかり行っていくのが大切です。
AIはよく修正案を提供してきますが、これが回答を複雑にしてしまうこともあります。そのため、「修正案を出してくださいと言うまで、修正案を出さないでください」と明示することで、シンプルな回答を得られ、効率よく壁打ちができます。
弊社で使用しているDIVX GAIでは、プロンプトを設定することでエラー解決の際に役立つ壁打ちが可能です。これにより、より理解を深めながら素早くエラー解決に取り組むことができています。
最後に
AIは便利なツールですが、全てを頼ると逆に時間がかかることがあります。
AIの回答を活用しながらも、自分でエラーメッセージを分析し、仮説を立てて検証することが重要だと思います。また、公式ドキュメントに目を通すことも大切です。
こうしたアプローチにより、効率よくエラーを解決できると思っています。
日々のエラー解決に、少しでもお役に立てれば嬉しいです。
お悩みご相談ください