Slide 1

Slide 1 text

© Nikkei Inc. Better insights for a better world ESLint Plugin Rule により事 業, 技術ドメインに沿った制約と 誓約を敷衍させるアプローチの すゝめ @Frontend Conference Hokkaido 2024

Slide 2

Slide 2 text

© Nikkei Inc. Better insights for a better world 🐵 whoami ● Web Developer@Nikkei ○ Currently, Working on Our Complicated Subsystems(e.g. CDN, build-tools) and Enabling Engineering ○ Especially, developing www.nikkei.com in Web Platform Team ● Interest: Browser, Network, OSS ● Blog: https://shinyaigeek.dev ● Hobby: 🍷 🍳 💻 林 仁(Shinobu Hayashi) @Shinyaigeek ( /GitHub) #frontendo #カケハシ

Slide 3

Slide 3 text

© Nikkei Inc. Better insights for a better world 制約と誓約?🤨 #frontendo #カケハシ

Slide 4

Slide 4 text

© Nikkei Inc. Better insights for a better world ⛓ 制約と誓約 HUNTER X HUNTER においては... ● 制約 ○ ルール ○ A してはならない/B しなければならない ● 誓約 ○ 制約に対する誓い, 覚悟 → これにより “念能力” を高める #frontendo #カケハシ

Slide 5

Slide 5 text

© Nikkei Inc. Better insights for a better world ⛓ 制約と誓約 開発文脈で @Shinyaigeek が捉えると... ● 制約 ○ アプリケーション開発におけるルール ○ コードベース/Ops における A してはならない/B しなくてはならない ● 誓約 ○ 制約に対する誓い, 覚悟 → これにより “アプリケーションの質” を高める #frontendo #カケハシ

Slide 6

Slide 6 text

© Nikkei Inc. Better insights for a better world ⛓ 制約と誓約 🙆 In Scope: アプリケーション固有の開発上の誓約 🙅 Out of Scope: ● アプリケーション固有の Ops 上の制約 ○ Linter の話ではなくなってくる ● アプリケーション一般の制約 ○ e.g. 型の import には type をつけよう ○ 普遍的な制約が故既に世にあるものを使うことになる #frontendo #カケハシ 普遍的 固有 dev 🈁 ops

Slide 7

Slide 7 text

© Nikkei Inc. Better insights for a better world 📝 Agenda ● 開発コンテキストにおける制約と誓約について ○ 日経電子版 Web 開発における実際の事例 ● 非属人的な誓約を実現する Custom Linter Rule ● Custom Linter Rule の開発、運用 Tips #frontendo #カケハシ

Slide 8

Slide 8 text

© Nikkei Inc. Better insights for a better world 日経電子版 Web 開発 における制約と誓約 #frontendo #カケハシ

Slide 9

Slide 9 text

© Nikkei Inc. Better insights for a better world 📝 日経電子版 Web における制約と誓約 ● To Enhance Application Performance ○ HTTP Request Header Field から値を取得する際の制 約 ● To Enhance Application Build Infrastructure ○ CSS In JS を記載する際の制約 #frontendo #カケハシ

Slide 10

Slide 10 text

© Nikkei Inc. Better insights for a better world ⛓ HTTP Request Header Field から値を取得する際の制 約 インフラを含む電子版のアーキテクチャからなる制約 ● 素早いページ遷移 ● アクセスのスパイクからなるサーバーの過負荷への対策 📝 これら目的のため日経電子版では CDN での Shared Cache First なアーキテクチャが採用されている #frontendo #カケハシ

Slide 11

Slide 11 text

© Nikkei Inc. Better insights for a better world ⛓ HTTP Request Header Field から値を取得する際の制 約 #frontendo #カケハシ 日経電子版ではほとんどのアクセスが CDN での Shared Cache で捌かれている

Slide 12

Slide 12 text

© Nikkei Inc. Better insights for a better world ⛓ HTTP Request Header Field から値を 取得する際の制約 ユーザーの属性情報(認可など)によって HTTP Response(=ページの内容)が変化するものを Shared Cache へと保存するために... ● CDN で Cookie を元にユーザーの属性情 報を紐解く ● それをアプリケーションサーバーへと送 信する HTTP Request Header Field へと 詰め込む ● アプリケーションからは Vary のその HTTP Request Header FieldName を詰め 込む これにより CDN での Shared Cache が可能に #frontendo #カケハシ

Slide 13

Slide 13 text

