Spinach Forest

April, 2015

/ 転校生の本歌取   / Custom Views の気楽さ加減   / Rust の Lifetimes   / 書き直さない時代の気分   / 確率的に犠牲的   / Web Push Protocol   / Funemployment   / ... 

転校生の本歌取

ふと思い出し書き直しの話 (1, 2) の続き。

書き直しの必要に迫られることも、たまにはある。それはオリジナルが手に入らないとき。転職先で前の職場にあった内製ツールが使いたい。慣れたプログラミング言語にあったライブラリが新しい言語に欲しい。そんなときは自分の新しい環境でオリジナル相当品を書き直したいかもしれない。再発明、移植なんて呼ぶこともある。

この書き直し、再発明は、必ずしもオリジナルを超えなくていい。越えるべきオリジナルを使えない事情があってのコードだから。スポーツの試合で怪我をしたエースのかわりに急遽投入された補欠の立場に似ている。ベストを尽くし役割を果たせばいい。補欠系書き直しとでも呼ぶことにしよう。

Bazel と補欠たち

一応の役目は果たすものの、オリジナルほどはぱっとしない。そんな補欠系書き直しはたくさんある。

プログラミング言語をまたいだ補欠系書き直しとして真っ先に思いつくのは Node.js 用のビルドツールたち。Grant にしろ Gulp にしろ、たしかに Node のライブラリを直接呼び出せるぶん JavaScript 専用ツールならではの仕事をしている。けれど素晴らしい出来とも言い難い。 かつて GNU Make がつとめた決定版としての貫禄を欠いている。

職場をまたいだ再発明はどうか。

少し前、 Google は Bazel というプロジェクトをオープンソース化した。クロス言語の汎用ビルドシステム。Google 社内のコードは大半が Bazel でビルドされている。公開こそ最近だけれど内製ツールとしての歴史は長い。大規模コードベース相手にも動作が速いと社内外にファンが多く、会社をまたいで何度も再発明されている。

Facebook は Buck という Android と Java 用のビルドツールを作った。Buck は Bazel の強い影響下にある。DSL の文法があまりにそっくりなので、Buck の存在を明かした講演ではサンプルコードが画面に現れた途端に会場が Google 社員の笑いに沸いた。Bazel と同じく Buck は巨大ツリーのビルドを得意とする。加えて Android 向けの高速インクリメンタルビルドというキラー機能がある。一方で制限も多く、用途も Android と Java に限られる。そのためか今のところ普及しているとは言えない。

Twitter には Pants と呼ばれるビルドシステムがある。これも Bazel の仲間。文法もよく似ている。Pants は Scala や Python など複数のプログラミング言語および Thrift コンパイラのようなコード生成ツールを標準でサポートする。Twitter の Polyglot な性格をよくあらわしている。そして Bazel や Buck 同様大きなコードベースを扱うのが得意。Twitter の Scala エコシステムに集う企業を中心に使われてはいるものの、やはり普及の兆しは弱い。

Google 自身も Bazel の亜種を持っている。Chrome は GN というビルドシステムへの移行を進めている。GN は高速低機能な make 代替品 Ninja の設定ファイルを出力するメタビルドシステム。だから Buck や Pants とは少しちがう。それでも Bazel からいくつかのデザインを引き継いでいる。GN は前身の GYP より高速で文法も簡潔なのがウリ。でも現状の Chrome ビルドが複雑すぎてまだ移行が終わっていない。Chrome 以外で使う話も聞かない。

みな Bazel を使えばいいじゃん、といえるほど簡単な話でもなかろう。表舞台に現れるのが遅かった上にまだオープンソース化がおわっていない。今となっては Buck や Pants の得意分野で同じだけの力を発揮できるのかすら怪しい。贔屓目に言っても膝に矢を受けている。

内製ツールとしての Bazel は補欠でない書き直しの成功例だ。表現力を絞り厳密さを強いる DSL にビルドシステム本体の拡張性を組み合わせる優れた設計を起点に分散ビルドまでを備え、社内の GNU Make を置き換えた。けれどその成功はより広い世界に届いていない。書き直された補欠達もどこかぱっとしない。これはビルドシステムの呪いなのか、それとも書き直しの宿命なのか。いずれにせよ冴えない。

