Programming with AI
ENRU
13.2

Folder structure

decisions you can't redo later

What it is

The folder structure is the architecture from article 13.1 written into the file system. Its main value isn't beauty but that it answers the question "where do I put this new code" without a discussion. Until there is an answer, every new piece lands wherever the cursor happened to be.

For working with an AI the structure has a second, equally important role: it is what you name in the request. "Add it to app/services/weather.py" is a request you can get a predictable answer to. "Add a weather function" is an invitation to invent the structure again.

What it looks like in code

Two ways of arranging things, and both are right

By kind of code — everything of the same kind together:

measured
app/
├── models/        what is stored
├── services/      what happens
├── routes/        what's visible from outside
└── utils/

By feature — everything belonging to one subject together:

measured
app/
├── weather/       the weather model, logic and screen
├── users/         the user model, logic and screens
└── shared/

Choosing between them isn't a matter of taste but the answer to "what changes as a whole more often":

Arranged by kindArranged by feature
Good whenthe project is small, few subjectsmany subjects, independent
An edit touchesseveral foldersusually one
Easy to seewhat's in the project at allhow one subject works
Breaks down whenthere get to be many subjectsthe subjects start to interweave

For a first project, arranging by kind makes more sense: everyone understands it, AI included, and it involves fewer decisions. Moving to arrangement by feature makes sense once the project has more than five or six independent subjects.

What you shouldn't do is mix the two. A models/ folder next to a weather/ folder with its own models inside isn't a compromise but two answers to one question. After that, new code can go in both places.

What is expensive to redo

Not all decisions are equal. These three are worth thinking about on day one:

1. The boundary between the layers. If the logic and the database work are mixed together from the start, separating them later means rewriting nearly everything. Nobody has ever asked to go the other way, from separated to mixed.

2. The root package's name. It goes into every import in the project. Changing it means changing every file. Not app and not src if the project might one day become a package: something of your own, short and clear.

3. Where the data lives. The decision "we'll just put it in JSON for now" lives longer than planned (article 13.3).

The rest — folder names, file order, where the tests are — is redone in half an hour and isn't worth long deliberation.

File size

A guideline that works: one file, one reason to change. If you have to open a file both when the API's response format changes and when a screen's appearance changes, there are two different files inside it.

The numeric guideline is 300–500 lines. Not a rule but a signal: once it reaches a thousand, two files are probably hiding in there.

With an AI that has a practical consequence: a file that fits into a request as a whole is far easier to discuss. An edit in a two-hundred-line file is a precise request and a precise answer. An edit in a thousand-line file is a paraphrase and a hope.

What shouldn't be there

measured
app/
├── utils.py          ← 900 lines of everything
├── helpers.py        ← the same thing, but different
├── main_v2.py        ← the previous version, might come in handy
├── test.py           ← not a test, a "let's try it"
└── new/              ← we're rewriting, but let's keep the old one for now

Every line here is a deferred decision. utils and helpers are "I'll sort it out later". main_v2 and new/ are "I'll delete it later". Neither ever happens, but every request to an AI now has to explain which files are the real ones.

The old doesn't live next to the new: git exists for that (article 5.1).

What to tell the AI

Prompt
Here is my project's structure:
<folder tree>

I'm adding a feature: <which>.
Say which files the code will go into and which files will
have to change. Name new files by my scheme.
Don't write the code yet.

That request is worth making a habit: it takes a minute and removes the commonest cause of mess — code put in the wrong place.

Prompt
Project structure: <tree>.
Rules: models imports nothing, services imports models,
routes imports services.
Check the file you just wrote against those rules.
Prompt
I have a 900-line file: <contents or description>.
Propose which files to split it into and on what basis.
One phrase per file saying what it's responsible for.
Don't move the code yet.
Prompt
The project has a utils.py with everything in it.
Distribute its contents across the existing folders.
For each function, say where and why.
If something really is shared, leave it, but name it more
precisely.

What to look for

The AI proposed a new folder next to an existing one. helpers/ while services/ is alive, db/ while models/ is alive. A sign that the structure wasn't named in the request.

Two arrangements mixed. models/ and weather/ in one folder.

utils is growing. Every function put there is a deferred decision.

Files with versions in the name. main_v2, app_new, index_final. Git exists for that.

Tests mixed in with the code. A separate tests/ folder alongside, not a test.py in every folder.

One file grows with every request. If the AI adds a fourth unrelated function to the same file, it is time to split.

Cheat sheet

  • The folder structure answers "where do I put this new code".

  • By kind or by feature — choose by what changes as a whole more often.

  • Don't mix the two arrangements: you get two answers to one question.

  • Expensive to redo: the layer boundary, the root package's name, where the data lives.

  • One file, one reason to change; 300–500 lines as a signal.

  • A file that fits into a request is many times easier to discuss.

  • Write the structure down as text in the repository and paste it into requests.

  • utils, main_v2, new/ are deferred decisions, not folders.