シャニマスのコード読んだ

ライセンス表記みるぐらいで深掘りはしてない

s/after-affect/after-effect

After effects to pixi.js | CodeSeek なら見つかったが、該当のライブラリ名で探してるが見つからず

正確にはFacebookのライブラリにちょっと前まで共通でついてたBSD+Patentの条項のライセンス表記を発見したが、componentDidMount で grep して引っかからなかったので、 Reactではなく別のライブラリのような気がする。ImmutableJSかなにか。

青羽美咲とは (アオバミサキとは) [単語記事] - ニコニコ大百科

Spine JS


追記

普通に難読化されてないやつ読まなくてもライセンス表記あった…

gyazo.com

たぶんだけどアセットの暗号化で使ってるモジュールの一部に blockchain の何かの何かがあるみたいな感じかな

感情の共有の時代だからこその Podcast 〜 今日からはじめるPodCast #技術書展4

今週日曜日(4/22)の技術書展の、PodCast 本の宣伝を任された mizchi です。最近推してるPodCast は https://turingcomplete.fm/ です。

この同人誌。

gyazo.com

表紙を見ての通り、非常に力が入っています。 yatteiki project の1企画みたいな位置づけの同人誌です。

discord で PodCast 収録する方法っていう妙にピンポイントなソリューションが紹介されてて、SoundFlower, Ladio Cast, Audio Hijackなどを使う方法の紹介されています。こういうのに興味がある技術系PodCastの人は大抵Macなんでだいじょうぶでしょう。nodeでdiscord API叩いてる感じなんで Win の人もまあなんとか。

いつでも撮れるようにしておくってのは本当に大事で、僕も genba.fm っていう PodCast やってるんですが、何回かやって録音に関するソリューションが複雑化してしまって、忙しさとあいまって更新が止まってしまったんで、この本にある discord での podcast を動かして再構築してる最中です。またすぐやりたいですね。

9m の技術的な内容の他には itopoid の番組の企画方法の話がエモい感じです。

PodCast に何を期待するのか

で、ここでポエムをはさみたいんですけども。

まず最初に言っておきたいことは、箇条書きになった技術スタックだとかノウハウ集には、テキストの方がいいわけですよ。コピペできて、検索できて、各自の速度で読めて、便利。

ポッドキャストへはそういう属性のリンク集的なものは期待していません。じゃあ何を?

僕が思うに、漏れ出る美意識とか、対象に対する温度感とか、その当人の譲れないものとか、または話の展開の傾向、それによって滲んでくる人格、そういうものが PodCast に期待されるものだと、僕は理解しています。

だからこそ、偶発性は大事で、事前の台本通りにはいかない上でオチがつくのがいい番組だと思っています。ただし事前の併せ読みはします。最初に、告知があって、「わ〜」とか言ってるのは、(僕とitopoidのやるやつは)結構な確率で茶番です。聞いてる方も、なんとなくわかるでしょうが…

僕はあんまりにも台本的なセリフを読んでると、耐えられなくて吹き出しちゃったりするんですが、itopoid は結構そのまま通していて、これが「エンタメ的な所作か…」と感心してたりしますね…

ちなみに、 yatteiki.tv の方は @sageszk というニコ生などで生配信の機材セットアップやその運用などを本職でやってる友人に手伝ってもらっていて、併せ読み用のプロ品質の原稿があって、事前打ち合わせをみっちりやって、話す話題、話せない話題を調子して、複数台マイクがセットアップされてミキサーで調整されてたり、あれは手間が掛かってます。Youtuberはたいへん。vTuber とかもっと大変だから、興味ある人はとりあえず PodCast など取ってみたりするのはいかがでしょうか。美少女にはなれませんが…

https://yatteiki.fm/tv/

mizchi の PodCastへの出演

書き出してみたら結構出ていた。暇だったら聞いてください。

誰もが話すべきテーマを抱えているはずです。インターネットでは誰もが主役になれる。そういう、ちょっと古いかもしれないインターネットのイデオロギーで動いてるのが yatteiki です。

キャッシュフレンドリーなステートレスアプリケーション設計について考える #CDN_Study

CDN_Study という勉強にいってきた。 https://http2study.connpass.com/event/81469/

そこで、Akamaiの方が、「個人の意見だけど、アプリケーション側がもっと基礎設計でステートレスでキャッシュフレンドリーな設計になってないといけないよね」という旨の発言をしていて、最近そのことにアプリケーションエンジニアとして同じようなことを考えていたので、書き出してみる。

SPAとかSSRとかフロントの不毛な話は出さないようにしてるが、主にサーバレス環境を意識している。

前提

  • 世の中のアプリケーション内のモジュールは、Statefull or Stateless に分類でき、それをツリー状に表現できれば差分検知できる、という React の仮想 DOM 的な世界観が自分にある
  • 以下の話は、基本的には Fastly のサロゲートペアーとそのためのミドルウェアを作れば既に実現できる話ではある
  • (Akamaiは自分は使ったことないです。ごめんなさい…)

参考: https://docs.fastly.com/ja/guides/purging/single-purges

ウェブアプリケーションへの需要/要求

現状のウェブアプリの設計において、動的ページの HTML をキャッシュさせるのは、それ専用の設計をしないととても難しい。まず、何が入力に対してべき等で、何が外部IOを含んでいるかを事前に分類する必要がある。

で、それを乗り越えると何が嬉しいか。PWAやAMPみたいなフロントエンドからみたパフォーマンスチューニングでは、ある一定以上の体験を求めると、キャッシュ済みの *.htmlCDN のエッジに配信させる必要がある。基本的にアプリケーションサーバに飛ばしたら負け。ここで同意が取れない人もいると思うが、そういう前提とする。

HTMLをエッジに配信させると、体感値としては、1000~5000ms ぐらいのオーダーだったものが50~150ms のオーダーの世界になる。このインパクトは大きい。しかし現状そのアーキテクチャを取るための手法は発達していないので、誰でも出来るといった状況にはない。

具体的にあったらいいなと思うもの

fastly にはサロゲートペアーという機能がある。これはパスに対してタグを打っておいて、そのタグがついてるものをまとめてパージするというもの。

これを使って、たとえば /article/1 というURLがあって、HTMLレスポンスが変える時、そのページのキャッシュ判定を行うサロゲートペアーは次のようなオブジェクトで表現できるかもしれない。

var Relations = {
  '/': ['APP_VERSION:0.0.1', 'app.js', 'app.css'],
  '/article/1': [
    'APP_VERSION:0.0.1',
    'app.js',
    'app.css',
    'function:getArticle?id=1:update_at=1523717246877'
  ],
  // ...
}

まず、デプロイの度に全破棄するような実装のために、 APP_VERSION みたいなキーを張っておく。 他は、ある他のリソースに対して、サロゲートキーへのリレーションの表現をする。

app.js, app.css に違和感はないだろうが、3番目のサロゲートキーで update_at をキーにしたものを登録することで、なんらかの function(AWS Lambdaなどを想定) へ渡すまえにキャッシュ判定をして、CDN内で外部IOが発生することなく高速にキャッシュを返せる判定ができるはず。

これをどのように実装したいか。たとえばこれがDBアクセスを仮定すると、基本的には何らかのDBのレコードのtimestamp を収集することになると思う。

これを実現するような、Node.JS の擬似コードを書くならこう。

// なんらかのORMの post-update を想定
Article.onUpdate(item => {
  AssetCache.invalidate('function:getArticle?id=1:*')
  AssetCache.registerNewSalogateKey(
    'function:getArticle?id=1:' + item.update_at
  )
})

あえて冗長な表現をしている。

で、事前に宣言した Relations に対して、 invalidate したアセットに関連するものを再帰的にパージすれば、「id=1 で表現した入力とその結果が本当にべき等なら」、これで更新後に最初の一回だけ function を実行してあとはキャッシュを返し続けるような実装が、たぶんうまくいく。新規デプロイ時はAPP_VERSION更新すれば全部消える。function を個別に管理するならAPP_VERSIONも不要。運用難しいけど。

ここがうまくいくと嬉しいのは、キャッシュルールはデプロイごとに静的として、サーバーは単方向に invalidate を発行し続けるだけ、クライアントのリクエストも常に同一、動的なのはキャッシュミドルウェアのKVS的な内部状態だけで、デプロイまたはDBのレコード更新に動的に反応するだけのステートレスなアプリケーションが出来る。

こんな実装は可能なのか

構成としてはこうなるだろう。

Function(Application) <=> CasheMiddleware <=> CDN <=> Browser

上に述べた実装を仮に CasheMiddleware という名前にしたが、こいつで上のサロゲートキーのリライトルールを実装する。内部ステートは大雑把に redis かなんかで作れる。転地インデックス作るぐらいでそんなに難しくない。

Fastly のキャッシュパージ速度は公称150msなので(正直実測はしてない)、一時的な不整合も最小限に抑えられる。CloudFront は10分ぐらいかかるので、そうもいかなかったりするが。

それで、実際にはアプリケーションコードから、関連サロゲートキーフレームワークの機能として、動的に決定できるようにする。たとえば、アセットは自明として、なんらかのビューライブラリで

<x-article-component data-json={ useCache(fetchArticle(1)) }/>

みたいなヘルパをかまして、中のエンドポイントを収集してヘッダに埋め込むなりして紐付けたうえでViewとそのリソースへのリレーションを生成すればいい。

この実装を作る気があるか

NO

なぜならこの設計はfastlyのみで動くサロゲートキーの仕組みに強く依存している。fastly でしか動かないものを作りたくない。(個人的な話で言えばフリーランスなんで fastly が使えないと最適化できませんみたいな人材になってしまうのが一番のリスク)

また、セッションを考慮してないので、クッキー依存の動的ページをJSまたはwebcpomonts/iframeなどに必ず切り離さないといけない。そうしないとちょっと前のメルカリで他人のクレカが見えちゃった的なミスが起こる。JS書く人間には抵抗はないが、これを受け入れられない人も多いだろう。だいたいHeaderの右側のログイン情報とか。

また、全てのAPI実装が関連するレコードのタイムスタンプに対してステートレスである、という制約が厳しく、運用で事故る未来しか見えない。ここは人間を信用してはいけないレイヤー…。

しかし、ApplicationServerでHTMLを作って返すより、CDNエッジからHTMLを返した際に得られる速度はわかりやすく圧倒的なので、「fastly のサロゲートペアーまたはそれに準ずる仕様が何らかの標準にあるなら」、「フレームワーク組み込みでサロゲートキー生成を込みで」実装する恩恵が出てくる。ここまできてはじめてフレームワークかなんらかのヘルパ作って嬉しくなるなぁ、という感じ。そうなったら 特定のCDN 使わなくても nginx middleware とか書けばいいし。

ただ、「こういうカスタマイズ要素を標準化しちゃうと、各プラットフォームの売りがなくなっちゃうよね」みたいな話があるのもわかる…ビジネス難しい…

個人的には、CDNはキャッシュのパージ速度、IOへの課金、ストレージサイズへの課金で選ぶので、DevelopperExperience 以外の内部拡張がどうこうって話は正直知りたくすらないみたいな気持ちもある。あるいは世の中すべてが fastly になるならサロゲートキーにロックインされに行くぞ!ってなりますね。

終わりに

なんかこういうの既にありそうだけど面倒で調べなかった。 キャッシュファーストなアプリ設計、初心者がハマりがちで、あんまり流行らなさそうという実感もある。 パフォーマンスチューニングおじさんしてると、諦めてスクラッチで書き始める。辛い。

プロトタイピング

Path of Exile ってハクスラにはまってるんだけど、ゲームってやってたら作りたくなりませんか。 途中まで作った。

https://infallible-easley-5955b5.netlify.com/

使ったもの

  • React
  • Redux
  • SVG

ひたすら素朴な react-redux で作るのを心がけた 他はすべて気合で0から実装

実装したもの

  • 経路探索
  • カメラ
  • ポインタシステム
  • 当たり判定

詰んだ

きれいな設計にするために副作用起こせる場所とアクセス絞ってたら無限に想定から外れて、「絶対に副作用を起こさないと詰む」みたいな箇所が増えてしまい、コードを大幅に書き直さないといけなくなったので、一旦断念。オブジェクト間のメッセージシステムをもっと念頭に置くべきだった。

React部分で困ったことはない(複雑なものもない)。当初懸念していた reducer の実行コストと描画コストだが、pure つけまくるとどうとでもなった。

とはいえSVGをマップ描画すると、描画コストが想定よりだいぶ重かったので、諦めて Canvas で描画して data URI をimage要素にぶち込むと速かった。

近況

ローソンのブランパン(糖質2.0g)、ビーフジャーキー、サラダ、炭酸水…

年末に太った 69kg を 65.kg まで落とした。身長170cmなので平均は62kg。72kgで準肥満。

完全に無の食生活だが、特に空腹感はないし、わかりやすくどんどんやせる。高校生時代の58kgまで落としたい。

ブランパンは本物の虚無の味がする。

TODO: 最近の活動のまとめ書く

ServiceWorker as a Service, または Universal ServiceWorker という発想

ServiceWorker とは本質的に リクエスト&レスポンスモデルであるので、それをサーバーサイドで実装で一種のサーバーロジックとして動かしてしまって良いはずだ ー

という発想に目から鱗だったので、ちょっと考えてみたいと思う。

www.publickey1.jp

ここで試せる。

https://cloudflareworkers.com/#a9bc9ef6b4248289c71518581df30bc7:https://tutorial.cloudflareworkers.com

Cloudflare はCDN業者なので、 それに特化して Service Worker as a Service みたいな表現はしていないが、実態としてはサーバーサイド ServiceWorker だ。Fastly では varnish のミドルウェアなどでキャッシュ破棄設定のロジックやリダイレクトを書いていたが、それが ServiceWorker という体を取っている(ように見える)。

ちゃんと使い込んでいないので、おそらくだが、 cache オブジェクトの実装が cloudflare の asset cache の実装にそのままつながっていて、そこでキャッシュ破棄、構築命令に翻訳されているのだろう。


追記: 実際はレスポンスを書き換えるだけで、まだキャッシュ実装はないが、やりたいとのこと


Cloudflare の手法で優れているのは、現状、すべてのブラウザが Service Worker を実装しているわけではない(というかIEのことだが)ので、将来的には標準的な手法になるはずの中間層を用意したことで、たとえばpush イベントも(Safariであっても) をサーバーサイドで溜め込んで置いたり出来る。また、クライアント用 Service Worker と密に連携したスクリプトも配信できる。ビジネス的にもストレージサイズやイベントドリブンなエンドポイントの実行回数で課金できる。

イベントドリブン標準としての ServiceWorker

ミドルウェアでなんでもできる、という点で AWS Lambda や Google Cloud Function と同じようなものとして使うことが可能だろう。FaaS の1スペックとして、ServiceWorker as a Service という形式はアリだろうか。

cloudflare の チュートリアルで配布されてるコードはこんな感じ。

addEventListener('fetch', event => {
  event.respondWith(fetchAndLog(event.request))
})

/**
 * Fetch and log a given request object
 * @param {Request} request
 */
async function fetchAndLog(request) {
  console.log('Got request', request)
  const response = await fetch(request)
  console.log('Got response', response)
  return response
}

普通の service worker のコードだ。普通の。

毎度 Lambda と Cloud Function を使ってて思うのは、いまいち Node 側からのインターフェースが仕様化・共通化されていないので、毎回ベンダロックインされたコードを書くことになってしまっている。それぐらいだったら一応は仕様がW3Cではっきりしている ServiceWorker の仕様を使うのは筋悪ではない、と思う。

しかしどうせ各プラットフォームが独自な名前空間を持ってイベントを生やしてくる。Cloudflare もその方向を最初検討したようだし、それ自体は現実的なユースケースを踏まえると必要だと思う。実際ユニバーサルな ServiceWorker というものは、なんらかの抽象化層を経ないと到達し得ないだろう。

また、express で cluster モジュールなどを使ってOSSなサーバーサイドServiceWorkerを実装するのは、そこまで難しくなさそうに見える。Open FaaSなどとも方向性が同じになる。

なので、方向性としては歓迎したいが、大きなプレーヤーが標準化に向けて動いてくれないと難しそう。

フロントエンドの負債と向き合う

某所で書いたものを公開用に書き直したもの

前提

フロントエンドでTDDは難しい、というかほぼ不可能である。なぜなら事前に副作用をデータとして表現できるか不明だからだ。たとえばあなたのプロダクトの画面の何処かにボタンを追加するために、その内部表現を事前に思い浮かべることが可能だろうか?

react-redux などのFluxフレームワークは如何に副作用をアクションとして表現することで、テスト・デバッグのための情報を残すか、という視点で発展してきた側面がある。あの冗長なアクション定義は、全てデバッグのために書いていると言っても、過言ではない。それすら「Textは文字がある」といったトートロジーなデータになりがち。

フロントエンドの現実的な単体テストは、他の開発者のために、自分が書いたコードの要求を満たしているか検知する手段として、防衛的にテストアフターしておく。これぐらいしか現実的な手法がない。それ以上は、手動テスト、E2E、あるいはUXデザインの範疇だ。

GUIの開発は、最終的に目に見えてるものがすべてであって、DevTools上のパフォーマンス上のメトリクスではなく、「ユーザーがどう感じたか」に集約される。そして人間の感性は理不尽だ。その理不尽さに付き合うために、どの部分を外部から入力可能な値にしておくか、が設計の妙になる。

要は、UXは単体テストの範疇外だが、そのための変数は外部入力で受け付けるべきだ。

負債と向き合う

よくある状況

このデッドロックを乗り越えるためには、実際必要なのは「気合い」である。ただし「気合い」にも種類がある。より効率が良い「気合い」を目指すべきだ。

とはいえ、最初にいうべきは次のような言葉になる。

「このような状況は会社、開発チームとして溜め込んだ負債であるからして、この改善には痛みを伴う」

残念なことに、まずこれを認めることから始めなければならない。自分も、チームも。

手段

負債と向き合うための現実的な施策として、次のような感じになるだろう。

  • 手動テストを密にやる
  • 書き換えたコードを一部のユーザーにだけ徐々に適用していく
  • エッジケースのエンバグを一時的に許容してもらう
  • (厳密にはリファクタリングではないが)コードの簡略のために仕様を変更することを認めてもらう
  • そもそもコードを破棄して作り直す

個人の裁量でやれるのは、おそらく最初の手動テストの厚さだけで、それすら外部テスターがいる場合にはそれを共有する必要があり、要は大抵は「政治」が付いて来る。誰を説得するか、それもリファクタリングに必要な手続きだと思うしかない。

とにかく手動テストを繰り返しながらコードを分解する。

破棄して作り直すかどうかは、プロダクトの成長を一時的に止めることになるので、それが許容されるかどうかはプロダクトの事情による。見積もり次第。

一つの指標として、暗黙知を多量に要求するものは困難なので、他人が書いた jQuery は作り直した方がいい。

リファクタリングの仮のゴール設定

最初の単体テストの話をした理由だが、自分は大抵、「単体テストが書ける」という状況を、リファクタリングの仮のゴールに設定する。単体テストが書けるということは、不要な依存が切れて、モジュールの境界面が自明になった状態にたどり着いたということである。

備考: これは最近までモジュールシステムが存在しなかった、 JS ならではの事情による。長く開発されてきたプロダクトは、グローバル変数渡しや、自前のDI機構が残っていることだろう。

正直な所、「単体テストできるような状況」を目標にすればよく、実際に単体テストを書く必要はなかったりするのだが、わかりやすい成果物としてユニットテストを追加することが多い。この状態に至ってから、目的を達するコードを書き始めることができる。パフォーマンスだったり、ボタン追加だったり。

ただ、そういうテストがメンテされなくなって久しくなったときに、捨てられるかどうかの判断をできる必要はある。担当者が去ったテストが、カーゴカルト的に維持されてることは多い…

できることからやる、ために小さく分解する

大変なものを大変だと言いながらやる前に、痛みが少ないものからまず向き合う。外堀を固めよう。

フロントエンド開発で、最初に考えるべきは、

  • lint ルールを追加する
  • コードフォーマッタを入れる
  • 型を書く

これらは比較的痛みがない。まず eslint を導入して、CI に入れる。そしてルールを思いつくだけ書く。(各プラグインのrecommendedを全部突っ込むなど) 現代では、ホワイトスペースや改行ルールは人間が考えるものではない。prettier の導入で終わり。

経験上、この3つがわかりやすく効く。

  • globals: 使用可能なグローバル変数の列挙
  • no-unused-vars: 未使用変数の禁止
  • no-unreachable: 到達不可能なコードの禁止

ES Modules がある現代では、特殊な事情(ライブラリが勝手に生やしたり、期待しているもの)がある場合を除いて global 変数は一切使わずにコードが書けるはずだ。その場合も部分的な lint 無効化で対応できるはず。

(この節は、人によって意見が異なるだろう)

現代のJavaScriptにとって、一定以上のプロダクトで静的型は避けては通れないテーマだと思っている。フロントエンドは前述したように自動テストが困難なので、静的解析で出来る限りカバーするだけ事故が事前に検知できる。

とくに静的検査で強烈な開発体験の向上として現れるのが、React の JSX の props に対する静的検査だ。プロパティを変えると、大抵呼び出し元のプロパティ名を修正しないといけない。または自分の想定以上に使われていて、その修正漏れなどが発生する。手元でコードを書いていてもうっかりするから、ましてや他人のコードなど考慮外である。

JSXの発明の偉大な点の一つは、テンプレートに対する入力値を宣言的に記述することで、事前条件を明示したことにある。React以外の他のフレームワークも、現代的なテンプレート、コンポーネントは、ステートレスな関数的な振る舞い、またはステートフルであることが明示されたオブジェクトの形をしているはずだ。また、Component という単位にしたことで、依存が明示的になる。

ただ、痛みのない型の導入、は難しい。というかコツがいる。

flowtype で @flow アノテーションを段階的に追加したり、 typescript の allow-js モードで一旦全部tsコンパイラ管理下に入れてしまって、段階的に拡張子 .ts に置き換える、などの手法がある。

ただし、これはプリコンパイラが前提にある。プリコンパイラがない場合は、まずその導入からはじまる。これはデプロイワークフローに絡んでくるので、なかなかに辛い。ステージング環境を用意して、何度もトライアンドエラーするしかないだろう。

辛いものは辛い。そこは認めるべき。

リファクタの諦めが発生する原因

自分が経験した状況としては…

  • 元のコードの担当者が忙しい or 退職していて、レビューが受けられれない
  • そもそも仕様が↑の脳内にしかなく、逸失している
  • コードが予想もつかない場所で使いまわされていて、テスト範囲が膨大になる(駄目なDRY)
  • 特に仕様が決まっていなかった部分を、一部の人間(開発側またはユーザー両方)にバグと認識されてコミュニケーションコストがかさむ
  • プルリクエストが巨大化 or 細分化され、ブランチの維持で手一杯になるうちに心が摩耗していく

全部辛い。辛いものは辛い。 辛い時は辛いと言う。アラートを上げることは恥ではない。

リファクタリングの目的

プロダクトの改善の為、という大義名分は立てやすいが、そもそも何のためにリファクタするのか、見失わないように意識しておく。

  • 機能追加の速度を上げる
  • バグ抑止
  • 人員追加のためにキャッチアップコストを抑える
  • パフォーマンスチューニングの下準備
  • 自己満足
  • 採用アピール
  • 離職対策

後半の理由は後ろ向きだが、表向きの理由とは別に、現実的にこの目的で行われるリファクタリングは多い。自己満足を含むことが別に悪いことではないが、ただ過度に自己目的化してないか見つめ直す必要はある。過剰な抽象度を備えてしまうと逆に品質は悪化する。

中長期的なリファクタリングは、そのイテレーションの間で発生した現実的な泥臭いユースケースを踏まえ、プロダクトの向かう方向性を描きながら自分たちの事情(ドメイン)をコードで表現するとよい。

自分はフリーランスとしてパフォーマンスチューニングの案件を受けることが多いが、何にせよ最初にやることは大抵リファクタリングである。パフォーマンスの伸びしろは、ほとんどの場合、きれいなアーキテクチャによって担保されるからだ。そして、機能追加の速さも一つの「速度」ではある。

まとめ

リファクタは大変だがやりがいがある。やりすぎは危険。需要に応じて段階的にアーキテクチャを変化させていくとよい。

おまけ