{"course":{"slug":"building-apps-with-ai","title":"Building Apps with AI","url":"https://addaly.com/learn/building-apps-with-ai","level":"beginner","track":"build"},"lesson":{"slug":"dependency-trouble","title":"Dependency trouble, and the lock file","url":"https://addaly.com/learn/building-apps-with-ai/dependency-trouble","position":32,"of":87,"minutes":8,"answer":"Commit the lock file and reach for npm ci before deleting it, because removing the lock to fix a problem silently changes several hundred versions at once.","keyIdea":"Commit the lock file and reach for npm ci before deleting it, because removing the lock to fix a problem silently changes several hundred versions at once."},"body":{"format":"markdown","text":"## Your app is mostly other people's code\n\nA plain Next.js project installs several hundred packages. You chose four of them; the rest arrived as dependencies of dependencies. This is normal, it is how modern software is built, and it is also where a category of failure lives that has nothing to do with your code.\n\n## The three files\n\n**`package.json`** lists what you asked for, with ranges: `\"next\": \"^15.1.0\"` means 15.1.0 or any later 15.x.\n\n**`package-lock.json`** (or `pnpm-lock.yaml`, or `yarn.lock`) records exactly which versions were actually installed, all several hundred of them. **Commit this file.** Without it, your machine and your server can install different versions of the same project and behave differently, which produces the most maddening class of bug in this course.\n\n**`node_modules`** is the downloaded result. Never committed, never edited, deletable at any time.\n\n`npm install` respects the lock file. `npm update` deliberately ignores it and moves versions forward. Knowing which of those you ran is often the answer to why things changed.\n\n## Reading a version number\n\n`15.1.3` is major.minor.patch, and the convention says a major bump may break your code deliberately, a minor adds things, a patch fixes things.\n\nThe prefix in `package.json` decides how much movement is allowed. `^15.1.0` accepts any 15.x. `~15.1.0` accepts 15.1.x only. `15.1.0` with no prefix accepts exactly that.\n\nThe convention is widely followed and not guaranteed. Plenty of minor releases have broken something for somebody. This is why the lock file exists.\n\n## The failures and what they actually mean\n\n**`Module not found`** after pulling somebody's changes. They added a dependency; you have not installed it. `npm install`.\n\n**Two versions of the same library.** Two packages each depend on a different version of a third. Symptoms are strange rather than obviously wrong: a React app where hooks throw about invalid calls is the classic. `npm ls <package>` shows you the tree.\n\n**Peer dependency warnings.** A package wants a version of something else that you do not have. Sometimes fine, sometimes the actual cause of your problem. Do not reflexively reach for `--force` or `--legacy-peer-deps`; they silence the complaint and keep the mismatch.\n\n**It broke and I changed nothing.** An unpinned range let a new version in on a fresh install. This is exactly what the lock file prevents, which is why deleting it to fix a problem is nearly always the wrong move.\n\n## The reinstall ritual\n\n```bash\nrm -rf node_modules package-lock.json\nnpm install\n```\n\nThis fixes corrupted installs. It also throws away your record of known-good versions and installs whatever is newest within your ranges, which can introduce a different problem than the one you had.\n\nSo try the smaller version first: `rm -rf node_modules && npm ci`. `npm ci` installs exactly what the lock file says, nothing newer. If that fixes it, the install was corrupt. If it does not, the lock file was not the problem and deleting it would have taught you nothing while changing several hundred versions.\n\n## Every dependency is a decision\n\nAn agent asked to add date formatting may install a library rather than write four lines. Each package added is code you did not read, running in your app, with its own dependencies and its own security history.\n\nThis is the reason for the standing rule in your rules file: **no new dependencies without asking.** When one is proposed, three questions are enough. Is it maintained: when was the last release? How many packages does it drag in? Could this be ten lines instead?\n\nSometimes the answer is clearly yes, install it. Date and time handling, authentication, anything cryptographic: write those yourself and you will get them wrong. The judgement is about whether the problem is hard.\n\n## Updating on purpose\n\n```bash\nnpm outdated       # what has moved on\nnpm audit          # known vulnerabilities in what you have\n```\n\nUpdate deliberately, one significant package at a time, with a commit before and a check after. Not the day before a launch, and never all at once: a hundred packages updated together means an unexplained failure has a hundred suspects.\n\n`npm audit` deserves a note of honesty: it over-reports. Many of its warnings are for build-time tooling that never runs in production, or for attack paths that do not exist in your app. Read what the vulnerability actually is rather than chasing the number to zero."},"check":{"question":"An app that worked last week fails on a colleague's fresh install, with the same code. What is the most likely cause?","options":[{"text":"Their editor is configured differently.","correct":false,"tempting":"Editor settings affect authoring rather than what gets installed and executed."},{"text":"The lock file is not committed, so their install resolved newer versions within the allowed ranges.","correct":true},{"text":"They are missing an environment variable.","correct":false,"tempting":"A genuine cause of first-run failures, but it produces configuration errors rather than behaviour that differs from identical code."},{"text":"Their node_modules folder is corrupt.","correct":false,"tempting":"Describes a fresh install, which is precisely the case where corruption has had no chance to accumulate."}]},"prev":{"slug":"finding-the-change-that-broke-it","title":"Finding the change that broke it","url":"https://addaly.com/learn/building-apps-with-ai/finding-the-change-that-broke-it"},"next":{"slug":"it-builds-here-but-not-there","title":"It works here and not there","url":"https://addaly.com/learn/building-apps-with-ai/it-builds-here-but-not-there"},"authorship":"Founder and series editor: Dr Mustafa Mun. Written by AI models to a written standard. Exam keys checked blind by a second model: 3,665 of 3,666 agreed. Academic review invited.","review":{"status":"Draft, open for academic review"},"cite":"Addaly (2026) 'Dependency trouble, and the lock file', in Building Apps with AI. Edited by M. Mun. Addaly. Available at: https://addaly.com/learn/building-apps-with-ai/dependency-trouble (Accessed: 13 October 2026).","licence":{"name":"CC BY-NC-SA 4.0","url":"https://creativecommons.org/licenses/by-nc-sa/4.0/"},"reader":{"schema":1,"title":"Dependency trouble, and the lock file","courseTitle":"Building Apps with AI","module":{"index":4,"slug":"when-it-breaks","title":"When it breaks, and getting back"},"answer":null,"keyIdea":"Commit the lock file and reach for npm ci before deleting it, because removing the lock to fix a problem silently changes several hundred versions at once.","hero":{"kind":"plate","image":null,"alt":null,"caption":null},"blocks":[{"t":"h2","id":"your-app-is-mostly-other-people-s-code","runs":[{"text":"Your app is mostly other people’s code"}]},{"t":"p","runs":[{"text":"A plain Next.js project installs several hundred packages. You chose four of them; the rest arrived as dependencies of dependencies. This is normal, it is how modern software is built, and it is also where a category of failure lives that has nothing to do with your code."}]},{"t":"h2","id":"the-three-files","runs":[{"text":"The three files"}]},{"t":"p","runs":[{"text":"package.json","b":true,"code":true},{"text":" lists what you asked for, with ranges: "},{"text":"\"next\": \"^15.1.0\"","code":true},{"text":" means 15.1.0 or any later 15.x."}]},{"t":"p","runs":[{"text":"package-lock.json","b":true,"code":true},{"text":" (or "},{"text":"pnpm-lock.yaml","code":true},{"text":", or "},{"text":"yarn.lock","code":true},{"text":") records exactly which versions were actually installed, all several hundred of them. "},{"text":"Commit this file.","b":true},{"text":" Without it, your machine and your server can install different versions of the same project and behave differently, which produces the most maddening class of bug in this course."}]},{"t":"p","runs":[{"text":"node_modules","b":true,"code":true},{"text":" is the downloaded result. Never committed, never edited, deletable at any time."}]},{"t":"p","runs":[{"text":"npm install","code":true},{"text":" respects the lock file. "},{"text":"npm update","code":true},{"text":" deliberately ignores it and moves versions forward. Knowing which of those you ran is often the answer to why things changed."}]},{"t":"h2","id":"reading-a-version-number","runs":[{"text":"Reading a version number"}]},{"t":"p","runs":[{"text":"15.1.3","code":true},{"text":" is major.minor.patch, and the convention says a major bump may break your code deliberately, a minor adds things, a patch fixes things."}]},{"t":"p","runs":[{"text":"The prefix in "},{"text":"package.json","code":true},{"text":" decides how much movement is allowed. "},{"text":"^15.1.0","code":true},{"text":" accepts any 15.x. "},{"text":"~15.1.0","code":true},{"text":" accepts 15.1.x only. "},{"text":"15.1.0","code":true},{"text":" with no prefix accepts exactly that."}]},{"t":"p","runs":[{"text":"The convention is widely followed and not guaranteed. Plenty of minor releases have broken something for somebody. This is why the lock file exists."}]},{"t":"h2","id":"the-failures-and-what-they-actually-mean","runs":[{"text":"The failures and what they actually mean"}]},{"t":"p","runs":[{"text":"Module not found","b":true,"code":true},{"text":" after pulling somebody’s changes. They added a dependency; you have not installed it. "},{"text":"npm install","code":true},{"text":"."}]},{"t":"p","runs":[{"text":"Two versions of the same library.","b":true},{"text":" Two packages each depend on a different version of a third. Symptoms are strange rather than obviously wrong: a React app where hooks throw about invalid calls is the classic. "},{"text":"npm ls <package>","code":true},{"text":" shows you the tree."}]},{"t":"p","runs":[{"text":"Peer dependency warnings.","b":true},{"text":" A package wants a version of something else that you do not have. Sometimes fine, sometimes the actual cause of your problem. Do not reflexively reach for "},{"text":"--force","code":true},{"text":" or "},{"text":"--legacy-peer-deps","code":true},{"text":"; they silence the complaint and keep the mismatch."}]},{"t":"p","runs":[{"text":"It broke and I changed nothing.","b":true},{"text":" An unpinned range let a new version in on a fresh install. This is exactly what the lock file prevents, which is why deleting it to fix a problem is nearly always the wrong move."}]},{"t":"h2","id":"the-reinstall-ritual","runs":[{"text":"The reinstall ritual"}]},{"t":"code","lang":"bash","label":"Bash","text":"rm -rf node_modules package-lock.json\nnpm install"},{"t":"p","runs":[{"text":"This fixes corrupted installs. It also throws away your record of known-good versions and installs whatever is newest within your ranges, which can introduce a different problem than the one you had."}]},{"t":"p","runs":[{"text":"So try the smaller version first: "},{"text":"rm -rf node_modules && npm ci","code":true},{"text":". "},{"text":"npm ci","code":true},{"text":" installs exactly what the lock file says, nothing newer. If that fixes it, the install was corrupt. If it does not, the lock file was not the problem and deleting it would have taught you nothing while changing several hundred versions."}]},{"t":"h2","id":"every-dependency-is-a-decision","runs":[{"text":"Every dependency is a decision"}]},{"t":"p","runs":[{"text":"An agent asked to add date formatting may install a library rather than write four lines. Each package added is code you did not read, running in your app, with its own dependencies and its own security history."}]},{"t":"p","runs":[{"text":"This is the reason for the standing rule in your rules file: "},{"text":"no new dependencies without asking.","b":true},{"text":" When one is proposed, three questions are enough. Is it maintained: when was the last release? How many packages does it drag in? Could this be ten lines instead?"}]},{"t":"p","runs":[{"text":"Sometimes the answer is clearly yes, install it. Date and time handling, authentication, anything cryptographic: write those yourself and you will get them wrong. The judgement is about whether the problem is hard."}]},{"t":"h2","id":"updating-on-purpose","runs":[{"text":"Updating on purpose"}]},{"t":"code","lang":"bash","label":"Bash","text":"npm outdated       # what has moved on\nnpm audit          # known vulnerabilities in what you have"},{"t":"p","runs":[{"text":"Update deliberately, one significant package at a time, with a commit before and a check after. Not the day before a launch, and never all at once: a hundred packages updated together means an unexplained failure has a hundred suspects."}]},{"t":"p","runs":[{"text":"npm audit","code":true},{"text":" deserves a note of honesty: it over-reports. Many of its warnings are for build-time tooling that never runs in production, or for attack paths that do not exist in your app. Read what the vulnerability actually is rather than chasing the number to zero."}]}],"check":{"question":[{"text":"An app that worked last week fails on a colleague’s fresh install, with the same code. What is the most likely cause?"}],"options":[{"text":[{"text":"Their editor is configured differently."}],"correct":false,"tempting":[{"text":"Editor settings affect authoring rather than what gets installed and executed."}]},{"text":[{"text":"The lock file is not committed, so their install resolved newer versions within the allowed ranges."}],"correct":true,"tempting":null},{"text":[{"text":"They are missing an environment variable."}],"correct":false,"tempting":[{"text":"A genuine cause of first-run failures, but it produces configuration errors rather than behaviour that differs from identical code."}]},{"text":[{"text":"Their node_modules folder is corrupt."}],"correct":false,"tempting":[{"text":"Describes a fresh install, which is precisely the case where corruption has had no chance to accumulate."}]}],"order":[2,1,0,3],"why":null},"prev":{"slug":"finding-the-change-that-broke-it","title":"Finding the change that broke it","minutes":8},"next":{"slug":"it-builds-here-but-not-there","title":"It works here and not there","minutes":8}}}