Программирование с ИИ
ENRU
13.1

Как разбить задумку на части

три вопроса и одно правило направления

Что это

Архитектура звучит солидно, но на деле это ответ на один вопрос: что от чего зависит. Не «какие папки завести» — папки появятся потом, сами, — а кто кого имеет право трогать.

Пока проект маленький, это кажется лишним: один файл, всё работает. Проблема не в размере, а в том, что без ответа на этот вопрос каждый новый ответ ИИ кладёт код туда, где он оказался удобен сейчас. Через месяц в проекте три места, где обращаются к базе, два разных способа читать настройки и функция на двести строк, которую никто не хочет трогать.

Как выглядит в коде

Три вопроса к любой задумке

Прежде чем просить код, ответь на три вопроса. Не ИИ — себе.

1. Что хранится? Перечисли вещи: город, пользователь, заметка, заказ, подписка. Это существительные, и из них потом получатся таблицы и модели данных.

2. Что происходит? Перечисли действия: добавить город, обновить погоду, отправить уведомление. Это глаголы, и из них получатся функции.

3. Что видно снаружи? Экран, страница, команда бота, адрес API. Это то, через что с программой разговаривают.

Три списка на полстраницы — и у проекта уже есть скелет. Приём работает и для бота на двести строк, и для приложения на двадцать экранов.

Правило направления

Дальше эти три списка раскладываются по слоям, и между слоями действует единственное правило, которое стоит помнить:

Схема из книги — будет перерисована для сайта
Стрелки идут только вниз. Слой данных не знает, что над ним есть экран, — поэтому его можно проверять отдельно.

Верхний слой знает о нижнем. Нижний о верхнем — нет. Экран обращается к логике, логика к данным. Обратно — никогда.

Это не теория и не вкусовщина: нарушение правила видно сразу, потому что программа перестаёт запускаться. Вот два файла, которые знают друг о друге:

# 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")]

Запуск:

измерено
ImportError: cannot import name 'fetch_weather' from partially initialized
module 'app.services' (most likely due to a circular import)

Круговой импорт — это сообщение о том, что слои перепутаны. Данные полезли наверх, в логику. Лечение не в хитром импорте внутри функции (так советуют в интернете, и так часто предлагает ИИ), а в развороте зависимости: модель не знает ничего, сервис знает о модели.

# app/models.py — ничего не импортирует
class City:
    def __init__(self, name, temp=None):
        self.name = name
        self.temp = temp
# app/services.py — знает о модели
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)]

Чем мельче, тем лучше — до предела

Разбивать имеет смысл до того уровня, где каждый кусок можно объяснить одной фразой. «Берёт город, возвращает температуру». «Хранит список городов пользователя». Как только для объяснения нужно слово «и» трижды — пора делить дальше.

Обратный предел тоже есть: двадцать файлов по десять строк — это не архитектура, а россыпь. Признак, что перестарался: чтобы понять одно действие, приходится открыть пять файлов подряд.

Как сказать ИИ

Главная ошибка — просить приложение целиком. Вместо этого разбиение стоит получить текстом, до кода:

Запрос
Задумка: <опиши в двух-трёх предложениях>.
Не пиши код. Разложи на части:
1. Что хранится — список сущностей с полями.
2. Что происходит — список действий.
3. Что видно снаружи — экраны или адреса.
Для каждого действия укажи, какие сущности оно трогает.

Ответ на такой запрос читается за минуту, а исправляется за две. Код, который пришлось бы переписывать, — за вечер.

Запрос
Вот моё разбиение: <три списка>.
Предложи структуру слоёв: что от чего зависит.
Нарисуй стрелками, кто кого импортирует.
Если где-то получается стрелка в обе стороны — скажи об этом.
Запрос
Я получил ImportError с упоминанием circular import.
Не предлагай перенести импорт внутрь функции.
Объясни, какой из двух модулей не должен знать о другом,
и как развернуть зависимость.

На что смотреть

Приложение одним куском. Попросили «сделай приложение» — получили один файл на четыреста строк, где всё вперемешку.

Круговые импорты. Разворачивать зависимость, а не прятать импорт.

Запрос к базе прямо в обработчике экрана. Интерфейс полез через голову логики сразу в данные. Работает — до первого изменения в базе.

Одно и то же действие в трёх местах. Три места, где форматируется температура, и все три надо не забыть поправить.

Файл с именем utils растёт. Туда складывают всё, чему не нашлось места. Это не слой, а шкаф, в который сметают.

Разбиение придумал ИИ, а ты его не читал. Структуру проекта стоит понимать целиком — это единственная часть, которую нельзя делегировать (статья 1.1).

Шпаргалка

  • Архитектура — это ответ на вопрос «что от чего зависит».

  • Три списка до кода: что хранится, что происходит, что видно снаружи.

  • Три слоя: интерфейс → логика → данные. Стрелки только вниз.

  • Круговой импорт — сигнал о перепутанных слоях, а не ошибка Python.

  • Кусок правильного размера объясняется одной фразой без слова «и».

  • Проси у ИИ разбиение текстом до кода — исправлять текст дешевле.

  • Папка utils — место, куда сметают то, что не разложили.