>   >  痴山紘史の日本CG見聞録:第35回:より良いプログラムを書くために[使いやすいプログラムのつくり方]
第35回:より良いプログラムを書くために[使いやすいプログラムのつくり方]

第35回:より良いプログラムを書くために[使いやすいプログラムのつくり方]

不幸な事例

プログラムの動作確認をするための事前準備に30分程かかった挙句、本番処理の凡ミスで一瞬でエラーになった不幸な例がこちらです。

D:\xlgan> D:\xlgan\getPlayListMovie.py

Shotgun related transactions start in shortly.

 project name: XXXXXX
 dest dir: C:\temp\review
 mode: fetching all playlists mode.

 start to fetch 32 play lists.

phase 1: retrieve play list information!

[FETCH LIST][0001/0032]: XXXXXXXXXXXXXXXXXXXXX
[FETCH LIST][0002/0032]: XXXXX
[FETCH LIST][0003/0032]: XXXXXXXXXXX
[FETCH LIST][0004/0032]: XXXXXXXXXXXXXXXXXXXXX
[FETCH LIST][0005/0032]: XXXXXX
[FETCH LIST][0006/0032]: XXXXXXXX
[FETCH LIST][0007/0032]: XXXX
[FETCH LIST][0008/0032]: XXXXX
[FETCH LIST][0009/0032]: XXXXXXXXXX
[FETCH LIST][0010/0032]: XXXXXXXXXXXXXXXXXXXXX
[FETCH LIST][0011/0032]: XXXXXXXXXXXXXXXXXXXXX
[FETCH LIST][0012/0032]: XXXXXXXXXXXXXXXXXXXXX
[FETCH LIST][0013/0032]: XXXXXXXXXXXXXXXXXXXXX
[FETCH LIST][0014/0032]: XXXXXXXXXXXXXXXXXXXXX
[FETCH LIST][0015/0032]: XXXXXXXXXXXXXXXXXXXXX
[FETCH LIST][0016/0032]: XXXXXXXXXXXXXXXXXXXXX
[FETCH LIST][0017/0032]: XXXXXXX
[FETCH LIST][0018/0032]: XXXX
[FETCH LIST][0019/0032]: XXXXXXXXXX
[FETCH LIST][0020/0032]: XXXXXXX
[FETCH LIST][0021/0032]: XXXXXX
[FETCH LIST][0022/0032]: XXXXX
[FETCH LIST][0023/0032]: XXXXXXXXX
[FETCH LIST][0024/0032]: XXXXXX
[FETCH LIST][0025/0032]: XXXXXXXXXX
[FETCH LIST][0026/0032]: XXXXXXXXXXXXXXXXXXXXX
[FETCH LIST][0027/0032]: XXXXXXXXXXXXXXXXXXXXX
[FETCH LIST][0028/0032]: XXXXXXXXXXXXXXXXXXXXX
[FETCH LIST][0029/0032]: XXXXX
[FETCH LIST][0030/0032]: XXXXXXXX
[FETCH LIST][0031/0032]: XXXXXX
[FETCH LIST][0032/0032]: XXXXXXXXXXXXXXXXXXXXX

phase 2: prepare destination folder!
         done!

phase 3: download movies!

[LOAD][Movie #0001/3910]: XXXXXXXXXXXXXXXXXXXXXX.mov is downloading ...
Traceback (most recent call last):
  File "D:\xlgan\getPlayListMovie.py", line 392, in 
    dispatcher(sys.argv)
  File "D:\xlgan\getPlayListMovie.py", line 374, in dispatcher
    main(arg_dict)
  File "D:\xlgan\getPlayListMovie.py", line 349, in main
    tf2 = retriever.download()
  File "D:\xlgan\getPlayListMovie.py", line 218, in download
    self.adjust_file_time_stamp(fname, mov['updated_at'])
  File "D:\xlgan\getPlayListMovie.py", line 169, in adjust_file_time_stamp
    os.utime(path, (atime, mtime))
WindowsError: [Error 2] 指定されたファイルが見つかりません。: u'C:\\temp\\review\\XXXXXXXXXXXXXXXXXXXXX\\XXXXXXXXXXXXXXXXXXXXXX.mov'
Traceback (most recent call last):
  File "D:\JCGSLauncher\JCGSLauncherCmd.py", line 66, in 
    arg=sys.argv[4:])
  File "D:\JCGSLauncher\JCGSLauncherCore.py", line 989, in run
    self.logger.info('\n'.join(msg))
