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 が未発達なことに課題を感じている。


おわり。