これらのツールを使ってる当人たちは困ってないだろうから、補欠よわばりが失礼なのはわかっている。でも Makefile を書いては自己嫌悪に陥り GNU Make を呪う身の自分はがっかりしてしまう。誰かあいつをやっつけてくれよ・・・。

転校生 Git

落ち込んできた・・・明るい話をしよう。補欠が思わぬ活躍をすることもある、とかね。

ある人は補欠系書き直しの金字塔として Git の名を挙げる。Linux カーネルのバージョン管理システムとしてうまれた Git は、ライセンスの変更に伴い使うことが難しくなった商用の分散バージョン管理システム BitKeeper の穴を埋めるために生まれた。分散バージョン管理がまだ珍しい 21 世紀初頭のことだから、Git の開発者が分散バージョン管理について多くを BitKeeper から学んだであろうことは想像に難くない。

今や Git はバージョン管理ソフトウェアの代名詞となり、一方の BitKeeper はユーザを探すのすら難しい有様。

Git を補欠よわばりするのはいくらなんでも畏れ多い。もうちょっと妥当な例えはないものか。転校生なんてどうかしら。ある土曜日、決勝戦当日の朝。エースストライカーが交通事故で骨折との報が届く。自分ら人数ギリギリだったのにどうすんの。青ざめるチームメイト達。そんな中、オレを出せと歩み出たのはウチのクラスの転校生だった。名前はたしか… Linus Torvalds…

Git の素晴らしさに異論はないし Linus Torvalds のことは尊敬している。でも転校生がロックスターだったこのストーリーに自分はあまり胸踊らない。転校生モノの面白さってそこじゃないでしょ。

Finagle

別の転校生 Finagle の話。Finagle は Twitter で開発された Scala の PRC フレームワーク。Twitter の Microservices 基盤の中核ともいえるコンポーネントだ。プロトコルは Thrift を話す。

Finagle の元ねたシステムの一つは Google の内製 RPC フレームワークだろう。GRPC の前身たるそのシステムは C++ で書かれ Protobuf を話す。

直列化フォーマットとしての Protobuf は以前からオープンソースだけれど、当時その RPC 実装は公開されていなかった。Protobuf のライバルたる Thrift にはいちおう PRC もあったが、多重化も非同期 API もないしょぼい代物。本気の RPC 実装は Twitter の Microservices 化に不可欠。でも使えるコードがない。そこで Finagle が生まれた・・・と自分は理解している。

Finagle は単なる補欠ではない。Scala の言語機能や Netty の拡張性をとりこみ、同世代には類のないフレームワークとなった。Future ベースのスマートな非同期 API。Zipkin に代表される様々なプラグイン。派生元の C++ RPC 実装にこのカッコ良さはなかったに違いない。(あったらごめんなさい。)

Finagle 開発者の一人 Marius Eriksen はもともと Google でインフラまわりのコードを書いていた BSD ハッカー。良いプログラマには違いない。一方で件の内製 RPC を作っていたわけではない。それでも前職の経験と Twitter の Scala 力、それに彼自身の技術を組み合わせて Finagle を作り上げた。

大企業からスタートアップに移って活躍をするエンジニアは、都会の私立高校から田舎の公立校にやってきた転校生とどこか似ている。都会のクラブでは当たり前の殺伐とした厳しさがここの部活にはない。田舎の公立で Rails なんて・・・。最初は馬鹿にしていた主人公。それでも段々と心を開いて練習に打ち込み、ついには全国大会へと進出する。会場で彼を待っていたのは一昨年まで在籍していた検索学園だった。「おまえ Ruby 使ってるんだって?クラッシュしない言語は楽だよナァ」ビッグデータだクラウドだとプロプリエタリな C++ コードをちらつかせつつ主人公を嘲笑するかつてのチームメイト達。でも今の俺には Scala がある!ノンブロッキングの未来が俺を待っているんだ!!逐次型マシン語と関数型バイトコードの戦いの火蓋がいま切って落とされた!!!

