{"slug": "give-your-coding-agent-a-bundle-budget-size-limit-fails-the-branch-before-you-it", "title": "Give Your Coding Agent a Bundle Budget: size-limit Fails the Branch Before You Land It", "summary": "A developer outlines a workflow for gating AI coding agents with Size Limit, a JavaScript performance-budget tool, so that oversized bundle changes fail CI before merge. The approach assigns a per-entry-point budget in package.json, uses the small-lib preset (rolldown-bundled since 14.0.0) and the rolldown-why plugin to generate an HTML report explaining what bloated the bundle, and writes failure messages aimed at the agent. The example shows a default lodash import slipping past green tests because lodash lacks ES module metadata, defeating tree-shaking.", "body_md": "You asked your coding agent for a debounce on the search box. It came back with seven lines and green tests. One of those lines was `import _ from \"lodash\"`, and one line in `package.json` added `lodash` to get a single function.\n\nNothing in your test suite cares. The diff looks small. But the main [lodash](https://www.npmjs.com/package/lodash) package has no `module` field and no `type: \"module\"` in [its package metadata](https://registry.npmjs.org/lodash/latest), and a default import is the whole lodash object. Webpack's [tree-shaking guide](https://webpack.js.org/guides/tree-shaking/) puts the rule plainly: dead-code elimination \"relies on the static structure of ES2015 module syntax\". So the bundler keeps all of it, and every visitor downloads a utility belt to run one function.\n\nNobody notices until the bundle has grown by a few of these. The fix is to make bundle size a check that fails, the same way a test fails, so the agent hears about it before you do.\n\n[Size Limit](https://github.com/ai/size-limit) calls itself \"a performance budget tool for JavaScript\". It bundles a file with all its dependencies, minifies and compresses it (Brotli by default), and \"throws an error if the cost exceeds the limit\". The current release is [14.3.0](https://registry.npmjs.org/size-limit/latest), which needs Node.js 22.19+, 24.5+ or 26+. Since 14.0.0 the small-library preset bundles with rolldown ([changelog](https://github.com/ai/size-limit/blob/main/CHANGELOG.md)).\n\nInstall it with the preset and the plugin that powers `--why` for that preset:\n\n```\nnpm install --save-dev size-limit \\\n  @size-limit/preset-small-lib \\\n  @size-limit/rolldown-why\n```\n\nThen give every entry point its own budget in `package.json`. Here the app has a main entry and a search widget that loads on its own:\n\n```\n{\n  \"scripts\": {\n    \"size\": \"size-limit\",\n    \"size:why\": \"size-limit --why\"\n  },\n  \"size-limit\": [\n    {\n      \"name\": \"app\",\n      \"path\": \"src/index.js\",\n      \"limit\": \"12 kB\",\n      \"message\": \"Do not raise this limit. Fix the import.\"\n    },\n    {\n      \"name\": \"search\",\n      \"path\": \"src/search.js\",\n      \"limit\": \"2 kB\",\n      \"message\": \"Do not raise this limit. Fix the import.\"\n    }\n  ]\n}\n```\n\nThis uses the small-lib preset because it bundles each entry straight from source, with no build step. The README recommends that preset for libraries under 10 kB; for a full app it points to `@size-limit/preset-app` or `@size-limit/file`, which measure your built files. The budget-and-gate workflow below is the same with any of them.\n\nThe numbers are examples. Run `npm run size` once and read the current sizes. The [README](https://github.com/ai/size-limit) suggests about a kilobyte of headroom for a small library and 25% for an app. For a gate an agent has to pass, go tighter than that: a loose budget lets several heavy imports land before it says anything.\n\n`name` labels each check in the report. `message` is, in the README's words, \"an optional custom message to display additional information\", and it is the line your agent will read when the check fails, so write it for the agent. If you ship a library rather than an app, the `import` option measures what a consumer pays for one export, for example `\"import\": \"{ formatPrice }\"`.\n\n*One budget per entry point, so a heavy import shows up on the check it belongs to.*\n\nA red check tells you a budget broke. `npm run size:why` tells you why. With `@size-limit/rolldown-why` installed, `size-limit --why` analyses the bundle and writes an HTML report, `rolldown-why.html`, to the current directory. Pass `--save-bundle <DIR>` to put it somewhere else.\n\nIn the report for the search check, look for lodash: a default import brings the whole package along, so expect it to dwarf your few lines of widget code. That is the evidence to put in front of the agent, or in front of yourself before you accept the change.\n\nThe fix is one of two edits. A named import from [lodash-es](https://www.npmjs.com/package/lodash-es), which is published as ES modules with `\"sideEffects\": false` ([package metadata](https://registry.npmjs.org/lodash-es/latest)), so the bundler can drop what you don't call:\n\n``` js\nimport { debounce } from \"lodash-es\";\n```\n\nOr no dependency at all, because a debounce fits in a few lines:\n\n``` js\nexport function debounce(fn, ms) {\n  let timer;\n  return (...args) => {\n    clearTimeout(timer);\n    timer = setTimeout(() => fn(...args), ms);\n  };\n}\n```\n\nEither way, run `npm run size` again.\n\nA check only helps if it runs without anyone remembering to run it. Put it in two places.\n\nFirst, a pre-push hook. Git runs `pre-push` before every push, and per the [githooks docs](https://git-scm.com/docs/githooks), \"if this hook exits with a non-zero status, git push will abort without pushing anything\". [Husky](https://typicode.github.io/husky/get-started.html) keeps the hook in the repo, so every clone that runs `npm install` gets it:\n\n```\nnpm install --save-dev husky\nnpx husky init\necho \"npm run size\" > .husky/pre-push\n```\n\n`husky init` creates a `pre-commit` hook and a `prepare` script. Keep them or edit them; the line that matters here is the `pre-push` file.\n\nThe hook runs on the machine that pushes, and anyone can skip it. The [git-push docs](https://git-scm.com/docs/git-push) say that with `--no-verify`, \"the hook is bypassed completely\". An agent with shell access can type that flag as easily as you can. So the second place is CI, where the check runs whether or not anyone remembers it:\n\n```\n# .github/workflows/size.yml\nname: size\non:\n  pull_request:\n  push:\n    branches: [main]\njobs:\n  size:\n    runs-on: ubuntu-latest\n    env:\n      HUSKY: 0\n    steps:\n      - uses: actions/checkout@v7\n      - uses: actions/setup-node@v7\n        with:\n          node-version: 24\n      - run: npm ci\n      - run: npm run size\n```\n\n`HUSKY: 0` follows [Husky's advice](https://typicode.github.io/husky/how-to.html) to keep hooks from installing on CI servers. The version tags match the current [setup-node README](https://github.com/actions/setup-node). If you want the size change posted on each pull request as a comment, [andresz1/size-limit-action](https://github.com/andresz1/size-limit-action) does that and fails the check when a limit is exceeded; its example workflow grants `pull-requests: write`. To actually block the merge, mark the `size` job as a [required status check](https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-protected-branches/about-protected-branches) in your branch protection rules. Without that, a red check is only a warning.\n\n*The same check at three gates: after the agent's turn, before the push, and in CI.*\n\nThe pre-push hook stops the push, but by then the agent has finished and moved on. The cheaper moment is the end of the agent's turn, while it still has the change in context.\n\nWithout any tooling, that is a habit: after each task, run `npm run size`, and if it fails, paste the output back with a prompt that closes the easy exit:\n\n```\nnpm run size fails on the \"search\" check.\nDo not change the size-limit section in package.json.\nRun npm run size:why, find the import that grew,\nand switch to a named ES-module import or drop the\ndependency. Run npm run size again until it passes.\n```\n\nPut the \"never raise a limit\" rule in the instructions file your agent reads, too. The `message` field repeats it in the failure output.\n\nIn [Hermes IDE](https://hermes-ide.com), your own checks can decide when a task is done, so `npm test` and `npm run size` can both sit on that list. I build Hermes, so weigh this with that in mind. The per-repository setup lives in a worktree recipe; the [recipe docs](https://github.com/hermes-hq/hermes-ide/blob/main/docs/worktree-recipes.md) show how to list the checks. Whichever agent you run, [Claude Code](https://code.claude.com/docs/en/overview) or another, the prompt above is what you send back when the size check fails.\n\nThen read the change before it lands. The Review Desk (⌘G, Ctrl+Shift+G on Windows and Linux) shows everything the agent changed, by turn or by file, with risk flags. A new dependency shows up as a `package.json` and `package-lock.json` change in the turn that added it, and the `package.json` line shows exactly which package came in. If the agent quietly raised a limit instead of fixing the import, that is one line in the same diff. A line comment there goes back to the agent that wrote the line.\n\n*The Review Desk (⌘G) in By turn view: two turns listed, turn 1's diff of src/db.ts, the changed-files list, and a commit message drafted from the turns.*\n\nOnce the size check and the tests pass, Land commits, opens a pull request or merges locally in one step, into the branch the task started from, and it can be undone. CI still runs the same `npm run size` on the pull request.\n\nA size budget measures bytes. It won't tell you a dependency is unmaintained, has a bad license or pulls in a postinstall script; those need their own review, which is why reading the lockfile change matters as much as the number.\n\nIt also only measures what you point it at. If an agent adds a new entry point, it has no budget until you add one, so add \"new entry point means a new size-limit section\" to the same instructions file.\n\nHermes IDE is free to download, source-available under BSL 1.1, and runs on macOS, Windows and Linux. It isn't affiliated with Nous Research, Anthropic, OpenAI or any agent vendor. [Download it](https://hermes-ide.com/download), and if it helps you catch a heavy import before it lands, [star it on GitHub](https://github.com/hermes-hq/hermes-ide).\n\nA bundle budget is a small version of a bigger habit: decide what \"done\" means before work starts, and make a check enforce it. If your agents end up running as workers off a queue instead of in your terminal, the *Event-Driven Architecture Pocket Guide* covers the retries, idempotency and outbox patterns that keep that kind of system honest. It is one of 8 short books in the *Pocket Guides for Developers* series, on agents, RAG, prompts, observability, events, databases and system design: [the whole series is here](https://www.amazon.com/dp/B0GYLH38X4).", "url": "https://wpnews.pro/news/give-your-coding-agent-a-bundle-budget-size-limit-fails-the-branch-before-you-it", "canonical_source": "https://dev.to/gabrielanhaia/give-your-coding-agent-a-bundle-budget-size-limit-fails-the-branch-before-you-land-it-2hn5", "published_at": "2026-11-06 01:01:20+00:00", "updated_at": "2026-11-06 01:22:45.120546+00:00", "lang": "en", "topics": ["ai-agents", "developer-tools", "ai-tools"], "entities": ["Size Limit", "lodash", "lodash-es", "rolldown", "webpack", "Node.js"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/give-your-coding-agent-a-bundle-budget-size-limit-fails-the-branch-before-you-it", "markdown": "https://wpnews.pro/news/give-your-coding-agent-a-bundle-budget-size-limit-fails-the-branch-before-you-it.md", "text": "https://wpnews.pro/news/give-your-coding-agent-a-bundle-budget-size-limit-fails-the-branch-before-you-it.txt", "jsonld": "https://wpnews.pro/news/give-your-coding-agent-a-bundle-budget-size-limit-fails-the-branch-before-you-it.jsonld"}}