0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

今更ですがBiomeの速度を肌で感じたい

0
Last updated at Posted at 2025-12-20

はじめに

今年もアドベントカレンダーの時期がやってまいりました
気になっていたけど全く触っていなかったBiomeを触ります
「処理が早い」を一番よく聞くので、実際どの程度違うのか簡単に計測します
とりあえず一番Web業界で使われていそうなNext.jsのアプリケーションで試します

流れ

  • 簡単なアプリケーションをNextJSで作成
  • ダミーコードを大量に生成
  • ESLint & Prettierでlintして実行時間を計測
  • Biomeを導入して実行時間を計測
  • 比較

環境

  • OS: Windows11
  • Terminal: Git Bash

今回作ったものは下記にpushしておきました
https://github.com/engabesi/test-biome

検証

アプリケーションの作成

まずはベースとなるアプリケーションを作成します
設定値はデフォルト

$ npx create-next-app@latest test-biome --typescript --tailwind --eslint
Need to install the following packages:
create-next-app@16.1.0
Ok to proceed? (y) Y

√ Would you like to use React Compiler? ... No / Yes
√ Would you like your code inside a `src/` directory? ... No / Yes
√ Would you like to use App Router? (recommended) ... No / Yes
√ Would you like to customize the import alias (`@/*` by default)? ... No / Yes

...

Success!

Googleさんが気を利かせて翻訳してくるのでjaに変更