© Nikkei Inc. Better insights for a better world ⛓ HTTP Request Header Field から値を 取得する際の制約 ユーザーの属性情報(認可など)によって HTTP Response(=ページの内容)が変化するものを Shared Cache へと保存するために... ● CDN で Cookie を元にユーザーの属性情 報を紐解く ● それをアプリケーションサーバーへと送 信する HTTP Request Header Field へと 詰め込む ● アプリケーションからは Vary のその HTTP Request Header FieldName を詰 め込む これにより CDN での Shared Cache が可能に #frontendo #カケハシ

Slide 14

Slide 14 text

© Nikkei Inc. Better insights for a better world Vary に参照した HTTP Request Header FieldName を詰め込み損ね ると...?🧐 #frontendo #カケハシ

Slide 15

Slide 15 text

© Nikkei Inc. Better insights for a better world Shared Cache が混ざる🤯 #frontendo #カケハシ

Slide 16

Slide 16 text

© Nikkei Inc. Better insights for a better world ⛓ HTTP Request Header Field から値を 取得する際の制約 Shared Cache が混ざると、必要な認可がないな ど見えてはいけないコンテンツがユーザーに見 えてしまう. そうした事態を防ぐために「HTTP Request Header Field から値を取得しその FieldName を HTTP Response Vary Header Field へと詰め込 む」ためのモジュールを提供 ...ただ直接 HTTP Request Object を参照され ると意味がない #frontendo #カケハシ export const referReqHeader = ({ req, res, headerField, }: { req: IncomingMessage; res: ServerResponse; headerField: string; }): string | undefined => { appendToVaryHeader(res, headerField); const headerValue = req.headers[headerField]; if (Array.isArray(headerValue)) { return headerValue.join(','); } return headerValue; };

Slide 17

Slide 17 text

© Nikkei Inc. Better insights for a better world ⛓ HTTP Request Header Field から値を取得する際の制 約 ● ✅ referReqHeader 関数を介して HTTP Request Header Field の値を取り出す ● ❌ HTTP Request Object を直接参照する ☝のような制約が必要になる #frontendo #カケハシ

Slide 18

Slide 18 text

© Nikkei Inc. Better insights for a better world ⛓ CSS In JS を記載する際の制約 日経電子版の Build Infra の処理速度を上げるための制約 ● 日経電子版では CSS In JS が利用されている ● webpack plugin により CSS の抽出処理などが実行されて いる #frontendo #カケハシ

Slide 19

Slide 19 text

© Nikkei Inc. Better insights for a better world ⛓ CSS In JS を記載す る際の制約 CSS In JS の処理のための webpack plugin は babel-plugin ベースの実装になっている ● ビルド基盤は swc になっているためなる べく swc に寄せてしまいたい ● CSS In JS の処理が必要なファイルにだけ 処理系を適用したい {Component}.style.ts に StyleSheet を記述 し、処理系の対象をそのファイル名の規則に合 致したファイルだけに絞る #frontendo #カケハシ // Button.style.ts export const label = css` color: red; ` // Button.tsx export const Button = function({ label }) { return ( {label} ) }

Slide 20

Slide 20 text

© Nikkei Inc. Better insights for a better world ⛓ CSS In JS を記載する際の制約 ● ✅ stylesheet は {Component}.style.ts に記述する ● ❌ stylesheet をその規則に反するファイルに記述する ☝のような制約が必要になる #frontendo #カケハシ

Slide 21

Slide 21 text

© Nikkei Inc. Better insights for a better world ⛓ 制約と誓約はコードベースの数だけ存在する チームの文化、技術選定、アプリケーションの事業要件の数 だけ固有の制約は生じうる ● Repository 層では Error を throw せずに Result 型を返 す ● server/client 向けのファイルではそれぞれ ‘import xxx-only’ を付与する ○ Next.js AppRouter 向けの文脈 ● etc... #frontendo #カケハシ

Slide 22

Slide 22 text

© Nikkei Inc. Better insights for a better world ただこうした制約は果たして 守られうるのか?🧐 #frontendo #カケハシ

Slide 23

Slide 23 text

© Nikkei Inc. Better insights for a better world ⛓ チーム開発で誓約は機能するのか(?) Documentation やレビューなど属人的な努力(=誓約)でこの 制約は守られ得ない. ある個人の能力や悪意の話ではなく、人間の成すことなので ミスは容易に発生しうる. 人に依った誓約の限界を認め機械にできることは機械に任せ ていき初めてチーム開発で誓約が実現できる #frontendo #カケハシ

Slide 24

Slide 24 text

© Nikkei Inc. Better insights for a better world ⛓ 人に依らない誓約を実現する Custom Linter Rule によるコードベース検査で機械的な誓 約を実現する. ● ESLint: 🙆 Custom Linter Plugin を書ける ● Biome: 👷 Plugin のサポートが入る...かもしれない ○ ref: https://github.com/biomejs/biome/issues/2463 ● OXC: ❓ まだどうなるかわからない ○ 過去存在した DSL ベースの Plugin 機構は削除された ref ○ Discussion には Wasm ベースの Plugin 機構がやるべきかというのは ある ref #frontendo #カケハシ