というストーリーの方が転校生モノとしては面白いとおもうのだよ。なおこの投げやりな物語はフィクションであり、実在の人物・団体とは一切関係ありません。

Finagle をめぐる俺たちの RPC (仮題)には続きがある。少し前、Uber 高校は TChannel という RPC フレームワークを公開した。やはり Thrift を話し、Python, Node, Go をサポートする。TChannel は Finagle の影響を隠さない。Finagle みたいのが欲しかったけど Scala を使っていないから自分たちの使う言語で書いたよと言っている。組織ではなく言語の壁が書き直しを促すパターン。Finagle に限らず GRPC など他の選択肢も増えた昨今、オープンソースプロジェクトとしての TChannel にどれだけ魅力があるのかはわからない。コードを軽くひやかしたかんじさほど意欲的にも見えない。地味な幕開けの第二部。行方は暇なときにでも見届けたい。

本歌取

単なる書き直しには身構える自分だけれど、補欠系、転校生系の書き直しはけっこう好きだ。転校生系書き直しは本歌取でもある。舞台を変え、新しい制約や文脈の中で古いアイデアを再発見する。その歌の核が何なのか、何が偶然で、何が時代の要請なのか。本歌取の歌い手に助けを借りながら、自分も答えに近づくことができる。そんな期待がある。

期待は叶わない事も多い。それでもカバー曲を聞く楽しみは残されている。新しい時代の風をうけ軽やかに歌う姿はそれだけで励まされる。羨ましくもある。オリジナルの隣で書き直すとなかなかこうはいかない。本家のプレッシャーが強すぎて息苦しい。

あるソフトウェアが再発明なのかオリジナルなのか、その境目は必ずしもはっきりしない。たとえばプログラミング言語の実装なんて繰り返されるアイデアのるつぼだ。だから何かを再発明だモノマネだと非難するのは気が乗らない。同じものになったらむしろ大したものだけれど、そもそもカバーはモノマネと違う。そっくりじゃなくていい。書き直しでなくても、新しいソフトウェアに潜む耳慣れたモチーフに気づくと得をした気分になる。わかるよ、僕もそれ好きだよ、なんて頬が緩む。

そういうの、たまにはいいじゃん。


草稿にコメントをくれた @karino2012, @jmuk ありがとう。

Custom Views の気楽さ加減

唐突に Android 入門日記。

余暇につくっている小さな Android アプリを Hierarchy Viewer で眺めてみた。性能上 View tree はフラットなほど良い。自分のアプリは思ったよりツリーが深くてげんなり。

Custom Elements や React Componentsと同じノリで沢山の Custom View を定義しているのがまずいのだろうか。状態を持たせるためだけに View のサブクラスをつくっている。各 custom view はコンストラクタで inflate() してサブツリーを作り、それを表示する。View#onDraw() は override しない。今のところ必要がない。

このやり方で custom view のレイアウトを与えると、ルートとなる View (大抵はなんらかの Layout クラス) が自分自身とは別に必要。ルートになれる View は一つだけ。この制限がなければ custom view 自身がその Layout クラスを継承すれば済むのに、XML の構文だけが理由で単一ルートというこの制限を受ける。Custom view -> LinearLayout -> 本来の sub view … と一段 View 階層が深くなってしまう。やや理不尽。

Activity, Fragment

人々はどうしているのだろう。サンプルアプリの親玉 iosched を覗く。アプリの規模と比べて custom view の数は少ない。20 個くらい。むしろ Activity や Fragment の方が多そう。

Custom view を使わないなら、どのように View をグループ化するのか。

ある程度の規模があるまとまりには Fragments を使っている。Responsive にしない画面では Activity にベタな layout を流し込んで終わり。自分が custom view を作りたくなる典型的なケース、RecyclerView の個々のアイテムにも custom view は作らない。適当な layout を inflate し、そのサブツリーに必要な状態やイベントリスナをくっつけておわり。たしかにこれなら余計な階層はできない。でもオブジェクト指向の敗北みたいでなんとなく悲しい。Adapter の getView() が巨大になるし・・・

