ESLint+PrettierからOxlint+Oxfmt移行の理想と現実 ~pre-commitが20秒から5秒になるまで~

はじめに

フロントエンド領域で開発を行っている赤星です。

弊社はcommit時にgitのpre-commitフックによりESLint+Prettier+tscを走らせ、フォーマット+コードチェックを行っていたのですが、最近これをESLint+Oxlint+Oxfmt+tsgoの構成に変更を行いました。

しかし、移行にあたり、様々な障害にぶつかって詰まったので「理想と現実」「その乗り越え方」をここで紹介します。

プロダクトのESLint+Prettierが重くOxlint+Oxfmtに移行しようと検討されている方の一助になればと思います。

目次

結論

結論から言います。大変ですが移行してよかったです。「理想と現実」というタイトルをつけている以上「やめとけ」みたいな文脈にも見えてしまうので、あらかじめ断っておきます。事実commitをする度に20秒待たされていたのが5秒で終わるようになっています。4倍速くなったということです。これは大きな開発生産性の向上であり、移行して非常によかったと感じています。

Oxlint+Oxfmtのすごいところ(理想)

圧倒的な速さ

私がOxlintというツールを知った時非常に驚いたのはその圧倒的な速さです。Oxlintと同じRust製であるBiomeのLint機能はESLintの10倍以上高速と言われています。しかしOxlintはそのBiomeより2倍速いそうです。

ESLintやESLintプラグインの高い互換性

さらにOxlintはESLintのメジャーなプラグイン(eslint-plugin-reacteslint-plugin-jsx-a11ytypescript-eslint等)とも高い互換性があり(対応プラグイン一覧)、Oxlintの内部プラグインとしてRustで移植されています。さらにOxlintに移植されていないプラグインも外部プラグインとして実行できる仕組みが用意されています。(参考)

そしてESLintはコメントでのルール制御をする際、例えば

// eslint-disable-next-line react/no-array-index-key

と書くと思いますが、Oxlintは

// oxlint-disable-next-line react/no-array-index-key

と書きます。eslintの部分がoxlintになっただけであり、これは非常にとっつきやすいです。

移行コマンドの存在

Oxlint+Oxfmtを開発しているVoidZeroはoxlint-migrateというESLintからの移行コマンドを用意しています。flat-configで書かれたESLintのみの対応ですが、これにより、

npx @oxlint/migrate eslint.config.js --type-aware

とコマンドを叩くだけで既存のESLint設定を元に.oxlintrc.jsonを生成してくれます。

ESLint併用機能

Oxlintはまだまだ開発中です。既存のESLintルールやプラグインの中でまだ対応されていないルールもちらほらあります。(対応 or 非対応一覧はこのissueから確認可能)

そのためOxlintはESLintとの併用機能が用意されており、eslint-plugin-oxlintというESLintの拡張機能が用意されています。

これを既存のESLintに導入すると、ESLintで定義されているルールのうち、Oxlintで実装済みのルールをスキップすることができます。

移行奮闘記 ~ぶち当たった現実~

ESLintと何も考えずに併用すると思ったより速くならない

Oxlintを導入する前、ESLintの全体走査は約40秒程度かかっていました。試しに移行コマンドを行い、eslint-plugin-oxlintを導入した後、ワクワクしながらどれくらい短くなったのかとESLint+Oxlint併用での全体走査をしました。果たして何秒になったでしょうか。

なんと約30秒です🫠

速くなったと言われれば速くなりましたが、大して速くなりませんでした...

調査してみたところtypescript-eslintプラグイン(ESLint側)に大きな落とし穴がありました。

ESLintのtypescript-eslintプラグインは名前の通りTypeScript向けのESLintプラグインですが、TypeScriptにおける型情報のルール違反検知を実現するために、内部でTypeScriptのコンパイラエンジンを利用しており、ローカルのtsconfig.jsonを参考に動作します。これが非常に重く実行時間の増加の原因となっていました。Oxlintのtypescript-eslintプラグインはこれを回避するために内部にtsgoを利用したtsgolintを利用しているのですが、Oxlintはtypescript-eslintプラグインを全て移行しきれている訳ではないので、ESLint側のtypescript-eslintが動作し、結果的に実行時間を上げていました。

弊社OfferBoxの環境ではESLint側で残っていたtypescript-eslint系は

  • typescript-eslint/no-throw-literal
  • typescript-eslint/dot-notation

だけであり、それぞれOxlintに別名で実装済み / 近い将来正式実装される ことがわかったので、思い切って削除をすることでこの問題を解決しました。

結果的に全体走査にかかる時間を約20秒まで縮めることに成功しました。(本当はもっと速くしたいんですがね...)

Oxlintの仕様をある程度は理解しないといけない

