How to break an idea into parts
three questions and one rule about direction
What it is
Architecture sounds grand, but in practice it is the answer to one question: what depends on what. Not "which folders to create" — the folders will appear later, by themselves — but who is allowed to touch whom.
While the project is small this feels unnecessary: one file, everything works. The problem isn't size but that without an answer to that question every new AI answer puts code wherever it was convenient at that moment. A month later the project has three places that talk to the database, two different ways of reading settings, and a two-hundred-line function nobody wants to touch.
What it looks like in code
Three questions for any idea
Before asking for code, answer three questions. Not the AI — yourself.
1. What is stored? List the things: a city, a user, a note, an order, a subscription. Those are nouns, and tables and data models come out of them.
2. What happens? List the actions: add a city, refresh the weather, send a notification. Those are verbs, and functions come out of them.
3. What is visible from outside? A screen, a page, a bot command, an API address. That is what people talk to the program through.
Three half-page lists, and the project already has a skeleton. The technique works for a two-hundred-line bot and for a twenty-screen application alike.
The rule about direction
Those three lists are then laid out in layers, and between the layers there is a single rule worth remembering:
The upper layer knows about the lower one. The lower one doesn't know about the upper. The screen talks to the logic, the logic to the data. Never the other way.
That isn't theory or a matter of taste: breaking the rule is visible at once, because the program stops starting. Here are two files that know about each other:
# app/models.py
from app.services import fetch_weather
class City:
def refresh(self):
self.temp = fetch_weather(self.name)# app/services.py
from app.models import City
def all_cities():
return [City("Oslo"), City("Bergen")]Running it:
ImportError: cannot import name 'fetch_weather' from partially initialized
module 'app.services' (most likely due to a circular import)A circular import is a message saying the layers have been mixed up. The data climbed upwards, into the logic. The cure isn't a clever import inside a function (that is what the internet advises, and what an AI often proposes) but reversing the dependency: the model knows nothing, the service knows about the model.
# app/models.py — imports nothing
class City:
def __init__(self, name, temp=None):
self.name = name
self.temp = temp# app/services.py — knows about the model
from app.models import City
def all_cities():
return [City(n, fetch_weather(n)) for n in ("Oslo", "Bergen")][City(Oslo, 4), City(Bergen, 4)]The smaller the better — up to a point
It makes sense to split down to the level where each piece can be explained in one phrase. "Takes a city, returns a temperature." "Holds the user's list of cities." As soon as the explanation needs the word "and" three times, it is time to split further.
There is a limit in the other direction too: twenty files of ten lines isn't architecture, it is scatter. The sign that you overdid it: understanding one action means opening five files in a row.
What to tell the AI
The main mistake is asking for the whole application. Instead, get the breakdown as text, before any code:
The idea: <describe it in two or three sentences>.
Don't write code. Break it into parts:
1. What is stored — a list of entities with their fields.
2. What happens — a list of actions.
3. What's visible from outside — screens or addresses.
For each action, say which entities it touches.The answer to such a request reads in a minute and is corrected in two. Code that would have to be rewritten takes an evening.
Here is my breakdown: <the three lists>.
Propose a layer structure: what depends on what.
Draw with arrows who imports whom.
If an arrow comes out pointing both ways, say so.I got an ImportError mentioning a circular import.
Don't propose moving the import inside a function.
Explain which of the two modules shouldn't know about the
other, and how to reverse the dependency.What to look for
The application in one piece. You asked for "make an app" and got one four-hundred-line file with everything mixed together.
Circular imports. Reverse the dependency rather than hiding the import.
A database query right in a screen's handler. The interface reached over the logic's head straight into the data. It works — until the first change in the database.
The same action in three places. Three places where the temperature is formatted, and all three have to be remembered.
A file called utils that keeps growing. Everything that found no home
gets put there. That isn't a layer, it is a cupboard to sweep things into.
The AI did the breakdown and you didn't read it. The project's structure is worth understanding whole — it is the one part that can't be delegated (article 1.1).
Cheat sheet
Architecture is the answer to "what depends on what".
Three lists before any code: what is stored, what happens, what's visible from outside.
Three layers: interface → logic → data. The arrows only go down.
A circular import is a signal of mixed-up layers, not a Python error.
A piece of the right size is explained in one phrase without the word "and".
Ask the AI for the breakdown as text before code — text is cheaper to fix.
A
utilsfolder is where you sweep what you didn't sort out.