3回に1回出力するだけの簡単ではないお仕事

なんかさ、3回に1回出力するだけの簡単なプログラムのお仕事ってあるじゃん。

  if ( (++counter % 3) == 0)
    printf("Fizz\n");

これって意外と難しいんだよね。


……なんてことを言うと「おいおい、天下のやねうらお、ついに頭おかしくなったか」とか言われるだろうけど、これ実際うちの仕事であった話で、このコードが原因でお客さんと大きなトラブルになった。


あまり具体的には言えないので、ちょっと別のものに置き換えて話すけど、それは、ひよこの餌やりプログラム(仮)だったわけ。


上のプログラムは、3回に1回だけど、このソフトには、N時間に1回、餌をやるロジックが書いてあった。

  if ( (++counter % N) == 0)
    printf("餌やるでー\n");

なんかこんな感じな。それでNの値は、UI(ユーザーインターフェース)で調整できる作りにしてあった。一度開始ボタンを押すとNの値は変更できない。動作中にNの値を変更できたらおかしいことになる。わかるよな?


それでしばらくはうまく動いていたが、開始ボタンを押したときに、counter = 0にリセットするかしないかでお客さんと喧々諤々の議論になったわけ。


このカウンターを開始ボタンを押したときにリセットされると、ひよこ部屋の掃除をしたいときに一時停止させて部屋に入るので(ひよこ部屋に入っているときに機械が動いて餌が出てくると困るので)、停止→開始のときはcounterはリセットしないでくださいとのことだった。私は言った。わかりました、では、ソフト起動時にのみcounterをリセットしましょうと。あと、停止させている最中はややこしくなるのでカウンターを進めない仕様でいいとのことだったのでそうさせてもらった。(餌やりの時間が来ているのに機械が停止中だったため、その餌やり動作がskipされると困るのだ。)


それで、最初はお客さんはN = 10でランニングしてたわけだ。10時間ごとに餌が出てくるわけね。ところが、お客さんはこれだとひよこの餌が足りなくなることに気づいた。じゃあ、9時間に変更しようと、お客さんは機械を停止させてN = 9に変更して、また開始を押したわけ。そしたら、どうなったか。


ひよこがすべて死んだのだ。


たぶん、機械を停止させたときにcounter = 9だったんだろうな。そのままいけば1時間後に餌が出るはずだった。ところが、N = 9に変更したから、counter = 18になるまで餌が出てこない。(9で割って余りが0なのは9の次は18だから。)


そうすると、お客さんにしては10時間間隔では餌が足りないから9時間間隔に時間をわざわざ短くしているのに、なんと18時間もの間、餌が全く出てこない事態になったわけだ。そりゃ、ひよこも死ぬさ。これにはお客さんもカンカンである。


剰余にしたのがまずかったのか?

  if ( ++counter >= N)
  {
    printf("餌やるでー\n");
    counter = 0;
  }

そう。確かにこうなっていればこのような問題は起こらなかったかも知れないし、お客さんが望むコードもこういうコードだったかも知れない。


しかし、そもそも
1) あと何時間後に餌が出てくるのかだとか、前回餌をやったのはいつかだとか、前回の餌やりから次回の餌やり時間までの間隔はどれだけかだとか、その手の表示がUI上に無かった。そしてその間隔が長すぎたり短すぎたりするときに警告を出すような失敗を保護する仕組みが無かった。そういうのを事前にお客さんに提案できていなかった。
2) 停止させてNを変更した場合の仕様が明確化されていなかった。お客さんと考えが共有出来ていなかった。事前にそういう事態を想定していなかった。たかがひよこと甘く見ていた。ひよこのことをまるでわかっていなかった。UIを操作するときのお客さんの心理を理解していなかった。
などいろいろ反省すべき点の多いプロジェクトだった。


そういうのを踏まえて最初に書いたプログラムをもう一度ご覧いただきたい。

  if ( (++counter % 3) == 0)
    printf("Fizz\n");

どうです?難しいでしょう?