Не кажется ли вам, что ИИ снизил ценность операционных знаний, типа «как сделать ту или иную вещь на том или ином языке программирования», но повысил ценность архитектурного мышления и умения правильно декомпозировать код, то есть делить его на модули. Причём принципы этого деления никак не изменились с 70-х годов. И даже наоборот теперь им нужно следовать более строго и щепетильно, иначе ИИ (вы же всё равно прибегаете к его помощи хотя бы для каркаса) наворотит такую безграничную вайб-лапшу, что распутать человеческими мозгами будет невозможно.
Закон Конвея
Аж в 1967 году программист Мевил Конвей, работавший над компиляторами и операционными системами, сформулировал закон (по типу законов Мерфи) — «Организации, проектирующие системы вынуждены создавать проекты, которые копируют структуры коммуникаций в этих организациях», т.е. фактически, структура ПО повторяет структуру компании, в которой оно создано. Так получается естественным образом и грамотный архитектор должен держать это в голове — либо архитектуру подгонять под команду, либо наоборот форматировать команду под архитектуру, иначе трудно будет получить ожидаемый результат.
Если в современные команды ворвался ИИ на правах джуниор-разработчика, то фактически он изменил структуру команд, что должно изменить и структуру кода. Пока ИИ удаётся удовлетворительно справляться с небольшими замкнутыми кусками кода. Логично, что и система должна дробиться на более мелкие и обособленные куски кода. Но на какие именно, вероятно, ИИ не может решить самостоятельно — поскольку по закону Конвея это зависит от структуры команды не меньше, чем от технических параметров.
Меньше зацепления, больше связности
К счастью для нас принципы разделения (или декомпозиции или сегрегации) кода были сформулированы даже раньше закона Конвея — начиная с 60-х появляются термины «связность» (cohesion) и «зацепление» (coupling). Последнее, кстати говоря, вполне позволительно перевести как «совокупление», что делает термин ещё звучнее и понятнее - совокупление программных модулей — зрелище жалкое и отталкивающее, его следует избегать всеми доступными средствами. Это такая ситуация когда один модуль вызывает методы другого, а другой первого (ну или взаимно пользуются константами и свойствами), что делает их по сути неразделимыми. Связность — иная ситуация — это когда модуль сфокусирован на решении единственной задачи и не использует перекрёстных зависимостей и предоставляет стандартизированное API для общения с собой.
Если на примерах.
Банальная задача — нужно поменять пользователю пароль залогировать это и отправить емейл с уведомлением. Как сделает неопытный программист (я двадцатилетней давности)
// App.code * .code - это любой язык
// сделаем объект в котором запомним ID-шечку, это ж ООП
let user = new User(userId)
user.setPassword(pass) // и всё
// а уж в User.code
// естественно, в контексте экземпляра User - там же все данные есть
user.setPassword(pass) => {
// захэшируем пароль, может даже с солью, закинем в БД
// закинем в БД лог-запись, чтоб два раза не вставать
// и вызовем отправку почты - это ж каждый раз надо делать при смене пароля
}
Не, ну для отправки е-мейла, конечно, напишем все таки отдельный модуль — мы ж за модульность, просто вызывать будем его в экземпляре User'a:
// это строчка из user.setPassword(), this - это user
// мейлер сам достанет почту из user’a
// и имя достанет, если надо красивое обращение написать
mailer.sendMail(this, “Вам изменили. Наверное, пароль”)
Здесь у User’a низкая связность — задачи, которые он делает слабо связаны между собой — он и в БД лезет в разные таблицы, и логирует, и пароли хэширует и лезет в мейлер. Понадобится ещё и СМС отправлять - допишем.
У обоих модулей сильное зацепление — юзер знает каким именно образом информируется пользователь, мейлер знает как устроен пользователь, если кто-то вдруг у юзера переименует поле firstName в name (что вообще-то стрёмно) то посыпятся эксепшены в модуле мейлер, а юзер будет себя хорошо чувствовать.
Как сделаю я сейчас — наделаю файликов. Развязываем зацепление, фокусируем связность.
UsersRepo.code // только лазает в БД только в таблицу юзеров
User.code // запись о юзере из бд
UserLogRepo.code // лазает в БД только в таблицу логов
MailService.code // отправляет текст на e-mail адрес, о юзерах не слышал даже
UsersService.code // совмещает всё, делая нужную работу
И уже в UsersService будет метод setPassword(userId, pass) в котором
let user = UsersRepo.findById(userId) // упадёт, если юзер не существует
UsersRepo.savePass(userId, pass)
let event = this.formatEvent(user) // сдесь же в сервисе формируем запись лога
UserLogRepo.save(event)
let message = this.formatMessage(user) // как-то там формируем текст почты
MailService.send(user.email, message)
Так выходит даже многословнее, но каждый отдельный модуль становится существенно проще, они не знают друг о друге, за исключением UsersService который знает обо всех, но при этом остальные модули никак на сервис не завязаны и ничего о нём не знают. Такую разработку уже можно распараллелить — отдать …Repo знатоку SQL, отдать мейлер тому кто слышал про POP3 и IMAP. А UsersService, тому кто общается с менеджерами. И такое даже безопаснее скормить ИИ. (Причём это не единственный способ разделения кода - какие то методы можно перетащить из сервиса в самого юзера. Можно сделать формирование месседжей отдельным классом или внедрить в другой класс - код от этого не потеряет модульность, просто изменится характер некоторых связей)
Таким образом мы осуществили декомпозицию с целью уменьшить зацепление и повысить связность. Мы «управляли сложностью» — мы не уменьшили её. Парадоксальным образом нам даже пришлось её повысить, чтобы упростить разработку. Мы раздробили монолитную сложность на множество несложных изолированных компонентов и структурировали их связи.
Декомпозиция - это важно
«Меньше зацепления, больше связности» это настолько фундаментальная цель, что, например, распространённые в ООП принципы SOLID — это фактически инструкция как достичь целевого уровня связности/зацепления. Эта целевая задача в явном виде входит в правила GRASP или в принципы «Чистой Архитектуры» (Clean Architecture). Это всегда было важно и помогало строить большие системы. А с появлением ИИ стало еще важнее.
Итог
Декомпозируйте свой код. Принимайте при этом во внимание не только известные паттерны, но и свои административно-организационные особенности. Это позволит не только другим разработчикам понимать что происходит, но и проще отлаживаться, и самому "въехать" спустя пару месяцев, и ИИ напрячь в локальной замкнутой области кода.
Может ли декомпозировать сам ИИ? Может, если явно попросить. Но без учёта вашей организационной структуры. Но и её можно уточнить, однако промпт придётся усложнять. Плюс к тому же ИИ может потерять связи между модулями или «забыть» правильные названия свойств и методов. Поэтому будьте бдительны и осторожны, если даёте ему больше одного файла!
Комментарии (2)

flancer
16.10.2025 08:56У ЯМ (языковых моделей) есть одно фундаментальное ограничение - максимальный размер выходного контекста. Который просто диктует необходимость разваливать кодовую базу на фрагменты, если для разработки используется LLM. Так что, да - направление верное.
И, это... то, что пишут разработчики LLM по поводу размера выходного контекста - лажа. Например, уверенный рефакторинг текстов через веб-интерфейс ChatGPT идёт на размерах в районе 4К токенов (примерно 12.5К символов), хотя заявлено про 32К+. Это заявленный размер именно выходного, а не всего контекстного окна.
garryq
Что называется – снял с языка. В последнее время я заметил, что даже на микроскопических пет-проектах и самописных инструментах, в которых я для ускорения пользуюсь LLM, я всё больше сам себе напоминаю не джуна-кодера, а CTO.