Pull to refresh

Comments 37

Пора уже составлять cook book с примерами, как получается правильно, и что не менее важно, как неправильно.

Это правда, но это так все стремительно устаревает. Вот для astra разработчики заявили, что нужно убирать формулировки "ты великий сеньор с 20 годами опыта".

Это заявляли, по-моему, ещё для Opus 4.6

Никогда не нужны были для хорошего результата. Такой же бред, как здороваться с нейросетью, говорить "спасибо" и "пожалуйста". Хотя несколько лет назад на Хабре меня убеждали, что это делает результат более точным.

несколько лет назад даже эмоциональное давление 'меня уволят если это не сделать, помоги а!' давало чуть чуть лучше результат...

сейчас в рекомендации нужно записывать:
- решай задачу несколькими прогонами с чистого листа, анализируй полученные решения и на основе этого делай финальную попытку.
- перепроверяй решения разными моделями конкурирующих компаний (openai/anthropic/google/deepseek/glm/qwen), в идеале кстати продумать систему построения размышлений автоматически обрабатывающим контекст разными ИИ.

Человек, который это написал, давно ушёл, причина нигде не записана. Сгенерируйте по спецификации новую версию — и вы аккуратно выкинете всё, что система узнала за годы работы

Значит, надо записывать. А что забыто - восстанавливать. И каждый раз изменения кода синхронизировать со спекой. Если докопаться, то спека станет сложнее кода - прямо по Фаулеру.

Но если спека очевидна и ловится дёшевоц проверкой, то можно и не писать. Любая LLM галлюционирует /восстановит её из общих знаний и не ошибётся.

В любом случае ADR + issues надо все хранить, это очень важно для легаси долгоживущих систем.

Ну не знаю. Я с помощью ИИ разбирал сложнейшие проекты написанные 10 лет назад, и поддерживаемые разными спецами. Вообще ни разу не пытался сам разобраться в коде, но по моим заданиям ИИ умудрялся и оптимизировать код и находить и устранять дыры безопасности и добавлять новые фичи и функционал, оптимизировать код и быстродействие и тд и тп

Все это верно. Я не утверждаю что ИИ не сможет чинить legacy. Задача оптимизации кода не меняет его поведение. А вот восстановить первоначальный замысел будет тяжеловато. Вы не сможете по коду понять почему он здесь, только догадываться.

Ну тут безусловно важна насмотренность

И не только в спецификации, но и в самом коде.

Я часто поправляю нейросетку чтобы она переписала через нужные мне конструкции, так как заранее понимаю что иначе сложно будет поддерживать такую разработку

С тех пор я и пытаюсь понять, как теперь должна выглядеть разработка

В разработеке вайб-кодинг это гусеница, harness это куколка, имаго-кодинг это бабочка.

Что такое имаго-кодинг расскажу позже, на этой неделе. Одним комментарием тут не обойтись.

Да, расскажите, пожалуйста, никогда не слышал. Очень интересно.

имаго-кодинг это бабочка

В смысле что живет всего один день?

Один проект.

Найденное в коде знание сначала возвращайте в спецификацию

Обычно, сначала в тест-кейсы как найденные случаи. Из test cases восстанавливать вручную use cases избыточно. А автоматического трекера у вас конечно же нет.

А вот если это на многих случаях, то в правила (домен и спеки).

Все-таки сначала в спеку, а потом в тесты и код. Спецификация - источник истины.

Соглашусь только что правила должны где-то жить и их можно будет там найти.

В легаси спека это НЕ источник истины. Почему "сначала в спеку" не работает. Найденное в коде знание в момент находки это факт поведения: система делает так. Спека фиксирует другое утверждение: система должна делать так.

Сначала нужно подтвердить, что это корректное, приемлемое поведение и только потом вносить в спеку

Да, согласен. Про легаси я не учел. Там чаще всего документации либо нет, либо она устарела.

Да, обычно найденную особенность поведения по умолчанию считают нормальной, если не прилетело с претензией. Но зазор есть и может жить неделями и месяцами

Спасибо за статью, познавательно!

Особенно приятно читать, что частично реализованные в нулевых идеи обретают новую интересную жизнь, вот цитата из моего небольшого опуса от 12-го года:

Концепция необходимого и достаточного ТЗ

Что такое необходимость ТЗ?:

Все сведения, содержащиеся в Техническом задании должны быть необходимы для разработки ПО, т.е. они содержат информацию, относящуюся исключительно к устройству разрабатываемой системы и не содержат как "воды", так и, например, детального описания физической модели данных.

Что такое достаточность ТЗ?

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

При этом:

-       ТЗ должно обеспечивать возможность исключения взаимодействия заказчик-программист, по крайней мере, до выхода первого прототипа ПО, в частности, это означает, что заказчик понимает из ТЗ как будет работать система, а программист понимает, как разработать такую систему, причём и заказчик и аналитик и программист "говорят на одном языке";

-       ТЗ должно содержать некоторую логическую модель будущей системы, реализуемость которой должна быть доказуема.

Понятие "операция"

Связующим звеном, или даже универсальным понятием, которое позволило организовать диалог заказчики – аналитики – программисты на одном языке, а также увязать в единую картину различные технологические фрагменты управления требованиями и разработки программного обеспечения, стало определение понятия "операция".

Про организацию требований, что такое операция и почему это не юзкейс вот здесь:

"Операционно-ориентированное управление требованиями"

https://ooprogramme.blogspot.com/2012/07/blog-post.html

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

Спасибо, отличная статья и полностью повторяет мое впечатление от разработки с llm.
Долго думал, как же назвать того, кто c LLM Agents + SDD генерирует код, валидирует не просто код, а еще и поведение (не тестировщик) cо сбором обратной связи уже на прод среде, и думаю, "Продуктовый инженер" подходит наиболее точно.
Отдельное спасибо за список источников для ознакомления.

Основная мысль статьи - знания теперь должны жить в спецификации, а не в коде.

А почему? Что им мешает по прежнему жить в коде? Что код переписывается - это не аргумент.

Согласен, "код переписывается" само по себе не аргумент, его переписывали всегда. Разница в том, как именно вносят изменения.

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

При генерации выживает только то, что попало на вход. Если на входе спецификация, переживёт то, что в ней записано. Если на входе старый код, агент видит поведение, но не видит замысла. Продублированная валидация для него выглядит как копипаста, а не как защита, поставленная после конкретной аварии. Он её аккуратно "починит", и тесты останутся зелёными.

То есть "что делает система" в коде жить может, и вполне успешно. А вот "почему так" и "что из этого обязательно, а что случайно" в коде не жило никогда. Раньше оно жило в головах и в страхе трогать. Это работало, пока менять было дорого. Теперь менять дёшево, и этот предохранитель пропал. Ситуацию могут спасти комментарии в коде, но они есть далеко не всегда.

При этом спецификация не обязательно отдельный документ. Комментарий "не убирать, см. инцидент такой-то" и тест, который падает, если это убрать, - тоже спецификация, просто лежит рядом с кодом. Важно не где оно лежит, а что оно записано явно и проверяется.

И да, для кода, который меняется раз в полгода и руками, всё это лишнее. Там знание спокойно доживает в коде.

как будто из моей головы соображения взял и разложил 👍

Во времена, когда ИИ еще не было, а нейронками занимались узкие специалисты, в нормально поставленных проектах написание кода занимало 25-35% времени, на тестирование уходило 45-55%, остальное на документацию и это без учета предпроектного обследования, если с ним, то на код 10-15%. Это про разработку, а не про тяп-ляп и в прод. Сейчас отдельные решения на базе ИИ сократили время на написание кода, зато увеличили время на ревью кода. На выходе - с кодера сняли рутину, но при этом увеличили требования к его квалификации. Интересно, зачем это было нужно кодеру? Бизнес в среднем по больнице получил увеличение затрат и иллюзию светлого будущего, вот мне интересно, оно бизнесу точно надо?-)

С цифрами согласен, они и объясняют, откуда у многих разочарование. Если код был 10-15% проекта, бесплатный код даёт максимум эти 10–15%. Кто ускорил только набор, тот получил то, что вы описали: код быстрее, ревью дольше, в сумме ноль.