app/layout
   return (
-    <html lang="en">
+    <html lang="ja">
       <body
         className={`${geistSans.variable} ${geistMono.variable} antialiased`}
       >

機能はシンプルに、ボタンを押すとカウントアップするだけのものに

app/page.tsx
'use client';
import { useState } from 'react';

export default function Home() {
  const [count, setCount] = useState(0);

  return (
    <main className="flex min-h-screen flex-col items-center justify-center p-24">
      <div className="text-4xl mb-4">Count: {count}</div>
      <button
        className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded"
        onClick={() => setCount(count + 1)}
      >
        Increment
      </button>
    </main>
  );
}

npm run devで動くことを確認
image.png

ESLint & Prettier構成にする

現在デファクトスタンダードである ESLint + Prettier 環境を整える

npm install --save-dev prettier eslint-config-prettier
eslint.config.mjs
 import nextTs from "eslint-config-next/typescript";
+import prettierConfig from "eslint-config-prettier";

 const eslintConfig = defineConfig([
   ...nextVitals,
   ...nextTs,
+  prettierConfig,
package.json
     "start": "next start",
-    "lint": "eslint"
+    "lint": "eslint",
+    "format": "prettier --write .",
+    "lint:format": "npm run lint && npm run format"
   },
   "dependencies": {

ESLintとPrettierを動かします

$ time npm run lint:format

> test-biome@0.1.0 lint:format
> npm run lint && npm run format


> test-biome@0.1.0 lint
> eslint


> test-biome@0.1.0 format
> prettier --write .

app/globals.css 22ms (unchanged)
app/layout.tsx 44ms (unchanged)
app/page.tsx 6ms
eslint.config.mjs 14ms (unchanged)
next.config.ts 3ms (unchanged)
package-lock.json 61ms (unchanged)
package.json 1ms (unchanged)
postcss.config.mjs 2ms (unchanged)
README.md 22ms (unchanged)
tsconfig.json 2ms (unchanged)

real    0m4.146s
user    0m0.061s
sys     0m0.000s

大体4秒程度
初期でも結構かかるなという印象

また、以後共通の手順として、lintを走らせたあとの差分はすべて戻します

アプリケーションの肥大化

Lintの実行時間を長くするためにダミーファイルを作ります
手でやるのは面倒なのでスクリプトを作成し実行
スクリプトはCopilot君が頑張ってくれました

create-dummy.js
/* eslint-disable @typescript-eslint/no-require-imports */
const fs = require('fs');
/* eslint-disable @typescript-eslint/no-require-imports */
const path = require('path');

const DIR = path.join(__dirname, 'app', 'dummy');
if (!fs.existsSync(DIR)) fs.mkdirSync(DIR);

// 複雑な計算をするコンポーネントを500個生成
for (let i = 0; i < 500; i++) {
  const content = `
import React, { useState, useEffect, useMemo, useCallback } from 'react';

interface Props${i} {
  ${Array.from({ length: 50 }, (_, j) => `prop${j}: string;`).join('\n  ')}
}

export const DummyComponent${i} = (props: Props${i}) => {
  // AST解析に負荷をかけるための無駄なオブジェクト定義
  const heavyObject = {
    ${Array.from({ length: 500 }, (_, j) => `prop${j}: "value${j}",`).join('\n    ')}
  };

  // Hooks の大量使用でESLintの負荷を増やす
  const [state${i}, setState${i}] = useState(0);
  ${Array.from({ length: 10 }, (_, j) => `const [state${i}_${j}, setState${i}_${j}] = useState(${j});`).join('\n  ')}

  // useEffect で dependency チェックの負荷を増やす
  useEffect(() => {
    console.log('mounted');
  }, [state${i}]);

  // useMemo で複雑な依存関係
  const memoValue = useMemo(() => {
    return Object.keys(heavyObject).reduce((acc, key) => acc + key.length, 0);
  }, [${Array.from({ length: 10 }, (_, j) => `state${i}_${j}`).join(', ')}]);

  // useCallback でさらに負荷
  const handleClick = useCallback(() => {
    setState${i}(prev => prev + 1);
  }, []);

  // 深いネストと複雑な条件分岐
  const renderContent = () => {
    if (state${i} > 100) {
      return ${Array.from({ length: 20 }, (_, j) => `
        state${i}_${j % 10} > ${j} ? <div key="${j}">Content ${j}</div> :`).join('')}
        <div>Default</div>;
    }
    return null;
  };

  return (
    <div onClick={handleClick}>
      <h1>Dummy ${i}</h1>
      <p>State: {state${i}}</p>
      <p>Memo: {memoValue}</p>
      {Object.keys(heavyObject).map(key => (
        <span key={key} style={{ color: key.length % 2 === 0 ? 'red' : 'blue' }}>
          {key}
        </span>
      ))}
      ${Array.from({ length: 50 }, (_, j) => `<div key="div${j}">{props.prop${j}}</div>`).join('\n      ')}
      {renderContent()}
    </div>
  );
};
`;
  fs.writeFileSync(path.join(DIR, `Dummy${i}.tsx`), content);
}

console.log('500個のダミーコンポーネントを生成しました。');
$ node create-dummy.js

500個のダミーコンポーネントを生成しました。

計測:ESLint + Prettier

肥大化した状態で、ESLintの実行時間を計測します

$ time npm run lint:format

> test-biome@0.1.0 lint:format
> npm run lint && npm run format


> test-biome@0.1.0 lint
> eslint


> test-biome@0.1.0 format
> prettier --write .

...

real    0m57.757s
user    0m0.030s
sys     0m0.030s

とんでもなく長くなりました

Biome導入

これをBiomeにするとどれぐらい変わるのか確認します

$ npm install --save-dev --save-exact @biomejs/biome
$ npx @biomejs/biome init
package.json
     "lint": "eslint",
     "format": "prettier --write .",
-    "lint:format": "npm run lint && npm run format"
+    "lint:format": "npm run lint && npm run format",
+    "biome": "biome check --write ."
   },
   "dependencies": {
     "next": "16.1.0",

計測: Biome

同じ500個のダミーファイルがある状態でBiomeを実行します

$ time npm run biome

...

Found 2504 errors.
Found 5500 warnings.
Found 2 infos.
check ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  × Some errors were emitted while applying fixes.



real    0m5.170s
user    0m0.015s
sys     0m0.045s

まとめ

ツール 実行時間(real)
ESLint + Prettier 57.8s
Biome 5.2s

10倍早く終わりました かなり速度差がありますね
Biomeが早いとは聞いてましたがここまで顕著にでるとは思っていませんでした

実際の現場では乗り換えることは足りない部分もあり中々難しいと思いますが、
成熟してなお状況が変わらなければ、Biomeはかなり強力な選択肢になると感じました
とりあえず個人でさっと作るものにはBiome使っていこうかと思います

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?