Как стать автором
Обновить

Один выгоревший сеньор или два джуна с горящими глазами?

Уровень сложностиПростой
Время на прочтение7 мин
Количество просмотров1.9K

— Кому бы вы разрешили себя оперировать — одному опытному уставшему хирургу или двум интернам с горящими глазами?

Многое зависит от контекста. Удалить вросший волос можно доверить и самому себе.

Договоренности о границах

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

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

Не работал на серьезных проектах — не надо обобщать

Оставлю небольшую сводку о последнем большом android-проекте

Кодовая база на момент ухода — более миллиона строк кода в монорепозитории только на Kotlin.

За время работы написал несколько приложений для девайсов с кастомной ОС: видео-плеер, музыкальный-плеер, youtube, приложение для выбора фильмов — все были частью одного монорепозитория. Помимо типичных для мобильного приложения задач, встраивал cobalt-браузер в андроид приложение, писал сервис для контроля аудиофокуса между кодом на плюсах (голосовой помощник, будильник) и фокусом от андроид-рантайма, поддерживал сервисы обмена сообщениями (пользовались ими вместо http).

Разделяю фронтенд и мобильную разработку, а не пишу просто «фронтенд», потому что работал и там, и там — и это совершенно разная работа. Есть решения, вроде Flutter или KMM, позволяющие иметь одну кодовую базу для всего, включая веб и десктоп, но практически все пишут фронтенд на другом стеке.

Что касается способностей кандидатов, давайте упростим: все одинаковые и отличаются только опытом, рабочими навыками и ценой их труда.

Резюме статьи

  1. Ошибки в бекенде дороже, чем во фронтенде или мобильной разработке.

  2. Джуниоры создают шум и хаос: ошибки в коде, утечки памяти, race condition и data race, несоблюдение конвенций, стиля, push force в мастер, нарушение правил безопасности, плохие абстракции, много вопросов и т.п.

  3. Инструменты и процесс помогут решить часть из вышеперечисленного, но не всё и не всегда.

  4. В случае с джуном нужно решать гору проблем, с выгоревшим сеньором — одну: потерю мотивации.

Пример кода вместо тысячи слов

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

// 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}")
    }
}
Ошибки (спойлер)

Проблемы по коду сверху внизу, жирным выделены критичные:

  1. В дополнение к комментарию (или даже вместо него) можно было бы добавить проверку на то, что код в транзакции.

  2. Непонимание, как работает функция require: внутри блока ожидается строка, которая будет текстом в выброшенном IllegalArgumentException.

  3. Отсутствие защиты от SQL-injection, т.к. tableName тут является частью построения строки.

  4. Утечка памяти. PrepareStatement — ресурс, который нужно закрывать.

  5. Насколько хорошо делать утилитную функцию, уводящую от необходимости думать о том, какой lock нужен? Что если тут достаточно "for no key update" или "for key share"?

  6. Очередной неправильный require.

  7. Функция кидает ошибки разных уровней (нейтральный Runtime и завязанный на HTTP BadRequestException). Что если функция будет использоваться из джобы? Там не нужна специфичная для http ошибка.

Кстати, часть перечисленных выше ошибок (включая критичные), легко найти с помощью LLM уровня gpt4, причем без каких-либо специальных промптов.

Как отличить джуниора от сеньора

Точного ответа не существует, но можно посмотреть на проблему через три призмы:

  1. Доход. Сеньор тот, кто получает сеньорскую зарплату.

  2. Опыт. Субъективно, сеньор имеет хотя бы 3 года опыта в стеке и хотя бы 5 лет тем или иным образом разрабатывает софт. Можно подкорректировать числа в зависимости от стека.

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

Не путайте мидла и сеньора

На web-просторах гуляет вот такая картинка:

Тут мидл, а не сеньор
Тут мидл, а не сеньор

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

100 очков в пользу джуниоров
100 очков в пользу джуниоров

Почему джуны особенно страшны в бекенде

  1. Тестировать бекенд сложнее, чем мобильное приложение или фронтенд.

  2. Проблем с производительностью на бекенде больше.

  3. Проблем в архитектуре бекенда больше и они стоят дороже.

Разумеется, с каждым из этих пунктов можно поспорить, и всегда найдется контрпример, но кажется, что в общем случае, это так. Вижу аргументы против «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.

Поэтому с утверждением «нужны все» готов согласиться и по части джунов при соблюдении двух условий:

  1. Наличие процесса онбординга и контроля решения для менее опытных разработчиков: архитектурные презентации/защиты, дополнительные ревьюиры, pair programming.

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

С большинством задач по аналогии справляется и LLM, которой джун может воспользоваться, но это опасная комбинация! Согласно исследованию (AI Copilot Code Quality research), качество кода с появлением ИИ-помощников значительно ухудшилось. Неопытная команда, с ног до головы увешанная ИИ-тулзами, не сможет выстроить архитектуру и развивать проект, не утопая в accidental complexity. Опытный разработчик сможет — вот хороший доклад с примером переписывания плагина с Clojure на Kotlin с LLM.

В заключение

Возвращаясь к вопросу

Один выгоревший сеньор или два джуна с горящими глазами?

Джуны тоже выгорают, а сеньоры могут обнаружить интерес к работе, так что не вижу смысла фокусироваться на выгорании. Опыт и навыки сеньора в любом случае окажутся полезными.

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

P.S. Тот же самый вопрос задавал в ТГ-канале.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Один выгоревший сеньор или два джуна с горящими глазами?
65.45% 1 сеньор36
12.73% 2 джуна7
21.82% воздержаться12
Проголосовали 55 пользователей. Воздержались 8 пользователей.
Теги:
Хабы:
+6
Комментарии10

Публикации

Истории

Работа

Ближайшие события

25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань