amp-script の実用性について考える

AMP Conf 全体の感想は後回しにして(書かないかも)、 amp-script について

単に使ってみたい人は AMP で任意の JS を実行できる amp-script を試してみた - Qiitahttps://github.com/mizchi-sandbox/amp-script-preact

最初に

僕の amp に対するスタンスは http://ampletter.org/?lang=ja に示された懸念とだいたい同じで、基本的にAMPは邪悪なもの、現代のウェブの事情を考慮する必要悪と悪の中間ぐらいだと思っており、それと同時に極端にリソースが制約されたプログラミング環境というものが好きで、そういう点で AMP 由来の技術を追っている、という立場。

amp-script について知っていること

全然ドキュメントないので、コード読んで、書いて、触って、あと中の人達に直接聞いたりして知った知識

サイズ制限のせいで、いわゆる JS フレームワーク は preact(7kb) か lit-html(3kb) ぐらいしか選択肢にならない。もしかしたら svelte とか使えるかもしれないが…

複数コンポーネントは色々できそうな気がしたが、状態を共有しようとすると indexeddb を介したポーリングになると思って実験しようとしたが、 dexie を引っ張ってきたらその時点で 150kb オーバーした。webpack の minimize を常に有効にして watch させるとCPU負荷がかなり高くファンが唸りまくって、出先でやってたらバッテリ使い果たした。

preact も動いたり動かなかったりで、この Issue を見てる限り対応しようとしてるが、待つしかなさそう https://github.com/ampproject/worker-dom/issues/319

amp-bind との比較

AMP の動的コンテンツといえば amp-bind がある。これで専用のハンドラや mustache テンプレートの展開、 amp-live-list と組み合わせた動的コンテンツと組み合わせて、それっぽいものが作れる。

https://amp.dev/documentation/components/amp-bind

個人的な意見だが、これはよくある「プログラミング抜きで〜できる!」と称して一つの DSL 体系を一式覚えさせられるやつである。個人的に全くモチベーション上がらない。基本的には amp-script で全部済ませられるはずだが、amp-login でつくったセッション情報とかを amp-script 側に引き回せるか、まだよく調べてない。できたら同等の表現力になるはずだが。

コールバックで AMP.setState({counter: 1}) みたいな JS みたいなコードを書けるように見えるが、これは正規表現で使える表現が決まっている。また 1 行あたりの発行可能な文字列制限もある。

AMP Bento

カンファレンス中で発表されたもので、まだどこにも情報が上がってないのだが、AMP CDN 下になくても amphtml project の機能を使える、というやつである。 amp 制約下で全部これから作れと言われるとウッとなるのだが、逆に普通のウェブサイトでも使うと便利なウィジェットなどが色々あるので、これは普通に歓迎したい。

AMP の方向性

元来単純なものはAMP、複雑なものはPWAという棲み分けを意図していたように思うが、今回のカンファレンスで示された方向性からは、AMPがPWAやSPAという領域もカバーしようとしている、という印象を受けた。

どうせ Google のことだし内部で明確な方向性定めず部署ごとに競い合ってる状況だと思うが、個人的には邪悪に寄りすぎない範囲でやれることが増えると、ステークホルダーを説得する方便としては便利な技術であると思うので、邪悪ではない限りで応援したい。邪悪だと思っているが。

フロントエンドの開発環境に Docker は不要(少なくともMacでは)

追記: 前提部分

開発環境を docker-compose で抽象することが最近のベストプラクティスだとされているが、フロントエンドをコンテナに突っ込むと無視できないIOボトルネックが発生する。

とくにwebpackのファイル監視からのビルドで発生する高頻度のIO処理を捌くために、フロントエンドだけはホスト環境に移したほうがいい、という主張。


これについて

speakerdeck.com

自分の意見

  • Web開発者の主要な開発環境である Docker for Mac は I/O がとにかく遅い (3x~5x)
    • data volume の driver やら cache を工夫しても遅い
  • npm install/webpack は 基本的に I/O ヘヴィー
    • とくに大規模開発時の watch => build がクリティカル
    • webpack.conifg の entry で自分が関与する部分以外コメントアウトして開発してることもある
  • フロントエンドの最終成果物(.js, .css, .html)に環境依存なプログラムが含まれない(含まれてたらおかしい)
  • そもそも JS という言語自体がクロスプラットフォームに配慮された言語なので、ホスト環境によるランタイム差がほぼない

ネイティブモジュール周りの既知の問題

  • fsevents: webpack や chokidar などの裏側でファイルシステム監視を行うモジュール。npm の lockfile がビルドした環境次第で optional dependencies の native module をうまく依存に含めることができず npm install で再現されないことがある。ファイル変更監視が低速化するだけで動くには動く。yarn でこれが起きたことはない。
  • node-sass: libsass の node バインディングだが、ややビルドが不安定。nodeのバージョン上げると大抵コケるのでキャッシュ捨てて再ビルドが必要。最近は css-in-js が流行ってるのでnode-sass見かけることが減った。
  • node-gyp: node本体のビルドツール(python2)。 python3 にパスが刺さってる状態でネイティブモジュールをビルドすると失敗する。node でネイティブモジュールを使う環境は python が python2 を指している必要があるが、これは他のビルドツールでも何かと要求される要件ではある。

これらを知っていればそこまでクリティカルにはならない。

運用どうするか

CircleCI、デプロイ時、初期セットアップの手数を少なくするためにとりあえずNodeイメージ経由でのフロントエンドのビルドタスクは用意する。開発環境では使わない。

他のプログラミング言語の人から本体のバージョンを固定することを強く要望されることがあるが(それらの言語が不安定なことが多い)、直近の Stable 使ってる限りは Node の実行に差が出ることは稀で、フロントエンドのビルドツールツェインに関してはそこはあまり抽象してメリットがあるレイヤーではない。が、自分の詳しくない領域の安心感がほしいのはわかるので、上述のビルドタスクを提供する。

node のインストールが困難な開発メンバーがいる場合はサポートする。たぶん二手ぐらいで終わるので、docker それ自体の導入より遥かに簡単。

Windows/WSL環境 の場合

すいません自分がWindowsでフロントエンド開発したことないです。

あんまり人権がある方ではないとは聞いてる。

サーバーサイド node の場合

他のサーバーサイドと環境と違ってビルドによる環境差がそこまでないので気にしなくていいと思っているが、 node はネイティブモジュールの数次第で、 imagemagick あたりが鬼門。こいつは常に鬼門。別環境に追い出したい。

node server を運用する際はこの限りではない。

というのが自分の意見です。

Chrome 73 で Desktop PWA Support & mdbuf アップデート

developers.google.com

Signed HTTP Exchange もですが、個人的に待望だった Desktop PWA が正式リリース。 PWAアプリをウェブアプリのように振る舞わせることができます。

例えば mdbuf を開いて、「mdbuf をインストール」を選択すると…

https://i.gyazo.com/5f64a2c6f9d74b9c2851b4c96baa2a92.png

モーダルが出て

https://i.gyazo.com/e054fe8300c1d3fad1f37fc22f908bec.png

Win/Mac のアプリのように立ち上がります

https://i.gyazo.com/9f617215f1969f750482fd2009e4d3b8.png

mdbuf について

https://mdbuf.netlify.com

サンプルで紹介しましたが、これは僕が作ってる markdown のプレビューツール です。

amachang さんや結城浩先生へのインタビュー、その他このブログの記事、各種書籍への原稿も全部これで書いています。

mdbuf v1.0.0: 最高の Mardkown Preview を目指して - mizchi's blog

この時点からさらに自分で使い込みながら機能を足したので、Desktop PWA ついでに紹介。

アウトライン機能

ヘッダの位置にジャンプします。

prettier の整形

Cmd+S (Ctrl+S)で prettier で markdown を整形します。

バックグラウンドのコンパイル

WebWorker でコンパイルしているので、入力中のラグ(layout junk) が非常に発生しづらいです。普通に実装する markdown コンパイルはCPUを専有しがちなので、効果が大きいです。

編集中の領域に自動フォーカス

編集に対応するプレビュー側をハイライトします。

保存機能(IndexedDB)

保存します。

textarea => monaco editor => codemirror の切り替え

monaco エディタを使えば編集側でコードハイライトできます。

HTMLでコピペできそうでできない要素を作る

歌詞サイト内で湘南乃風の睡蓮花の歌詞がだんだん大きくなっていた「作詞XSSとか楽しすぎるものをみたけど…」 - Togetter

うたまっぷといえばコピペ禁止、コピペ禁止といえば最近こういう嫌がらせを考えていたので、やってみました。

U
O
H
Y
O
-
-
T
N
P
C
-
X
C
-
A
E
I
T
Y
S
O
T
N
T

↑をマウスで選択してコピペしてみてください。うまく範囲選択できないし、できたとしても無意味な文字列になります

仕組み

  • テキストをランダムに並び替える
  • ランダムに並び替えた文字を, 元の位置に来るように flex の order 属性を指定

フレックスアイテムの並べ替え - CSS: カスケーディングスタイルシート | MDN

コード

React でさっくり書いた

function Text() {
  const text = "YOU-CANNOT-COPY-THIS-TEXT";
  const chars = text.split("");

  const textOrders = chars.map(_ => Math.floor(Math.random() * 1000));
  const textOrdersSorted = textOrders.slice().sort();
  const idxMap = textOrdersSorted.map(v => {
    return textOrders.findIndex(n => n === v);
  });

  const chars = idxMap.map((to, original) => {
    return <div style={{ order: to }}>{chars[to]}</div>;
  });
  return (
    <>
      <div>
        <div style={{ display: "flex", flexDirection: "row" }}>{chars}</div>
      </div>
    </>
  );
}

const el = document.querySelector(".root");
ReactDOM.render(<Text />, el);

実行環境

https://markdown-code-runner.netlify.com/MQAgsghgTg1gJgewO4DsQGEFwKYgEoCuKK2UAUGQAbUAuAzmQGZEDGNAlgmgCrYAeNABQBKEAG8yIECy50aIGv3kBeEACIAmgHkAqgFp0AQQByxrdwNaAChr3cAEgEkAynYCiADW5qA3JOmy8iwAFtB0IKqKAgB0dAAOADbsQmpqwn7+MihyCkpaUDhQ4aohYdEAthBxggD6EQB84BA0wdGMCQgIUIKQLdFQECiI5SIgAFQgAIwADLPC6ZmBuQL5hXTOXYpwEcs0q6R0sUks2CKxmyIZUlk57HB8kHE7UXsFBxtQWxVVgoIAbgAaEAkJCOe6iZSNCRSKRQbA0AhQNAvfZFNrsIaOIb8QRoSHAiLKVR-BZSAC+pIC2XkIPQoSKOzuDyq32q7Aa0npdAA2uwALoLRbUkDYBLFEBMx6s340BBArrsADmGIgCQhUP8sPhiLQAB44Ow-iA5ABPBLYZRiMQgLqFABcCgQIDJZPqYlKRW5sr5ZN1AHoDX96n5yZS4QikSBBJqQLr6jGpPrDfGYanY4HjTQzRarSADfEEhATQ61O1+GogWW+AARdhwticFAlqDINTO11iUV0X0B5MJ2O9oMx-0pkALMkUG7yUU7RAsAjlbAoGjRACOBFIJucouwbC6gjU-U6NDSfjw2AgbGrWjA-SXhUEut4AhAfvqQNFC2olDIQA

だるくてマルチバイト対応してないので、コードポイントで分割してください。

謝罪文などでこれが来たらいい感じに読者の反感を買えると思います。 悪用禁止!!!!!!!!!!!!!!!

SPA が、ウェブ開発のベストプラクティスになる時代

最近のフロントエンドに関するお気持ち。正直まとまってはない。


最近、こんな感じのツイートや記事が増えた。

web 技術をキャリアの中心にしない

シングルページアプリケーション (以下SPA) の台頭により、私の観測範囲ではモダンな Web サイトは SPA で作られるようになった。サーバーサイドは JSON を返す API サーバーとなり、DB やバックエンドシステムのプロキシのような存在になりつつある。 私はサーバーサイドエンジニアとしてキャリアを積んできた。SPA が流行りだした頃、いずれサーバーサイドエンジニアは不要になって自分のキャリアを考え直さなくてはいけない時期がくるのではないかと戦々恐々としていた。

自分も元々、SPA を他サイトとの「差別化技術」と定義していた。ブラウザのタブページのライフサイクルにおいて、初期化プロセスを一回にまとめてシームレスな遷移を実現する技術。たとえばゲームだったり、Electron 環境でネイティブに負けないリッチな UI を提供するためのもの。

しかし、最近ではその潮目が変わってきている、と強く感じる。具体的には、SPA 技術は「すべてのウェブ開発におけるベストプラクティス」になってきたと感じる。

Node.js が Rails に「勝てなかった」理由

元々 Node.js が c10k 問題へのソリューションを謳って登場したのを覚えてる人は、どれだけいるだろうか。その時は他 WAF への Altenative であることが強く打ち出されていた。

それから数年経って、結論から言ってしまえば、Node.js は Rails に勝てなかったのではなく、勝つ必要がなくなった。Node.js は主にフロントエンドツールチェインとして発達し、マイクロサービスにおけるフロントエンド周りを担当する「補助輪」として機能すればよくて、バックエンドに興味を持たなくなった。

現代のストレージは、アプリケーションレイヤーで管理すると言うより、RDS や Aurora, Firebase や Spanner のように、フルマネージドなバックエンドで抽象されるものになった。データベースのスケーリング技術を知識として持つことは依然として重要だが、以前ほど重要ではなくなった。

現代では、並列実行を管理する主体がプログラミング言語のスケジューラから、前段のロードバランサやコンテナのスケジューラに移ってきた。Node.js の弱点はシングルスレッドのイベントループが process fork に向いておらず、シングルプロセスを酷使するや故に例外復帰が難しいことだったが、むしろ現代のマイクロサービス環境ではコンテナに割り当てられた CPU を使い切ることが重要で、例外復帰は投機的なコンテナ単位で行われるようになった。

結論から言えば、 RailsSymfony のような一つの言語に依存するフルスタックフレームワークは今世代が終わりで、これからはPaaSやマイクロサービス環境で他と協調することが前提のものに分解されていくだろう。しかしそのための土台になると予想される k8s は、数年前のフロントエンドと同じような「難しい抽象を一番うまい抽象化したやつが勝ち」みたいなOSS乱立期で、やや手を付けづらい。

JS の進化 / 漸進的型付けの時代

JS といえば昔はphpperlと並んで貧弱な言語の代表格だったが、近年の ES2015 以降の言語仕様の進化は目覚ましい。ランタイムにおいても V8 の JIT は異常な域で、繰り返し実行される処理に関しては、ネイティブから「多少遅い」程度のスピードが出てしまう。

また、今は動的型付に対する反動の時代で、ドキュメンテーションとしての型、ある種の Linter としての型というものが再評価されている。その点で TypeScript は漸進的型付けに必要なものを全部備えていて、現代における動的型付けと型の向き合いに関してはほぼ最強みたいなレベルに達している。

漸進的型付け言語の時代に必要なもの - mizchi's blog

というわけで言語仕様的な貧弱さの問題も解決しつつある。TypeScript の型定義も昔はないのが普通だったが、最近は大方ある、という感じになってきた。

そういえば、 python で pyre-checker や mypy を試してみたが、正直実用に程遠い完成度で、これだったらまだ TypeScript のが開発体験は良いという感想。(ただ js では numpy が現状置き換えられないので機械学習用途に使うのは難しい)

依然として後方互換のためのバッドパーツは残っているものの、ES2015 と TypeScript のプラクティスが普及することで解決される問題だと思っている。

WebAssembly は…、たぶん非常に限られた使い道になりそうな予感がしていて、GC インテグレーションの問題が解決しない限りは C++/Rust 以外でまともなサイズのwasmバイナリが生成できない。ツールチェインは Rust がよく出来るので、しばらくはピンポイントで重い処理を Rust を書く、というのがメインになりそう。

パフォーマンス問題

読み込み問題も、読み込みを lazy に分割する chunk splitting の技術や、precat や lit-html のような軽量 View ライブラリ、vue や angular の静的解析技術によるコード生成サイズの減少で、大きな問題にならなくなってきた。

一部のフロントエンドは、 HTTP/2 + ESModules 環境でブラウザネイティブの ESM ですべてを解決する夢を見ていたが、現状そのための仕様は頓挫し、Webpack を捨てることは、あと数年は困難になった。

Cache Digest と HTTP2 Server Push の現状 | blog.jxck.io

webpack が置き換えられることはあるかもしれないが、その目的はしばらく死にそうにない。

今後の SSR問題

SSRは高難易度の技術だったが、next.js や nuxt.js の普及、国内においては特に nuxt.js が普及したことで比較的手軽になった。自分は React 派だが、正直 next.js より nuxt のが遥かに良く出来てる。(というか next.js は過剰なミニマリズムの病気に掛かっている気がする…)

ついでにいうと、WebComponents は今の仕様のままだと依然として SSR の問題を抱えていて、shadowRoot 下に生成されるコンテンツをクローリングさせるために SSR が必要になっている。というわけで今後もSSRを避けて通ることができないと予想している。

自分が知る限り、SSR を行わないウェブサイトは、ニュースメディアのような速報性が強いサイトには依然として SSR が必要ではあるが、それ以外では意外とインデックスされるし、そこまで重要ではない。(依然として検索スコアが低いような気はしなくはないが…)

これは自分が知る限り、GoogleBOT によるウェブサイトのクローリングが行われると、JS 実行キューに入り、そこでページが構築されるとインデックスされる、という 挙動にある。JS によるページ構築は即座に行われるのではなく、数時間遅れて実行される。

今後の展開としては、BFFのレイヤーはマイクロサービスのクラスターの一部というより、 CDN Edge 上の Edge Worker に主戦場が移って、キャッシュマネジメントとSSRを同時に担うのではないか、と考えている。

Edge Worker PaaS の fly.io が面白い - mizchi's blog

workers.dev

本質的な作業に集中する、とはなんだろうか

主に非エンジニアのスタートアップ事業者や新規事業者にとって興味があるのは、UI と概念のモデリングとそこから生まれる体験であって、そのインフラストラクチャに対応として構成は「仕方なく」やるものなのは変わっていない。(それこそエンジニアリングといえばそうなのだが…)

なので、UI に興味が集中する。なので、競合に勝つための体験、「SPA」に興味が集中するのは、当然のような気もする。ただ、うまく使いこなせているプレーヤーは少なく、とくにパフォーマンスチューニングは属人化しているのが現状(なので自分のようなフロントエンド専門のフリーランスに需要がある)

あるジャンルが高度化しては PaaS/OSS/Container にラップされる、というのを今後も繰り返すだろうが、フロントエンドがそうならないのは、UI は人間の理不尽な要求に振り回される、という特徴にあるという気がしている。

例えばソーシャルゲームはリッチ化の過程でユーザーの期待値が膨れ上がり、開発費が高騰して参入障壁が跳ね上がっているが、似たような現象はどの分野でも起こる。UI への投資が減ることはないだろう。エンジニアとデザイナと連携する部分で AI による生産性の向上はあるかもしれない。

個人的には e2e が未発達なことに課題を感じている。


おわり。

Edge Worker PaaS の fly.io が面白い

なかなかよいおもちゃを見つけたので、紹介します。

fly.io

fly.io は CDN Edge Worker で JavaScript に特化した PaaS です。既存のサービスで近いものだと CloudFlare Workers もしくは Lambda@Edge でしょうか。

アカウント登録をして、次のようなコマンドを叩くとエッジで動くアプリケーションを作成することができます。

npm install -g @fly/fly
fly login
# mkdir my-flyio; cd my-flyio
fly new

最小コードはこんな感じ。CloudFlare Workers と同じような ServiceWorker 風と、Google Cloud Function 風の 2 つのパターンでワーカーを定義できます。

// index.js
addEventListener('fetch', function (event) {
  event.respondWith(new Response('Redirecting',
    {
      headers: {
        'Location': 'https://fly.io/docs/apps/'
      },
      status: 302
    }
  ))
})

fly.io のここがいい

フロントエンドの最適化の行き着く先の解の一つ、それは「CDN Edge で HTML をキャッシュして返却する」というものです。そして、ある程度動的な JS をユースケースに応じて返却したい場合、 Edge で SSR を行って、一度実行した HTML はキャッシュする、という戦略が有効です。

JS の SSR は、残念ながら今後も必要な技術だと想定されています。しかし、コンテナ技術の発展で、Node の運用の敷居は前より下がりましたが、それでも Node を運用するのを躊躇するエンジニアの方も多いでしょう。

SSR や HTML Cache に対するこれらの懸念を、Edge Worker はまとめて払拭できます。

  • 必然的にPaaS なのでフルマネージド
  • レスポンス最適化のために Edge Cache で HTML を返せる
  • SSR は複雑な処理なので、ほぼフルスペックの Node がいる必要がある

また、SSR / BFF 特有の事情として、次のような要求があります。

  • HTML をキャッシュする場合、セッション情報に触れないほうが安全に SSR できる
  • SSR の主な需要は大雑把なファーストビューの最適化と、 GoogleBOT 向けの情報を構築することなので、セッションに触れる必要はない
  • 起動後にリバースプロキシとして動いて API オーケストレーションを行う

イニシャルビュー最適化で、すべてのリクエストに対してセッション情報付きで SSR するのを要求する人もいますが、運用事故を避けたり、キャッシュ最適化やパフォーマンスチューンまで考えると、この辺が現実的な落としどころだと思います。

つまりは BFF (Backend for Frontend) と呼ばれていた層の一部を地理的に最適化される Edge Location に持ってくることができます。

そして、キャッシュを扱う以上、それなりに KVS を扱いたいわけで、他のサービスではなく fly.io が優れてるのは、この辺のために便利なパーツが一通り揃っていることです。

  • @fly/cache: リージョン単位の揮発する KVS
  • @fly/cache/global: 永続化されるグローバルな KVS
  • @fly/db: 永続化されたグローバルな簡易ストレージ(Mongo 風?)
  • @fly/static: 簡易アセットサーバー
  • @fly/fetch/mount 簡易ルーター
  • @fly/fetch/proxy 簡易リバースプロキシ
  • varnish surrogate key のような、タグによる Cache Invalidation

varnish surrogate key による Cache Invalidation が個人的に一番欲しかったやつで、現状他の CDN だと fastly ぐらいしかこの機能を持っていませんでした。

https://book.varnish-software.com/4.0/chapters/Cache_Invalidation.html

各種ストレージを使ってみた感じ、それぞれは本格的なものではないのですが、アプリケーションサーバーへ到達する前の エッジロケーションにこれがある、ということが大事で、とくに各種キャッシュと簡易アセットサーバー、簡易ルータがあるので、頑張れば Edge で完結するアプリケーション層として動くことすら可能です。

作ってみた

というわけで、簡単な掲示板アプリを作ってみました。各ページはその Edge ではじめてときだけ SSR され、それ以降はキャッシュされた HTML を返却します。初期化以降は CSR で動きます。

https://grave-crowd-822.edgeapp.net

ソースはここ

https://github.com/mizchi/flyio-playground/tree/master/examples/react-ssr

主に React + react-router + styled-components での SSR と、 fly/db に書き込みの内容を永続化して、 fly/cache で SSR 済み HTML をキャッシュしています。next.js などは使わず手作業で無理矢理 isomorphic にしてるので、コードの見通しは悪いです。あとでどうにかする…

