Принципы Joel Spolsky по разработке ПО всё ещё работают, даже когда ИИ быстро пишет код. Узнайте, как фокус на тестах, найме и простоте помогает сохранить корректность.

ИИ может генерировать код, который выглядит рабочим, за считанные минуты. Это меняет темп проекта, но не меняет то, что делает ПО успешным. Уроки из «software truths» Джоэла Спольски никогда не были про скорость набора. Они про суждение, циклы обратной связи и избежание само‑навязываемой сложности.
Изменилось то, насколько дешёво создавать код. Вы можете запросить три подхода, пять вариантов или полный перепис и получить результат мгновенно. Неизменным остаётся цена выбора правильного подхода, его проверки и жизни с ним в течение месяцев. Сэкономленное на написании время часто перераспределяется на решение того, что вы имели в виду, валидацию крайних случаев и гарантию, что сегодняшняя быстрая победа не превратится в завтрашний налог на поддержку.
Корректность, безопасность и поддерживаемость всё ещё требуют реального времени, потому что они зависят от доказательств, а не от уверенности. Поток входа в систему не завершён, когда код компилируется. Он завершён, когда он надёжно отвергает неправильные вводы, обрабатывает странные состояния и не сливает данные. ИИ может звучать уверенно и при этом пропустить одну критичную деталь: проверку прав на эндпоинте или условие гонки при обновлении платежа.
ИИ наиболее полезен, когда вы относитесь к нему как к быстрому инструменту для набросков. Он прекрасно справляется с бойлерплейтом, повторяющимися паттернами, быстрыми рефакторингами и исследованием опций, которые можно сравнить бок о бок. При правильном использовании он сокращает фазу «чистого листа».
ИИ наносит наибольший вред, когда вы даёте ему размытые цели и принимаете результат как есть. Повторяются одни и те же паттерны ошибок: скрытые предположения (неозвученные бизнес‑правила), непроверенные пути (обработка ошибок, ретраи, пустые состояния), уверенные ошибки (правдоподобный код, который тонко неверен) и «умные» решения, которые трудно объяснить потом.
Если код стал дешёвым, новым дефицитом становится доверие. Эти истины важны, потому что они защищают это доверие: перед пользователями, командой и вашим будущим «я».
Когда ИИ генерирует фичу за минуты, появляется соблазн считать тестирование той медленной частью, от которой нужно избавиться. Точка Спольски всё ещё верна: медленная часть — это то место, где живёт правда. Код легко произвести. Правильное поведение — нет.
Полезный сдвиг — рассматривать тесты как проверяемые требования. Если вы не можете описать ожидаемое поведение в проверяемом виде, вы ещё не завершили размышления. В работе с ИИ это важно даже больше, потому что модель может уверенно выдать что‑то чуть неверное.
Начинайте тестировать с того, что причинит наибольший вред при ошибке. Для большинства продуктов это ключевые потоки (регистрация, оплата, сохранение, экспорт), права доступа (кто может смотреть, редактировать, удалять) и целостность данных (нет дубликатов, правильные итоги, безопасные миграции). Затем покройте края, которые обычно вызывают ночные инциденты: пустые вводы, длинные строки, часовые пояса, ретраи и ненадёжные внешние границы вроде платежей, писем и загрузки файлов.
ИИ отлично предлагает тесткейсы, но он не знает, что вы реально пообещали пользователям. Используйте его как партнёра по мозговому штурму: попросите недостающие краевые случаи, сценарии злоупотребления и комбинации прав. Затем выполните человеческую работу: сопоставьте покрытие с реальными правилами и удалите тесты, которые «тестируют реализацию», а не поведение.
Делайте ошибки информативными. Упавший тест должен сказать, что сломалось, а не отправлять на поиски сокровищ. Держите тесты маленькими, называйте их как предложения и делайте сообщения об ошибках конкретными.
Предположим, вы создаёте простое приложение «заметки команды» при помощи ИИ. CRUD‑экраны появляются быстро. Риск корректности — не интерфейс. Риск в контроле доступа и правилах данных: пользователь не должен видеть заметки другой команды, правки не должны перезаписывать более новые изменения, удаление заметки не должно оставлять сиротские вложения. Тесты, которые закрепляют эти правила, будут казаться узким местом, но они же и ваша страховочная сеть.
Когда тестирование — узкое место, оно заставляет ясность. Эта ясность мешает быстрому коду превратиться в быстрые баги.
Одна из самых надёжных истин: простой код побеждает хитрый. ИИ делает соблазнительным принимать вычурные абстракции, потому что они выглядят отшлифованными и приходят быстро. Цена проявляется позже: больше мест для скрытия багов, больше файлов для просмотра и больше моментов «что это вообще делает?».
Когда код дешёв, сложность — это то, за что вы платите. Небольшой, скучный дизайн проще тестировать, проще менять и проще объяснить. Это особенно важно, когда первый набросок пришёл от модели, которая может уверенно звучать, оставаясь тонко неверной.
Практическое правило — держать функции, компоненты и модули настолько небольшими, чтобы коллега мог просмотреть их за несколько минут, а не часов. Если React‑компонент требует нескольких кастомных хуков, локальной машины состояний и слоя «умного рендера», остановитесь и спросите: решаем ли мы реальную проблему или просто принимаем архитектуру, потому что ИИ её предложил?
Несколько «тестов простоты» помогут вам сопротивляться:
Промпты здесь важны. Если вы просите «лучшую архитектуру», вы часто получаете переусложнённую. Просите ограничения, которые тянут в сторону меньшего числа движущихся частей. Например: используйте самый простой подход с минимальным количеством файлов; избегайте новых абстракций, если они не устраняют дублирование в трёх и более местах; предпочитайте явный код универсальным хелперам.
Конкретный пример: вы просите ИИ добавить ролевой доступ на админ‑страницу. Хитрый вариант вводит фреймворк прав, декораторы и DSL конфигурации. Простой вариант проверяет роль пользователя в одном месте, защищает маршруты в одном месте и логирует отказ в доступе. Простой вариант легче ревьюить, легче тестировать и сложнее неверно истолковать.
Если вы работаете в чат‑инструменте вроде Koder.ai, простота также делает снимки и откат более ценными. Небольшие очевидные изменения проще сравнить, сохранить или вернуть назад.
Когда код легко генерировать, дефицит навыка — в умении выбирать, что вообще должно существовать, и следить, чтобы это было корректно. Старый совет «нанимайте отличных программистов» остаётся в силе, но роль смещается. Вы нанимаете не для того, чтобы кто‑то печатал быстрее. Вы нанимаете того, кто может судить, дорабатывать и защищать продукт.
Самые ценные люди в разработке с поддержкой ИИ обычно обладают четырьмя чертами: суждение (что важно), вкус (что хорошо), навык отладки (нахождение корня проблемы) и коммуникация (ясное объяснение компромиссов). Они могут взять фичу, написанную ИИ, которая «в целом работает», и превратить её в то, чему можно доверять.
Вместо того чтобы просить идеальное решение с нуля, дайте кандидату pull request, сгенеренный ИИ (или вставленный diff) с несколькими реалистичными проблемами: неясные имена, скрытый краевой случай, отсутствующие тесты и небольшая ошибка безопасности.
Попросите объяснить, что код пытается сделать простыми словами, найти самые рискованные части, предложить исправления и добавить (или набросать) тесты, которые поймают регрессии. Если хотите сильный сигнал, попросите также изменить инструкции так, чтобы следующая попытка ИИ была лучше.
Это покажет, как они думают в реальных условиях: с несовершенным кодом, ограниченным временем и необходимостью выбирать приоритеты.
ИИ часто звучит уверенно. Хорошие сотрудники умеют возражать. Они способны сказать «нет» фиче, которая добавляет сложность, «нет» изменению, которое ослабляет безопасность, и «нет» отправке без доказательств.
Конкретный сигнал — как они отвечают на «Смерджишь это?» Сильные кандидаты не дают ощущения; они дают решение и короткий список необходимых изменений.
Пример: вы просите «быстрое» обновление контроля доступа, и ИИ предлагает втихую раскидать проверки по хендлерам. Сильный кандидат отвергает этот подход и предлагает один явный слой авторизации плюс тесты для путей админа и не‑админа.
Наконец, создавайте общие стандарты, чтобы команда редактировала вывод ИИ одинаково. Держите всё просто: одно определение «готово», единые ожидания ревью и базовая матрица тестирования.
Когда ИИ генерирует много кода за минуты, легко пропустить обдумывание и просто итерировать. Это работает для демо. Это ломается, когда нужна корректность, предсказуемое поведение и меньше сюрпризов.
Хороший промпт обычно — это короткая спецификация в маскировке. Прежде чем просить код, превратите размытое желание в несколько критериев приёмки и явных не‑целей. Это предотвращает тихое расширение области задач моделью (и командой).
Держите спецификацию маленькой, но точной. Вы не пишете роман. Вы задаёте границы вокруг:
Определите «готово» до генерации, а не после. «Готово» должно быть больше, чем «компилируется» или «интерфейс похож на правильный». Включите ожидания по тестам, обратную совместимость и что мониторится после релиза.
Пример: вы хотите «добавить сброс пароля». Чёткая спецификация могла бы сказать: пользователь запрашивает сброс по email; ссылки истекают через 15 минут; сообщение одно и то же, есть ли email в базе или нет; лимит запросов на IP; логируем попытки сброса без хранения токенов в открытом виде. Не‑цель: редизайн страницы логина. Теперь ваш промпт имеет ограждения и ревью становятся проще.
Ведите лёгкий журнал изменений решений. Один абзац на решение достаточно. Запишите, почему вы выбрали подход и почему отклонили альтернативы. Когда через две недели кто‑то спросит «почему так?», у вас будет ответ.
Самое большое изменение с ИИ — код производить легко. Сложная часть — решить, что код должен делать, и доказать, что он это делает.
Начните с записи цели и ограничений простым языком. Включите, что никогда не должно случиться, что может быть медленным и что вне области. Хорошее ограничение — проверяемое: «Ни один пользователь не должен видеть данные другого пользователя» или «Итоги должны совпадать с экспортом финансов до копейки».
Прежде чем просить код, попросите простой дизайн и перечисление компромиссов. Вы хотите, чтобы ИИ показал своё мышление в форме, которую вы можете оценить: что он будет хранить, что будет валидировать и что логировать. Если он предложит хитрое решение, нажмите на это и попросите самый простой вариант, который всё ещё удовлетворяет ограничения.
Повторяемая петля выглядит так:
Вот небольшой сценарий: вы добавляете «статус возврата» на экран заказа. ИИ быстро генерирует UI, но корректность живёт в краевых случаях. Что если возврат частичный? Что если платёжный провайдер повторно шлёт webhook? Сначала опишите эти кейсы, затем реализуйте одну часть (столбец в БД плюс валидация) и проверьте её тестами перед тем, как двигаться дальше.
Если вы используете Koder.ai, такие функции, как режим планирования, снимки и откат, естественно вписываются в эту петлю: планируйте сначала, генерируйте небольшими кусками и фиксируйте точку восстановления для каждого значимого изменения.
Когда генерация кода быстрая, легко начать думать, что продукт — это сам код. Это не так. Продукт — это поведение: приложение делает то, что нужно, даже когда что‑то идёт не так.
ИИ часто звучит уверенно, даже когда догадывается. Ошибка — пропустить скучную часть: запуск тестов, проверку краёв и валидацию реальных вводов.
Простая привычка помогает: прежде чем принять изменение, спросите «Как мы узнаем, что это правильно?» Если ответ «похоже правильно», вы играете в азартную игру.
ИИ любит добавлять бонусы: кэширование, ретраи, лишние настройки, дополнительные эндпоинты, красивый UI. Некоторые идеи полезны, но они повышают риск. Многие баги рождаются из «приятных иметь» функций, которых никто не просил.
Держите жёсткие границы: решите задачу, зачем вы её решали, и остановитесь. Если предложение действительно ценно — вынесите его в отдельную задачу с собственными тестами.
Большой коммит от ИИ может скрывать дюжину несвязанных решений. Ревью превращается в формальность, потому что никто не может охватить всё.
Обращайтесь к выводу чата как к черновику. Разбивайте его на маленькие изменения, которые можно прочитать, запустить и откатить. Снимки и откат полезны только если вы делаете их в разумных точках.
Несколько простых ограничений предотвращают большинство проблем: одна фича на change set, одна миграция на change set, одно рискованное изменение за раз (auth, платежи, удаление данных), тесты обновлены в том же change set и короткая заметка «как проверить».
ИИ может воспроизводить паттерны из обучающей выборки или рекомендовать зависимости, которые вы не понимаете. Даже если с лицензией всё в порядке, больший риск — безопасность: захардкоженные секреты, слабая обработка токенов или небезопасные операции с файлами и запросами.
Если вы не можете объяснить, что делает фрагмент, не шипуйте его. Попросите более простой вариант или перепишите сами.
Многие баги «на моей машине» — это на самом деле проблемы данных и масштаба. ИИ может создать смену схемы, не подумав про существующие строки, большие таблицы или время простоя.
Реалистичный пример: модель добавляет NOT NULL‑столбец в PostgreSQL и заполняет его медленным бэкфиллом. В продакшене это может заблокировать таблицу и сломать приложение. Всегда думайте, что произойдёт с миллионом строк, с медленной сетью или с неудачным деплоем посередине.
Относитесь к выводу ИИ как к быстрому черновику, а не к законченной фиче. Сначала пропишите 3–5 проверяемых критериев «пройден/не пройден», затем сгенерируйте одну маленькую часть (один эндпоинт, один экран, одну миграцию) и проверьте её тестами и сценарием отказа перед тем, как идти дальше.
Потому что тесты — это место, где вы узнаёте, что код на самом деле делает. ИИ может сгенерировать правдоподобную логику, которая пропускает одно ключевое правило (права доступа, повторные попытки, крайние состояния). Тесты превращают ваши ожидания в то, что можно запускать, повторять и доверять.
Начните с того, что причинит наибольший вред при поломке:
Добавляйте покрытие после того, как критичные для ущерба поведения зафиксированы.
Попросите самый простой подход с явными ограничениями, затем удаляйте лишние уровни, если они не «оправдывают аренду». Хорошее правило: не вводите новую абстракцию, если она не устраняет дублирование в трёх и более местах или не делает проверяемость проще.
Напишите короткую спецификацию: входы, выходы, ошибки, ограничения и не‑цели. Включите конкретные примеры (запросы/ответы, крайние случаи). Затем определите «готово» заранее: какие тесты нужны, требования к обратной совместимости и быстрый способ проверки.
Разбивайте. Делайте каждое изменение небольшим и удобным для ревью в течение нескольких минут:
Так ревью остаётся настоящим, а не формальным.
Не доверяйте уверенности — доверяйте доказательствам. Запускайте тесты, пробуйте неверные данные и проверяйте границы прав. Также ищите типичные ловушки ИИ: пропущенные проверки авторизации, небезопасная сборка запросов, слабая обработка токенов и безмолвное подавление ошибок.
Предпочитайте явные конечные точки переходов вместо «обновить что угодно». Например: submit, approve, reject, pay вместо общего update. Затем пишите тесты, которые проверяют, кто может совершать каждый переход и какие переходы запрещены.
Дайте кандидату AI‑сгенеренный diff с реальными проблемами: неочевидные имена, упавший тест, краевой случай и небольшая уязвимость. Попросите объяснить намерение кода, найти наиболее рискованные места, предложить исправления и набросать тесты, которые они бы добавили.
Используйте возможности инструмента, чтобы поддерживать дисциплинированный цикл: сначала план, генерируйте маленькими порциями, делайте снимки перед рискованными изменениями и откатывайтесь, если проверка не проходит. В чат‑ориентированных платформах вроде Koder.ai это хорошо сочетается с режимом планирования, снимками и откатом — особенно при изменениях в авторизации, платежах или миграциях.