「何も考えずにESLintからコマンド1発置き換え🎵」みたいなのは少し厳しいです。単純なESLint設定ファイルならまだしも、問題はeslint-config-airbnbのような設定パッケージを利用していたり、あるいは単純に複雑なESLint設定を書いている場合です。

分かりやすい例としてno-throw-literalというルールがあります。これは簡単に言うと「throwを書くときはErrorオブジェクトを利用してね」というルールなのですが、Oxlintでは非推奨となっています(参考)。代わりに似たルールであるtypescript/only-throw-errorを使うことが推奨されているのですが、これを利用するにはOxlintの設定に手動で書き込まなくてはいけません。

もう一点分かりやすい例としてはESLintのreact-hooksプラグインです。Oxlintはreact-hooksプラグインを完全移行済みなのですが、このプラグインはルールが2つしかないこともあり、reactプラグインに統一されています(該当issue)。なので、これを知らないと生成された.oxlintrc.jsonを見て「あれ...?react-hooksプラグインが見当たらないぞ...?」となります。(ちなみにこちらは移行コマンド実行時にreact/exhaustive-deps: "error"のようにreactプラグインに変換されているので特別な対応は必要ありません)

後は

  • Oxlintにtypescript-eslintをしっかり見てもらうには--type-awareオプションをつける必要がある (参考)

等細かい仕様の違いがあります。

Oxlintの未発見バグに3種類当たった(Oxlint最新版で修正済み)

Oxlintへの変換コマンドを実行したのち、試しに実行してみたところ、ESLintとの差分(ESLintの時は検出されなかったのがOxlintでルール違反として検出される、あるいはその反対)が発生しました。

最初は最新のESLintのバージョンを利用していなかったのが原因なのかなと考えていたのですが、Oxlintに指摘された(あるいは指摘されなかった)コードをみたところどうやらOxlintのバグであることが分かりました。

実際に当たったバグはそれぞれ以下であり、issueに報告しました。

github.com

※keyの値をテンプレートリテラル等で加工した場合、配列のindexをkeyに利用していてもreact/no-array-index-keyが引っかからない

github.com

※imgタグのaltにhelloImageのようにImageという単語が使われているが前の単語とスペースなしでくっついている場合の時、jsx-a11y/img-redundant-altの誤検知が発生する

github.com

※ asを利用して「型」としてuseEffectの外にある変数が利用されている場合、react/exhaustive-depsの誤検知が発生する

Oxlint側の対応がされるまでの一時的な対応策として、誤検知が引っかからないようなコードに書き直したり、// oxlint-disable-next-line-***を書くことで対応しました。 やはり新しいOSSということもあり、仕方ないのですが、特定条件の時バグにちらほら遭遇した印象です。

その他

上記全てを対応したところ、pre-commitは10秒程度になりました。個人的にはまだ速くしたいと思って調べてみたところ、tscもどうやら重そうということが分かり、ここの記事の本題から逸れるので詳細は書きませんが、tsgoを導入し、tsc --noEmitからtsgo --noEmitにすることでさらに5秒短縮に成功しました。

まとめ

Oxlint+Oxfmtの仕様を調べるためにGitHubのissueを探したりリファレンス漁ったり非常に大変でした...

しかし、前述したようにpre-commitの時間がかなり短縮され、開発体験を上げることに成功したので、やった甲斐はあったなと感じています。 さらに、新しいOSSということもあって積極的に開発が行われており、実際私が上記に挙げたreact/no-array-index-keyのバグはissueを出してから1時間以内に対応され、mainブランチにマージされていました(すごい)。ESLintルール移植もどんどんされており、今後が非常に楽しみなツールです。

いつかESLintを完全廃止できたらいいなぁと思いつつこの記事を締めたいなと思います。

PHPerKaigi 2026にシルバースポンサーとして参加してきました

PHPerKaigi2026のロゴが映った写真

はじめに

先日開催されました、PHPerKaigi2026に、i-plugはシルバースポンサーとして協賛しております。

phperkaigi.jp

今年も、参加した社員のレポートとして、印象に残ったセッションを紹介していきます。

  • はじめに
  • 印象に残ったセッション
    • モジュラモノリス導入から4年間の総括:アーキテクチャと組織の相互作用について
    • キーワードは「延命」 ― リプレイス困難システムの現実的バージョンアップ戦略
    • 「お金で解決」が全てではない!大規模WebアプリのCI高速化
    • Laravel Nightwatchの裏側 ― Laravel公式Observabilityツールを支える設計と実装
    • PHPでできる!自作IPルーター
    • AIと共に「使うOSS」から「育てるOSS」へ
    • 存在論的プログラミング: 時間と存在を記述する
    • php-srcの話が多かった印象がある
    • 見せてもらおうか、OpenSearchの性能とやらを!
    • Ruby VM 開発者がZend VMのopcodeを眺めてみた
    • Feature Toggle は捨てやすく使おう
  • 最後に
