Upgrade to Pro — share decks privately, control downloads, hide ads and more …

ESLint Rule により事業, 技術ドメインに沿った制約と誓約を敷衍させるアプローチのすゝめ

ESLint Rule により事業, 技術ドメインに沿った制約と誓約を敷衍させるアプローチのすゝめ

フロントエンドカンファレンス北海道 2024 の登壇資料です

コードベースに秩序をもたらすため各種 Linter を利用している方も多いのではないでしょうか?一般的なルールについては Linter 内蔵のものや既存の Plugin ですでにカバーできるでしょうが, 一方で事業ドメイン特化であったり UX/DX を向上させるための技術ドメイン特化なルールまではサポートし切れないケースの方が多いでしょう.

日経電子版においてもコードベースにそうした事業ドメイン, 技術ドメイン特化な制約が存在しています. こうした日経電子版特化な制約について日経電子版開発では属人的なレビューで弾くといったアプローチでなく, できるものは ESLint Plugin を開発してそれにより機械的に弾くアプローチをとっています.

本セッションでは事業, 技術ドメイン特化な ESLint Plugin を開発する利点や How To を具体例とともにご紹介します.

Shinobu Hayashi

August 24, 2024
Tweet

More Decks by Shinobu Hayashi

Other Decks in Programming

Transcript

  1. © Nikkei Inc. Better insights for a better world ESLint

    Plugin Rule により事 業, 技術ドメインに沿った制約と 誓約を敷衍させるアプローチの すゝめ @Frontend Conference Hokkaido 2024
  2. © 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 #カケハシ
  3. © Nikkei Inc. Better insights for a better world ⛓

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

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

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

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

    Web 開発 における制約と誓約 #frontendo #カケハシ
  8. © 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 #カケハシ
  9. © Nikkei Inc. Better insights for a better world ⛓

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

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

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

    Cache が混ざる🤯 #frontendo #カケハシ
  15. © 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; };
  16. © Nikkei Inc. Better insights for a better world ⛓

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

    CSS In JS を記載する際の制約 日経電子版の Build Infra の処理速度を上げるための制約 • 日経電子版では CSS In JS が利用されている • webpack plugin により CSS の抽出処理などが実行されて いる #frontendo #カケハシ
  18. © 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 ( <button className={label}>{label}</button> ) }
  19. © Nikkei Inc. Better insights for a better world ⛓

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

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

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

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

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

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

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

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