ViewHolder

オープンソースの Twitter クライアント Talon を覗く。すると状態は ViewHolder という呼ばれるクラスを定義し View#setTag() で個々の View に紐付けている。たしかにこれなら View を継承しなくて済みそう。よく見るとオフィシャル文書でも紹介されているパターン。主に性能上の理由で使えと言ってるけど、ここに振る舞いや状態を与えてもよさそうじゃない?ダメ?

ViewHolder はパターンにすぎず、必ずしも決まった型があるわけではない。ただ RecyclerView には RecyclerView.ViewHolder なんてクラスもある。これを使うのが流儀なのかね。

さて custom view を定義するのはいつなのか。 再利用可能な部品を作る時、特殊な描画が必要なときに使っているように見える。

少ないサンプルから得た自分の理解をまとめると:

  • レイアウトを inflate してリスナや値を差し込めば済むケースではいちいち Custom View を作らない。
  • 粒度の大きな View の集まりは Fragment でグループ化する。主に responsive な画面をつくるため。
  • Fragment が必要ないなら Activity だけで済ますのもあり。
  • リストの要素など小さなまとまりには ViewHolder クラスを定義して状態をまとめ、そのインスタンスをサブツリーのルートに setTag() する。
  • 凝った描画をしたいときや再利用可能な汎用 View 部品は custom view にする。

今書いているコードと結構違ってしまうなあ・・・。

アンチ Fragment の Custom View

ふと思い立ち Jake Wharton のサンプルアプリ u2020 を覗いてみる。この人はカジュアルに custom view を定義している。ただしそうした View 自身は subview を inflate しない。Adapter が custom view をルートとする layout を inflate する。これなら余分な view のネストは発生せず、かつ custom view を view tree のカプセル化に使う馴染み深いスタイルを踏襲できる。先の iosched なら Fragment を使いそうな場面でこうなっているのは、彼らがアンチ Fragment 派だからかもしれない。

Adapter に inflate させると本来は View が隠すべき詳細たるレイアウトが漏れ出ている気がするけれど、一方で onFinishInflate() など View 標準のライフサイクルに則りやすくなるのはよい。このバランスがちょうどいいかも。

この流儀では:

  • View ツリーをまとえる手段としても気軽に Custom View を定義して良い。
  • Custom View のインスタンス化は全体を Inflater に任せ、View を直接 new したり View 自身に inflate() させたりしない。

大げさ加減

Custom view は大げさで、わざわざそれが欲しくなるほどの複雑さは多くないとも聞いた。抽象の厚さには好みもあるので一概には言えないけれど、たしかに inflate して OnClickListener つけておわりくらいなら custom view はいらなそう。どうせクラスがないなら functional な感じにかけるとかっこいいんだけどなー・・・

  • まとまりをつくるのに新しいクラスが必要とは限らない

ドラフトにコメントをくれた @karino2012 ありがとう。

Rust の Lifetimes

もうすぐ 1.0 という Rust。 久しぶりにチュートリアルを読む。以前見た時は不可解な機能が多く、こりゃ難しさに溺れて死にそうだな・・・と思っていた。今見るとずいぶんすっきりしている。単純さに舵を切れたのはえらい。

チュートリアルに登場したうちわからなかった機能は 2 つ: Lifetimes と Macros. Macros はさておき Lifetimes は理解してよさそう。詳しい情報を求めてリファレンスをひやかすが、驚くほど何も書いてない。かわりにメモリ安全な C の方言 Cyclone による region based memory management が元ネタとして言及されている。勢いでその資料にも目を通す。解説 PDF ひとつとマニュアルの一部。どちらもよく書けていた。記法も Rust の lifetimes と似ている。

Cyclone の region based memory management では、フレームに確保したメモリの出処、つまりメモリの生存期間を、そのメモリを指すポインタ変数に静的な型情報としてくっつける。この生存期間が Regions. 型は総称的で、その総称性を通じ関数の呼び出し元から呼び出し先へと変数毎の reigons が伝わっていく。そして長い regions のポインタに 短い regions のポインタを代入しようとすると型チェックに失敗してコンパイラに怒られる。だから dangling pointer がおきない。