Но у нас картина другая, и я её вижу не по ощущениям, а по срокам. Time to market снизился. Сейчас идёт проект, который без ИИ мы бы в этот срок точно не сделали. И усилился у нас каждый в команде, не только сильные.

Разница в том, что именно ускоряется. ИИ у нас работает не только в коде, а во всех тех 85%, которые вы перечислили: обследование, спецификация, документация, тесты. Там выигрыш и лежит.

Про ревью: никто не заставляет читать все артефакты. Если проверять сгенерированное построчно, выигрыш действительно съедается. Мы проверяем поведение и границы, а не строки.

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

Так что бизнесу это надо, но не как "купили токены и ускорились". Кто поменял только инструмент, получил рост затрат и иллюзию, тут вы правы. Кто поменял процесс, получил эффект.

А на чем спецификацию-то писать?

Неужели на простом человеческом английском? Так будучи детально написанной она во-первых, весьма вероятно будет больше программы по размеру, во-вторых, как обеспечить непротиворечивость спецификации?

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

Где проверка в спецификациях? Как обеспечить инварианты сквозь весь стек спецификаций?

P.S. разумеется речь про системы более сложные чем "тут кнопка зеленая, а иконка справа"

Это отличный вопрос! Как я понимаю, спецификация не должна быть полным описанием поведения. Если она полная и детальная - вы правы, это и есть программа, только на плохом языке и без компилятора. Она меньше кода ровно потому, что неполная: в ней решения и причины, а не всё поведение.

Типы при этом никуда не деваются. "Невалидные состояния должны быть невыразимы" - это строчка в спецификации, а компилятор - проверка для неё. Я вообще предпочту ограничение, которое ловит компилятор, любому абзацу текста: оно дешевле и надёжнее. Система типов - тоже спецификация, просто машинно-проверяемая, и где ограничение выражается в ней, там его и надо выражать.

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

Про непротиворечивость особенно интересно: верификатора для текста на русском, да и на любом другом языке, нет. Противоречие всплывает двумя способами - агент приходит с вопросом либо две проверки не проходят одновременно. Это сильно хуже тайпчекера, и как это закрыть, я не знаю. Разные фреймворки пытаются это решить дополнительными проходами ревью, кросс-проверками ну и так далее. Все эти несоответствия находятся при написании кода.

Инварианты сквозь стек держатся не текстом, а исполняемыми проверками на границах. Критерий у Фаулера такой: если переписать сервис на другом языке и тесты становятся бесполезны, они стоят не на той границе. Текст говорит "почему", проверка держит "что".

Во-многом вы правы (вы автор или это перевод) - думаю мы к этому придём в любом случае, просто нужно время.

Ну вот как всю историю работы до этого выделяли "слои абстракции": Процессы -> Система (инфраструктура -> программа(модель -> преставление -> обработка)), так и сейчас выделят.

Тут есть несколько проблем (не про "зрелость" инструментов и подходов, а что надо поменять относительно "сегодня"):

  1. Поумнение агентов (а пока оно идёт зверскими темпами о чём-то стибальном говорить рано)

  2. Агенты пока ещё плохи в написании "настоящей" архитектуры. При попытке скатать ему "ты архитектор, задача сделать Y задизайни" - он НЕ спрашивает "как будут использовать Y", он спрашивает: "мне применить подход X1 или X2".

  3. Spec - рано или поздно придётся сильно разедять: любой достаточно большой и старый проект обрастает Jira/Confluence текстом, который больше, чем сам код проекта. Вот этот "мега-спек" надо будет разделить на слои (замысел, дизайн, ограниченяи), так, чтобы можно было рано или поздно сказать: "возьми модуль Х и перепиши.

Это не перевод, а действительно моя статья :)

Пункт 3 - ровно то, что я вижу каждый день, и мне кажется, дело тут не в зрелости. Агент оптимизирует внутри рамки, которую ему дали, а саму рамку под сомнение не ставит. Сказали "сделай дизайн Y" - он так и сделает, а вопрос "нужен ли Y и кому" не его. У меня то же самое с интерфейсом: разложит архитектуру, очереди, ретраи - и ни слова про то, что человек увидит на экране, пока не ткнёшь носом. Поэтому для кого и что обещаем пока остаются человеку. Не потому что агент глупый, а потому что это не вопросы про решение поставленной задачи.

Пункт 4 - согласен, и у меня это пока только гипотеза. Критерий разделения, к которому я пока склоняюсь, - время жизни. Верхний слой: то, что переживает замену реализации - обещания системы, границы, причины решений. Нижний: то, что умирает вместе с модулем. Удобный тест есть у Фаулера: если переписать сервис на другом языке и проверки стали бесполезны, значит, они лежали не в том слое. То есть начинаем с абстрактных вещей: конституция, код-стайл, все то, что не меняется или редко меняется, а затем переходим к частному.

Про мега-спеку из Jira и Confluence добавлю одну ловушку. Объём это еще полбеды. Хуже, что он копился без направления: правка шла в код, а документ оставался как был. Восстановить из такого замысел до конца не получится, видно что сделано, но не почему. Способа лучше, чем начинать не с разбора накопленного, а с записи решений по ходу, я пока не нашёл.

Агент оптимизирует внутри рамки, которую ему дали, а саму рамку под сомнение не ставит.

Я специально проводил эксперимент так, чтобы надо было уточнить способ использования (как минимум агенту была назначена роль, на которой человек в первую очередь уточняет способ использования) и на его основе принять решение - а он тупо спрашивает "мне применить техническое решение Х1 или Х2". Пока к дизайну кажется он не готов.

Ну я сам об этом почти направленно не думаю. Сейчас мы по сути сняли с телеги сбрую выкинули лошадь и поставили на телегу ДВС с ремнём на колёса. Каким будет "программирование 2.0" - отдельный большой слой работы во-первых непдъёмный для одного человека, во-вторых таки ожидащий "устаканивания умности агентов".

Хуже, что он копился без направления: правка шла в код, а документ оставался как был.

Теоретически (Jira, история Confluence, история git) - раскопать можно, практически - вряд ли достижимо.
Вероятно (в процессе формализации программирования 2.0 это всё разделят по границам о которых вы пишите и ещё по десятку разных) будут выделять контуры разной степени актуальности - и агенту будут давать доступ к минимальному необходимому, со словами "сделай красиво, перепиши этот модуль, вот вся необходимая дока".

Да, сделай красиво - это мечта :)

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

Почти всё, что я видел в сети про разработку с помощью AI, подталкивало меня к этой мысли, а эта статься привела к ней окончательно.

Не знаю, у кого как, а у меня кодинг начинается задолго до того, как мысль сформирована окончательно. Примерно понял, что должно получиться — и вперед. За несколько итераций разработки появляются сущности и взаимоотношения между ними, а так же код верхнего уровня, построенный на их базе. В идеале, должен получиться код, обладающий свойствами нотации, особенно, если взять ЯП с хорошей системой типов. Собственно, сам факт приобретения кодом таких свойств уже говорит о том, что произошла материализация ментальной модели. Если модель корректная — то налицо сплошной профит — и код хороший, и программист сам понял, что написал.

Очевидно, такой подход не всегда оправдан, иногда нужно просто заставить железяку сделать должное. Но если проект долгосрочный, а база, то есть сущности и взаимоотношения между ними, предполагает широкое повторное использование, то смысл появляется.

Это с одной стороны.

А с другой, если мы из процесса убираем программирование как инструмент формирования и валидации ментальной модели, заменяя его на принуждение одним черным ящиком с нестабильными свойствами (человек) другого черного ящика с нестабильными свойствами (LLM) создать третий черный / серый ящик с заданными свойствами (программа) посредством спецификации, то мы попадаем на неизбежное разрастание спецификации, и весь вопрос в том, что быстрее настанет — завершение разработки или момент, за которым усилия по осознанию и поддержанию спецификации в порядке превысят выгоду от генерации кода с помощью ИИ.

При этом даже спецификация на ЯП (тесты) нас не сильно от этого спасет. Неоднократно сталкивался с феноменом, когда тестирование в лоб по методу ЧЯ приводило к безумному разрастанию количества тестов, и было это задолго до того, как разработка с AI инструментами стала массовым явлением.

Так что же делать? Не знаю. Скорей всего, нужно купить участок земли побольше вдали от крупных городов и дорог, освоить натуральное хозяйство. Если получится пережить грядущие катаклизмы без падения технологического уровня цивилизации, то, возможно, мы придем к рабовладельческому строю на новом этапе — рабами станут роботы. Если доживу — брошу IT. А если не доживу — то и хрен со мной.

Если бы программирование сводилось к переводу, проект выходного дня не занял бы два месяца. Переводом как раз занимались агенты, и ушло на него меньше всего.

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

Но риск вы называете настоящий, и я его у себя вижу. Писать руками заставляет пройти через каждое решение. Читать сгенерированное позволяет проскользнуть мимо. Защита у меня одна - проверять не глазами, а падающим статическими проверками. Достаточно ли этого? Вряд ли. Но и дебаг и починка становится кратно быстрее.

Про разрастание спецификации - это ровно тот вопрос, которым я закончил статью. Делим на фичи, не повторяем то, что уже написано. Туда идёт не поведение целиком, а решения и причины. Поведение проверяется, а не описывается.

А про взрыв числа тестов вы сами назвали причину. Тестирование в лоб по чёрному ящику перечисляет случаи вместо того, чтобы задавать инварианты, поэтому и растёт комбинаторно. Критерий, которым я пользуюсь: если переписать модуль на другом языке и тесты стали бесполезны, значит, они стояли не на той границе.

А если технологический уровень упадёт, то спецификации станут не очень актуальны.

Пост суперский в том смысле, что оголяет корень проблемы, но кринжовый в том смысле, что “сляпан на коленке” (тут и характерное для нейрослопа обилие безличных предложений и инфинитивов, и возникающие из ниоткуда “Вастрики” и акронимы, и обилие листов, болдов, “выводов” и прочей структурщины, не характерной для мясных программистов), как и подавляющее большинство теперешнего ИИ-кода, созданного без осмысленного человеческого ввода и проверки. А по сути (IMHO, конечно), всё сводится к нахождению баланса между “настоящие программисты не нуждаются в комментариях: текст программы все объясняет сам” (есть тут люди, которые сходу вспомнят, откуда цитата? ;), и “текстовое программирование в конечном итоге исчезнет, разработчики будущего будут компилировать сами UML-модели напрямую в исполняемые файлы” (кстати, Фаулер - автор одной из популярных книжек про UML). И это, действительно, “вопрос на миллион”, и, похоже, он не будет решён окончательно никогда* (*прим.: никогда - момент наступления технологической сингулярности).

P.S. Тем, кто не верит в сингулярность, нужно просто дождаться плато на графике (экспоненциальном ;) увеличения возможностей ИИ. У меня есть опасения, что основной заботой нашего брата на ближайшее будущее будут попытки “закрепиться” на этом, почти отвесном, склоне, на тех небольших “уступах”, которые возникнут по факту техногенных катастроф от внедрения ИИ, увы…

Фаулеры разные: UML Distilled писал Мартин, а "Regenerative Software", на которую я ссылаюсь, - Чад. Совпадение фамилий случайное. Хотя сравнение у вас точное: между "код объясняет сам себя" и "компилируем модель напрямую" и правда лежит весь спор. Цитата, кстати, - Ed Post, «Real Programmers Don't Use Pascal», 1983.

Черновик у меня действительно готовит агент по моему потоку мыслей, дальше я вычитываю, правлю и проверяю факты. А блоки "Вывод" - осознанный элемент формата, в блоге это оформленные врезки. На Хабре такого блока нет, поэтому при переносе они выродились в цитату с болдом, и структуры стало видно больше, чем задумывалось.

Вастрик не из ниоткуда: он вспомнился из-за вот этой заметки - https://vas3k.blog/notes/pets_vs_cattle/, ссылка есть в источниках.

По теме не соглашусь с "без осмысленного человеческого ввода и проверки". Опыт весь мой: свои проекты, свои грабли, свои цифры, подобранные и перепроверенные источники.

Что случилось со списком ссылок?))

Sign up to leave a comment.

Articles