На каждом ревью найдётся кто‑то, кто спросит «Зачем четыре файла, если это один пайплайн?»
А затем, давайте объясню!
Как это происходит
Очевидно, что никто не садится и не пишет processor.py на 900 строк намеренно.
Пайплайн трансфера артефактов стартует на 200 строк. Потом добавляется фильтрация, контроль целостности, обработка состояний, уведомления — и каждый раз кажется логичным добавить сюда же, рядом с похожим кодом. Через полгода открывая файл думаешь только о том, что лучше не трогать это без веской причины.
Причём архитектура при этом может быть вполне нормальной: файлы разделены по слоям, именование понятное. Просто один конкретный файл незаметно стал большим и это не джун‑ошибка — это нормальная энтропия под живые дедлайны.
Почему лимит вообще работает
Популярный аргумент звучит так: «большие файлы труднее читать, потому что рабочая память ограничена» — с отсылкой к Миллеру (1956) и Коуэну (2001). Это правда, но прямой экстраполяции нет: Миллер изучал запоминание случайных слогов, не чтение кода в IDE с навигацией по символам.
Реальная проблема — не в скролле. Она в неопределённости.
Когда открываешь permissions.py — область понятна из имени. Когда открываешь service.py на 800 строк с четырьмя разными ответственностями — сначала нужно восстановить карту файла в голове, и только потом трогать. Этот overhead не катастрофичен сам по себе, но он накапливается каждый раз, когда ты заходишь в файл.
Есть и эмпирика: Jay et al. (2009) проверили более 1,2 млн файлов из SourceForge и обнаружили линейную зависимость между LOC и цикломатической сложностью — устойчивую к языку и парадигме. Большой файл с высокой вероятностью сложный, независимо от аккуратности написания. Правда, Landman et al. (2016) эту корреляцию оспаривают — на Java и C результаты слабее. Данные неоднозначны. Но как первая метрика, которая дёшево считается и достаточно часто срабатывает — LOC работает.
Мои цифры — и почему они разные
Важный момент, который обычно упускают: я не использую одно число для всего кода. Разные типы файлов — разные лимиты.
Функция — 80 строк. Если не помещается в экран без скролла, скорее всего делает больше одного дела. 80 строк — это точка, где я останавливаюсь и спрашиваю: это действительно одна задача?
Handler / pipeline — 350 строк. Хендлер принимает событие, валидирует данные, передаёт дальше. Всё. Бизнес‑логики здесь нет. 350 строк — это около 10–12 хендлеров с валидацией. Больше — возможно, логика уже поехала не туда.
Processor / service — 450 строк. Здесь живёт реальная логика. 450 — точка, после которой мне становится тяжелее держать в голове внутренние зависимости класса без постоянного возврата наверх. Если перерастает — почти всегда это два смешанных контракта, а не просто «много кода».
Конкретные числа — из опыта, не из формул. Важнее сам принцип: для файлов с разной ответственностью лимиты разные и всегда есть исключения.
Пример 1 — FSM‑хендлер, который вырос органически
Задача: пошаговый мастер подачи заявки в боте (aiogram 3 + FSMContext). Девять шагов с ветками, отмена на любом этапе, inline‑клавиатуры на каждый переход.
До:
bot/ ├── handlers/ │ ├── hr_request/ │ │ ├── __init__.py # регистрация router │ │ └── wizard.py # ~800 строк за полгода выросло из 200: │ │ # добавлялись ветки флоу, keyboards рядом с хендлерами, │ │ # валидация там же - ведь "один флоу, незачем дробить" │ └── admin.py ├── services/ │ └── ticket_service.py # 310 строк └── db/ └── repository.py # ~490 строк: один TicketRepository, CRUD + аналитические JOIN # добавлялись методы по запросам
~800 строк в wizard.py — не хаос. Логика прослеживается, имена понятны. Проблема появляется, когда клавиатура на шаге 6 рендерит не то и нужно найти конкретный InlineKeyboardMarkup среди всего потока переходов. А правка клавиатуры рядом с логикой перехода — это риск задеть соседний код, который ты в этот момент вообще не читаешь.
Часто возникает вопрос: почему не два класса в одном файле? Потому что StepsHandler и WizardKeyboards меняются по разным причинам — первый при изменении бизнес‑логики, второй при изменениях UI.
В одном файле это смешивается в git diff: правка кнопки выглядит как правка флоу.
В разных файлах граница видна сразу — и в diff, и при импорте.
После:
bot/ ├── handlers/ │ ├── hr_request/ │ │ ├── __init__.py │ │ ├── states.py # 25 строк - StatesGroup │ │ ├── steps.py # 310 строк - только переходы │ │ ├── keyboards.py # 195 строк - клавиатуры по шагам │ │ └── validators.py # 120 строк - input валидация │ └── admin.py ├── services/ │ └── ticket_service.py # 310 строк └── db/ ├── ticket_repo.py # 280 строк - CRUD + простые выборки └── ticket_queries.py # 230 строк - агрегаты, JOIN, аналитика
Суммарный LOC немного вырос — за счёт импортов и init.py, но цель не в этом, цель — предсказуемая граница изменений: клавиатура шага 6 это keyboards.py на 195 строк, а не поиск по 800.
Репозиторий разбился по той же логике: CRUD‑методы и аналитические запросы меняются по разным поводам и теперьticket_queries.py трогаешь при новых отчётах, ticket_repo.py — при изменениях схемы. Разная частота, разные причины.
aiogram 3.x добавил Scene‑классы как осознанную альтернативу — весь флоу в одном изолированном классе. Валидный подход. Но если внутри сцены начинают жить генераторы клавиатур и валидационная логика — она вырастет точно так же.
Пример 2 — Maya‑процессор по типам ассетов
Начинали с mesh. Потом пришли rig, animation, fx — у каждого своя логика нормализации имён и валидации LOD.
До:
tools/ └── maya_asset_tool/ ├── ui/ │ └── main_window.py # 290 строк ├── core/ │ └── asset_processor.py # ~680 строк: BaseProcessor + MeshProcessor + RigProcessor + AnimProcessor │ # тут казалось логичным держать все процессоры вместе └── integrations/ ├── perforce.py # 170 строк └── maya_api.py # 200 строк
Четыре хорошо написанных класса. Проблема в тестах: from core.asset_processor import RigProcessor тянет весь модуль — включая Maya‑зависимости MeshProcessor, которых в тестовой среде нет. Мокаешь то, что к тесту вообще не относится.
После:
tools/ └── maya_asset_tool/ ├── ui/ │ └── main_window.py # 290 строк ├── core/ │ ├── base_processor.py # 110 строк │ ├── mesh_processor.py # 185 строк │ ├── rig_processor.py # 170 строк │ ├── anim_processor.py # 150 строк │ └── validators.py # 125 строк └── integrations/ ├── perforce.py # 170 строк └── maya_api.py # 200 строк
from core.rig_processor import RigProcessor — только то, что нужно.
Пришёл VFX — создаёшь vfx_processor.py, остальные не трогаешь.
Граница изменений стала явной.
Когда большой файл нормален
Тексты и шаблоны. Файл на 900 строк из констант или строк, сгруппированных по классам, читается совершенно иначе. Нет зависимостей между блоками, нет сайд‑эффектов. Открыл, нашёл нужный класс, поправил.
Репозиторий на 500+ строк — нормально, если все методы независимы, каждый не длиннее ~50 строк, никакой бизнес‑логики. Например, get_by_id() ничего не знает про get_with_analytics() (у сервисного слоя зависимостей между методами больше, поэтому и порог ниже).
Scene в aiogram 3x — осознанное архитектурное решение фреймворка, а не исключение из правил.
Как это контролировать
Лимиты работают только если их не нужно помнить. Мы с коллегой ввели простую проверку в CI: скрипт считает непустые строки в файлах по суффиксу имени и возвращает варнинги, если файл перевалил за лимит своего типа. Не как строгий запрет, а чтобы сделать превышение видимым.
Если файл растёт — это должно быть осознанным решением с явным исключением, а не случайным дрейфом, который замечаешь через три месяца.
Инструмент, которым пользуются каждый день, не прощает «знаю где баг, но не знаю что зацеплю». Лимиты на размер файлов — один из способов держать эту уверенность на протяжении нескольких лет жизни проекта.
Не универсальный рецепт. Просто то, что работает у меня уже несколько лет.
А у Вас есть формальные ограничения в команде, или держитесь на договорённостях?
Комментарии (0)

