
— Кому бы вы разрешили себя оперировать — одному опытному уставшему хирургу или двум интернам с горящими глазами?
Многое зависит от контекста. Удалить вросший волос можно доверить и самому себе.
Договоренности о границах
Прежде чем ввязываться в дискуссию, хочу договориться о границах. Сейчас я занимаюсь бекендом, а вопрос получил от облачного провайдера, поэтому то, что скажу далее по части дискриминации джунов, имеет отношение в первую очередь к серверной разработке.
В статье буду касаться бекенда, фронтенда и мобильной разработки, потому что имел опыт в этих стеках. Чтобы избежать комментариев вроде:
Не работал на серьезных проектах — не надо обобщать
Оставлю небольшую сводку о последнем большом android-проекте
Кодовая база на момент ухода — более миллиона строк кода в монорепозитории только на Kotlin.
За время работы написал несколько приложений для девайсов с кастомной ОС: видео-плеер, музыкальный-плеер, youtube, приложение для выбора фильмов — все были частью одного монорепозитория. Помимо типичных для мобильного приложения задач, встраивал cobalt-браузер в андроид приложение, писал сервис для контроля аудиофокуса между кодом на плюсах (голосовой помощник, будильник) и фокусом от андроид-рантайма, поддерживал сервисы обмена сообщениями (пользовались ими вместо http).
Разделяю фронтенд и мобильную разработку, а не пишу просто «фронтенд», потому что работал и там, и там — и это совершенно разная работа. Есть решения, вроде Flutter или KMM, позволяющие иметь одну кодовую базу для всего, включая веб и десктоп, но практически все пишут фронтенд на другом стеке.
Что касается способностей кандидатов, давайте упростим: все одинаковые и отличаются только опытом, рабочими навыками и ценой их труда.
Резюме статьи
Ошибки в бекенде дороже, чем во фронтенде или мобильной разработке.
Джуниоры создают шум и хаос: ошибки в коде, утечки памяти, race condition и data race, несоблюдение конвенций, стиля, push force в мастер, нарушение правил безопасности, плохие абстракции, много вопросов и т.п.
Инструменты и процесс помогут решить часть из вышеперечисленного, но не всё и не всегда.
В случае с джуном нужно решать гору проблем, с выгоревшим сеньором — одну: потерю мотивации.
Пример кода вместо тысячи слов
Босс разрешил использовать этот блок кода как есть. Попробуйте найти проблемы сами, прежде чем заглядывать в ответ:
// assume this is invoked in transaction
fun ConnectionWrapper.lockTableRow(tableName: String, rowId: Int) {
require(!connection.autoCommit) {
RuntimeException("lockTableRow() designed to be invoked in transaction wrapper")
}
val result = connection.prepareStatement("""SELECT id FROM ${tableName} WHERE id = ? FOR UPDATE""").apply {
setInt(1, rowId)
}.executeQuery()
require(result.next()) {
throw BadRequestException("Can't lock ${tableName} id ${rowId}")
}
}
Ошибки (спойлер)
Проблемы по коду сверху внизу, жирным выделены критичные:
В дополнение к комментарию (или даже вместо него) можно было бы добавить проверку на то, что код в транзакции.
Непонимание, как работает функция require: внутри блока ожидается строка, которая будет текстом в выброшенном IllegalArgumentException.
Отсутствие защиты от SQL-injection, т.к. tableName тут является частью построения строки.
Утечка памяти. PrepareStatement — ресурс, который нужно закрывать.
Насколько хорошо делать утилитную функцию, уводящую от необходимости думать о том, какой lock нужен? Что если тут достаточно "for no key update" или "for key share"?
Очередной неправильный require.
Функция кидает ошибки разных уровней (нейтральный Runtime и завязанный на HTTP BadRequestException). Что если функция будет использоваться из джобы? Там не нужна специфичная для http ошибка.
Кстати, часть перечисленных выше ошибок (включая критичные), легко найти с помощью LLM уровня gpt4, причем без каких-либо специальных промптов.
Как отличить джуниора от сеньора
Точного ответа не существует, но можно посмотреть на проблему через три призмы:
Доход. Сеньор тот, кто получает сеньорскую зарплату.
Опыт. Субъективно, сеньор имеет хотя бы 3 года опыта в стеке и хотя бы 5 лет тем или иным образом разрабатывает софт. Можно подкорректировать числа в зависимости от стека.
Навыки. Сеньор хорошо владеет стеком, умеет общаться с заказчиком и прочими агентами, эффективно решает проблемы.
Не путайте мидла и сеньора
На web-просторах гуляет вот такая картинка:

