Опять отказ?! Почему Java-разработчик с 2–3 годами опыта не проходит на middle, как следствие теряет в зарплате
Ты работаешь на Java уже 2-3 года. Пишешь код, закрываешь локальные задачи, небольшие фичи, готов «поднять голову» выше, логично, что следующий шаг это middle, более крупные задачи и рост по деньгам.
Но на собеседовании снова отказы…
Я провёл около 150 интервью на backend Java-позиции и заметил, есто то, что встречается максимально часто: кандидат начинает отвечать уверенно, но через 30–40 секунд становится понятно, что глубины не хватает.
Обычно проблема не в том, что человек ничего не знает. Чаще он знает термины, общую картину, «правильные» формулировки. Но не может объяснить, как механизм реально работает, почему пришли к такому решению, где он возможно сломается и какие последствия это даёт в проде.
Именно здесь многие разработчики упираются в потолок. Формально опыт уже есть, но на middle все чет не хватает. В итоге - отказы, слабые офферы или годы на одном уровне без заметного роста по зарплате.
Ниже - три вопроса, которые можно встретить максимально части и на которых видно отношение кандидата к вопросу, как следствие его уровень.
1. «Опиши lifecycle бина в Spring — от определения до готовности к работе»
Что часто отвечают:
«Spring сканирует аннотации, находит @Component, создаёт объект, кладёт его в контекст и внедряет зависимости».
На первый взгляд ответ звучит нормально. Но по сути он почти ничего не объясняет. Это пересказ общей идеи, а не понимание жизненного цикла бина.
Здесь важно не просто назвать знакомые слова про Spring, а показать, что ты понимаешь, что именно происходит между моментом, когда контейнер увидел описание бина, и моментом, когда бин готов к работе.
Более зрелый ответ выглядит примерно так: сначала контейнер формирует BeanDefinition, затем на этапе BeanFactoryPostProcessor метаданные ещё могут быть изменены до создания объекта. После этого идёт инстанцирование, внедрение зависимостей, прогон через BeanPostProcessor, init-колбэки (@PostConstruct, InitializingBean, init-method) — и только потом бин становится полностью готов.
Но важнее даже не перечислить этапы, а объяснить, зачем они нужны на практике.
Например, именно через post-processor-ы Spring создаёт прокси для @Transactional и @Async. Если разработчик не понимает этот этап, он не поймёт, почему @Transactional не срабатывает при вызове метода внутри того же класса.
Вот это уже ответ, который показывает не заучивание, а рабочее понимание механики.
2. «REST-сервис начал отвечать 3 секунды вместо 200 мс. Как будешь искать проблему?»
Один из самых частых ответов:
«Посмотрю логи».
Это не катастрофа, но и не ответ уровня middle. Потому что вопрос не про первую ассоциацию, а про способ мышления, и вот как раз на таких вопросах/рассуждениях тебя “считают” за 1 - 2 минуты.
На backend-разработке ценится не только знание инструментов, но и умение локализовать проблему. Где именно задержка: сеть, приложение, база, внешний сервис, блокировки, GC, пул потоков?
Хороший ответ обычно начинается со структуры.
Сначала понять, проблема на всех эндпоинтах или только на одном. Затем посмотреть метрики и latency percentiles, а не только average. Если есть подозрение на БД — проверить slow query log и EXPLAIN. Если тормозит внешний сервис — смотреть таймауты, retry, circuit breaker. Если проблема в приложении — thread dump, profiler, heap dump. Если есть признаки GC-пауз — смотреть GC-логи.
Необязательно знать наизусть весь набор инструментов. Но важно показать сам подход: не хаотично смотреть всё подряд, а последовательно сужать область поиска.
На собеседовании это считывается очень быстро. Один кандидат говорит: «Посмотрю логи». Другой показывает маршрут расследования. Формально оба что-то знают, но уровень воспринимается совершенно по-разному.
3. «У тебя prototype-бин открывает соединение в @PostConstruct и закрывает его в @PreDestroy. В чём риск?»
Здесь многие чувствуют, что подвох есть, но не могут сформулировать его до конца.
Проблема в том, что Spring не управляет полным lifecycle prototype-бина так же, как singleton-бина. Контейнер создаёт такой бин, внедряет зависимости, вызывает init-этап — и дальше перестаёт его отслеживать.
Это значит, что @PreDestroy для prototype-бина автоматически вызван не будет.
Если внутри @PostConstruct открывается внешний ресурс, например соединение, а логика закрытия завязана на @PreDestroy, то ресурс может остаться неосвобождённым. Под нагрузкой это превращается в накопление утечек, а потом — в проблемы с пулом соединений и деградацию приложения.
И здесь снова проверяется не знание отдельного факта из Spring, а умение увидеть реальный эксплуатационный риск.
Итого: Что всё это значит на практике
Большинство отказов на middle происходят не буквально потому, что человек слабый разработчик. Чаще причина в другом: его опыт пока довольно поверхностный. Все чаще разработчики надеятся «повесить нужную аннотацию» и все будет хорошо, всегда… Не будет…
Разработчик уже знает инструменты, но не всегда понимает механику. Знает, как делать, но не может объяснить, что происходит внутри и где это ломается в реальной системе.
А именно это и отличает человека, который просто пишет код, от человека, которому готовы доверить более сложную ответственность и платить за это больше.
Переход на middle — это не про то, чтобы выучить ещё 100 вопросов из подборки. Это про другой уровень мышления: не пересказывать термины, а объяснять причинно-следственные связи; не просто знать фреймворк, а понимать его реальные точки отказа; не просто отвечать, а показывать зрелый способ думать.
Если хочешь проверить себя в формате, близком к реальному интервью, я провожу пробные собеседования для Java backend-разработчиков: задаю вопросы, разбираю ответы и показываю, где именно не хватает глубины до middle или strong middle.
А если пока просто интересна тема — веду Telegram-канал , где разбираю похожие кейсы, типичные ошибки на интервью и карьерные потолки backend-разработчиков.