iamkisly
11.05.2026 12:46Очевидно, что никто не садится и не пишет
processor.pyна 900 строк намеренно.Стоит сделать оговорку о исключениях.. потому что одна из ниш которую прочно занимает python - это одностраничные скрипты. Они удобны просто из-за формата распространения. Увидеть там что-то на 1-3 тысячи строк? Легко!
iamkisly
Ну не согласен. Мы уже давно живем в мире, где 16:9 это норма для соотношения сторон. Многие используют и мониторы пошире, включая многомониторные конфигурации.. с вертикальными экранами, и экранами-компаньонами. Сделать сплит файла в редакторе чтобы независимо посмотреть на две-три его части в отдельных вкладках - легко, вам ну нужно держать фокус на всем файле, а только на его частях.. я так уже много лет разрабатываю, и мне больше мешает что что тот же формат 16:9 позволяет писать длинные строки. Я напомню, что с древних времен в редакторах присутствует лимит на длину строки 80 символов. Первоначально придуманное для печати на бумагу правило, уже не актуально для этого, но не для многооконного редактирования.
k8r4a7n2fg23k Автор
Вы правы, про экран слабый аргумент с моей стороны - доработаю.
Но сплит как раз и показывает проблему: если для понимания одной правки нужно держать открытыми два места, то значит, они связаны сильнее, чем должны быть.
А лимит на строки здесь скорее симптом, причина - смешанная ответственность.
Andrey_Solomatin
А потом открываешь с 15 дюймового ноутбука и радуешься.
iamkisly
у всего есть свои недостатки