全てのポインタ変数が型に regions を持っている。関数の引数も同じ。そう聞くと仰々しいけれど、大抵はデフォルトの型でうまく動く。そしてデフォルトを使うかぎりコード上に regions は姿を見せない。だから普段は気にしなくて良い。Cyclone だと C からの移植で 8 割くらいは元のコードそのままでよかったという。それでも時々 lifetimes を指示したい場面があり、region 記法の出番となる。

Rust の仕組みはだいたい Cyclone と同じに見える。生存区間を表す型を Cyclone では Regions, Rust では Lifetimes と呼ぶ。Rust の reference すなわちポインタは, 変数の型に (多くは暗黙の) regions を含んでいる。

Lifetimes はコールスタックというかフレーム上のメモリの寿命をトラックするための仕組み。だから lifetimes の寿命は呼び出し関係に応じた入れ子になっている。ヒープに確保したオブジェクトの動的な寿命をトラックして正当性を担保するのは lifetimes の仕事ではない。ヒープへの reference も取りだすことはできるけれど、その reference の lifetime はヒープを指す変数があるフレームのスコープに紐づく。ヒープ上のメモリが関数のスコープを超えて outlive する事実を lifetimes で表現することはできない。

ではヒープにあるメモリの寿命はどう守るかというと、Box (unique_ptr 相当), RC, Arc (shared_ptr 相当) といったスマートポインタぽい型, move semantics と RAII がうまくやってくれる。ヒープへの reference を取り出すことはできる。ヒープとフレームの交わるところ - ヒープを指す reference の正当性は、ownership の borrowing 機構と borrowing が強制する imutability によって保証される。RAII-Move-Borrowing-Lifetimes の組み合わせが memory safety を実現する。この結論にたどり着いたのはえらい。デザインの勝利だと思う。元ネタたる Cyclone には汎用的な RAII が無いなどヒープの管理には大した工夫がなく、普通に Boehn GC を使っていると書いてあった。Rust のような GC-less にはできてない。

安全なぶん厳しい制限でもある。C++ と同じようにコードを書くわけにはいかなそう。いままで曖昧に済ませていた細部をよく考えないといけない。自分が混乱したのも lifetimes とヒープの関係が曖昧だったせい。おもったより寿命にルーズだった自分に気付く。

Rust には安全でないメモリを扱う raw pointer もある。どのくらいの頻度で raw pointer が必要になるのだろうか。動的サイズの配列 Vec 型の実装では raw pointer が使われていた。C++ で replacement new を呼んだり destrucdtor を明示的に呼んだりする程度に必要なかんじかしら。つまりしょぼいコードを書いてる分には必要ない。めでたい。

書き直さない時代の気分

なんとなく続き。(前回)

Martin Fowler が蒸し返すまで、 自分は書き直しについてことさら何か書く気が起きなかった。 どうでもよさは時がたつほど増していった。気分の出処を見直してみたい。

部分的に書き直す

最初の理由は、書き直しがゼロかイチかの大きな判断ではないという納得かもしれない。コードの書き換えには様々な粒度がある。一番小さいのが厳密なリファクタリング。一番大きいのがフルスクラッチの書き直し。その間には様々な大きさの書き直しがある。書き直しの粒度が連続的である以上、どちらか一端を擁護する議論は虚しい。

小さい刻みの変更を積み重ねれば危険を冒さず大きな変更ができる。それがリファクタリングのテネットだ。刻みは小さいほど安全だから、良いプログラマは大きな変更を連続した小さい変更へと噛み砕く。変更の難しさに実力が及ばないと刻みが大きくなり失敗の危険が増す。バグが増えたり納期に遅れたりする。

自分の能力を超えた変更を求められることはある。仕方なく刻みを緩める。とはいえ全開のフルスクラッチまで緩め切ることはない。大半は部分的な、たとえばモジュール単位の、書き直しで事足りる。まず書き換えるモジュールをくくり出すところから始めることもよくある。Microservices だってそんなかんじでプロセスを切り離すでしょ。たぶん。

