У меня небольшая продуктовая команда, 12 человек, пилим B2B-логистику. Go, React, PostgreSQL, всё на кубере. Предметка скучная снаружи, но внутри — ад: у каждого перевозчика свой API, и каждый API как будто писали в пятницу вечером. У СДЭК поле tariff_code в одном эндпоинте строка, а в другом число, я до сих пор не понимаю почему, и никто там не понимает, я спрашивал.
Ну и вот, осенью 2025-го у нас ушёл мидл. Типичная история — нашёл +80к в каком-то финтехе, ему там обещали «плоскую структуру и свободу принятия решений» (спойлер: через три месяца он написал мне что хочет вернуться, но мы уже наняли замену). Замену, кстати, нашли не быстро. Провели штук двадцать собесов, может больше, я со счёта сбился после пятнадцатого.
Рынок такой: половина не может обход дерева написать (я серьёзно, я уже думал может я что-то не так спрашиваю, но нет), трое врали про опыт настолько плохо что мне неловко было за них, и один спросил можно ли работать из Бали при условии что созвоны в девять утра. Нельзя, у нас on-call.
Короче, появился кандидат. Назову его Дима.
Дима прошёл нормально. Не блестяще. Не из тех кто цитирует спеку Go по памяти и рисует на доске идеальные архитектуры (таких я вообще-то побаиваюсь, они обычно в реальных проектах потом страдают от того что мир не идеален). Он объяснил как бы спроектировал сервис уведомлений, нарисовал вменяемую схему. Спросил правильные уточняющие вещи — что важнее, at-least-once или exactly-once, какие требования по latency. В горутинных паттернах немного поплыл, но я и сам в них плыву, если честно, я каждый раз лезу перечитывать пост Самира Аджмани когда надо что-то сложное с каналами сделать.
300к гросс, согласился, вышел. Первую неделю онбордился, читал доку, поднимал локальное окружение, задавал вопросы в слаке. Вопросы адекватные. Я выдохнул.
А потом.
Интеграция с API регионального перевозчика. Задача средняя: документация на 40 страниц PDF (половина на ломаном английском, половина вообще не документация а маркетинговые слайды, забыли убрать), написать клиент, тесты, воткнуть в пайплайн. Обычно 5–7 рабочих дней.
Дима сделал за два.
Я удивился. Полез в PR.
Код работал. Тесты зелёные. Линтер молчит. Но у меня... ну, знаете это ощущение когда что-то не так, но ты не можешь сформулировать что? Я два дня не мог это сформулировать, а потом до меня дошло по кусочкам.
Первое — комментарии. У нас стиль такой: комментарий пишем только когда код делает что-то неочевидное, и объясняем почему, а не что. Дима навешал комментарий на каждую функцию, и все одинаковые: // functionName handles the processing of X and returns Y. Я сказал — у нас так не принято. Он убрал. Из СЛЕДУЮЩЕГО PR. В этом остались. Мелочь, ладно.
Второе — нейминг. Тут сложнее объяснить. Переменные названы нормально, но... слишком нормально? Вот у живых программистов нейминг — это привычка. Кто-то всегда пишет resp, кто-то res, кто-то apiResp. И это стабильно, это рефлекс. У Димы в одном файле client, в другом apiClient, в третьем transportClient. Каждый раз немного по-другому. Не ошибка, а отсутствие привычки. Хотя... может я придираюсь. Может человек просто так пишет, без устоявшегося стиля. Я тогда решил что придираюсь.
Третье — обработка ошибок. Go-шная классика, if err != nil. У нас в проекте сообщения короткие, типа "unmarshal transport resp", потому что рядом stack trace и wrap, длинные строки только мешают потом grep-ать. Дима писал развёрнутые предложения: "failed to unmarshal transport response: unexpected format". Тоже не ошибка. Но за два месяца стиль проекта обычно подхватывают, а он не подхватывал.
Я дал фидбек, показал конвенции.
Дима сказал «понял, исправлю».
В следующем PR повторил всё то же самое.
(Тут, кстати, надо отступление. Тарификация в логистике — это вообще отдельный уровень ада. У нас там if на три экрана, вложенные, потому что тарифы зависят от типа доставки, региона, веса, объёмного веса (это вес / 5000, для тех кто не в курсе, и этот коэффициент 5000 зашит у нас хардкодом с 2019 года, потому что «потом поменяем на конфиг» — ну вы знаете как бывает с потом), и ещё от того, есть ли у клиента договорная скидка или промокод или и то и другое.)
Вот этот рефакторинг я и дал Диме. Разложить ифы на стратегии. Задача требует не столько кода, сколько понимания предметки.
Дима спросил где документация. Я ответил — Confluence, тред в слаке от марта, запись созвона с продактом. Нормальная ситуация, у нас документация как у всех, то есть существует кусками, а остальное — устная традиция и git blame.
Через четыре дня — PR, 1200 строк. Разложено по файлам, паттерн Strategy, интерфейсы, тесты. Красиво.
На третьем файле я остановился.
Он обработал случай «договорная скидка + промокод одновременно» неправильно. У нас порядок такой: договорная скидка к базовой цене, промокод к цене после скидки, но не ниже минимальной маржи. Дима применил оба дисконта параллельно и взял минимум. Тесты проходили — он же сам написал тесты, и тесты проверяли его логику, а не нашу.
Я написал: порядок скидок неправильный, посмотри как работает сейчас. Через 20 минут новый коммит. Теперь промокод первый. Опять не то. Я написал подробнее. Третий коммит — правильно.
Вот тут меня кольнуло. Не ошибка — все ошибаются. А то, что три раза подряд три разных варианта, и ни один не показывал что он ПОНЯЛ принцип. Он не спросил «а почему такой порядок?». Не уточнил «а минимальная маржа — она где задаётся?». Просто выдал другую реализацию. Как будто... ну, перегенерировал с новым промптом.
Хотя, возможно, я уже тогда был предвзят. Не знаю. Может он просто стеснялся спрашивать. Бывает такое у новичков в команде — боятся показаться глупыми.
Ладно. Я сделал вещь, за которую мне стыдно.
Точнее — не то чтобы стыдно, я бы сделал это снова, но осадочек остался.
Я написал Диме задачу с ловушкой. Попросил добавить новый тип доставки и в описании написал что для расчёта стоимости «используется адаптированный алгоритм Bellman-Ford». Это бред. Bellman-Ford — это про кратчайший путь в графе, к расчёту стоимости он вообще никаким боком. Любой бэкендер с вменяемым CS-бэкграундом переспросит.
Дима не переспросил.
Через два дня пришёл PR с функцией BellmanFordPricing. Внутри — линейный расчёт по зонам, к алгоритму Bellman-Ford никакого отношения. В комментарии написано // implements an adapted Bellman-Ford algorithm for zone-based pricing. Я открыл GPT, вставил свою задачу, попросил написать реализацию. Подход тот же. Имя функции то же.
Ну.
Позвал на созвон. Один на один.
Я спросил прямо: ты скармливаешь задачи в ChatGPT?
Он помолчал. Сказал что Copilot подсказывает, как у всех.
Я сказал что не про подсказки, а про копипаст целиком.
Он сказал — ну, использую как инструмент, результат же есть, все используют.
И вот дальше был разговор, и мне сложно его пересказать объективно, потому что я к тому моменту уже был на взводе. Но попробую.
Его аргументы: код работает, тесты зелёные, какая разница как именно написано. Сеньоры в фаанге публично пишут про Copilot. За 300к на рынке найти мидла который всё руками пишет — ну, удачи.
Мои: он не понимает что делает его код. Когда я спрашиваю «почему такая структура» — мнётся. Не может объяснить trade-off-ы. Не может сказать что будет при десятикратной нагрузке. За два месяца не стал лучше понимать наш домен. Обычно мидл через пару месяцев начинает замечать вещи — «а может тут лучше через event?», «мы же дважды ходим в базу, можно оптимизировать». Дима задавал одни и те же вопросы.
И ещё — дебаг. Когда на стейджинге упал его сервис, он полтора часа ковырялся и не нашёл причину. Я нашёл за десять минут — race condition в горутинах, классика. Но для этого надо понимать как горутины работают, а не знать слово «горутина».
Впрочем, вот тут я сам себя ловлю на том что подбираю аргументы в свою пользу. Потому что есть и другая сторона, и она меня грызёт.
Код РАБОТАЛ. Это факт. Задачи ЗАКРЫВАЛИСЬ. Тоже факт.
Если бы я не полез смотреть код внимательно — я бы не заметил. Если бы не ловушка с Bellman-Ford — вообще бы не поймал, возможно, до первого серьёзного инцидента. А может и не было бы серьёзного инцидента, кто знает. Может он бы проработал год, закрывал задачи, получал зарплату, и всё было бы нормально. Я не знаю. Это не риторический приём, я правда не знаю.
Ещё меня гложет вот что. Граница между «использую LLM как инструмент» и «я оболочка для LLM» — она размытая. Я сам иногда прошу Claude написать тест. Или бойлерплейт. Или «как называется эта функция в стандартной библиотеке, забыл». Чем это принципиально отличается от того что делал Дима? Масштабом? Процентом? Где порог?
Не знаю. Серьёзно.
(Кстати, забегая вперёд: мы потом поменяли процесс найма, и это помогло, но не так сильно как я надеялся. Расскажу ниже.)
Расстались. Без скандала. Я объяснил что нам нужен человек, который понимает код. Он не спорил, по-моему ждал этого разговора. Отработал две недели, передал дела.
Мне его жаль. Он не дурак и не мошенник. Он попал в ситуацию, когда инструмент позволил делать работу, которую он не тянул сам. Какое-то время это работало.
Тут коротко, потому что это скучная часть, но кому-то может пригодится.
В live-coding секции теперь даю не «напиши», а «найди баг». 20 строк Go с race condition, или утечкой горутин, или неправильной обработкой контекста. К такому сложнее подготовиться через LLM, потому что надо ЧИТАТЬ код, а не генерировать.
Ещё на испытательном — одна задача на созвоне, шаря экран, без подготовки. Не на время. Мне не скорость нужна, мне нужно видеть процесс — как читает код, как формулирует гипотезы, как гуглит (гуглить можно и нужно), как дебажит.
Помогло? Ну... вроде да. Одного кандидата отсеяли на этом этапе, он очень нервничал когда я попросил шарить экран. Но выборка маленькая, двух человек наняли после нового процесса, оба вроде ок, хотя прошло мало времени чтобы судить.
Вот я это всё написал и думаю — а ведь проблема не в Диме. Дима — симптом.
Мы (ну, вся индустрия, но мы в том числе) годами строили процесс найма вокруг «ЧТО ты умеешь делать». Напиши функцию. Спроектируй систему. Реши задачу. И если человек выдаёт правильный результат — мы его нанимаем. Нам было плевать КАК он пришёл к результату, потому что раньше единственный способ выдать правильный результат был — уметь это делать.
Теперь есть второй способ. И наш процесс к этому не готов.
Но, может, это и не страшно? Может через пять лет «уметь правильно промптить» будет легитимным навыком, и мы будем нанимать не разработчиков, а операторов LLM, и платить им те же 300к, и всё будет нормально?
Я не знаю. Вроде бы нет, потому что когда прод падает — тебе нужен человек который понимает что происходит, а не человек который умеет описать проблему для чатбота. Но может я просто старею и бурчу.
Мой тимлид 2018 года наверное точно так же бурчал когда я гуглил ответы на StackOverflow вместо того чтобы читать маны.
UPD: после перечитывания заметил, что мой «тест с Bellman-Ford» — это, мягко говоря, спорный метод. Если бы Дима пришёл ко мне и сказал «я вижу что ты написал Bellman-Ford, но это же алгоритм графов, тут ошибка в ТЗ?» — а я бы ответил «нет, ты просто не понял, делай как написано» — это было бы манипуляцией. Я был готов к такому исходу и признал бы ошибку в ТЗ. Но он не спросил. В этом и суть.
Источник