続きを読む

Claude Code × Playwright MCPでローカルの動作確認からバグ発見まで任せてみた話

Claude Code × Playwright MCPでローカルの動作確認からバグ発見まで任せてみた話

  • Claude Code × Playwright MCPでローカルの動作確認からバグ発見まで任せてみた話
    • はじめに
    • Claude Code × Playwright MCP とは
      • Claude Code
      • Playwright MCP
    • セットアップ
    • 実践:テンプレート切り替え機能の動作テスト
      • テスト対象の概要
      • テストシナリオの設計
      • Claude Codeに渡したプロンプト
      • テスト実行で使われた手法
        • 1. browser_snapshot でページ構造を把握
        • 2. browser_click でモーダル・ラジオボタン・送信を操作
        • 3. browser_evaluate でhidden inputの排他制御を検証
        • 4. docker compose exec でDB検証・データリセット
      • テスト結果
    • テスト中にバグを発見した話
      • 異変の報告
      • browser_evaluateでDOM調査
      • 原因特定:JSファイルのパス不整合
      • 修正→再テストで解決
    • 使ってみて感じたこと
      • よかった点
      • 注意点
    • まとめ
続きを読む

React Tokyo 2026 参加レポート

React Tokyo 2026参加レポート

プラットフォームチームの三品です。 2/28に開催されたReact Tokyoに参加しましたので、その参加レポートになります。

React Tokyo 2026

きっかけ

オライリーの「Reactハンズオンラーニング」を読みました。 書籍ではuseStateやuseEffectといったHooksの設計思想が「なぜこうなっているのか」という視点で書かれており、普段バックエンドを中心に開発している自分にとって新鮮でした。「この理解を深めるには実際にReactコミュニティの人たちと話すのが一番だろう」と思い、React Tokyoへの参加を決めました。

ポスターセッションがメイン

ポスターセッションとは、会場にReactに関するポスターが展示され、参加者とポスターの投稿者同士でディスカッションするスタイルです。

通常の登壇形式のカンファレンスだと聞く側に回りがちですが、ポスターセッションは興味のあるトピックを選んで投稿者に直接質問できるので、自分のペースで参加できるのが良かったです。ポスターはReactの基礎的な内容から上級者向けの内容まで幅広くカバーされていました。

開発現場で使って難しいと感じたNext.js

Next.jsが難しいと感じたことを投稿されていました。具体的には

  • もともとLaravelを使ったフルスタックの開発をしており、React/Next.jsの基礎知識が不足した状態で使い始めたため難しいと感じた。
  • ReactでわからないのかNext.jsでわからないのか曖昧になっている。
  • キャッシュ周りが複雑。

であることを挙げていました。

私自身も思い通りの挙動にならなかったときに、その原因がReactなのかNext.jsの問題なのか分からなかったことや、キャッシュ周りの問題でAPIでPOSTされたデータが画面側で反映されずに苦労したことがある ので、投稿者のけいすけさんと話が盛り上がりました。

特にキャッシュについては、Next.jsのApp Routerで導入されたfetch単位のキャッシュやRouter Cacheなど、複数のレイヤーにキャッシュが存在しており、どのキャッシュが効いているのか切り分けるのが大変だったという点で意見が一致しました。自分としてはバックエンドエンジニアとしてLaravelのキャッシュの方が明示的で扱いやすいと感じる一方、フロントエンドはユーザー体験を追求するために複雑なキャッシュ戦略が必要になるのだなと改めて実感しました。

また、けいすけさんからuseMemo()はパフォーマンスの最適化をしてくれるが、React Compilerを導入すると、コンパイラ側で最適化してくれることを教えてもらいました。 つまり、開発者がuseMemo()やuseCallback()を手動で書く必要がなくなり、メモ化の判断をコンパイラに任せられるようになるとのことでした。これはDX(開発体験)の大きな改善になりそうだと感じました。

目で見てサッと理解する!Reactの思想と内部実装入門

Reactの内部で使われているFiberアーキテクチャと、レンダリングが「トリガー → スケジューリング → レンダー → コミット」という4つのフェーズで処理される仕組みが図解でわかりやすくまとめられていました。Reactの内部実装まで意識することはなかなかないので、こうした仕組みを知ることで「なぜReactはこういう挙動をするのか」がより理解しやすくなると感じました。

RSCの仕組みをNext.jsとTanstack Startの比較から理解したい

RSC(React Server Components)の仕組みを両フレームワークの視点から紐解いてわかりやすく解説されていました。

Tanstack Startについては知らなかったのですが、ざっくり言うとTanStack Router(クライアントサイドのルーティングライブラリ)をベースにしたフルスタックフレームワークで、Next.jsのようにSSRや サーバーサイドの機能を提供しつつも、クライアントサイドのルーティングを軸にしている点が特徴的だと解説されていて、勉強になりました。

