低速で無駄のある小規模ツール開発を実現する方法

「低速で無駄のある」はツール本体なのか開発手法なのか、はたまた保守フェーズか。

例1:外部コマンドを何度も実行するシェルスクリプト/バッチファイルとして実装する

シェルスクリプトもバッチファイルも個人的に愛憎相半ばする気持ちを抱いてしまう言語というかツールだけど、その有効性は否定しない。自分もよく使っているし。

個々の小さなコマンドを組み合わせて目的を達成する――大抵のテキスト処理はその方法で何とかなるし、私が普段扱うデータ量は高が知れているので実行速度が問題となることもない。そして今の所はハードウェア性能は足りている。

とはいえコマンドを実行すれば(それが組み込みコマンドやシェル関数でなければ)その度にプロセスが生成されるし、各コマンドが個別にファイルにアクセスすればその度にファイルIOが発生する。気をつけないとこれらのコストが嵩んで信じられないほど低速なツールに仕上がってしまうことがある。というか覚えている限り2回ほど痛い目にあった。Windowsはセキュリティツールの影響もあってプロセスの生成コストが高いからなあ。

ループの中で外部コマンドを実行していたり、更にそこで毎回ファイルを引数指定していたりしたら要注意だ。テストデータでは気にならない時間で終了しても、現実的なデータ量ではウンザリするほど遅くなることがある。ループ数が増加すればするほど遅くなる。

該当する箇所をシェル組み込み機能に変えたり、テキストフィルタに置き換えることで済むなら良いが、それが駄目なら他の言語で書き直す羽目になるかもしれない。

例2:本格的に内部構造を変更するリスクを避けて無茶苦茶小手先の手法で既存ツールを改造する

作業コストや自分の力量の兼ね合いで、時として小手先の手法に頼らざるをえないこともある。しかし大抵は後でしっぺ返しをくらう気がする。何でだろう。

MVC (Model View Controller) や、それに微妙に似たDocument/Viewアーキテクチャなんて言葉がある。これに基づくと、例えばデータの画面表示に関して、

  • 表示したい元データはModelが保持している。
  • Modelからデータを取得し、表示用に加工してViewに突っ込む。
  • Viewには加工後のデータが存在する。

――こんな感じになると思う。実はデータの加工をどこでやるべきか分かっていない。

ある時遭遇したプログラムはViewerの類で、Model側に元データが残らない設計になっていた。Viewに加工後のデータが残っているだけだが、表示方法が1種類だけだったので何の問題もなかった……当初は

これがある時、表示方法を2種類に拡張して動的に切り替えられるように改造することになった。Model側に元データが残っているなら簡単だけど、残っていない! ピンチッ!

ここでModel側に元データを残すように修正すればよかったのに、何故か「Viewに残っている加工後のデータを使う」という力任せな方針を採ってしまった。表示方法を切り替える際にModelからデータを取得するのではなく(残っていないので不可)、Viewに残っているデータを使うのだ。この方法で何とかなってしまったのが悪かった。

その後、更に表示方法が増えたのだ! 私はこの対応の時に初めて駆り出された……。

表示を切り替える際に「Viewに残っている加工後のデータを使う」という実装の為、例えば次のような問題が噴出してしまった。

  1. 表示を切り替える度に、異なる加工済みデータが後に残る。
  2. 表示方法が3種類以上になると、表示を切り替えるときに複数の異なる種類のデータが元データとなる。つまり一つの表示方法に対して2種類以上の変換処理を実装する必要が生じる。
  3. データによっては、表示方法を色々と切り替えていくうちに誤差が生じる可能性がある。

ああ、こういうのが積み重なって「誰も弄りたがらない負債コード」と化してコールタールの沼のように踏み入れたプログラマを引きずりこんでいくのだろうなあ。

例3:よく分からんけどネット上の色々なサンプルをざっくり切り貼りして実装する

別にネット上のサンプルコードを使うことは……ライセンス絡みで問題ないなら構わないと思っている。

しかしですね、何の整理もせずに大雑把に切り貼りされると困るのですよ。低速になるか否かは運次第でもソースコード自体は確実に無駄の多い代物になりますから。ええ私も経験がありますあの全体の3分の1ぐらいが全く不要なコードでバラバラなコーディングスタイルがキメラのように組み合わさっていてライブラリ関数を使わずに自前で処理を書いてたりして当然のごとく関数化なんて申し訳程度にしかされてない一枚岩でこれまたバラバラな命名スタイルの変数が山のようにあるアレですよあのたった400行足らずのコードの整理に暇を見てこつこつ実施したとはいえ2週間近く掛かったのは災難なのかそれともその程度で済んだ幸運をかみ締めるべきなのかは未だに分かりません。

世間からすればまだまだ軽い事例だとは思うけど、嫌なものは嫌だ。

ある程度まとまった量のコードを外部から持ってくる――例えば関数単位やクラス単位でサンプルコードをコピーするのならともかく、ステートメント単位で切り貼りする時に何の整理もしないのは死亡フラグだ。

  • ネット上のサンプルコードを切り貼りする時はコードのスタイルを直すべし。
    • 大概のサンプルコードのスタイルは組み込む先のコードのそれと異なる。
    • コードの一貫性を保つためにも、スタイルは直すこと。まあ後で整形ツールにかけるという前提があるのなら話は別かもしれない。
  • ネット上のサンプルコードを使う時は、自分が欲しい部分以外は徹底的に取り除くべし。
    • 残っていると、後でそのコードを解読する羽目になった人が発狂します。

あとステートメント単位でコピーする場合もまとまった量のコードをコピーする場合も、何も考えずに使うのはダメだ。

  • ネット上のサンプルコードを使う時は、その内容の是非を問うこと。必要なら書き換えるべし。
    • バグもあれば質の悪いコードだってあるさ。
    • 例えばVBScriptのサンプルコードにて文字列を格納した配列をForループで舐めて中身を連結していることに深く高度な理由があるのか、それとも単に作者が組み込み関数Joinを知らなかっただけなのか?
  • ネット上のサンプルコードを使う時は、そのコードが前提としている実行環境・開発環境・依存ライブラリ等に留意すること。
    • サンプルコード中で新しく便利なAPIを使っていて、しかしターゲット環境が古いためにまだそのAPIは使えなかった……なんてことがある。
    • あと「このクールなライブラリを組み込めば一発だぜ」という記事を鵜呑みにして実装したが、実はライブラリのライセンスが……なんてこともある。

結局のところ、ネット上のサンプルコードを読んで内容を理解してからでないと、適切に切り貼りすることは不可能なのだ。

まとめ

  1. IOの頻度に注意。
    • 例ではファイルIOを挙げたが、TCPのようにタイムアウトが起こりうる通信処理などでも注意すること。
  2. 急がば回れ。その方法は「小手先の対応」ではないか、手を付ける前に考えること。
    • ただし、本質的な対応を実施するに足るコストを賄えるか否かは別の話。
  3. ネット上のサンプルコードは、よく読んで内容を理解した上でコピペしてプログラミングすること。
    • コピペプログラミングで楽できるのは「ゼロから考えなくて済む」という点だけ。
    • 何も考えずにコピペプログラミングで一定以上の品質のモノが出来上がるはずがない。出来上がるのはウンコなコードだ。