一見フルスクラッチを避けがたい場面ですら、十分な(あるいは例外的な)能力をもってすれば安全に大きな変更をやりとげられる。Russ Cox の 異言語間 Go コンパイラ移植 はその極端な例。わかりやすい「細かい変更」の集合ではないけれど、変換を通すための小細工を山ほど積み重ねたはず。

反対から見れば、変更の難しさから技量を差し引き足が出た分を危うい資金で補填するのが粒度の大きい書き直しだとも言える。

互換性を諦める

もう一つの理由は、互換性への期待が変わったことだろうか。フルスクラッチな書き直しへの批判は、書き直された新版が旧版と互換性を保つ難しさを論拠の一つにしている。一方で多くのソフトウェアは互換性を捨てながら前に進んでいる。API は deprecate される。刷新された UI からは不人気な機能が姿を消す。ウェブ標準ですら古い API のうち評判が悪かったものを取り下げている。この非互換は書き直しのバグではなく意思決定の結果だ。 意図した互換性の破棄は前もってわかる。だからランダムに壊れるフルスクラッチの非互換よりだいぶマシ。

互換性を損ねると決めた途端、書き直しは楽になる。決めた範囲で互換性を壊しつつ新しい方向に進めばいい。自暴自棄のフルスクラッチに走りたい衝動は遠ざかる。

Windows 10 に載る新しいウェブブラウザのレンダリングエンジン Edge は、古いエンジン Trident をフォークしたものだ。フルスクラッチの書き直しではない。けれどその違いは既に WebKit と Blink より大きいという。Edge は近代的なウェブでの競争力を理由に IE との互換性を捨てると決め、大きな書き直しの扉を開けた。IE の足を引っぱっていたのはひどいコードでなく互換性の鎖だった。

まあコードもひどかったに違いないけれど、それだけが相手ならプログラマの力量でなんとかできる。Microsoft にプログラマ不足の心配はなかろう。

世紀末の悲鳴

書き直しの切り口はいくらでもあるし、必要なら互換性を切り崩してもいい。フルスクラッチでの書き直しを強行する理由を自分はもはや見出せない。けれど 15 年前に Joel Spolsky が書き直しは悪と書いた時、それは人々の心に響いた。なぜか。西暦 2000 年と今は何が違うのか。

槍玉にあげられたソフトウェアの一つは、奇しくもまたウェブブラウザ。Netscape の失敗を Joel は批判した。Netscape はその後 IE に負け AOL に買収されてしまったわけだから、この書き直しを失敗とみなすのは的外れでもない。今だったら「フルスクラッチで書き直しとかあいつら狂ってるな・・・」で終わる話。けれどたしかに 15 年前、フルスクラッチの書き直しはよくある話だった気がする。

書籍 Refactoring の出版が 1999 年。等価書き換え健康パズルのアイデアは今ほど浸透していなかった。コードは少しずつ腐っていくもの。腐敗を遅らせるのがせいぜい。ソフトウェアのリリースも今よりずっと離散的で、オートアップデートなんてなかった。だから仮に少しずつコードを直せても、それを撒いて試すのは難しかった。人々は健全性を取り戻す術をフルスクラッチ以外に持っていなかったのかもしれない。

一方、ソフトウェアの互換性に対する期待は今よりずっと高かった。それはたぶん Microsoft が持ち込んだゲームのルールだった。彼らは互換性のために莫大な工学資本をつぎ込んで高いバーを設けた。ウェブの時代がそれを塗り替えるまで、互換性への期待はソフトウェアの前に立ちふさがっていた。「同じものは壊れない」というユーザの期待が「まったく新しい何か」へと開発者を追い込み、結果として「すっかり壊れた何か」を生み出し破滅を招いた。

15 年前、ソフトウェア開発者は少しずつ直すことを知らず、少しだけ壊すこともできなかった。フルスクラッチの決断は板挟みになった開発者の悲鳴だった。だいぶ単純化してるけど、時代の空気はそんな風だった気がする。