TypeError: can only join an iterable

D:\xlgan>

このときは、phase 1 のリストの取得で30分ほどかかった後にエラーになっています。そして、このプログラムの次の動作確認には、また30分の準備時間が必要になります。正直、これは心が折れます......。自分がつくったプログラムなら自分の不注意を呪いながら修正するだけですが、他人がつくったプログラムでこれが起きたらデスノート行きですね。

エラーは可能な限りまとめて伝える

プログラムの実行中にエラーが起きても、その場ですぐに処理を終了してしまうのではなく、可能な限り処理を継続して、情報を収集するようにします。

例えば、100個のファイルに対して何か処理を行う場合、不親切なプログラムだと最初のエラーが出た時点で処理を終えてしまいます。 その場合、ユーザーはエラーになったファイルに対して修正を行い、再度プログラムを実行します。しかし、プログラムは次に見つかったエラーの時点で終了してしまいます。 ユーザーは、最終的にいくつのファイルを修正しなければいけないのかわからないですし、新しいエラーを見つけるために毎回プログラムを実行し、ひとつ、ひとつ対応しなければなりません。これはとても不便です。

このような場合、エラーが起こっても、エラーの情報は保持しながら別のファイルの処理を続け、一通りの処理が終わった段階でエラーになったファイルのリストとエラーの内容をユーザーに知らせるようにします。 そうすることで少なくとも全てのファイルが処理されるまではユーザーは別の作業ができますし、一度実行しただけで多くの情報を得られるので、一度に対応できることも増えます。 結果、実行する回数も必要な時間も削減できます。

さらに言うと、ひとつのファイルに対する処理の中でも同様の対応ができます。ひとつの処理を行う際に複数のチェック項目があった場合、ひとつの項目がエラーになっただけでその処理を終えてしまうのではなく、可能な限りチェック項目を実行してエラーを洗い出してから処理を戻すようにします。

このように、とにかく一度の実行で情報を絞り出せるだけ絞り出すことで、ユーザーにかかる負担を大幅に軽減することができます。

dryRun モードを用意する

データを更新するような処理をする場合、その処理を実行することで何が起きるのかわからず、不安になることがあります。 そのようなとき、データの更新を行わないまま処理を進める dryRun モードを用意すると便利です。dryRun モードがあれば、実際にはデータを更新せずに動作をシミュレーションし、予想外の事態が起きないか確認することができます。

この機能は、特に開発途中などでプログラムが安定していないときに威力を発揮します。データ更新部分以外のバグを早い段階で潰すことができるので、開発効率の点でも、精神衛生の点でも良いことしかありません。

良いコードを書く(再)

最近はCPUのコア数を増やすことで処理速度を上げるのが主流になってきている反面、どうしてもひとつのコアでしか処理できない内容も存在します。そのような場合でも、小さな処理を同時に大量に実行することで、全てのコアを効率的に使用して、十倍以上のスループットを出すことが可能になります。これを実現するためには、処理の内容を分解・整理して、一塊の大きなプログラムではなく、小さな処理の連なりとしてプログラムを構成する必要があります。これは正に前回コードを整理するときに行なった内容そのものです。ぜひ今回の内容を念頭に前回の記事を読み直してみてくだざい。きっと、得られるものがあるはずです。

まとめ

今回はユーザー(ライブラリを使ってプログラムを書くプログラマーも含む)が使う際に、ストレスを感じないプログラムを書くための考え方をご紹介しました。いずれもちょっとした心遣いであり、仕様書やカタログスペックとして現れづらいものなので、見落とされがちな部分です。また、ベースに良いコードがあって初めて実践できることなので、今書いているコードは良いコードなのか? 使いやすいプログラムなのか? ということを常日頃から意識する習慣を身に付けましょう。



第36回の公開は、2021年8月を予定しております。

プロフィール

  • 痴山紘史
    日本CGサービス(JCGS) 代表

    大学卒業後、株式会社IMAGICA入社。放送局向けリアルタイムCGシステムの構築・運用に携わる。その後、株式会社リンクス・デジワークスにて映画・ゲームなどの映像制作に携わる。2010年独立、現職。映像制作プロダクション向けのパイプラインの開発と提供を行なっている。新人パパ。娘かわいい。
    @chiyama

その他の連載