投稿者のIORIさんからお話を聞いて、Tanstack Startでは、Composite Componentという'use client'を開発者が意識しなくてもよくなるコンポーネントの仕組みが開発中とのことで、DXが変わると思うのでリリースされることをとても楽しみにしています。

まとめ

普段はバックエンド側の開発がメインである私でも、本イベントはポスターセッションで気軽に話しかけられるようになっており、とても楽しめました。

今回の参加を通して、React Compilerによるメモ化の自動化やTanstack StartのComposite Componentなど、「開発者が意識しなければならないことを減らす」方向にフロントエンドのエコシステムが進化していることを感じました。バックエンドエンジニアとしても、こうしたフロントエンドの動向をキャッチアップしておくことで、チーム内での技術的な議論やアーキテクチャの意思決定に貢献できると思います。

次回のイベントにもぜひ参加したいです。

SQLアンチパターン読書会を半年間開催して見えてきたこと

はじめに

学生チーム所属の井田です。

私たちのチームでは、個人の知識・専門性の向上に加え、チーム全体の知識を実務で使える形に定着させること、そしてメンバー間のコミュニケーションを促進することを目的に、半年間にわたり技術書の読書会を実施してきました。

題材として選んだのは『SQLアンチパターン 第2版』です。

本書は、単なる SQL の書き方を解説するものではなく、「なぜその設計が問題になりやすいのか」「どのような背景でアンチパターンが生まれるのか」を豊富な事例とともに扱っており、日々の開発業務と親和性の高い一冊でした。

本記事では、読書会の運営方法、実際の議論内容、そして半年間を通して得られた学びについてご紹介します。

  • はじめに
  • 1. 読書会の目的と運用ルール
    • 開催概要
    • 進め方:その場で読む「30分+30分」形式
  • 2. 実際の読書会の一例:第8章「マルチカラムアトリビュート」
    • テーマの概要
    • チーム内での議論と気づき
  • 3. 現場経験から見えたアンチパターンの実態
    • 3-1. アンチパターンが招いた苦労
      • ランダム抽選機能の停止(第16章 ランダムセレクション)
      • 一括購入処理によるシステムパンク(第2章 ジェイウォーク)
    • 3-2. 「あえて」採用して正解だったパターン
      • 固定数前提のマルチカラム設計(第8章 マルチカラムアトリビュート)
      • 小規模な ENUM の利用(第11章 サーティワンフレーバー)
    • 3-3. 「正しい設計」が招いたパフォーマンスの壁
      • JOIN の増加と待機時間の合意(第17章 プアマンズ・サーチエンジン)
      • 分割によるオーバーヘッド(第18章 スパゲッティクエリ)
  • 4. 開催してみての評価
    • 朝開催による出席率と効果
    • 「30分読書+30分議論」という時間配分のバランス
    • オンライン開催ならではの工夫と、良かった点・改善点
  • 5. 半年間続けてみての所感
    • 「違和感」に名前がついた
    • バックエンド・フロントエンドの視点の共有
    • 知識から経験に変わる感覚
  • 6. 今後の展望
  • おわりに:チームの共通認識を作る
続きを読む

TSKaigi Hokuriku 2025に参加+登壇しました!

会場の様子

はじめに

フロントエンドを中心にエンジニアをしている赤星です。今回は11/23(日)に金沢で開催されたTSKaigi Hokuriku 2025に参加+登壇しましたので、そのレポートを書いていきたいと思います。

hokuriku.tskaigi.org

  • はじめに
  • 登壇内容
  • 印象に残ったセッション
    • フロントエンドにおける「型」の責任分解に対する1つのアプローチ
    • denoとtypescriptの関係について改めて考えてみる
  • さいごに
続きを読む

FindyTeam+(Four Keys)で見る、開発プロセス改善の確かな成果

はじめに

プロジェクトチームのリーダーを務める生江です。
日々の開発業務に取り組む中で、「自分たちのチームは本当に成長できているのだろうか?」「改善はしているものの、その効果は現れているのだろうか?」と疑問を感じることはありませんか?実は私も、データによる可視化を行うまではそうした漠然とした不安を抱いていました。
この記事では、私たちのチームが過去半年間、開発プロセスを改善するために行ってきた様々な取り組みと、その成果をFourKeys を用いて客観的に可視化し、皆さんと共有したいと思います。

  • はじめに
  • 私たちが抱えていた課題
  • 課題に対する取り組み
  • 取り組みの成果
    • 1. リードタイムの短縮とプルリクエストの変化
    • 2. デプロイ頻度の向上
      • 3. 変更行数の削減とレビュー時間の短縮
  • まとめ
続きを読む

ⓒ i-plug,inc. All Rights Reserved.