そして今。書き直しは多くの変数を持つ入り組んだトレードオフに姿を変えた。もはやフルスクラッチの書き直しでわざわざ自分の稼ぎ口をふいにするがさつな開発者はそういるまい。Sacrificed Architecture も全てをゼロから書き直せとは言わない。

時代が変わり、書き直しに関する議論はかつての意味を失った。自分の興味も遠のいた。けれどその一方、誰かが書き直しについて話すのを聞くと私は落ち着かない気持ちになる。意味がないのに落ち着かない。この矛盾がどこから来るのかと考える。一つの理由は、私がまだ時代の変化を自分のものにできていないからかもしれない。古い価値観の染みはなかなか落ちない。

たまにはフルスクラッチもいいじゃん、という話を書こうと思ってたのにまた長くなりすぎて尻切れ・・・。

確率的に犠牲的

Martin Fowler が Sacrificial Architecture と言い出した時は驚いた。“変化を受け入れよ” はどこにいったの。書き直しはダメと自分の中の結論が出たのは随分前のことだけれど、ひさしぶりに考え直してみる。

Sacrificial Architecture の論拠として Martin Fowler はいくつかのインターネッツ企業を例にとっている。でも一般化するには偏ってないか。それにこれら企業が面していたのはごく限られた種類の変化だ: 彼らはもっぱら性能不足と戦っていた。

機能の変化に強いコードは柔軟性の裏で性能を犠牲にしがち。機能の変化を捉えることに先鋭化した従来の Agility は性能要件の変化を必ずしもやり過ごせない。一方で存在感を増すスタートアップの世界では性能への期待が当たり前のように大きく変わる。だから Agile はあてにならない、堅牢なアーキテクチャで急成長に備えよ。そう訴えて勢いづく BDUF Microservices 勢を威嚇すべく Sacrificial Architecture は生まれた。でも行き過ぎた一般化に拡大解釈が加わり技術的負債を自己破産する言い訳として流布してしまった。誤解されても無理はない名前だ。焦って口が滑ったな。

性能に対する法外な期待に応えるには書き直しが必要かもしれない。これは妥当な意見に思える。それに別の見方もできる: 性能が追いつかないほどビジネスが急成長しているときは、勢いに乗って書き直しても元がとれる。性能不足は好調さのあらわれ。無理をしてでもこの勢いを逃したくない。機会損失が書き直しの危険を上塗りする。

一方、古き良きダメな書き直しは機能の変化や複雑化に音を上げておこる。この書き直しは何がまずいか。

別の角度から眺める。ソフトウェアの方向性を大きく変えたいとき、あるいは機能をやたらと追加したいとき、だいたいビジネスは失速している。行き詰まりを打破しようと方向転換や守備範囲の拡大を図っている。この投機的な場面で更に書き直しの博打を打つのは雪解けの薄氷で踊るようなもの。危険に見合う期待値がない。

Martin Fowler はこう言うべきだった: ソフトウェアは確率的に sacrificial である。どんなソフトウェアも連続した打ち手では乗り切れない急激な変化に襲われる可能性を孕んでいる。特に成功したインターネット企業では成長という大波が性能上の限界にコードを打ち付ける。けれど波の狭間に新大陸の影が見えていたら? 賭けに出よう。その愛するコードを生贄に…

こうして犠牲となってしまうコードはある。でもそのためのアーキテクチャなんてない。やっぱり失言だな。

拡大解釈された Sacrificial Architecture もけっこう人気がある: スピードのためにひどいコードを書き、うまくいって長期的展望がもてた頃合いでゴミを捨て書き直す。このアイデアは妥当か。うまくいくこともあるだろう。性能だけが書き直しを正当化するとは言わない。でも Martin Fowler 名義でそれを語るのはやめてあげたい。傷に塩塗るみたいで気の毒。例に挙げるのも別の何かにした方がいい。個人的には Windows NT あたりを推したい。人生を書き換えるアーキテクチャとでも呼ぼう。

書きたいことは別にあったけど長くなったので一旦おしまい。

Web Push Protocol