На мой взгляд, ошибка в том, что на правой стороне — не сеньора, а мидл. Разумеется, бывают исключения, но обычно злоупотребление абстракциями («крутой код», принципы ООП, «финты», что-то хайповое) — это больше про мидлов и даже джунов, чем сеньоров. Доказать тезис не могу — отвечу мемом на мем:

Почему джуны особенно страшны в бекенде
Тестировать бекенд сложнее, чем мобильное приложение или фронтенд.
Проблем с производительностью на бекенде больше.
Проблем в архитектуре бекенда больше и они стоят дороже.
Разумеется, с каждым из этих пунктов можно поспорить, и всегда найдется контрпример, но кажется, что в общем случае, это так. Вижу аргументы против «1» и «2» даже в общем случае, разберу их ниже.
Тестирование front/mobile и backend
В мобильном приложении необходимо добиться корректной работы и адекватного UI на всех разрешениях экрана, у всех производителей. «Радуют» самсунги с их гнущимися экранами и модифицированной OS, а иногда нужно адаптировать UI и под планшеты, и под девайсы с квадратными экранами. Чтобы это тестировать, нужны образцы — по-хорошему, ферма устройств, на которых будут прогоняться болезненно долгие UI-тесты. Даже unit-тесты запускаются дольше, чем на backend-проекте, сопоставимом по размерам кодовой базы.
На фронтенде с тестами полегче. Пробовал Playwright на ClojureScript, чтобы писать web-тесты интерактивно с REPL, получилось легко, быстро и удобно.
С бекендом сложность в обилии технологий, которое является следствием сложности решаемых задач. Представьте систему из десятокв сервисов, общающихся друг с другом через разные брокеры сообщений, http, веб-сокеты, graphql, свои протоколы. Каждые сервис может иметь свой API Gateway. Данные могут храниться в разных базах данных, доступ к которым проходит через прокси, которые вносят свою логику (пример с pgbouncer). Чтобы настроить полноценное E2E-тестирование, нужно разобраться с кучей систем и понимать хотя бы поверхностно, как работает каждая.
Производительность front/mobile vs backend
В мобильной разработке нужно на слабом девайсе (если такие остались) добиться плавных анимаций, уберечь пользователя от видимых задержек и уместиться в малую оперативную память. Что касается применения алгоритмов, видел свою реализацию только однажы — обход графа зависимостей в огромном монорепозитории, но это больше про систему сборки большого проекта, чем про мобильную разработку.
О масштабируемости ни мобильному, ни фронтенд-разработчику думать не нужно: приложение всегда обслуживает одного юзера.
Если где-то требуются оптимизации или решение какой-то алгоритмически-сложной задачи (стриминг, DRM, сжатие данных) — такое обычно решается подключением библиотек.
В бекенде чаще всего узким горлышком является база данных, и когда проблемы с производительностью наступают, отлаживаться ой как непросто. Ситуация напоминает софонов из книги «Задача трёх тел».
Спойлер про софонов (не открывайте, если собираетесь читать книгу).
Софоны — многомерные элементарные частицы с ИИ, — искажали результаты научных экспериментов, чтобы замедлить прогресс. Это было нужно инопланетной враждебной цивилизации, чтобы иметь технологическое преимущество на момент, когда ее флот подойдет к Земле.
Если вы теперь захотели прочесть книгу, заверяю, что там еще куча интересных идей. Извиняюсь за спойлер.
С отладкой базы вместо софонов выступают кеши базы, наличие прокси (pgbouncer), различия в ресурсах тестового и продового сервера, concurrent-активность на проде, которую очень сложно воспроизвести в других условиях.
Что касается каких-либо алгоритмов, то за 10 лет опыта в проде они не пригодились ни разу, но как пришел в бекенд, постоянно возился с оптимизациями базы (уменьшение количества запросов, переписывание запросов, добавление и удаление индексов, изменение типов данных и индексов, добавление кешей, инвалидация кешей, удаление триггеров, денормализация, установка таймаутов и пр.).
Архитектура бекенда сложнее архитектуры мобильного приложения и фронтенда
Как уже писал выше, работал в трех стеках, и этот пункт кажется очевидным. Приведу несколько коротких тезисов в поддержку мнения:
Ни разу не видел должности мобильного или фронтенд-архитектора.
Дыры в безопасности могут привести к потере и/или порче данных всех пользователей — не одного, как в случае с фронтендом и мобильной разработкой.
Неправильный дизайн данных или архитектуры в бекенде сказывается экспоненциальным ростом расходов на масштабируемость. Фронтенд и мобильное приложение переписать легче, так как не нужна миграция данных и обратная совместимость.
Работать с одним процессом проще, чем с десятками или даже десятками тысяч процессов, раскиданных по разным географическим точкам.
Я провел около 30-ти архитектурных собеседований для мобильных разработчиков и был наблюдателем на нескольких, штук 10, в Сбере и Яндексе. Всё одинаково — MVP (MVVM, MVI). В редких случаях может понадобиться сервис для бекграунд-работы или content provider для общения между приложениями. Для разнообразия могут уточнить, не собирается ли кандидат создавать новые объекты во
View::onDraw
. В бекенде же system design — это всегда что-то новое, нельзя подготовиться на 100%, даже если потратить всю жизнь.
Горящие глаза и выгорание
Профессионализм — это не про удовольствие от работы, а про способность ее выполнять. Что при этом разработчик чувствует и горят ли у него глаза — это за интерфейсом, во всяком случае до тех пор, пока работа выполняется.
Горящие глаза, вспышки интереса к работе — плохой индикатор долгосрочного успеха. Что толку от того, что сверх мотивированный новичок наворотит проблем, занимая время более опытных коллег, обучится на ошибках и уйдет мидлом или даже сеньором на другое место? В этом плане джун выглядит как рискованная инвестиция.
Нужны все
Слышал мнение, что нужны и джуны, и мидлы, и сеньоры. Потому что сеньору будет не интересно делать задачу, которую он уже делал много раз. А что для мидла — рутина, для джуна — точка роста.
С появлением LLM-моделей уровня GPT-4 или китайской Qwen2.5-Max, рутину быстрее реализовать силами более опытного разработчика с ИИ-помощником. Но тут однозначно говорить сложно и всё зависит от контекста. Все-таки поговорить с заказчиком, коллегами из смежной команды и перевести требования в работающий в заданном контексте код LLM еще не может, а джуна можно научить. Кроме того, LLM до сих пор не умеют решать некоторые задачи: the Best AI Is "Unable To Solve the Majority" of Coding Problems.
Поэтому с утверждением «нужны все» готов согласиться и по части джунов при соблюдении двух условий:
Наличие процесса онбординга и контроля решения для менее опытных разработчиков: архитектурные презентации/защиты, дополнительные ревьюиры, pair programming.
Готовая архитектура, чтобы новый код можно было писать по аналогии.
С большинством задач по аналогии справляется и LLM, которой джун может воспользоваться, но это опасная комбинация! Согласно исследованию (AI Copilot Code Quality research), качество кода с появлением ИИ-помощников значительно ухудшилось. Неопытная команда, с ног до головы увешанная ИИ-тулзами, не сможет выстроить архитектуру и развивать проект, не утопая в accidental complexity. Опытный разработчик сможет — вот хороший доклад с примером переписывания плагина с Clojure на Kotlin с LLM.
В заключение
Возвращаясь к вопросу
Один выгоревший сеньор или два джуна с горящими глазами?
Джуны тоже выгорают, а сеньоры могут обнаружить интерес к работе, так что не вижу смысла фокусироваться на выгорании. Опыт и навыки сеньора в любом случае окажутся полезными.
Распределяя ограниченный бюджет на команду, в общем случае, я бы взял сеньора в бекенд, мидлов — в мобильную и фроентенд-разработку. При росте команды и продукта, возможно, добирал бы джунов туда, где процессы и архитектура уже выстроены.
P.S. Тот же самый вопрос задавал в ТГ-канале.