{
  "$type": "site.standard.document",
  "coverImage": {
    "$type": "blob",
    "ref": {
      "$link": "bafkreiewo6khpqy6jnuc6ruzckwblfqb4tkcjcm3l3anzgqd7su6bihshi"
    },
    "mimeType": "image/jpeg",
    "size": 288593
  },
  "description": "How I wrangle the many tools that we use to build Val Town",
  "path": "/gardening-dependencies",
  "publishedAt": "2025-09-11T00:00:00.000Z",
  "site": "at://did:plc:a2rdzfdxkjwerrfrpbwcipb2/site.standard.publication/3jd443afc2222",
  "textContent": "Val Town is a React application with a ton of dependencies. It's complicated,\nand we have to deal with dependency upgrades all the time. We are committing the\ncardinal sin of overcomplicating the web: our  directory is 863MB\nas of this writing. Whew!\n\n\n\nIs it, though? Are we installing dependencies freely, taking on technical debt\nleft and right? I'd say not really.\n\nThe thing is, there's some\nessential complexity in\nwhat we're trying to build. We aren't going to DIY our own TypeScript transpiler\nor avoid installing CodeMirror and use a textarea for\nour code editing. I spend a little time every week looking through\n and thinking which of these can I remove? Sometimes I find a\ndependency that can be yanked, but a lot of times I come up empty-handed: we\nactually need all this junk. My ability to judge other people fades as I learn\nthe hard way how principles make contact with reality.\n\nBut that's not to say that there's no art in dependency grooming. There are a\nbunch of techniques and tools that all fit together into a general sort of\ndependency hygiene that I've developed. I'm not sure I've written it down\nanywhere in full. Here's a shot.\n\nRead all new dependencies (except React)\n\nRule #1 is to read. This is very literal: read the source code of any dependency\nthat you're about to introduce into your project. And, of course, the README. I\nhighly recommend doing this the old fashioned way, with your eyes and brain, but\nLLMs can be helpful too if that's more your speed: but don't offload the whole\ntask to a robot. Actual understanding is the goal, and you can't achieve that\nsecondhand.\n\nFairly often you'll do this and discover that the new dependency you're adding\nis just 50 lines long, and is better off\nvendored rather than\ninstalled with NPM: just copy that code over and preserve its open source\nlicense in a code comment.\n\nOr you'll realize that the module is 2MB gzipped and introduces 3 new transitive\ndependencies, but you're only actually using 50 lines of it. Again, this is not\na good scenario: you're introducing a lot of surface area that's pure downside.\nIt'll take up more room in , or it could have security\nvulnerabilities in the parts that you aren't using and you'll have to triage\nthem just the same.\n\nI make an exception for React and other mega-dependencies: I've seen the inside\nof React's algorithms and TypeScript compiler code, and decided that I just need\nto trust the whizzes at those companies.\n\nIf you don't read, you won't succeed.\n\n and reading  is your friend\n\nOr, if you're using ,  and . And a similar\ncommand for whatever other package manager you're using. The reason is pretty\nsimple: your direct dependencies are inevitably the tip of the iceberg. What\nreally fills up  is all of the stuff they bring along, and that\nstuff - transitive dependencies - is extremely important.\n\nFor example, let's say your project needs to transpile TypeScript. It's fairly\nlikely that you already have a transpiler installed: in our project, we have\ncopies of esbuild that are dragged in by drizzle-kit,\nby Vite, and by tsx.\nSo using  as a direct dependency costs us nothing: it gets deduped to\nthe same  binary that everything was using under the hood before. This\nis a useful little game: when you're installing something for your application,\nif you can find a way to reuse something that's already installed transitively,\nyou can get a dependency for free!\n\nAnd read package-lock.json or pnpm-lock.yaml. It's not that bad, and you'll\nlearn something. There's a lot in there. It'll familiarize yourself with what\nmodules others rely on, which builds a little PageRank algorithm in your head so\nthat you can recall, off the bat, what you might want to use for something new.\nFeed your curiosity and open those  webpages.\n\nAnalyzing the actual size of packages is pretty nifty\n\nThere are two distinct impacts of big NPM modules: their contribution to your\ndistributed application, and how much room they take up in  while\nyou're developing the application. It's good to focus on application size first,\nbut both matter: an application that relies on 2 gigabytes of code in\n is going to be pretty slow to test in CI, and slower to deploy\nbecause it just requires more downloading.\n\n\n\nI use Grand Perspective, a vintage\napplication that's been around for 20 years, to keep track of  on\ndisk, but there are a lot of other\ndisk space analyzers\nworth considering if you're using Linux or Windows.\n\nAnalyzing the size of your distributed application is a much more complicated\nand system-specific. We're using React Router with Vite, so\nrollup-plugin-visualizer is\nthe solution, but it's different for every bundler and some frameworks have\ntheir own solution, like\nNext.js's bundle analyzer.\n\nWhat makes a good NPM module\n\nBut what are you really looking for? The definition of a good module keeps\nshifting, but pretty often it'll look something like: a decent history of\nmaintainership, built-in TypeScript types, passing tests, good documentation.\n\nA slang shorthand for this would be that it should have a vibe of competence.\nEven if you're building something by the seat of your pants and making lots of\nmistakes, you want the parts that you're using to be solid. An application's\nbugs are the sum total of the bugs you write and the bugs you inherit from\nothers, so it's actually kind of fair to have higher standards for code that you\ninstall than code that you write.\n\nWhat makes a bad module? Of course something that's abandoned and poorly written\nis bad, but even worse than that is a module that solves the wrong problem -\nsomething that doesn't actually fit the problem you have and instead you have to\nshift the problem to make it work. You can fix this by reading and spending a\nlittle time understanding both your problem and the solution. Or by asking the\nLLM, you little baby.\n\nGet rid of what you don't use and keep the rest up to date\n\n\n\nYou should be using Renovate. It'll nag you to\nkeep your modules up to date, and it's better to do that kind of work\nincrementally rather than in one yearly push.\n\nAnd you should be using Knip. It's just straight-up magic:\nit's so fast, extremely accurate. It'll tell you which modules you have\nspecified in  but that you aren't actually using. It's super easy\nto lose track and have a lot of junk lying around from old versions of a\nproject. Get rid of it with Knip! It'll even show you which files your project\nno longer uses. If there was a Knip t-shirt I'd be wearing it right now, that's\nhow good it is.\n\nHave a quick cheatsheet of people who write good modules\n\nThe NPM module ecosystem is made up of people. It's useful to know who those\npeople are! For example, when I'm looking for something related to Promises (or\nmany other topics), I'll check if\nSindre Sorhus\nhas already published something. Very often he has!\n\nOther folks who have lots of solid work are good to know, like\nisaacs,\nMatteo Collina,\nMafintosh.\n\nIf you're doing something with Markdown, you should know all the\nwooorm and unified\nrepos. Nextgen Node.js stuff? Check unjs. Transpiler\ninternals, you should always check\nRich Harris's\nprojects, which include so many gems.\n\nTune in for a follow-up post that just includes some starting points for\ndependencies that you can even put in an AGENTS.md file if you want to.\n\nDependencies are an inevitability\n\nThat's the truth: we're all building on the shoulders of each other. But there's\nan art to finding the right shoulders to build on.\n\nIn a way, it's frustrating that the web platform, and the NPM module ecosystem,\nmoves so fast and requires so many nitpicky updates and decisions. But that's\npretty much the norm: even next-generation languages that have learned the\nlessons of NPM and Node suffer from bloated package ecosystems. Gardening\ndependencies is part of the job and we should do it well.",
  "title": "How to keep package.json under control"
}