{"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":"the-shape-of-a-project","title":"The shape of a project you did not lay out","url":"https://addaly.com/learn/building-apps-with-ai/the-shape-of-a-project","position":21,"of":87,"minutes":8,"answer":"Package.json, the entry point and the routes folder map any project; keep components free of database access as a folder rule so you can check it by looking.","keyIdea":"Package.json, the entry point and the routes folder map any project; keep components free of database access as a folder rule so you can check it by looking."},"body":{"format":"markdown","text":"## Find the three landmarks\n\nOpen an unfamiliar project and you see thirty folders. Three of them tell you almost everything.\n\n**`package.json`**: the project's identity card. The `scripts` section tells you how to run it. The `dependencies` section tells you what it is built from. Reading those two lists takes a minute and tells you more than an hour of clicking through folders.\n\n**The entry point.** Where execution begins. `app/layout.tsx` and `app/page.tsx` in a Next.js app router project. `src/main.tsx` in a Vite app. `manage.py` and `urls.py` in Django. `app.py` in Flask.\n\n**The routes folder.** Where URLs map to code. In Next.js the folder structure *is* the URL: `app/bookings/[id]/page.tsx` serves `/bookings/71`. In Express or Flask it is a list of paths in one file. Once you can go from a URL to a file, you can navigate anything.\n\n## The folders you can ignore\n\n**`node_modules`**: downloaded libraries. Hundreds of megabytes, thousands of files, none of them yours. Never edit anything in here; it is deleted and recreated by `npm install`.\n\n**`.next`, `dist`, `build`, `.venv`, `__pycache__`**: generated output. Also never edited by hand.\n\n**`public`** or **`static`**: files served exactly as they are. Images, fonts, `favicon.ico`. Anything here is public to the whole internet, which is worth remembering before you put a spreadsheet in it.\n\nWhen an agent reports that it edited something in `node_modules` or `.next`, that change will vanish on the next install or build. Say so, and ask for the real fix.\n\n## The files that decide behaviour\n\n- **`.env` / `.env.local`**: secrets and configuration. Never committed. If you can see this file in a public repository, you have an incident, not a question.\n- **`.gitignore`**: what git deliberately ignores. `.env` and `node_modules` must be in it.\n- **`package-lock.json`** / `pnpm-lock.yaml` / `requirements.txt`: exact versions. Committed, never hand-edited.\n- **`tsconfig.json`**, **`next.config.js`**, **`tailwind.config.js`**: build and framework settings. Small files with large effects.\n- **`drizzle/` or `migrations/` or `prisma/schema.prisma`**: the database's shape and history.\n\n## A layout worth imposing\n\nGenerated projects drift into disorder because each request is answered locally. Deciding the shape yourself, once, and writing it in the rules file is what keeps it navigable:\n\n```\napp/            pages and API routes only\ncomponents/     things that draw. No database access, ever.\nlib/            logic. queries.ts, dates.ts, money.ts, slots.ts\ndrizzle/        schema and migrations\nscripts/        one-off jobs you run by hand\n```\n\nThe second line is the one that prevents an entire failure class. A component that runs in the browser must not import anything that talks to the database: do it and you either leak server code into the browser bundle or break the build in a way that is very hard to read. Making it a folder rule means you can check it by looking rather than by reasoning.\n\n## Two commands that map anything\n\n```bash\n# The structure, ignoring the noise\nfind . -type d -not -path '*/node_modules/*' -not -path '*/.git/*' | head -40\n\n# Where is this concept mentioned?\ngrep -rn \"overlap\" --include=\"*.ts\" --include=\"*.tsx\" . | grep -v node_modules\n```\n\nThe second is how you find a rule when you do not know where it lives. Search for the domain word (`overlap`, `refund`, `cancel`) rather than for a function name you would have to already know.\n\nIf `grep` returns three files for one rule, that is a finding in itself: the rule exists in three places and they may not agree.\n\n## The README is worth ten minutes\n\nMost generated projects ship a README describing a framework rather than your app, and everybody ignores it forever. Replace it once with five lines: what this app is for, how to run it, what has to be in `.env`, how to reset the database, and where the deployed version lives.\n\nYou are writing it for yourself in four months, when you have forgotten which command starts the thing. That is not a hypothetical reader. It is the most likely one, and the ten minutes will feel like a gift.\n\n## Ask for the map\n\n```\nGive me a one-line description of every folder in this project,\nand name the single file where a booking is written to the database.\n```\n\nAn agent that has read the project answers this in seconds. If the answer names a file that does not exist, you have learned something important about how much it currently knows, and the right response is to point at the real file rather than to argue."},"check":{"question":"An agent says it fixed a bug by editing a file inside node_modules. What is the problem?","options":[{"text":"Editing a dependency violates its licence.","correct":false,"tempting":"Raises a legal question that mostly does not apply to local edits, and misses the practical failure entirely."},{"text":"The change will be discarded the next time dependencies are installed, so the fix does not exist on any other machine.","correct":true},{"text":"Dependencies are minified, so the edit will be unreadable later.","correct":false,"tempting":"True of some builds and irrelevant; readability is not why the change fails to persist."},{"text":"It will make the repository much larger when committed.","correct":false,"tempting":"Assumes node_modules is committed, which .gitignore prevents in every normal setup."}]},"prev":{"slug":"reading-a-diff-properly","title":"Reading a diff properly","url":"https://addaly.com/learn/building-apps-with-ai/reading-a-diff-properly"},"next":{"slug":"enough-html-and-css-to-audit","title":"Enough HTML and CSS to audit the screen","url":"https://addaly.com/learn/building-apps-with-ai/enough-html-and-css-to-audit"},"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) 'The shape of a project you did not lay out', in Building Apps with AI. Edited by M. Mun. Addaly. Available at: https://addaly.com/learn/building-apps-with-ai/the-shape-of-a-project (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":"The shape of a project you did not lay out","courseTitle":"Building Apps with AI","module":{"index":3,"slug":"reading-and-checking","title":"Reading and checking what came back"},"answer":null,"keyIdea":"Package.json, the entry point and the routes folder map any project; keep components free of database access as a folder rule so you can check it by looking.","hero":{"kind":"plate","image":null,"alt":null,"caption":null},"blocks":[{"t":"h2","id":"find-the-three-landmarks","runs":[{"text":"Find the three landmarks"}]},{"t":"p","runs":[{"text":"Open an unfamiliar project and you see thirty folders. Three of them tell you almost everything."}]},{"t":"p","runs":[{"text":"package.json","b":true,"code":true},{"text":": the project’s identity card. The "},{"text":"scripts","code":true},{"text":" section tells you how to run it. The "},{"text":"dependencies","code":true},{"text":" section tells you what it is built from. Reading those two lists takes a minute and tells you more than an hour of clicking through folders."}]},{"t":"p","runs":[{"text":"The entry point.","b":true},{"text":" Where execution begins. "},{"text":"app/layout.tsx","code":true},{"text":" and "},{"text":"app/page.tsx","code":true},{"text":" in a Next.js app router project. "},{"text":"src/main.tsx","code":true},{"text":" in a Vite app. "},{"text":"manage.py","code":true},{"text":" and "},{"text":"urls.py","code":true},{"text":" in Django. "},{"text":"app.py","code":true},{"text":" in Flask."}]},{"t":"p","runs":[{"text":"The routes folder.","b":true},{"text":" Where URLs map to code. In Next.js the folder structure "},{"text":"is","i":true},{"text":" the URL: "},{"text":"app/bookings/[id]/page.tsx","code":true},{"text":" serves "},{"text":"/bookings/71","code":true},{"text":". In Express or Flask it is a list of paths in one file. Once you can go from a URL to a file, you can navigate anything."}]},{"t":"h2","id":"the-folders-you-can-ignore","runs":[{"text":"The folders you can ignore"}]},{"t":"p","runs":[{"text":"node_modules","b":true,"code":true},{"text":": downloaded libraries. Hundreds of megabytes, thousands of files, none of them yours. Never edit anything in here; it is deleted and recreated by "},{"text":"npm install","code":true},{"text":"."}]},{"t":"p","runs":[{"text":".next","b":true,"code":true},{"text":", ","b":true},{"text":"dist","b":true,"code":true},{"text":", ","b":true},{"text":"build","b":true,"code":true},{"text":", ","b":true},{"text":".venv","b":true,"code":true},{"text":", ","b":true},{"text":"__pycache__","b":true,"code":true},{"text":": generated output. Also never edited by hand."}]},{"t":"p","runs":[{"text":"public","b":true,"code":true},{"text":" or "},{"text":"static","b":true,"code":true},{"text":": files served exactly as they are. Images, fonts, "},{"text":"favicon.ico","code":true},{"text":". Anything here is public to the whole internet, which is worth remembering before you put a spreadsheet in it."}]},{"t":"p","runs":[{"text":"When an agent reports that it edited something in "},{"text":"node_modules","code":true},{"text":" or "},{"text":".next","code":true},{"text":", that change will vanish on the next install or build. Say so, and ask for the real fix."}]},{"t":"h2","id":"the-files-that-decide-behaviour","runs":[{"text":"The files that decide behaviour"}]},{"t":"ul","items":[[{"text":".env","b":true,"code":true},{"text":" / ","b":true},{"text":".env.local","b":true,"code":true},{"text":": secrets and configuration. Never committed. If you can see this file in a public repository, you have an incident, not a question."}],[{"text":".gitignore","b":true,"code":true},{"text":": what git deliberately ignores. "},{"text":".env","code":true},{"text":" and "},{"text":"node_modules","code":true},{"text":" must be in it."}],[{"text":"package-lock.json","b":true,"code":true},{"text":" / "},{"text":"pnpm-lock.yaml","code":true},{"text":" / "},{"text":"requirements.txt","code":true},{"text":": exact versions. Committed, never hand-edited."}],[{"text":"tsconfig.json","b":true,"code":true},{"text":", "},{"text":"next.config.js","b":true,"code":true},{"text":", "},{"text":"tailwind.config.js","b":true,"code":true},{"text":": build and framework settings. Small files with large effects."}],[{"text":"drizzle/","b":true,"code":true},{"text":" or ","b":true},{"text":"migrations/","b":true,"code":true},{"text":" or ","b":true},{"text":"prisma/schema.prisma","b":true,"code":true},{"text":": the database’s shape and history."}]]},{"t":"h2","id":"a-layout-worth-imposing","runs":[{"text":"A layout worth imposing"}]},{"t":"p","runs":[{"text":"Generated projects drift into disorder because each request is answered locally. Deciding the shape yourself, once, and writing it in the rules file is what keeps it navigable:"}]},{"t":"code","lang":"","label":"Text","text":"app/            pages and API routes only\ncomponents/     things that draw. No database access, ever.\nlib/            logic. queries.ts, dates.ts, money.ts, slots.ts\ndrizzle/        schema and migrations\nscripts/        one-off jobs you run by hand"},{"t":"p","runs":[{"text":"The second line is the one that prevents an entire failure class. A component that runs in the browser must not import anything that talks to the database: do it and you either leak server code into the browser bundle or break the build in a way that is very hard to read. Making it a folder rule means you can check it by looking rather than by reasoning."}]},{"t":"h2","id":"two-commands-that-map-anything","runs":[{"text":"Two commands that map anything"}]},{"t":"code","lang":"bash","label":"Bash","text":"# The structure, ignoring the noise\nfind . -type d -not -path '*/node_modules/*' -not -path '*/.git/*' | head -40\n\n# Where is this concept mentioned?\ngrep -rn \"overlap\" --include=\"*.ts\" --include=\"*.tsx\" . | grep -v node_modules"},{"t":"p","runs":[{"text":"The second is how you find a rule when you do not know where it lives. Search for the domain word ("},{"text":"overlap","code":true},{"text":", "},{"text":"refund","code":true},{"text":", "},{"text":"cancel","code":true},{"text":") rather than for a function name you would have to already know."}]},{"t":"p","runs":[{"text":"If "},{"text":"grep","code":true},{"text":" returns three files for one rule, that is a finding in itself: the rule exists in three places and they may not agree."}]},{"t":"h2","id":"the-readme-is-worth-ten-minutes","runs":[{"text":"The README is worth ten minutes"}]},{"t":"p","runs":[{"text":"Most generated projects ship a README describing a framework rather than your app, and everybody ignores it forever. Replace it once with five lines: what this app is for, how to run it, what has to be in "},{"text":".env","code":true},{"text":", how to reset the database, and where the deployed version lives."}]},{"t":"p","runs":[{"text":"You are writing it for yourself in four months, when you have forgotten which command starts the thing. That is not a hypothetical reader. It is the most likely one, and the ten minutes will feel like a gift."}]},{"t":"h2","id":"ask-for-the-map","runs":[{"text":"Ask for the map"}]},{"t":"code","lang":"","label":"Text","text":"Give me a one-line description of every folder in this project,\nand name the single file where a booking is written to the database."},{"t":"p","runs":[{"text":"An agent that has read the project answers this in seconds. If the answer names a file that does not exist, you have learned something important about how much it currently knows, and the right response is to point at the real file rather than to argue."}]}],"check":{"question":[{"text":"An agent says it fixed a bug by editing a file inside node_modules. What is the problem?"}],"options":[{"text":[{"text":"Editing a dependency violates its licence."}],"correct":false,"tempting":[{"text":"Raises a legal question that mostly does not apply to local edits, and misses the practical failure entirely."}]},{"text":[{"text":"The change will be discarded the next time dependencies are installed, so the fix does not exist on any other machine."}],"correct":true,"tempting":null},{"text":[{"text":"Dependencies are minified, so the edit will be unreadable later."}],"correct":false,"tempting":[{"text":"True of some builds and irrelevant; readability is not why the change fails to persist."}]},{"text":[{"text":"It will make the repository much larger when committed."}],"correct":false,"tempting":[{"text":"Assumes node_modules is committed, which .gitignore prevents in every normal setup."}]}],"order":[2,3,0,1],"why":null},"prev":{"slug":"reading-a-diff-properly","title":"Reading a diff properly","minutes":8},"next":{"slug":"enough-html-and-css-to-audit","title":"Enough HTML and CSS to audit the screen","minutes":8}}}