Не кажется ли вам, что ИИ снизил ценность операционных знаний, типа «как сделать ту или иную вещь на том или ином языке программирования», но повысил ценность архитектурного мышления и умения правильно декомпозировать код, то есть делить его на модули. Принципы этого деления никак  не изменились с 70-х годов. Но теперь им нужно следовать куда как более тщательно, иначе ИИ создаст настолько плохо структурированный код, что распутать человеческими мозгами будет невозможно.


Закон Конвея

Аж в 1967 году программист Мевил Конвей, работавший над компиляторами и операционными системами, сформулировал закон (по типу законов Мерфи) — «Организации, проектирующие системы вынуждены создавать проекты, которые копируют структуры коммуникаций в этих организациях», т.е. фактически, структура ПО повторяет структуру компании, в которой оно создано. Так получается естественным образом и грамотный архитектор должен держать это в голове — либо архитектуру подгонять под команду, либо наоборот форматировать команду под архитектуру, иначе трудно будет получить ожидаемый результат.

Роль ИИ в современных командах напоминает роль разработчика-стажера, приставленного к каждому программисту, но требующего постоянного надзора. И это должно изменить структуру кода. Пока нейросетям удаётся удовлетворительно справляться с небольшими замкнутыми кусками кода. Логично, что и система должна дробиться на более мелкие и обособленные куски кода. Но на какие именно, вероятно, ИИ не может решить самостоятельно — поскольку по закону Конвея это зависит от структуры команды не меньше, чем от технических параметров.

Меньше зацепления, больше связности

Основные принципы разделения (или декомпозиции или сегрегации) кода были сформулированы даже раньше закона Конвея — начиная с 60-х появляются термины «связность» (cohesion) и «зацепление»  (coupling). Внешнее зацепление — это такая ситуация когда один модуль вызывает методы другого, а другой первого (ну или взаимно пользуются константами и свойствами), что делает их по сути неразделимыми. Внутренняя связность — иная ситуация — это когда модуль сфокусирован на решении единственной задачи и не использует перекрёстных зависимостей, вместо этого предоставляет стандартизированное API для общения с собой.

Если на примерах.  

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

// Примеры приведены в псевдокоде для универсальности, 
// но принципы применимы в любом языке с поддержкой модульности — от Go до Kotlin

// App.code
// сделаем объект в котором запомним ID
let user = new User(userId)
user.setPassword(pass) // и всё

// а уж в User.code
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). Это всегда было важно и помогало строить большие системы. А с появлением ИИ стало еще важнее.

Итог

Декомпозируйте свой код. Принимайте при этом во внимание не только известные паттерны, но и свои административно-организационные особенности. Это позволит не только другим разработчикам понимать что происходит, но и проще отлаживаться, и самому "въехать" спустя пару месяцев, и ИИ привлечь в локальной замкнутой области кода.

Может ли декомпозировать сам ИИ? Может, если явно попросить. Но без учёта вашей организационной структуры. Но и её можно уточнить, однако промпт придётся усложнять. Плюс к тому же ИИ может потерять связи между модулями  или «��абыть» правильные названия свойств и методов. Поэтому будьте бдительны и осторожны, если даёте ему больше одного файла!