手で触る面を一枚にする ── 集計ツールの設定を .toml に畳む
同じ値が、3つのファイルに書かれている。実行ごとに触るJSONに1つ、利用者ごとに書き換えるiniに1つ、そしてモジュールの先頭にリテラルで1つ。どれか1つを変えたら、残りも合わせて変える。変える順番もだいたい決まっている。ただ、その作法はどこにも書かれていない。運用者のあいだで、口伝のように受け継がれている。
集計ツールを何年か回していると、こうした「書かれていないが、運用者は知っている」領域が、静かに広がっていく。実行のたびに書き換える値はここ、利用者ごとに変わる固有値はあちら、計算で導けるはずの値が──なぜか──コード内にリテラルで残っている。固定のファイルパスが、モジュールの途中にインラインで埋まっていることもある。ひとつずつを置いた時点では理由があったはずで、個別には筋が通っている。ただ、全体を整理した人は、たぶん誰もいない。
このツールには、完全には自動化されていない工程が残っている。実行のたびに手で触る箇所があり、毎回ほぼ同じ確認を繰り返す。どの値がどこにあって、今回はどれを変えるか、系統Aと系統Bで名前がどう対応しているか──。一回ごとの負荷は小さい。ただ、ツールを回すこと自体が繰り返しのなかにあるので、同じ注意を同じ箇所に払い続けることになる。削られていくのは時間というより、毎回そこに置いている注意のほうだと思う。
運用者が経験で覚えていることの量が、ツールの「わかりにくさ」をそのまま表している。今回の作業は、この量を減らすことだった。
手で触る面を一枚にする
置き場所の話は、直せば直る。もっと厄介なのは、値と値のあいだにある見えない糸のほうだ。
ある値を変えたら、別のファイルの別の値も合わせて直す。その連動は、どのファイルにも書かれていない。運用者の頭のなかにだけある。新しく入った人が同じ動きをできるようになるまで、何度か失敗を踏む必要がある。失敗が記憶に焼き付いて、ようやく「この値を触るときは、あれも見る」という対応表ができあがる。この対応表は、ファイルに書かれていない。
処理系統の側にも、似た事情がある。系統が複数並んでいて、それぞれが独自の語彙で閉じている。同じ対象を指す値が、系統ごとに別の名前で現れる。逆に、同じ名前が、系統によっては別の対象を指していることもある。どの名前とどの名前が対応しているかは、やはり運用者の記憶のなかにある。
こうした暗黙知が積もっていくのは、置き場所が分散しているせいだけではない。運用者が「触ってよい値」と「触ってはいけない値」の境目を、ファイルから読み取れないことが大きい。更新頻度の違う値が、同じファイルの中に区別なく並んでいる。実行ごとに変わる値と、構成として固定の値のあいだに、境界がない。どれが実行パラメータで、どれが前提条件なのか、注釈がなければ判別できない。
そこで、運用者が手で触る面を、1つのファイルに絞ることにした。それ以外の設定──内部の構成ファイル群を含む──は、そのファイルから計算で導く。手で触る面の広さが、ツールのわかりにくさの量の上限になる。
いくつかの形式を試した結果として
手で触る面を1つにする、と決めた時点で、その面をどのファイル形式で持つかが次の問題になった。新しい問題ではない。この数年、いくつかの形式を順に試してきた結果として、いまの構成がある。
最初は .json だった。値の構造を素直に書ける形式として、長いあいだツールの中心にいた。困ったのは、コメントが書けないことだった。値の意味や触ってよい範囲を、設定ファイル自身に書き残せない。補足はREADMEに書いた。しかしREADMEは、必要なときに開かれるものではなかった。開かれない場所に書いた説明は、書かれていないのとあまり変わらない。変数が増えてくるにつれて、当人にしかわからない領域が少しずつ広がっていった。
.ini も使った。型をつけられないこと──だったと思う──が合わなかった。数値も真偽値も文字列として読まれ、使う側で解釈するコードを書くことになる。設定ファイルを単純にしたつもりが、読み込み側の責務を増やしていた。
.yamlも候補として触った記憶がある。やめた理由は、いまとなっては取り出せない。インデントの揺れだったかもしれないし、別の理由だったかもしれない。記憶に残っていないということは、少なくとも積極的に採用する決め手が見つからなかったのだろうと思う。
最終的に .toml に落ち着いた。コメントが書ける。型がつく。項目が少ないうちはキーと値の対応がフラットに読める。手で触るファイルとして必要だった3つの条件が、まとめて揃っていた。手で触る面を少数に絞る、という今回の方針とも相性がよかった。
集約後の構成
.toml に並ぶ項目は、実行条件を決めるうえで最小限のものに絞った。これ以外の設定値は、.toml の値から Python 側の導出ロジックで計算される。ツールを実行すると、.toml を読んだ直後にこの計算が走る。
この切り替えは、既存の処理系を書き換えずに導入した。.toml を新しく起点として置いたうえで、それまで運用されていた設定ファイル群を、.toml から導出される中間出力として作り直す形にした。処理系の側から見ると、読みに行く設定ファイルは以前と同じ場所に、以前と同じ形で存在する。変わったのは、それを誰が書いているかだ。運用者が直接書くのをやめ、.toml からの導出が書くようになった。
ただし、処理系の側にまったく手を入れなかったわけではない。同一の項目を指しているにもかかわらず、SQLごとに変数名が揃っていなかった箇所がある。こちらは .toml 側で吸収するのではなく、SQL側を揃える方向で整えた。計算で導けるものは導出に任せて運用者の負荷を減らし、処理系の側に残っていた複雑さは揃えて再現性に寄せた。
結果として、運用者が結果の差分を生み出すために触る場所が、.toml 1つに収まった。.toml の書き換えが、実行条件の変更としてそのまま結果に現れる。逆に言えば、.toml を触らずに結果が変わることは、原則として起きない。既存のコード資産も、この経路に接続した。
kawakamisekkei / 株式会社川上設計
