目次
バグとは
デバッグとは
デバッグの具体的な手順
テストとデバッグの違い
私が担当している具体的な作業

はじめまして。エム・フィールドグループの子会社で、データ分析、AI構築支援、AI教育事業を手がける株式会社エイアイ・フィールドに新卒で配属された傍示健太です。好きな色は黄色です。
今年の4月に入社し、3ヶ月の研修を経て、現在はIoT機器を使ったデバイスの制作(後日発表したいと思います)と、自社プロダクトの開発そしてデバッグを行っています。

この記事では、新卒で入社して人生で始めてデバッグを担当している私が学んだ、デバッグについての基礎と具体的にどのような作業をしているのか?をご紹介します。

■バグとは 

デバッグを説明する前にまずバグについて説明します。
バグと言えば、例えばゲームをしている際に、操作していたキャラが動かなくなってしまったり、壁をすり抜けてしまうなど、「想定していない挙動」のことを指します。
このバグ(bug)は英語で虫の意味、コンピュータの分野においてはプログラムの誤りや欠陥を指します。

プログラムを実行しているときに何らかのエラーを出して動かなくなってしまったり、動いたとしても自分が想定している挙動をしない場合に「バグが発生している」と言います。

■デバッグとは 

Designed by vectorjuice / Freepik

上記で説明したバグを解消する作業のことを「デバッグ」と言います。
コンピュータの不具合の原因である「バグ(bug)」を「取り除く(de)」と言う意味で、これらを繋げて「debug(デバッグ)」と呼ばれるようになりました。 

ソフトウェア開発を進めていくと、何らかの「バグ」が発生するので、これらを解消する「デバッグ作業」は、開発の中で必要不可欠な作業といえます。

■デバッグの具体的な手順 

ここからは自分が行ったデバッグ業務をもとに、デバッグの具体的な手順を説明したいと思います。今回のシステム開発では言語がPython3.8.8、統合開発環境としてPyCharmを利用したので、それをもとに説明していきます。

デバッグ主な流れは以下の通りです。
1. バグを見つける
2. バグの原因を探す
3. バグを取り除く
この順番で説明します。

1.バグを見つける
バグを見つけるためには、開発したプログラムのテストを行うことが必須になってきます。テストとデバッグは意味合いが多少異なってくるため、デバッグの流れを把握した後で説明していきます。

2.バグの原因を探す
バグを見つけたらその原因の特定作業に移っていきます。その原因の特定には以下の方法が考えられます。

2-1:エラーメッセージを読む
2-2:print()文を利用して処理の流れを確認する
2-3:テストコードを記述して検証する

2-1:エラーメッセージを読む
バグの原因を特定するためには、プログラムを作動させた時に異常な状態になったときに発生するエラーメッセージを読むことが必要不可欠になってきます。

fizzbuzz.pyのコード

def fizzbuzz(x):
   if x % 3 == 0 and x % 5 == 0:
       print('fizzbuzz')
   elif x % 3 == 0:
       print('fizz')
   elif x % 5 == 0:
       print('buzz')
   else:
       print(x)

fizzbuzz(11)
fizzbuzz(15)
fizzbuzz('1')



11
fizzbuzz

Traceback (most recent call last):
  File "C:\Users\kenta.hoji.ea\Desktop\blog\fizzbuzz.py", line 13, in <module>
    fizzbuzz('1')
  File "C:\Users\kenta.hoji.ea\Desktop\blog\fizzbuzz.py", line 2, in fizzbuzz
    if x % 3 == 0 and x % 5 == 0:
TypeError: not all arguments converted during string formatting

実際のconsoleの結果からエラーメッセージを確認してみましょう。
このプログラムは3の倍数を入力した場合に文字列”fizz”、5の倍数を入力した場合に文字列”buzz”、15の倍数を入力した場合に文字列”fizzbuzz”、それ以外の場合には入力した数字をそのままを出力するものになっています。

下の赤い文字がエラーメッセージになります。このエラーメッセージの内容を理解し、それに沿った対応を行うことでエラーを解決することができます。

今回は2行目の余りを計算する部分【x % 3】、【x % 5】の部分において、xに文字列型である’1’を代入したことにより、TypeErrorが発生しているため、
fizzbuzz(‘1’)をfizzbuzz(1)と変数の型を文字列型から数値型へと修正すればエラーの解決ができます。注1

エラーメッセージは英語で書かれていることが多いので敬遠しがちですが、プログラムのどこに問題が起こっているのかを把握するのに重要な情報が詰まっているので、まずはエラーメッセージを読む習慣を付けることが大切です。
Google検索にエラーメッセージをコピーすることで、エラーの原因や対処方法について調べることが可能です。分からないエラーメッセージが出たら検索機能を活用して解決するようにしています。

注1:この関数の引数として’%d’をつかった場合にはエラーはでません。これは%が引数によって挙動が変わってしまうことが原因です。%の引数が数値の場合は、最初の引数に2つ目の引数を除算します。例えば32 % 3 の場合では32÷3の余りである2が出力されます。これに対して、引数が文字列の場合は2番目の引数でフォーマットします。
fizzbuzz関数のx % 3 == 0でxに’%d’を代入すると’%d’を3でフォーマットし、3==0の比較になります。x % 5 == 0についても同じ結果となるのでこの関数はエラーを吐かず実行できると言う事になります。ちなみに本稿でxに’1’を代入した際にTypeErrorが出たのはこの文字列のフォーマットが出来なったことが原因です。