Generic Event Delivery Using HTTP Push” なる RFC を知った。 JavaScript の Push API をネットワークレベルでどう実現するか決めるものらしい。 Push API, システムが提供する GCM なんかのラッパーだと思い込んでいた。 Chrome の現状はそのようだけど、標準的にはスタック全体を定義しようとしている模様。 仕様を書いているのは Mozilla の人。えらい。

Web Push は HTTP/2 の上に定義されており、PUSH_PROMISE で UA にメッセージを送る。 TCP で現実的なプッシュを作れるとは思っていなかったので驚いた。 ざっと調べてみたところ GCM も TCP ベースらしい。 接続を保つべく適当な間隔で heartbeat していると、世間の資料にはある。 SMS など cell network の技術は使わないのか。 もっとも Wi-Fi だけでも動かす手前 TCP 頼みは仕方がない? よくわからないな。 Web Push の仕様は SMS を使うなど適当な方法でそれらモニタリングを置き換えて良いと断っている。

PUSH_PROMISE 以外で面白いところ: 個々のメッセージは URL を持たない。 URL は Subscription 単位(=UA 単位)で割り振られる。メッセージの到達も保証しない。 適当に expire するのでアプリケーションはメッセージがドロップされてもちゃんと動くように書けと注意している。 トランザクションとか言い出さないあたり、地に足が付いている。好感。 Heartbeat の方法も指示しない。HTTP/2 の PING を使えということだろう。下にあるプロトコルの出来が良いとラクだ。

Web Push をブラウザの外、たとえばサーバ間、あるいはデスクトップアプリで使うのはどうか。 XMPP でいい気もすれど、セマンティクスの違いや単純さから Web Push を選ぶのもそれはそれで悪くないかも。 ACK がないとだめかな。 あとは GCM の代替実装としてフルスタックを揃えると Cyanogen などの Forked Android 勢に喜ばれるかもしれない。HTTP/2 実装の普及が待たれる。

Funemployment

“A beginner’s guide to funemployment” を読んで Funemployment という言葉を知った。満足に楽しくやってる無職を指す言葉らしい。

Urban dictionary に登録されたのは 2004 年。 Google Trend によれば 2009 年に盛り上がり、 今日までゆっくりと滑空を続けている。2009 年といえばサブプライム危機で大きな不況があった年。 当時の記事では 不況の煽りでレイオフされた若者がいい機会だからと funemployed を自称し、 仕事をさがすかわりに楽しくフラフラしている様が描かれている。 強がりのような楽観のような、アメリカ的いいかげんさが眩しい。いま流行が下火なのは好景気の裏返しだろう。

私の知り合いには現役フルタイムを含め funemployment 経験者が一定数いる。 羨望の眼差しで体験談に聞き入ったのを思い出す。彼らは自分を無職や NEET と呼ぶ。 間違ってはいないが、言葉の示唆する悲壮感を逆手にとる感じの悪さが鼻についた。 まあ完全に労働者の僻みなんだけど、彼らが funemployed を名乗っていたら手放しで羨ましがってあげられたかもしれない。 私もときどき無職になりたいと呟いてしまうことがある。このとき求めているのは funemployment であって失業ではない。 身勝手な願望に名前がついてよかった。Sabbatical と呼ぶ人もいる。でも funemployed の方がポップ。 日本語だと “有閑無職” くらいだろうか。いずれにせよ謙遜よりも強がり相手のほうが付き合いやすい。

冒頭の記事も認めるとおり, funemployment はどこか特権的だ。 最終的にはなんとかできる自負があるからこそ楽しさを見いだせる。 不景気だった 2009 年の funemployment は強がりだったかもしれない。 でもいま funemployed になるプログラマは余裕があるにちがいない。 余裕の裏付けは若さかもしれないし、高い技能かもしれないし、金銭的なゆとりかもしれない。 自国経済への信頼かもしれないし、性格からくる楽観かもしれない。私の目にはどれも手の届かない贅沢に映る。

だから仕事のない優雅な日々を満喫されている諸兄は無職や NEET を気取らず胸を張って funemployed を名乗り、 素直に身分を羨ましがらせてほしい。明日も仕事の会社員はそうおもう次第。 おまえらが無印無職を連呼するのはほんとに、むかつく。いいなあ。