Опять отказ?! Почему 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-разработчиков.

Начать дискуссию