2-2:print()文を利用して処理の流れを確認する
コードが長くなってくるとその処理の流れがどのようになっているのか把握しづらくなってきます。
仮にプログラムがエラーを吐かなかった場合でも自分の期待した結果が得られない場合もあり、そのプログラムの処理がどのようになっているのかについてprint文を書くことで把握することが有効になるケースがあります。
またPycharmなどの統合開発環境ではプログラムの一部のみを実行できるデバッグ機能があるのでそうした便利な道具も利用するのもオススメです。

2-3:テストコードを記述して検証する
テストコードを記述するのもバグの検出には有効です。Pythonではテスト用にunittestと言うテスト用のライブラリが用意されています。
unittestではテスト用のデータを作成してその結果を検証する事ができるので、複数のテストを作成し、どの場合にエラーが発生してどの場合に発生しないのかと言った情報が手に入るのでエラーの原因を特定するのにとても役立ちます。
以下のコードは割り算を行うためのコードであるcalculation.pyとそれに対するテストを記述したテスト例のtest_calculation.pyになります。
今回はx=3, y=1の3÷1の場合とx=3,y=0の3÷0の場合のテスト用データを作成してテストを行いました。


calculation.pyのコード

def division(self, x, y):
    return x / y

test_calculation.pyのコード

import unittest
from calculation import division

class CalculationTestCase1(unittest.TestCase):
   def test_division1(self):
       obj = division(x=3, y=1)
       exp = 3
       self.assertEqual(obj, exp)

   def test_division2(self):
       obj = division(x=3, y=0)
       exp = 3
       self.assertEqual(obj, exp)

上の画像がPycharmでテストを行った結果になります。2つのテストのうち1つが失敗しており、x=3、y=0で計算したときにZeroDivisionError(ゼロ除算をした場合に発生するエラー)が発生していることが分かります。
このように様々なテスト用のデータを作成することで、どの場合にエラーが発生するのかを検証することができ、バグの検出にとても役立ちます。注2

注2:pythonのエラーや例外について記述されている公式ドキュメントです。https://docs.python.org/ja/3/tutorial/errors.html
https://docs.python.org/ja/3/library/exceptions.html


3.バグを取り除く
バグの原因が把握できたら、コードを修正してプログラムが正常に動くようにしていきます。ありがちなバグとして以下のものが挙げられます。

・Typo(入力操作での打ち間違え)
→多分1番多いバグの原因だと思います。
 エディタの補完機能を活用すれば減らすことが可能です。

・プログラミング言語やライブラリなどのバージョン違いで生じるバグ
→pyenvなどで仮想環境を切った上で、開発環境に合わせたライブラリをインストール
 しましょう。

・型の違いで生じるバグ(数値を入れないといけない部分に文字列が入っている)
→print文などで変数に何が格納されているか確認するようにすると良いと思います。

この作業では、コードを修正することで他の場所でバグが発生してしまう可能性がある点を念頭に置いておく必要があります。このリスクを避けるために、コードを変更した後はユニットテストやタイプアノテーションを再度行って、新たなバグが発生していないかしっかり確認するようにします。

■テストとデバッグの違い 

テストとは、「バグを見つける作業」で、少なくともテストしているその条件ではバグが発生していないことを保証する目的で行われます。
これに対してデバッグとは、「バグの発見からそれらの修正を行う一連の作業」のことで、デバッグ作業の一部としてテストを行います。

以上の事が原因でテストとデバッグは混合されがちですが、あくまで「デバッグ作業の中にテストが含まれている」という事になります。

■私が担当している具体的な作業 

私は現在、グループで開発している『RBO-Field(ロボフィールド)』に携わっています。RBO-Fieldは、AIによる予兆検知やあらゆる機器のインフラ構築、設定作業、障害復旧をノンプログラミングで自動化することで、インフラ運営業務に関わる時間やコストの大幅な削減をする自社開発サービスです。

新人研修期間が終わり、部署に配属された7月からRBO-Fieldの機能の一部であるAIを使った「予兆検知」のコードのデバッグ作業や、プログラムを動かすために必要なファイルの作成などを主に行ってきました。
現在はシステムが期待通りの振る舞いをするかどうかを検証するための、バリデーターの開発部分を担当しています。

これらの業務の中で特に、「Pythonで他の人が見ても理解しやすいコードの書き方」であったり、「チーム開発で必須となるGitの使い方」を学ぶことが出来ました。
また自分の作成したテストが無事に通ったり、自分が修正したコードが実際にシステムの中に組み込まれるのを見ると、やはりやりがいを感じます!

まだまだ未熟な部分が多いですが、一日でも早く一人前のエンジニアになれるように先輩社員の方のアドバイスを頂きながら頑張っていきたいと思います。

以上が新卒の私が、入社後4ヶ月で、デバッグ業務を担当して学んだことです。
私と同じように、デバッグ作業を始めたばかりの方の参考になれば幸いです。


あなたもエム・フィールド グループで働いてみませんか?

エム・フィールドグループは事業拡大に伴い、一緒に働く仲間を通年で募集しています。
データサイエンティスト、Webアプリケーションエンジニア、AWSエンジニア、ITコンサルタント、サービス運用エンジニアなどさまざまな職種とポジションで、自分の色を出してくださる方をお待ちしています。ご興味のある方は、採用サイトもご覧ください。

・エムフィールドの採用サイト
・人事が考える、エム・フィールドを一言で表すと、〇〇な会社!