{"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":"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","position":33,"of":87,"minutes":8,"answer":"Filename case, missing environment variables and the gap between the dev server and a real build cause most first deployments to fail.","keyIdea":"Filename case, missing environment variables and the gap between the dev server and a real build cause most first deployments to fail; running npm run build and npm run start locally catches nearly all of it."},"body":{"format":"markdown","text":"## Two computers, six differences\n\nThe dev server on your laptop and the build running on your host are not the same program. Six differences account for nearly every first deployment failure, and all six are checkable in ten minutes.\n\n## 1. Case sensitivity\n\nMac and Windows treat `BookingCard.tsx` and `bookingcard.tsx` as the same file. Linux, which your host runs, does not. An import with the wrong capitalisation works perfectly on your machine and fails on the server with `Module not found`.\n\nThis is the single most common one, it is invisible locally, and it is why the error message can look absurd: the file is right there.\n\n```bash\ngit ls-files | grep -i bookingcard   # what git actually stored\n```\n\nGit can also hold the wrong case even after you rename the file, because it did not notice a change that your filesystem considers meaningless. `git mv -f oldname newname` forces it.\n\n## 2. Environment variables\n\nYour `.env` file never left your laptop, deliberately: it is in `.gitignore`. Every value in it must be entered again in your host's settings panel, and one missing entry produces a failure that reads like a code bug.\n\nKeep a committed `.env.example` listing every key with fake values. It is the checklist for a new environment, and it is the file that makes a fresh setup take five minutes instead of an afternoon of guessing.\n\n## 3. Dev server versus build\n\nThe dev server compiles on demand, tolerates type errors, prints warnings and keeps going. The production build compiles everything ahead of time and stops on anything it does not like.\n\nSo run the build locally before you push:\n\n```bash\nnpm run build\n```\n\nThat one command catches most deployment failures on your own machine, in thirty seconds, where the feedback loop is fast. Put it in your rules file as something the agent runs before claiming a change is finished, and you will almost stop meeting this category.\n\n## 4. Where the code runs\n\nA component that reads `window` or `localStorage` works in the browser and crashes during a server render, because on the server there is no window. The error mentions `window is not defined` and the fix is either to guard it or to mark the component as client-only.\n\nThe dev server sometimes hides this by rendering differently. The build does not.\n\n## 5. The database is not the same database\n\nThe tables you created locally do not exist on the new one. Your migrations have to be run against production, as a deliberate step, and forgetting it produces confident errors about missing columns.\n\nAlso: your production database is empty of the test data you have been relying on without noticing. A page that assumed at least one row will meet zero for the first time.\n\n## 6. Versions and dependency types\n\nYour host may default to a different Node version. Pin it: an `engines` field in `package.json`, or a `.nvmrc` file, or a setting in the host's dashboard.\n\nAnd a package installed with `--save-dev` that the running app actually needs will not be present in production, because dev dependencies are skipped during a production install.\n\n## The pre-deploy check, in order\n\n```bash\ngit status              # nothing uncommitted that matters\nnpm ci                  # exactly the locked versions\nnpm run build           # the real build\nnpm run start           # run the built version, not the dev server\n```\n\nThat fourth line is the one nobody does, and it is the closest thing to production you can run locally. Open the app and click through it. Several classes of bug appear only here.\n\n## When it fails only in production\n\nSometimes the build passes locally and the deployed app is still wrong. Then the difference is environment, not code, and there are exactly four places to look: the environment variables in the host's panel, the build log, the runtime log, and whether migrations ran.\n\nRead the build log from the bottom. Hosts print a great deal of progress output and one failure line, and it is not usually at the end."},"check":{"question":"A deploy fails with `Module not found: ./components/bookingCard` while the file is clearly present and the app runs locally. What is happening?","options":[{"text":"The build cache is stale and needs clearing.","correct":false,"tempting":"A common remedy for odd build states that does nothing when the import genuinely does not resolve on a case-sensitive filesystem."},{"text":"The component was added to .gitignore by mistake.","correct":false,"tempting":"Would produce the same message, and is worth ruling out with git ls-files, but is far rarer than the case mismatch."},{"text":"The import's capitalisation differs from the stored filename, which only matters on the Linux server.","correct":true},{"text":"The file is a dev dependency and was skipped during the production install.","correct":false,"tempting":"Confuses your own source files with installed packages; dependency pruning never removes your components."}]},"prev":{"slug":"dependency-trouble","title":"Dependency trouble, and the lock file","url":"https://addaly.com/learn/building-apps-with-ai/dependency-trouble"},"next":{"slug":"was-it-actually-fixed","title":"Was it actually fixed, or just silenced","url":"https://addaly.com/learn/building-apps-with-ai/was-it-actually-fixed"},"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) 'It works here and not there', in Building Apps with AI. Edited by M. Mun. Addaly. Available at: https://addaly.com/learn/building-apps-with-ai/it-builds-here-but-not-there (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":"It works here and not there","courseTitle":"Building Apps with AI","module":{"index":4,"slug":"when-it-breaks","title":"When it breaks, and getting back"},"answer":null,"keyIdea":"Filename case, missing environment variables and the gap between the dev server and a real build cause most first deployments to fail; running npm run build and npm run start locally catches nearly all of it.","hero":{"kind":"plate","image":null,"alt":null,"caption":null},"blocks":[{"t":"h2","id":"two-computers-six-differences","runs":[{"text":"Two computers, six differences"}]},{"t":"p","runs":[{"text":"The dev server on your laptop and the build running on your host are not the same program. Six differences account for nearly every first deployment failure, and all six are checkable in ten minutes."}]},{"t":"h2","id":"1-case-sensitivity","runs":[{"text":"1. Case sensitivity"}]},{"t":"p","runs":[{"text":"Mac and Windows treat "},{"text":"BookingCard.tsx","code":true},{"text":" and "},{"text":"bookingcard.tsx","code":true},{"text":" as the same file. Linux, which your host runs, does not. An import with the wrong capitalisation works perfectly on your machine and fails on the server with "},{"text":"Module not found","code":true},{"text":"."}]},{"t":"p","runs":[{"text":"This is the single most common one, it is invisible locally, and it is why the error message can look absurd: the file is right there."}]},{"t":"code","lang":"bash","label":"Bash","text":"git ls-files | grep -i bookingcard   # what git actually stored"},{"t":"p","runs":[{"text":"Git can also hold the wrong case even after you rename the file, because it did not notice a change that your filesystem considers meaningless. "},{"text":"git mv -f oldname newname","code":true},{"text":" forces it."}]},{"t":"h2","id":"2-environment-variables","runs":[{"text":"2. Environment variables"}]},{"t":"p","runs":[{"text":"Your "},{"text":".env","code":true},{"text":" file never left your laptop, deliberately: it is in "},{"text":".gitignore","code":true},{"text":". Every value in it must be entered again in your host’s settings panel, and one missing entry produces a failure that reads like a code bug."}]},{"t":"p","runs":[{"text":"Keep a committed "},{"text":".env.example","code":true},{"text":" listing every key with fake values. It is the checklist for a new environment, and it is the file that makes a fresh setup take five minutes instead of an afternoon of guessing."}]},{"t":"h2","id":"3-dev-server-versus-build","runs":[{"text":"3. Dev server versus build"}]},{"t":"p","runs":[{"text":"The dev server compiles on demand, tolerates type errors, prints warnings and keeps going. The production build compiles everything ahead of time and stops on anything it does not like."}]},{"t":"p","runs":[{"text":"So run the build locally before you push:"}]},{"t":"code","lang":"bash","label":"Bash","text":"npm run build"},{"t":"p","runs":[{"text":"That one command catches most deployment failures on your own machine, in thirty seconds, where the feedback loop is fast. Put it in your rules file as something the agent runs before claiming a change is finished, and you will almost stop meeting this category."}]},{"t":"h2","id":"4-where-the-code-runs","runs":[{"text":"4. Where the code runs"}]},{"t":"p","runs":[{"text":"A component that reads "},{"text":"window","code":true},{"text":" or "},{"text":"localStorage","code":true},{"text":" works in the browser and crashes during a server render, because on the server there is no window. The error mentions "},{"text":"window is not defined","code":true},{"text":" and the fix is either to guard it or to mark the component as client-only."}]},{"t":"p","runs":[{"text":"The dev server sometimes hides this by rendering differently. The build does not."}]},{"t":"h2","id":"5-the-database-is-not-the-same-database","runs":[{"text":"5. The database is not the same database"}]},{"t":"p","runs":[{"text":"The tables you created locally do not exist on the new one. Your migrations have to be run against production, as a deliberate step, and forgetting it produces confident errors about missing columns."}]},{"t":"p","runs":[{"text":"Also: your production database is empty of the test data you have been relying on without noticing. A page that assumed at least one row will meet zero for the first time."}]},{"t":"h2","id":"6-versions-and-dependency-types","runs":[{"text":"6. Versions and dependency types"}]},{"t":"p","runs":[{"text":"Your host may default to a different Node version. Pin it: an "},{"text":"engines","code":true},{"text":" field in "},{"text":"package.json","code":true},{"text":", or a "},{"text":".nvmrc","code":true},{"text":" file, or a setting in the host’s dashboard."}]},{"t":"p","runs":[{"text":"And a package installed with "},{"text":"--save-dev","code":true},{"text":" that the running app actually needs will not be present in production, because dev dependencies are skipped during a production install."}]},{"t":"h2","id":"the-pre-deploy-check-in-order","runs":[{"text":"The pre-deploy check, in order"}]},{"t":"code","lang":"bash","label":"Bash","text":"git status              # nothing uncommitted that matters\nnpm ci                  # exactly the locked versions\nnpm run build           # the real build\nnpm run start           # run the built version, not the dev server"},{"t":"p","runs":[{"text":"That fourth line is the one nobody does, and it is the closest thing to production you can run locally. Open the app and click through it. Several classes of bug appear only here."}]},{"t":"h2","id":"when-it-fails-only-in-production","runs":[{"text":"When it fails only in production"}]},{"t":"p","runs":[{"text":"Sometimes the build passes locally and the deployed app is still wrong. Then the difference is environment, not code, and there are exactly four places to look: the environment variables in the host’s panel, the build log, the runtime log, and whether migrations ran."}]},{"t":"p","runs":[{"text":"Read the build log from the bottom. Hosts print a great deal of progress output and one failure line, and it is not usually at the end."}]}],"check":{"question":[{"text":"A deploy fails with "},{"text":"Module not found: ./components/bookingCard","code":true},{"text":" while the file is clearly present and the app runs locally. What is happening?"}],"options":[{"text":[{"text":"The build cache is stale and needs clearing."}],"correct":false,"tempting":[{"text":"A common remedy for odd build states that does nothing when the import genuinely does not resolve on a case-sensitive filesystem."}]},{"text":[{"text":"The component was added to .gitignore by mistake."}],"correct":false,"tempting":[{"text":"Would produce the same message, and is worth ruling out with git ls-files, but is far rarer than the case mismatch."}]},{"text":[{"text":"The import’s capitalisation differs from the stored filename, which only matters on the Linux server."}],"correct":true,"tempting":null},{"text":[{"text":"The file is a dev dependency and was skipped during the production install."}],"correct":false,"tempting":[{"text":"Confuses your own source files with installed packages; dependency pruning never removes your components."}]}],"order":[3,0,1,2],"why":null},"prev":{"slug":"dependency-trouble","title":"Dependency trouble, and the lock file","minutes":8},"next":{"slug":"was-it-actually-fixed","title":"Was it actually fixed, or just silenced","minutes":8}}}