Slide 25

Slide 25 text

© Nikkei Inc. Better insights for a better world ⛓ Custom Linter Rule の開発 Tips ユースケースが絞られた Plugin となると標準 ルールでも OSS ででも当たり前に存在せず自前 での開発が必要になる 基本的には「コードの AST を舐め回して、特定 の条件において error/warning を出す」という ものになる. ● AST は astexplorer を覗いてみると雰囲 気が掴みやすい ● また参考実装は OSS としてその辺に転 がっているので覗いてみるのがおすすめ ● (あと今だと ChatGPT や GitHub Copilot が全部やってくれそう) #frontendo #カケハシ

Slide 26

Slide 26 text

© Nikkei Inc. Better insights for a better world ⛓ Custom Linter Rule の開発例 StyleSheet の記載を {Component}.style.ts に 絞る, という制約の場合... 1. CSS In JS から StyleSheet を記述するた めのモジュールをインポートしていたら 2. そのファイル名を読んで 3. それが規則にマッチしてなかったら 4. ⚠ 意外とシンプルで簡単そうじゃないですか? シンプルに実装できない場合、制約が複雑すぎ るので見直すべきという見方もある #frontendo #カケハシ ImportDeclaration(node): void { const isCSSinJSImported = /** AST から Import 文で CSS in JS を import しているか否かみる */; if (isCSSinJSImported){ const targetPath = getFilename(); const filename = path.basename(targetPath); if (!filename.endsWith('.style.ts') && filename !== 'style.ts') { const suggestedFilename = filename.replace(/\.ts(x?)$/, '.style.ts'); report({ node, messageId: ‘xxx’, data: { suggestedFilename }, }); } } }

Slide 27

Slide 27 text

© Nikkei Inc. Better insights for a better world ⛓ Custom Linter Rule の開発例 ESLint からテストを実行するための土台が提供 されている ● こういうコードでは怒らないべき ● こういうコードは怒るべき とかなり直感的に統合テストを記述することが できる. #frontendo #カケハシ const { RuleTester } = require("eslint"); const tester = new RuleTester(); test('xxx, () => { tester.run('yyy', plugin, { valid: [ { name: 'with style.ts', code: ` import { css } from "css-in-js"; const style = css\` p { color: red; }\` `, filename: 'fuga/style.ts', }, ] }); });

Slide 28

Slide 28 text

© Nikkei Inc. Better insights for a better world ⛓ Custom Linter Rule の運用 基本はアプリケーションのコードのあるリポジトリの中で workspace を切って開発して良い 寧ろ同一リポジトリ中に配置することでどのような誓約が存 在するかも明示できる. また commit log でその背景も残せ る また依存管理のコストも下げられる. #frontendo #カケハシ

Slide 29

Slide 29 text

© Nikkei Inc. Better insights for a better world ⛓ Custom Linter Rule の恩恵 ● 言わずもがな開発上の制約を機械的に守らせることがで きる ● 新たな制約を課したい際に漸進的な導入にも有用 ○ 新たな制約に沿うようコードを直していっても並行し て Commit が入っていきイタチごっこになることを防 止 ○ 制約を守れていない箇所がどれほどあるか可視化もし やすい #frontendo #カケハシ

Slide 30

Slide 30 text

© Nikkei Inc. Better insights for a better world 制約と誓約 #frontendo #カケハシ

Slide 31

Slide 31 text

© Nikkei Inc. Better insights for a better world ⛓ 機械的なアプローチを前提とすれば取れる制約の幅が広がる Custom Linter Rule で機械的に ”誓約” を実現できれば取 れる “制約” の幅も広がる 🧐「こういう制約を敷きたいけど守られる気がしないな...」 一般的に「何らかの制約」を守って欲しい時にはガードレー ルを敷くのが一番効果がある #frontendo #カケハシ

Slide 32

Slide 32 text

© Nikkei Inc. Better insights for a better world ⛓ Conclusion ● アプリケーション開発にはそれ固有の制約が生じうる ● ドキュメンテーションやレビュー以外にも、Linter Plugin で機械的に誓約を実現させるというアプローチも ある ● (Linter で Custom Linter Plugin をサポートしてほし いモチベーションとして自分のアプリケーション特化の ユースケースをサポートしたいのもある...) 皆さんも良い “エンペラータイム” を🟥 👁 #frontendo #カケハシ