ソフトウェアは設計上の階層構造を持つことが多く、ほぼ間違いなく最上位をアプリ層(そのアプリ固有の処理)とした複数の階層を重ねた形になっています。ごく単純なアプリの場合は最上位のアプリ層のみが存在すると解釈すれば、やはり階層構造です。このとき、特に最初が単純な一階層のアプリだったものに機能を追加していく場合、ある程度の規模を越えると階層構造を組まないと管理不可能になっていきます。本日はこの階層構造と「丸投げ」について思うことを書きます。
現在、私が仕事で扱っているモノは過去の資産に増築を繰り返してきたアプリです。困ったことにそれは「機能拡張時に動いているコードに触るな」という間違った考え[*1]でメンテナンスされてきたものであり、そのためか機能追加を行う際に「上位層に影響を与えないように下位層を修正する」ような作業が繰り返されてきたようです。しかし過去との互換性は維持しなくてはならないため新機能を常に使うよう製品仕様を変更することはできません。したがって下位層がアプリの設定ファイルを読み出して動作を切り替えるようになり、上位層は設定ファイルを扱うUIをユーザ向けに用意するようになりました。
これはこれで問題無さそうに聞こえるかもしれませんが、上位層が下位層へ「丸投げ」している構造欠陥は困ったことを起こします。私が直面した問題のなかでもっともクリティカルだったものは、過去にSMTP認証の機能が追加されたメール送信モジュールで「SMTP認証が拒否されたことをメールサーバ名とともにユーザが閲覧可能なログに出す」改良を施そうとしたときの話です。このメール送信モジュールのアプリ層に相当する部分は、困ったことに送信先メールアドレスと文面データ程度しか管理しておらず、メールサーバのホスト名から送信元アドレスまで様々な重要パラメータの決定を下位層に丸投げしていました(下位層が勝手に設定ファイルなどを読んで解決)。そして丸投げされた下位層は、自分で「成功したかどうか」しか上位層に報告しない仕様でした(ただし自分で非公開の動作ログを記録するため障害対応時はそこから詳細を得られる)。さらに面倒なことに下位層は状況によって使われるSMTPサーバなどの動作パラメータを切り替えるため、使われたSMTPサーバ名などの詳細情報は上位層から直接知ることが難しい状態でした。ここで先ほどのログ改善を下位層を変更せずに実現するには下位層の動作パラメータ解決処理をまるごとアプリ層にコピーしなければなりません。あるいはアプリ層が出すユーザ向けログを下位層が出すか、です。当然いずれも私にとって「ありえない」選択でしたので、最終的には下位層が持っている「動作パラメータを解決する処理」を上位層から呼べるようにして、アプリ層がすべての重要動作パラメータを把握できるようにしました。さらに下位層すべてに「上位から受け取った動作パラメータで処理を行う」ように修正を加えました。この修正の後は、他の作業もずいぶんやりやすくなったと感じています(なおログ出し以外にも様々やることがあったので大修正に踏み切っています)。
さて、丸投げ、です。なんとなく、この話を考えている最中に身近な(?)大企業の丸投げ体制とAppleの独裁(?)体制の対比が頭に浮かびました。意識しているかどうかはさておき組織は「細かい話を丸投げすべし」になりがちなのでは、と思います。しかし丸投げして良いかどうかは細かさで決まる問題ではありません。細かくとも重要な部分に最上位層が口を出すからApple製品は素晴らしいのだろうかと、ぼんやり連想しました。これが本日の締まらない締めです 🙂
[*1]既存コードに手を入れれば新しい不具合を作ってしまう確率が確かに上がりますが、機能拡張に応じてアプリ層のあるべき姿が変わるごとにリファクタリングしてコード・構造の可読性などを管理しなければ「読みにくいことが原因で不具合を作ってしまう確率」が上がり続け、いつか超えてしまうと私は思っているので。ま、とぐろを巻いたスパゲッティをさらに巻けと言っても限度がある、という話です 🙂