TECH PLAY

webpack

イベント

該当するコンテンツが見つかりませんでした

マガジン

該当するコンテンツが見つかりませんでした

技術ブログ

AI コーディングアシスタントに簡単なこと、例えば関数名の変更やファイルの移動を依頼すると、突然復旧作業に追われることがあります。インポートが壊れたり、参照が存在しないファイルを指したりします。5 分前にコンパイルできていたコードベースが、至る所でエラーを投げ始めます。20 秒で終わるはずのリファクタリングが、5 分間のデバッグとクリーンアップセッションに変わってしまうのです。 エージェントにとってリファクタリングが難しい理由 リファクタリングは単なる大規模な検索置換ではありません。コードベースのセマンティック構造全体にわたるグラフトラバーサル問題なのです。関数名を変更すると、変更は連鎖します。ワークスペース全体のすべての呼び出し箇所、それを参照する型定義とインターフェース、import/export 文、テスト、そして(オプションで)ドキュメントとコメント。ファイルの移動はさらに複雑な波及効果を引き起こし、すべての依存ファイルのインポートパス、バレルファイル( index.ts )と再エクスポート、 tsconfig パスやバンドラー設定に組み込まれたモジュール解決の前提、Webpack 設定のような散在する設定ファイルなどに影響します。ここに根本的なミスマッチがあります。LLM はパターンマッチングを通じてもっともらしいコードを生成することに優れていますが、リファクタリングは もっともらしさよりも精度 を要求します。これは創造的なタスクではなく、シンボルの関係、言語固有のセマンティクス、プロジェクトの依存関係グラフの正確な理解を必要とする制約充足問題なのです。「正しく見える」が、深くネストさ れたモジュールの 1 つのインポートを見逃したエージェントは、単に小さなエラーを犯しただけではありません。本番環境まで表面化しないランタイム障害を導入したのです。これが、どれほど洗練されていたとしても、テキスト生成が構造的なコード変換において信頼性に欠けるツールである理由です。 問題:エージェントが効率的ではなく、非効率に動作するとき 多くの AI エージェントがリファクタリングでつまずくのは、 構造的 な編集を テキスト 編集として扱うからです。開発者が直面し続けている失敗モードをいくつか紹介します。 依頼内容: 「このメソッドの名前を変更して」 従来の失敗: エージェントはメソッド定義を更新しましたが、プロジェクト全体の呼び出し箇所を見逃しました。プロンプトで参照を更新するよう明示的に依頼した場合でも、プロセスは遅くエラーが発生しやすいループになりました。古い名前を検索して置換するのです。このプロンプトを考えてみましょう。 expression.js の get_loose_identifier を、それが何をするかをよりよく反映するように名前変更してください。このシンボルの名前変更は 4 つのファイルに伝播し、8 つの参照と 3 つのインポートに影響します。次の図の左側(従来のアプローチ)は、専用のリファクタリングツールなしでこの操作がどのように展開されるかを示しています。最初のファイル( expression.js )でシンボルの名前を変更した後、エージェントはコードベースで get_loose_identifier を検索し、複数の LLM 呼び出しとツール呼び出しを通じて CallExpression.js と AssignmentExpression.js を更新します。努力したにもかかわらず、残りの参照を見逃しています。 Kiro の対処法: 開発者が IDE でこのタスクを手動で実行する方法を考えてみましょう。 get_loose_identifier で F2 を押し、新しい名前を入力して、Enter を押します。IDE は、コードベース全体の 8 つの参照と 3 つのインポートすべてを更新しながら、自動的に名前変更を実行します。これがまさにセマンティックリネームツールが行うことです。次の図の右側(新しいアプローチ)は、Kiro が単一のツール呼び出しで名前変更全体を適切に実行する方法を示しています。 依頼内容: 「このファイルの lint エラーを修正して」 従来の失敗: エージェントは linter の出力をテキスト編集の ToDo リストとして扱いました。1 つのファイルでシグネチャの関数名を camelCase から snake_case に変更しましたが、他のファイルで「参照が見つからない」や「インポートが見つからない」エラーを導入しました。すべての使用箇所への変更の伝播に失敗したのです。 Kiro の対処法: ユーザーが直接名前変更を依頼しなくても、エージェントがセマンティックリネームツールから恩恵を受ける例を示します。ユーザーはエージェントに「 text_helpers.py の lint エラーを修正して」と依頼します。lint エラーは、 utils/text_helpers.py 内の normalizeText と slugifyTitle を snake_case に変更する必要があることを示しています。コードベースの部分的なスナップショットを以下に示します。 これらの修正をテキスト編集として扱うエージェントは、関数定義の名前を変更し、ローカル参照を修正するかもしれませんが、他の場所のインポートや呼び出し箇所を見逃す可能性が高く、実行時に ImportError / NameError を引き起こします。セマンティックリネームツールを使用することで、Kiro は定義だけでなく、 api/routes.py と services/indexer.py のインポートと呼び出しも更新します。以下の画像の通りです。 依頼内容: 「コンポーネントを再編成して – Button.tsx を src/components/ から src/shared/ui/ に移動して」 従来の失敗: エージェントはタスクを単純なファイル操作として扱いました。ファイルの移動は成功しましたが、古い場所を指すすべてのインポート文が壊れています。エージェントはその後、検索置換操作でファイルごとにインポートを修正しようとしましたが、動的インポートを見逃しました。 import('../components/Button') 。 Kiro の対処法: Kiro がインポートパスを自動的に更新する具体的な例を示します。図は、プロジェクト構造の部分的なスナップショットと依存するコードスニペットの一部を示しています。 Button.tsx を src/components/ から src/shared/ui/ に移動した後、Kiro は移動したファイルに関連するすべてのインポート文を自動的に更新します。 主な利点: 組み込みの言語サーバーが編集を処理するため、手動の検索置換は不要です。 言語認識:TypeScript/JavaScript モジュール解決を理解します。 より安全:動作するコードを壊す可能性が低くなります。 エッジケースの処理:パスエイリアス、モノレポなどに対応します。 これは、VSCode のエクスプローラーでファイルをドラッグアンドドロップしたときに起こることとまったく同じです。セマンティックリネームツールはエージェントベースの同等機能です! Kiro エージェントのリファクタリング方法 IDE は、エージェント AI の台頭以前にこの問題をすでに解決していました。VSCode でシンボルの名前を変更するために F2 を押すと、IDE は推測しません。コードの構造を理解する言語サーバーに相談し、ワークスペース全体の編集を計算し、安全に適用します。VSCode のワークスペース編集機能により、単なるテキストパターンではなく、コードの構造を理解するプログラマブルなセマンティック検索置換が可能になります。Kiro エージェントは、LLM 推論だけでリファクタリングをシミュレートしようとはしません。代わりに、エージェントは上記と同じメカニズムを使用して、これらの実証済みの IDE 機能をプログラム的に公開する 2 つの新しいリファクタリングツールを登録します。エージェントがシンボルの名前を変更したりファイルを移動したりする必要がある場合、意図をインテリジェントに認識し、適切なリファクタリングツールを選択して呼び出します。エージェントはリファクタリングワークフローを調整し、IDE の言語サーバーが正確性の検証を支援します。 これらのエージェント登録リファクタリングツールが内部でどのように機能するかを見てみましょう。 セマンティックリネームツール:正しいリネームを実現 このツールは、VSCode のシンボル名前変更 API に直接接続します。F2 を押したときに使用するのと同じものです。 vscode.prepareRename を使用してシンボルが名前変更可能かどうかを検証し(例:キーワードではない)、 vscode.executeDocumentRenameProvider を使用してワークスペース全体で必要なすべての変更を含むワークスペース編集を生成します。TypeScript、JavaScript、TSX、JSX の場合、組み込みの VSCode 名前変更プロバイダーがすべてを処理します。Python、Go、Java などの場合、ツールはインストールされた言語拡張機能とそれらが提供する言語サーバーに依存します。 スマートリロケートツール:すべてを壊さずにファイルを移動 このツールは、VSCode のファイル移動機能を使用して、すべての参照を自動的に更新しながらファイルを再配置します。VSCode のエクスプローラーでドラッグアンドドロップするのと同等のプログラム的な操作ですが、エージェントがあなたのために実行できます。 vscode.WorkspaceEdit.renameFile と vscode.workspace.applyEdit を使用して、ツールは複数のファイルにわたる包括的な変更を生成し、影響を受けるインポートを更新します。 これが重要な理由 創造性よりも精度: リファクタリングは、コードがどのように見えるべきかを LLM に想像させる必要はありません。コードが 実際に何であるか を理解し、外科的に変更できるツールが必要なのです。 実証済みのインフラストラクチャを通じた信頼: これらは実験的な LLM 機能ではなく、開発者が日常的にすでに依存しているリファクタリングインフラストラクチャとの直接統合です。F2 を押したときに機能すれば、エージェントが実行したときにも機能します。 言語に依存しない: 重い作業は言語サーバーによって行われるため、このアプローチは技術スタックと言語全体に一般化されます。 生産性の維持: 20 秒の手動リファクタリングが、5 分間の AI 生成リカバリーミッションになるべきではありません。適切なツールを使用すれば、操作は高速でアトミックなままです。 より大きな視点 構築による正確性という私たちの哲学に基づいて、 IDE 診断統合 を導いたのと同じ原則で、VSCode のリファクタリング機能の全範囲をカバーするようにこのアプローチを拡張しています。エラーが複合する前にキャッチするためにリアルタイム診断を統合したのと同様に、これらの実証済みの決定論的 IDE 機能を、新しい内部スマートリロケートおよびセマンティックリネームツールに拡張しました。 しかし、リファクタリング機能は名前変更と再配置で止まりません。VSCode の言語サーバーは、エージェントが活用すべき豊富な自動コード変換スイートを提供します。コードブロックを再利用可能な関数に抽出するメソッド/関数の抽出、コードを簡素化する変数/関数のインライン化、すべての呼び出し箇所でメソッドパラメータを更新するシグネチャの変更、アロー関数への変換やその他の言語固有の変換は有力な候補です。 このアプローチを取ることで、エージェントが実行される基盤に正確性、セキュリティ、信頼性を組み込むことができます。これらのツールで確立したパターンは、ツールキットへの新しい追加を導きます。LLM に脆弱なテキスト置換スクリプトを生成するよう依頼する代わりに、インテリジェントなコーディングエージェントは、開発者がすでに信頼しているこれらの実証済みの IDE 操作を活用し続けます。IDE が正しく実行する方法を知っているとき、私たちはそれに作業をさせます。エージェントがより有能になるにつれて、これはその出力をより信頼できるものにするための良いテクニックでもあります。 違いを体験する準備はできましたか? Kiro を無料で始めて 、開発ワークフローをどのように変革できるかを確認してください。 Discord の成長するコミュニティに参加して、フィードバックを共有し、質問をし、AI 支援コーディングで構築している他の開発者とつながりましょう。 謝辞 エンジニアリングの洞察と貴重なフィードバックを提供してくれた Al Harris に感謝します。 本記事は 2026 年 2 月 5 日に公開された Pardis Pashakhanloo と Rajdeep Mukherjee による “ Refactoring made right: how program analysis makes AI agents safe and reliable ” を翻訳したものです。翻訳は Solutions Architect の吉村が担当いたしました。
はじめに この記事はBASEアドベントカレンダー2025の16日目の記事です。 こんにちは。Pay ID プラットフォーム Group で エンジニアをしている noji です。最近は Pay ID の認証基盤のフロントエンド開発を担当しています。 本記事では BASE のショップや Pay ID アプリでの買い物時にカートでの Pay ID ログイン機能を提供している JavaScript(以後 payid-js)のビルド環境を webpack/Babel から esbuild に移行した話を紹介します。 payid-js について payid-js は Pay ID ログイン機能を提供している埋め込み用の JavaScript です。Pay ID ログインすることで、Pay ID に登録されている住所情報や決済手段情報を連携することで、ユーザーはスムーズに購入手続きを進めることができます。 BASE のカートのフロントエンドで payid-js を読み込み、用意された関数を呼び出すと画面上にログイン用の画面が iframe 上に表示され、Pay ID にログインできます。iframe 内でログイン処理を行い、結果を postMessage API を使ってカートのフロントエンドに通知します。 payid-js は iframe 内外でやり取りを行うインターフェースを提供しており、iframe 内でログインが完了すると、結果をカートのフロントエンドに返すようになっています。(iframe の内側の画面については別システム) 技術としては、TypeScript で実装されており、ビルドには webpack と Babel を使用していました。 移行背景 payid-js は BASE のカートと iframe で表示される Pay ID ログイン画面の橋渡しをするだけのコンポーネントなので、軽量な JavaScript です。 軽量であるので、webpack のビルドに時間がかかるとは感じていませんでしたが webpack 時代のバージョンアップや設定変更が大変 Babel を含む関連ライブラリの設定が複雑 依存関係の脆弱性が多い payid-js には webpack ほど高度な機能が不要である などの課題があり、よりシンプルなビルドツールへの移行を検討しました。 esbuild を選んだ理由 候補として esbuild、vite、Rollup などがありましたが、最終的に esbuild を選択しました。理由は以下です。 シンプルで高速なビルドが可能 Go 製でビルドが非常に速いのに加えて、設定もシンプルでわかりやすく、TypeScriptのトランスパイルも内蔵されていてBabelも不要 依存関係が少なく、メンテナンスコストや脆弱性リスクが低い webpack や Babel に比べて関連ライブラリ等の依存関係が少なく、アップデートや脆弱性の対応に追われる負荷が軽減されそう 他ツールとの比較 vite:SPA 向けの開発サーバーは強力だけど、payid-js のような埋め込み用 JavaScript にはオーバースペック Rollup:esbuild ほど高速ではなく、設定もやや複雑になる。ライブラリ向けには良いが、今回は見送り esbuild は HMR(Hot Module Replacement) をサポートしていないですが、payid-js は埋め込み用の JavaScript であり、開発時に HMR は必要ないため問題ありませんでした。 参考 esbuild vite Rollup 移行で詰まったポイント ローカルの 開発サーバーの構築 今までは webpack-dev-server を使用してローカル開発環境を構築していました。 webpack-dev-server はビルドしたアセットをメモリに保持し、変更があれば自動で配信内容を更新してくれる開発サーバーを内蔵しています。 Docker からのアクセスでも常に最新が返ってくるため、ビルド・配信・更新反映をひとまとめに解決してくれる優れた仕組みでした。 一方、esbuildにはwebpack-dev-serverのような開発サーバーは内蔵されておらず、あくまで”ビルド”のみの機能です。今回は serve で簡易的に http-server を立ち上げるスクリプトを用意しました。watch だけだと変更を検知して Docker コンテナに反映させることができなかったので、 chokidar も利用し、変更を検知して明示的に再ビルドできるようにしました。 #!/usr/bin/env node import path from "path" ; import { fileURLToPath } from "url" ; import * as esbuild from "esbuild" ; import { spawn } from "child_process" ; import chokidar from "chokidar" ; const __filename = fileURLToPath ( import . meta . url ) ; const __dirname = path . dirname ( __filename ) ; const outdir = path . resolve ( __dirname , "dist" ) ; // esbuild の watch 用コンテキストを作成 const ctx = await esbuild . context ({ entryPoints : [ path . resolve ( __dirname , "src" , "index.ts" )] , bundle : true , sourcemap : true , platform : "browser" , outdir , entryNames : "bundle" , minify : true , loader : { ".html" : "text" , } , }) ; await ctx . watch () ; console . log ( "esbuild: watching" , outdir ) ; // chokidar でファイル変更を監視して rebuild const watcher = chokidar . watch ([ path . resolve ( __dirname , "src" )] , { ignoreInitial : true , usePolling : true , interval : 100 , }) ; let rebuilding = false ; async function scheduleRebuild ( event , filePath ) { if ( rebuilding ) return; rebuilding = true ; console . log ( `change detected ( ${ event } ):` , filePath ) ; try { await ctx . rebuild () ; console . log ( "esbuild: rebuild complete" ) ; } finally { setTimeout (() => ( rebuilding = false ) , 50 ) ; } } watcher . on ( "add" , ( p ) => scheduleRebuild ( "add" , p )) ; watcher . on ( "change" , ( p ) => scheduleRebuild ( "change" , p )) ; watcher . on ( "unlink" , ( p ) => scheduleRebuild ( "unlink" , p )) ; // ローカルサーバー (npx serve) spawn ( "npx" , [ "serve" , "-s" , outdir , "-l" , "9000" ] , { stdio : "inherit" , shell : true , }) ; このスクリプトを実行すると、chokidar がソースコードの変更を監視し、変更があった場合に再ビルドを行います。また、 npx serve を使用してローカルサーバーを立ち上げ、ブラウザから埋め込み用 JavaScript を確認できるようにしています。 ビルドの成果物の違い 基本的に成果物はほぼ同じでしたが、loaderの指定によりHTML の import 部分で差異がありました。 webpack: HTML モジュールをオブジェクトとして扱う esbuild: HTML モジュールを文字列として扱う そのため後々の移行の手順にもあるように、一定期間同じコードベースで webpack/esbuild の両方をビルドする必要があったため、どちらのビルド方法でも動作するように、下記のようなユーティリティ関数を追加しました。 const rawModule = require( "./container.html" ); const html = getHtmlStringFromModule(rawModule); // `*.html` をバンドルする方法はバンドラによって異なります。 // - esbuild や rollup の一部設定では、インポートはそのまま文字列になります。 // - もしくは `{ default: string }` のようなオブジェクトを返す場合もあります。 // ここで形を正規化することで常に文字列として扱えるようにします。 const getHtmlStringFromModule = ( mod : unknown ): string => { if ( typeof mod === "string" ) { return mod; } if ( typeof mod === "object" && mod !== null ) { const maybeDefault = (mod as Record < string , unknown >). default ; if ( typeof maybeDefault === "string" ) { return maybeDefault; } } throw new Error ( "unexpected HTML module shape" ); } ; ビルド用の設定 #!/usr/bin/env node import path from "path" ; import { fileURLToPath } from "url" ; import { build } from "esbuild" ; const __filename = fileURLToPath ( import . meta . url ) ; const __dirname = path . dirname ( __filename ) ; const outdir = path . resolve ( __dirname , "dist" ) ; const outfile = path . join ( outdir , "bundle.js" ) ; // 本番ビルド await build ({ entryPoints : [ path . resolve ( __dirname , "src" , "index.ts" )] , bundle : true , sourcemap : true , platform : "browser" , outfile , minify : true , define : { API_BASE_URL : JSON . stringify ( process . env . API_BASE_URL || "" ) , } , loader : { ".html" : "text" , } , logLevel : "info" , }) ; console . log ( "esbuild: built" , outfile ) ; ビルド用のスクリプトも非常にシンプルです。esbuild の build 関数を使用して、エントリーポイントや出力先、バンドル設定などを指定しています。 ビルドされたファイルを CircleCI のジョブで S3 にアップロードし、CDN 経由で配信する仕組みは以前と同様に維持しています。 移行の手順 移行は段階的に行いました。 ローカル/dev 環境のみ esbuild に切り替え stg/本番も esbuild に切り替え webpack/Babel 関連の設定・依存関係を削除 結果として、問題なく移行でき、切り替えによる影響もありませんでした。 移行結果 元々軽量な JavaScript であったため、ビルド時間の劇的な改善はありませんでしたが、設定が大幅にシンプルになり、依存関係の脆弱性も出にくくなりました。 元々が CircleCI 上で 2 ~3秒程度のビルド時間でしたが、esbuild に移行したことで 1 秒未満に短縮されました!! おわりに payid-js のビルドを webpack/Babel から esbuild に移行したことで、設定のシンプル化と依存関係の削減が実現できました。 今後も payid-js の開発を続けていく中で、さらなる改善点が見つかれば積極的に取り組んでいきたいと思います。 BASE / Pay IDではエンジニアを募集しているので、興味ある方は以下からご連絡ください。 明日のBASEアドベントカレンダーはIzuharaさんの記事です。お楽しみに。 binc.jp
概要 こんにちは。ニフティの山田です。 2025年10月21日に、Next.js 16がリリースされました。 https://nextjs.org/blog/next-16 大きな変更がそれなりにあるので、上記記事からピックアップしてみます。 破壊的変更点 ブラウザサポートのBaseline Widely Available基準への変更 Next.js 15からサポートブラウザが一気にバージョンアップしています。 Chrome Edge Firefox Opera Safari Next.js 15.5 64+ 79+ 67+ 51+ 12+ Next.js 16.0 111+ 111+ 111+ – 16.4+ これは単に上がったというだけでなく、 Web Platform Baseline におけるBaseline Widely Availableへの追従の結果としてこうなっています。 https://github.com/vercel/next.js/pull/84401  Widely Availableとなる要件は、主要ブラウザで30ヶ月以上利用可能であることです。 つまり、今後のNext.jsではメジャーバージョンアップのたびに 2.5年以上経過したブラウザは動作対象外 となることを想定する必要があります。 古いブラウザをサポートしたければ、 browserslistを設定してトランスパイルターゲットを変更 next.config.jsの transpilePackages で、クライアント側が使うライブラリをトランスパイル対象に追加 デフォルトではnode_modules以下はトランスパイル対象外となる… Fastly Polyfill などにより、トランスパイルで対応できない部分にPolyfillを追加 などの対応がより重要となってきます。特にpolyfillについて、Next.jsは独自に内蔵するpolyfillコードを使っているため、Next.jsサポート範囲外の対応には別のpolyfillコードを差し込まなければいけない点に注意が必要です。 middleware.ts → proxy.tsへの変更 middlewareの名前が変更となりました。 一般的にmiddlewareと呼ばれるものとは異なる機能であるため、わかりやすさのためにこうなったようです。 next lintコマンドの削除 以前のNext.jsでは eslint コマンドではなく、 next lint コマンドを利用してeslintを実行するのが推奨になっていました。eslint以外にもbiomeなどの選択肢が出てきたことから、この機能は削除となりました。今後は eslint コマンドを直接叩けばよいです。 なおNext.js 15.5の時点でdeprecatedになっていたので、そこで対応していれば問題ないはずです。 turbopackデフォルト化 バンドラとして従来使われてきたwebpackに代わり、ようやくturbopackがデフォルトとなります。 今まではturbopackを使用するのにCLIオプションが必要でした。 next dev --turbopack next build --turbopack 今後はturbopackがデフォルトとなり、逆にwebpackを使いたいときにのみオプションを指定する形となります。 next dev --webpack next build --webpack 新機能 Cache Components 今までExperimentalでPartial Pre-Rendering(PPR)やDynamicIOの名前で実装されていたものが正式版となります。 Next.js 13で導入されたApp Routerではキャッシュ制御が非常に重要になりますが、 fetch() がキャッシュ機能を持つものに暗黙的に置き換わるなど予測しにくい挙動をしていました。このため、Next.js 15でキャッシュのデフォルト無効化などの変更が行われる事態が発生しています。 そこで出来たのが新しい use cache によるキャッシュ制御です。以下はNext.js 16公式ブログから引用しています。 // File level 'use cache' export default async function Page() { // ... } // Component level export async function MyComponent() { 'use cache' return <></> } // Function level export async function getData() { 'use cache' const data = await fetch('/api/data') return data } use cache を記述することでキャッシュを有効化します。上記にあるように、記載位置によってファイルレベル・関数レベルでキャッシュ有効化単位を制御できます。 cacheLifeなど、キャッシュ時間を制御するための仕組みも増えました。 'use cache' import { cacheLife } from 'next/cache' export default async function Page() { cacheLife('hours') return <div>Page</div> } キャッシュの仕組みを変えることになるため、現状では設定しなければ有効になりません。 const nextConfig = { cacheComponents: true, }; export default nextConfig; おそらく今後はこちらを主流にしていくのではないかと思われます。 従来のキャッシュ制御も引き続き利用可能ですが、将来的にはどこかで移行することになるでしょう。 React Compiler対応 React CompilerはReactコードの最適化機能です。 https://ja.react.dev/learn/react-compiler ビルド時にコードを解析することで useMemo() や useCallback() などのメモ化コードを自動的に挿入し、レンダリングの最適化を行います。開発者はメモ化のことを意識しなくて良くなる、という機能です。 これがNext.jsでも使えるようになりました。 対応するには設定を加えたうえで const nextConfig = { reactCompiler: true, }; export default nextConfig; React Compilerのインストールが必要となります pnpm add -D babel-plugin-react-compiler 注意点として、React Compilerは babelプラグインとしての提供 となることが挙げられます。SWCで実行されないので、ビルドが遅くなる可能性が高いことに注意が必要です。 おわりに 今回は、2025年10月21日にリリースされたNext.js 16がリリースについて解説しました。 参考になれば幸いです。

動画

該当するコンテンツが見つかりませんでした

書籍

該当するコンテンツが見つかりませんでした