これは既に問題を解釈・定義してしまっており、問題を正しく定義できていない。問題になっているのはあくまで、HTML直書きしていると修正が大変であり、しかも修正においてミスが発生するということだ。「動的生成」やら「一括管理」やらではない。別の言い方をすると、「この条件が成り立つことによって問題自体が発生しない」という状態を定義するのである。このレベルで既知の問題全てに対して繰り返していく。全てを解決する必要はない次のステップは、「定義した条件の達成手段を定義する」ことである。これは多いほうがよい。その定義を終えたら、それぞれの手段について検証する。この検証は次の2点である。その手段は他の各問題にどのように影響するかその手段は新たにどのような問題を発生させるか他の問題に対して悪い影響を与えない手段は良い手段であり、他の問題を多く解消させる手段は優れた手段である。良い手段は常に良いが、優れた手段は常に良いとは限らない。良いことが最善であるとも限らない。解決が困難な、コストの高い問題を発生させた手段は、そのツリーまるごと破棄しなければならない。ここで目標としているのは解決のためのコストを下げることだから、新たに解決困難な問題を発生させてはならないのだ。それが全ての問題を解決してくれない、特に「全ての問題を解決する手段があるにもかかわらず、良い手段は全ての問題を解決しない」という状況に直面することがある。「処理速度が遅い」「子プロセスを頻繁に発生する」「処理が重い」などの問題はよく発生する。この問題が深刻な問題を発生するならばそれを軽減する方法を考えなければならないが、別に気持ちわるいだけで実害はないのならそれで良い。実害があるとしても、VPSのプランをひとつ上げれば良い、みたいな話だと「それでいいじゃん」ということもある。本質的に「問題を解消する」ということそのものが極めて困難なものというのも存在する。ミスをなくすとか、停止をなくすとか、障害をなくすとか、その手のものは解消しようとすると大概ドツボにはまる。した「問題をコンパクトに解決しようとした結果残ってしまうもの」に関しては、その問題を解消することではなく、「問題を許容可能な状態にする」ということを考えたほうが良い。フェイルセーフに設計するとか、ミスが発生する前提でリカバリー手順を確立するとか、障害が発生する可能性についてサービスとして明示するとかだ。それらは強い抵抗をうける可能性があるが、そのコストと、コストをかけた上でのリスクを考えれば、安上がりである可能性は低くない。小さいレベルで検証し、テストする多くのことはやってみなければわからない。考えた上でイケそうに思っても、あるいはダメそうだと思っても、実際は結構異なるものだ。大規模な実装の上でテストするというのは、何度も方向転換することになり、好ましくない。そこで、テスト自体をごく小さいレベルにして、感触を確かめるのが有効である。この話は論点がわかりにくいだろうから、詳しく説明しよう。PureBuilderの場合は、そもそもその手法が変遷したこともあり、非常に多くの方法が試された。Perlのワンライナーで置き換える方法、プログラムでビルドする方法、パート分けしてcatでつなぐ方法、PHPで置き換える方法、テンプレートを使う方法。テンプレートで使う方法といっても、プログラムからテンプレートを呼び出してそこに埋め込む方法(Perlのワンライナーも考え方はこれ)、テンプレート内にプログラムのロジックを埋め込む方法(PHPもこれに近い。eRubyはこれ)、テンプレート側からドキュメントを読み込む方法、さらには「環境変数にセットしてZshのヒアドキュメントをテンプレートにしてcatに流せばできるよね」なんてのも。template.zshとかやるだけでテンプレート展開ができるという話である。ACCSでは本当にこんなことをやっていた時期もある。ちなみに、環境変数をセットするのもZshスクリプトであり、こちらは結構複雑な処理であった。このスクリプトを見ると、「catと環境変数で展開できるのは当たり前だから試すまでもないじゃないか」と思うかもしれないが、思うのと実際書くのでは全く話が違う。いうよりも、これ単独ではあまり意味がないのだが、発想したものを一通り書くとわかる。ここで置き換えを想定したトークン埋め込みバージョン(Perlワンライナーでやれる)と、eRubyバージョンを作ったとする。環境変数バージョンは非常に汎用性が高く、呼び出し元がシェルスクリプトである場合にも親和性が高い。環境変数には128kBとか2MBとかのサイズ制限があり、ひっかかる可能性がある。書くのはものすごく簡単である。「書ける上に単純な仕組みを使う」という魅力を感じることができる。書かないと分からない、意外と大きなメリットだ。実際にやってみると意外と制限にひっかかったり、思わぬ事態を発生することがわかる。「これ外部から入力をうけるときはやっちゃ駄目だな」ということを確認できる。「簡単に実現できて効力が高いが問題も多い」というのは、このときに使わなかったとしても「即席でなんかテンプレート的なものがいるときに書けるな」という引き出しになる。トークン埋め込みバージョンは、シェルスクリプトでread/echo(print)し続ける方式だとかなり遅くて、シェルスクリプトでもperlを呼ぶことになる(sedだと難しい)。汎用性は高いのだが、テキスト処理を得意とする言語でないと難しい。した仕組み(構文)を作るかどうかというのは、この簡単さとコストとの兼ね合いになるなということを感じられる。最後にeRubyバージョンを作るととても知見が得られる。何より、今までと比べると断然めんどくさい。「ほんのちょっとのこと」なのだけど、そのほんのちょっとのことがこんなにめんどくさいんだなと感じられるし、考えるべき要素が多いことも面倒に感じられる。他の方式よりも最初からかなりクリーンに書けるから、構文を自分で作らなくて良いという意味でメリットがある。なにより、eRubyで書こうとすると、環境変数方式がほとんど唯一の解で、トークン方式がキーワードか構文か程度の話だったのが、いきなりたくさんの選択肢や考慮すべき点が降り掛かってくることがわかる。eRubyの中でドキュメントを展開しようとすると、呼び出し元はRubyスクリプトにならざるをえないのだが、eRubyのテンプレート側でドキュメントを読むようにするとeRubyで完結できる。そのテンプレート側で読む方法も、単純にSTDINから読んで埋め込む方法のほかに、STDINから読んだものを解析して扱うこともできるということがわかる。STDIN経由ならサイズ制限とかはないから、YAMLとかJSONとかで受け渡してもいい。普通は気づかない「eRubyではSTDINは使えるけどARGFは使えない」ということに気づいたりする。シェルスクリプトから扱いたい場合は、対象ドキュメントのパスを環境変数にセットして呼び出すというような方法もある。他にも様々なアプローチや工夫があり、「いい発想」に至るには書いて動かしてみないと難しいし、その前のふたつを書いてないとeRubyの問題や特徴は「そんなもんだよな」で終わってしまうのだ。ちなみに、私はRubyのMarshalというオブジェクトダンプ形式を環境変数で渡すプログラムを書いて、「環境変数にNUL(\000)が渡せない」という事実にぶち当たったりした。知ってれば「そりゃそうだ」と思うだろうけれど、やらないとなかなか気づかない。ちなみにこの件、2日前に授業で「LinuxはC言語であり、環境変数の受け渡しもC言語で書かれています」という話をしたばかりにもかかわらず、「NUL文字が渡せないなぁ」と思ってメーリングリストで聞いてしまったのは、私のIT人生においてトップクラスのやらかし案件である。SimplyはテンプレートシステムはPandocに依存しており、オプショナルにeRubyを使うこともできるが、少なくとも本文展開はPandocテンプレートを使っている。従来と比べてずっと複雑なシステムを採用したことになるが、「それを実装するのもメンテナンスするのも私じゃないし、機能が提供されている以上それを便利に使えばいいじゃない」ということで、「ドキュメント生成にPandocを採用するという選択肢をとった時点で、テンプレートシステムという問題は自動的に解消された」ということになる。Pluto開発のときにPlutoを「リニアスケールするプログラム」として書くのがテーマだったのだが、その途中で「実際に並列で走らせる」というテストをしている。「本質的な概念」を念頭に開発を進めているので、このときは並列に起動する方法は特に決めていなかった。重要なのは「並列に走ることでそれに比例したパフォーマンスが発揮される」ということなのだ。テストされるのは並列に起動する数によってパフォーマンスがどのように変動するか、である1。10000doneみたいな感じで起動したのである。これは並列処理のテストであるが、ここで重要なのは「どのように並列処理するかは決めていないので、確定された方法で並列処理を行っているわけではない」ということである。事前にテストできれば事前に決めてしまえるのだが、「並列処理の起動方法」をテストするにあたり呼び出すプログラム自体の実装に影響を受けるため、そのプログラムの実装が存在している必要があった。この上で、ごく単純な「形式違いの起動」のテストを繰り返すのである。様々なアイディアを、「そのアイディアの部分だけが違いになるもの」を書く。}endというところから始めるのである。こんなん同じだろ、と思ったそこのあなた。これですらめっちゃ違う。自明に思えることを試したり、より発展した方法が思いつくにもかかわらず小さなテストを繰り返すのは、このテストの省略は、決まった手法を採用する硬直した考えにつながりやすいからである。したテストは、想像だにしないところで自明だと思っていたことが覆されるのはよくある。問題と解決策は1:1ではない「有力そうな手から考える」というのは重要で、問題に視点を置いて考えているといい手が浮かばないことというのは普通にある。この視点において重要なのは、「有力そうな手のアイディアをストックしておく」ということだ。使う予定はないが、「こういうふうにすると色々うまくいくんじゃなかろうか」という筋の良さそうな直感をストックしておくことで、大胆な設計で問題を解消することができることもある。designもそのひとつだ。designは「小さなインスタンスが自分がすべきことだけを規定されており、それ意外のことに関知する必要はない」というルールに基づいて設計するものであり、連続的に変化するデータを扱う場合や、並列処理において大幅にコストを下げ、柔軟性を上げることができるものである。designは自然に誕生したものだが、これはやはり「干渉と同調が難しい」と感じたときに、「連携して動作すること自体をやめてしまおう」と考えたことから生まれている。designが誕生した背景には、私が「自分がやるべきことがあるのに、他の人がやらないせいで自分の手が止められて待たされる」という経験がすごく多かった2からというのがある。私は他者に左右されることなく自分のすべきことを完了したいと思うし、これは「完全に自己完結する方法」と、「他者との間に依存性をなくす方法」の2種類がある。当然ながら、それぞれが優秀だと仮定するのであれば後者のほうが良い。designの発想に至ったことは否めない。designは単純に並列実行と同期に関する問題を解決するだけではない。designは様々な問題を変えてしまう。designにおいて決められているのはデータベースの形式そのデータベースに書けるエージェントは1つだけであることデータベースへのリード及びライトはブロックされないことそのインスタンスがすべきことインスタンスは反復して動作することを想定することだけであり、このデザインパターンを守ることでタイミングだけでなく、同期の問題や、さらには実装にかかる制限すらも解消してしまう。簡単なサーチエンジンプログラムがあるとする。検索対象のデータを収集するボット検索対象データからインデックスを作成するエージェントユーザーに検索をできるようにするウェブアプリケーションの3つがある。収集ボットが書き込むデータベースインデックスエージェントが書き込むデータベースの2つのデータベースが必要となる。権限としては次の通りだ。readread及びwriteがブロックされないので、検索対象DBとインデックスDBは非同期に更新される。検索対象DBとインデックスDBには不整合が起こりうる。言えば、ウェブアプリは検索対象DBの情報は参照できない。(デザインパターン上、複数のDBからreadすることは違反ではないが好ましくない)だが、それこそが重要な点である。連続的に変化するデータを扱う場合、ある瞬間にデータが同期されていることにはあまり意味がなく、常にデータを同期しようとすると著しいパフォーマンス損失が発生するとともに、並列性が低下する。この例のように、データを構成する要素に外部リソースがあると「完全に同期する」ということはそもそも不可能であるため、むしろこうした割り切りが大切なのだ。これによってエージェントプログラムは小さく、そしてシンプルに動作できるようになる。収集ボットはシェルスクリプトとして書き、インデクサは文字列処理の得意なPerlやRubyなどを使用するといった方法も取れる。担当者の得意とするものや、そのケースに適したものにすることができ、分業にも適している。並列性を高めるために「IOブロックしない」というのがあるが、これも「データは非同期である」ということを前提とすると色々と方法があることがわかるだろう。spaceへ書き込む分にはブロックされることはない。spaceからオブジェクトを拾って実際にデータベースに書き込むインスタンスがひとつであれば、並列と同期の問題は発生せず、インデックスエージェントはマルチインスタンスで動作することができる。ちなみに、「一回の処理で同じキーに対する書き込みが発生しないことを保証する」という前提を作ってファイルシステムをKVSとして使うのもなかなか有力。したそれぞれの手法、そしてその結果が、元の問題ひとつひとつに対応しているわけではなく、あるデザインパターンを持ち込むことで多くの問題を解消している。それは個々の問題に対して「これを実現するには」と考えていては見えてこないこともある。「多くの問題が発生するのはデザインがまずいからである」という観点から、場当たり的な改善ではなく、スマートに問題が発生しない設計を導き出すのは、設計段階でしかできないのだ。法的にも厳しい条件が設けられたマイナンバーの管理。ストレージシステム、サーバー環境、セキュリティ環境の構築など結構重たい案件として私のところにも結構問い合わせがあった。これに対して、私に問い合わせがあった全てのケースにおけるベストプラクティスは、「紙に書いて金庫に保管してください」だった。私のところに相談にくるのは基本中小企業であるし、その場合人事情報を扱う部署というのは大抵ない。その規模の企業でも「金庫がない」というケースは稀であり、金庫へのアクセスが管理されていないことはない。金庫という存在によって既に「機密性があり、アクセス権限が管理されている環境」が確立されている状態なので、ヒューマンエラー対策が大変なITセキュリティシステムを作るよりも、既に高い管理意識が根付いていて、実際にそのポリシーで管理されているものを利用するほうが良い。それに、紙に鉛筆で書いたものというのは、データなんかよりはるかに強度があり、失われにくい。黒鉛は安定した物質であり、「鉛筆だと消える」と思う人が多いようだが、試しに何年も前に鉛筆で書いたものを消しゴムでこすってみれば良い。私の回答は「水でほろほろにならない紙に鉛筆でしっかりと書いて、それを金庫に保管してください」であった。全部受けていれば100万円はくだらない受注額になったと思うが、このアドバイスで全て解決してしまったので1件も受注にならなかった。それgrepですよねたとえば、大量のログファイルから、特定の項目を集計するソフトウェアを作って欲しいという相談を受けたことがある。(ログの量によっては厳しいのでひと工夫いる)また、「膨大な文献から、ある特定のワードを含むファイルを見つけたい。.な案件であり、CygwinとMSYSの説明をして丁重にお断りした。Yokohamaとしては開発案件は受けたほうが売上になるが、さすがに車輪の再発明でお金をとるのは気が引ける。それExcelでよくないですか割とある。これは私を知らない人には「なんのこっちゃ」だろうけれど、私はExcelをひどく憎んでいて、「Excelフォーマットでデータを送りつけて、Excelで編集して返せと言われたから団体から脱退した」なんてこともあったし、「Excelでデータを作ることを求められたから依頼を断った」なんてこともある。Excel(というより表計算ソフト)を使いたくないがためにソフトウェア開発したことも数知れないし、なにがなんでも使いたくない。少ないコストで問題が解決できるならそれで良いのである。textはYouTubeに上がっているテキスト動画のように「ハンズフリーでテキストを読みたい。それも、任意のスクロールスピードで」という動機で作られた。方法は実に色々あって、一番簡単なのは端末で流すことなのだけど、それだと読み心地がよくないので、「スムーズな画面のスクロールならJavaScriptにあるじゃん」ということで、HTMLページを生成するプログラムになった。単純極まりないが、かなり緻密にコントロールできるので良い解決策だった。キーボードやマウスのイベントを拾うのも楽。ちなみに、快適に見られるブラウザが欲しくて、結局ブラウザを自作した挙げ句、そのブラウザ自体が新規に書き直されるというような事態に至っているが、これは問題解決のためではなく、趣味とトレーニングを兼ねているので採算度外視である。余談だが、私のマシンはコア性能は低いものの20コア40スレッドで動作する。この手の設計のプログラムをテストしたり、実際に走らせるために、である。↩︎これは、なぜか私はめぐりが悪く、自責を果たさない人とばかり組むことになるという問題と、私が速度が速いために総じて待つことになりやすいという問題と、切り替えコストが非常に高い私はそうした事態においてひたすら待つより他にないという問題が組み合わさっている。問題は一定ではない解決方法は手段である。