作ってみた感想

  • node エンジニアなら特に迷うところサッと作れる
  • ローカルでほぼサーバーと同等のものが動くので開発が楽 (lambda/cloud function はここが微妙)
  • GitHubにソースが公開されてるので最悪これを自分でホスティングすればいいという安心感がある
  • express (node http) 互換ではなく、コールバックが (req, res) => {} ではなく、 (req) => new Response(...) みたいな形式
  • API ドキュメントがまだまだふわっとしている
  • 独自の v8 でビルドされていて、微妙に実装されてないものがある (crypto 周りで動かないモジュールがあった)
  • fly/db は、本当にただのおもちゃなので、信用しないほうがいい… (query や scan 相当のものもないし、列挙も出来ない…)
  • 本番環境での fly/cache/global への書き込みはそれなりに遅い

複雑なストレージを持つようなものではないので、これ一つで完結する、というものではありません。

参考になるリソース

examples 眺めながら、公式ドキュメントを読みつつ、わからなかったら GitHub で実装を読む、という感じでだいたいやりたいことをやれました。

料金

  • $25 の無料枠
  • 有料の場合、最低月 $10 から

https://fly.io/docs/#pricing

超大規模想定で適当に試算してみても $100 超えるのは難しそう

で、本格的に使っていいの?

ぶっちゃけリリース直後なので、なんとも言えません。人柱募集したいところ。

とりあえずは、Edge Worker 使った際のアーキテクチャを考察するための実験場として使うのがいいんじゃないでしょうか。ユーザーが増えて、実質 SLA どのぐらいか判明したら、プロダクションに突っ込むかどうか考えれば良さそう。

cloudflare も Edge Worker というよりは Serverless として Edge Worker 市場に参入するぞ!みたいなことを言い出してるので、こっちも注視したいです。

https://workers.dev

Node.js エンジニアの活路としての Edge Side

前々から思っていたこととして、フロントエンドとサーバーサイドの間に、 エッジサイドのエンジニアという職域がうまれるんじゃないかと想像しています。先に述べたように API オーケストレーション+SSR を行う Node BFF がここに位置するのではないか、と思っています。

日本においては node.js エンジニアは(SFなどの海外と比べて)あまりサーバーサイドでシェアを取れませんでしたが、まともな ORM がない Node 一本で行くのは僕もちょっと厳しいと思っていて、Node が本当に生きるのは、ブラウザと緊密に連携する前段の、Edge Side なんじゃないかと考えています。

Edge Workers は単なるパフォーマンス要件を満たす選択だけではなく、BFF ベストプラクティスとしての活用があると思っています。SPA を何年も作ってきましたが SPA をフルに運用するには、やはりフロントエンドエンジニアが握る BFF が不可欠だという気持ちがあります。

という感じでみなさんも fly.io で遊んでみてはいかがでしょうか。

ちょっと前に Edge ヘヴィーなフロントエンドのアーキテクチャの考察をしたので、こちらもどうぞ

光を超えるためのフロントエンドアーキテクチャ - Speaker Deck

Kaggle Titanic やってみた感想

データサイエンスの一連の流れってどんな感じなんだろう?と体験して見るために、Kaggleのチュートリアルをやってみた。

https://www.kaggle.com/c/titanic

Kaggle Titanic は、Kaggle のチュートリアルでよく使われる題材。タイタニック号の乗客名簿と、生存できたかを含むデータを与えられ、予測モデルを作成し、その精度を競う。

データをダウンロードする

  • https://www.kaggle.com でユーザー登録をする。
  • https://www.kaggle.com/<username>/account にアクセスし API => Create New API Token から kaggle.json をダウンロード
  • ダウンロードしたアクセストークンを ~/.kaggle/kaggle.json に置く

以下 pipenv を使った例

$ pipenv install kaggle
$ kaggle competitions download -c titanic

これで train.csv と test.csv のデータが手に入る。test.csv は解答用で、生存できたか(Survived)のデータは含まれない。

大雑把な流れ

  • 欠損値の処理(今回は単に捨てた)
  • 値の正規化(年齢など)
  • train.csv のうち 80% を訓練用、20% をテスト用データとして分割
  • 訓練モデルを作成
  • 訓練モデルにテストデータを入力し、精度を測定

今回は kaggle への提出は行っていない。

keras によるモデル作成

https://github.com/linxinzhe/tensorflow-titanic/blob/master/keras_titanic.py を参考に、理解するためにリファクタしてみた。

from keras.optimizers import SGD, Adam
from keras.layers import Dense, Activation
from keras.models import Sequential
from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import LabelEncoder
import numpy as np
import pandas as pd


def normalize_data(data):
    not_concerned_columns = ["PassengerId", "Name",
                             "Ticket", "Fare", "Cabin", "Embarked"]
    data = data.drop(not_concerned_columns, axis=1)
    data = data.dropna()

    # normalize
    dummy_columns = ["Pclass"]
    for column in dummy_columns:
        data = pd.concat([data, pd.get_dummies(
            data[column], prefix=column)], axis=1)
        data = data.drop(column, axis=1)

    # normalize Label:Sex to int
    le = LabelEncoder()
    le.fit(["male", "female"])

    data["Sex"] = le.transform(data["Sex"])

    # normalize Age
    ss = StandardScaler()
    data["Age"] = ss.fit_transform(data["Age"].values.reshape(-1, 1))
    return data


def split_train_and_test(data, rate=0.8):
    data_y = data["Survived"]
    data_x = data.drop(["Survived"], axis=1)

    train_valid_split_idx = int(len(data_x) * rate)
    train_x = data_x[:train_valid_split_idx]
    train_y = data_y[:train_valid_split_idx]

    valid_test_split_idx = (len(data_x) - train_valid_split_idx) // 2
    test_x = data_x[train_valid_split_idx + valid_test_split_idx:]
    test_y = data_y[train_valid_split_idx + valid_test_split_idx:]

    return train_x.values, train_y.values.reshape(-1, 1), test_x.values, test_y.values.reshape(-1, 1)


def build_model(input_dim):
    model = Sequential()
    model.add(Dense(20, input_dim=input_dim))
    model.add(Activation('relu'))
    model.add(Dense(1, input_dim=20))
    model.add(Activation('sigmoid'))
    model.compile(optimizer=SGD(lr=0.01),
                  loss='binary_crossentropy',
                  metrics=['accuracy'])
    return model


# load data
train_data = pd.read_csv("data/train.csv")
normalized_data = normalize_data(train_data)
train_x, train_y, test_x, test_y = split_train_and_test(normalized_data, 0.8)

model = build_model(train_x.shape[1])

# train
model.fit(train_x, train_y, nb_epoch=120, batch_size=16)

# test
[loss, accuracy] = model.evaluate(test_x, test_y)
print("loss:{0} -- accuracy:{1}".format(loss, accuracy))

だいたい 80~86%ぐらいの予測率だった。他のチュートリアルを見てもそのぐらいに収束するっぽい。 competition で100点だしてる人たちは、答えをなんやかんややってチートしてそう。

感想

  • あんまり綺麗なコード例が見つからない。今回の題材は、チュートリアルなのに、コードを綺麗に見せようという努力がなされたものを見かけなくてイライラした。
  • jupyter notebook でやる人が多いのか、数行のスニペット単位で整形することが多く、プログラマ的なモジュール分割ではない、という印象を受けた
  • 卒論でR触ってたのでpandas のデータフレームなんとなくわかったが、各種utility が覚えゲーっぽい
  • 今回は単に欠損値を捨てたが、中間値で埋めたり、それ自体に予測モデルを作って埋める人が見受けられた。中間値それ自体がバイアスになったり、あるいは欠損値があることそのものがある種の特徴量になってる場合、どう扱うべきか、コンテキストごとに迷いそう
  • 出先でやっていたが、バッテリ消費が激しい
  • 一応、このデータでは「女・子供は助かる可能性が高い」というのは知っていたのだが、ディープラーニングを使うとその事実に気づくことなくモデルができてしまうので、ドメイン的な学び甲斐がなかった。NNのバイアスから結果に響かない特徴量を検出して捨てる、みたいな手法はありそうなので調べる