backend_interviewer | Unsorted

Telegram-канал backend_interviewer - Backend Interviewer

1273

Как успешно пройти backend собеседование и получить лучший оффер

Subscribe to a channel

Backend Interviewer

Как мы превращаем "понимаю" в "умею"

На собесах чаще всего сыпятся не те, кто не учил, а те, кто всё понял, но ни разу не применил на практике.

Можно часами смотреть видео и кивать в такт. Можно читать статьи и думать: "Ну да, логично". Но потом на практике – ступор.

Потому что между "знаю" и "умею" лежит одно – действие.

На менторстве мы не зубрим. Мы закрепляем знания через код, задачи и мини-проекты.

Лайвкодинг вместо заучивания

Многие темы мы изучаем через совместный кодинг. Смотрим, что под капотом, как поведёт себя код, что сломается, почему поведение не такое, как ожидали.

Вместо "volatile обеспечивает видимость" – пишем без volatile, нагружаем и видим расхождение.

Вместо "ConcurrentHashMap потокобезопасна" – проверяем, где она реально спасает, а где нет.

Вместо "Transactional откатывает изменения" – воспроизводим случай, где rollback не сработал.

Когда видишь поведение своими глазами, знание перестаёт быть теорией.

Мини-проекты вместо сухих задач

Мы не учим темы изолированно. Многие блоки превращаем в маленький, но живой проект.

Потоки – пишем собственный ThreadPool.

Коллекции – реализуем LRUCache с ограничением по размеру.

Транзакции – делаем мини-сервис, где нужно гарантировать целостность данных.

Spring – собираем упрощённый DI-контейнер, чтобы понять, что делает Spring за нас.

Это практические модели реальных систем, где теория проявляется в действии.

Создаём ситуации, где сломается

Самые полезные задачи те, где что-то идёт не по плану. Мы специально даём разработчикам код, который работает почти правильно, но:

– Поток затирает данные
– HashMap теряет элемент
– Транзакция не откатывается

И вместе разбираем почему.

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

Диалог вместо экзамена

Мы не оцениваем "правильно или нет". Мы обсуждаем, почему именно так.

– Почему ты выбрал synchronized, а не lock
– Как поведёт себя код при 10 потоках

Каждый такой разговор – это развитие инженерного мышления. Разработчик перестаёт просто писать код – он начинает обосновывать свои решения.

Задачи без очевидных ответов

В реальной разработке нет единственного верного решения – есть компромиссы.

Мы разбираем такие задачи, где нужно выбрать подход, аргументировать и защитить:

– Как реализовать кэш для миллионов ключей
– Как спроектировать очередь задач с retry и timeout
– Как гарантировать целостность данных без транзакций

Объяснение = финальная проверка

Каждую тему мы закрываем простым принципом: "Если можешь объяснить своими словами – значит, понял".

Поэтому разработчик не просто сдаёт код. Он рассказывает:

– Как работает
– Почему сделал именно так
– Что бы изменил, если бы нагрузка выросла в 10 раз

Это момент, где из знания рождается экспертиза.

Мы учим через опыт

Мы не даём "задания на оценку". Мы создаём пространство, где можно ошибаться.

Ошибся? Отлично.
Поймал баг? Супер.
Сделал дедлок? Прелестно.

Потому что реальный прогресс начинается не с идеальных ответов, а с честного "не знаю, но хочу понять".

Читать полностью…

Backend Interviewer

Как выстроить систему подготовки, чтобы не выгореть

Мы начали готовиться. Собрали темы, нашли материалы, даже поставили цели.

Первые дни всё идёт хорошо. Мозг впитывает, темы ложатся одна за другой. А потом наступает момент, когда знания вроде растут, а внутри – усталость, раздражение, апатия.

Кажется, что мы идём вперёд, но не чувствуем прогресса. Каждый день похож на предыдущий. И мы начинаем терять энергию.

Почему мы выгораем не в конце, а в середине

В начале нас держит мотивация.
В конце – цель (оффер, повышение).
А посередине – пустота.

Это та самая яма, где мы учим, но не чувствуем результата. Где знаний уже много, но уверенности всё ещё нет. Где каждое новое "понял" тут же сменяется "ничего не помню".

И вот здесь система становится критична. Потому что без неё мы просто тонем в информационном потоке.

Как построить систему, которая держит темп

1. Строим ритм, а не план

План – это список дел.
Ритм – это темп, в котором мы можем учиться.

У каждого свой темп. Кто-то может по 3 часа в день. Кто-то – по 30 минут.

Главное – не сжечь топливо за первую неделю. Лучше идти каждый день по чуть-чуть, чем рваться и срываться.

2. Чередуем типы нагрузки

Подготовка – не только теория.

Есть разные типы умственной деятельности:

– аналитическая, когда мы читаем и разбираемся в механизмах
– практическая, когда пишем код
– рефлексивная, когда повторяем, рассказываем, объясняем

Если день тяжёлый – можно просто пересмотреть конспект или объяснить тему коллеге. Это тоже часть подготовки.

Мы не обязаны быть на 100% продуктивными каждый день. Важно – оставаться в движении.

3. Позволяем отдыхать

Наш мозг не может постоянно быть в режиме "учись – запоминай – отвечай".

Нужны точки выдоха:

– прогулки после учёбы
– смена контекста
– ленивые дни

Самое опасное – не усталость, а чувство вины за усталость. Как только мы начинаем себя грызть – мотивация исчезает.

4. Даём обратную связь

Мы выгораем, когда не видим результата. Поэтому нужен индикатор прогресса.

Простая привычка – каждый вечер записывать одно из трёх:

– сегодня понял, зачем нужен X
– сегодня объяснил Y вслух
– сегодня закрепил тему Z

Через неделю перечитываешь – и видишь, что путь есть. А мозг любит доказательства: я двигаюсь.

5. Меняем формат, когда застряли

Если тема не идёт – не значит, что мы тупые. Значит, формат не подходит.

Например:

– не идёт книга: найди видео
– не заходит лекция: попробуй руками закодить
– устал от практики: открой статью с иллюстрациями

Учёба – не конвейер. Иногда, чтобы двигаться, нужно просто поменять вектор.

6. Добавляем элемент удовольствия

Готовиться можно не только из под палки.

Добавь в процесс удовольствие:

– пиши заметки в блог
– делись инсайтами с коллегами
– собирай личную базу знаний, которую потом покажешь ученикам

Когда появляется смысл – усталость перестаёт быть врагом.

Главный вывод

Выгорание – это сигнал, что мы пытаемся жить в чужом ритме. Когда мы строим путь под себя – всё вдруг становится легче. Мы просто планомерно двигаемся вперёд.

❤️ и обсудим как учить глубоко, а не поверхностно.

Читать полностью…

Backend Interviewer

Почему документацию никто не пишет, но все хотят её читать

У каждой команды есть такая мечта:

Вот бы была нормальная документация…


И у каждой команды есть такая же реальность:

– Напиши доку сам?
– Да ну нафиг, потом


Почему мы не пишем документацию?

Представь, ты только что закончил сложный фикс, потратил 8 часов на отладку хитрого бага. Мозг кипит. Последнее, чего хочется – это переключаться в режим «писателя» и нудно описывать, что ты сделал.

Почему?

Проклятие знания

Вот есть метод getUserData(..). Кажется, что всё очевидно. Ну что тут непонятного, он получает данные юзера!

Но через полгода ты сам не вспомнишь, почему тут именно такой запрос, а не другой. А новый джун посмотрит на это как на клинопись.

Это же скучно!

Писать код, решать проблемы – это драйв, креатив.

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

Давай быстрее, дедлайн вчера!

Бизнес требует результат, а не талмуды с описаниями. Документация – первое, что летит под нож, когда горят сроки. Это «внутренняя кухня», которую не показать заказчику.

Польза где-то там, в будущем

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

Так почему мы все так хотим читать документацию?

А теперь перевернем ситуацию. Ты приходишь на новый проект. Перед тобой 100500 файлов и непонятная структура. Ты как слепой котенок.

Что спасает? Правильно, хорошая документация.

Это карта сокровищ

Вместо того чтобы часами блуждать по «минному полю» чужого кода, ты открываешь гайд и за 15 минут понимаешь архитектуру, ключевые моменты и как запустить проект. Экономия времени – колоссальная.

Независимость

Тебе не нужно каждые 5 минут дергать тимлида с вопросом: «А как тут…?». Ты можешь работать автономно. Это круто и для тебя, и для команды, которую ты не отвлекаешь.

Единый источник правды

Когда в команде споры, как должна работать та или иная функция, документация – это ваш судья. Она спасает от хаоса, когда «Петя говорил одно, а Вася делал другое».

Что в итоге?

Получается замкнутый круг.

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

Документация – это не побочный продукт, а часть самого продукта. Пока мы относимся к ней как к рутине, а не как к обязательному этапу разработки, мы так и будем тратить часы на то, что можно было бы узнать за 5 минут.

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

Читать полностью…

Backend Interviewer

0xCAFEBABE

Вот кто не любит пасхалки?

Давайте создадим простой класс:

public class Main {
public static void main(String[] args) {
System.out.println("Hello World");
}
}


Скомпилируем его:
javac Main.java


Проанализируем hex дамп:
xxd Main.class


Увидим следующее:
00000000: cafe babe 0000 003d 001d 0a00 0200 0307  .......=........
00000010: 0004 0c00 0500 0601 0010 6a61 7661 2f6c ..........java/l
00000020: 616e 672f 4f62 6a65 6374 0100 063c 696e ang/Object...<in
00000030: 6974 3e01 0003 2829 5609 0008 0009 0700 it>...()V.......
00000040: 0a0c 000b 000c 0100 106a 6176 612f 6c61 .........java/la
00000050: 6e67 2f53 7973 7465 6d01 0003 6f75 7401 ng/System...out.
00000060: 0015 4c6a 6176 612f 696f 2f50 7269 6e74 ..Ljava/io/Print
00000070: 5374 7265 616d 3b08 000e 0100 0b48 656c Stream;......Hel
00000080: 6c6f 2057 6f72 6c64 0a00 1000 1107 0012 lo World........
00000090: 0c00 1300 1401 0013 6a61 7661 2f69 6f2f ........java/io/
000000a0: 5072 696e 7453 7472 6561 6d01 0007 7072 PrintStream...pr
000000b0: 696e 746c 6e01 0015 284c 6a61 7661 2f6c intln...(Ljava/l
000000c0: 616e 672f 5374 7269 6e67 3b29 5607 0016 ang/String;)V...
000000d0: 0100 044d 6169 6e01 0004 436f 6465 0100 ...Main...Code..
000000e0: 0f4c 696e 654e 756d 6265 7254 6162 6c65 .LineNumberTable
000000f0: 0100 046d 6169 6e01 0016 285b 4c6a 6176 ...main...([Ljav
00000100: 612f 6c61 6e67 2f53 7472 696e 673b 2956 a/lang/String;)V
00000110: 0100 0a53 6f75 7263 6546 696c 6501 0009 ...SourceFile...
00000120: 4d61 696e 2e6a 6176 6100 2100 1500 0200 Main.java.!.....
00000130: 0000 0000 0200 0100 0500 0600 0100 1700 ................
00000140: 0000 1d00 0100 0100 0000 052a b700 01b1 ...........*....
00000150: 0000 0001 0018 0000 0006 0001 0000 0001 ................
00000160: 0009 0019 001a 0001 0017 0000 0025 0002 .............%..
00000170: 0001 0000 0009 b200 0712 0db6 000f b100 ................
00000180: 0000 0100 1800 0000 0a00 0200 0000 0300 ................
00000190: 0800 0400 0100 1b00 0000 0200 1c .............


Hex дамп начинается c cafe babe.

Если мы так же проанализируем любой другой скомпилированный класс, то увидим, что каждый из них будет начинаться с cafe babe.

Что же это за магическая штука?

Она позволяет JVM быстро идентифицировать и проверять файлы классов.

Если мы отредактируем файл Main.class, изменим первые числа и попытаемся запустить, то JVM выбросит исключение:
java.lang.ClassFormatError: Incompatible magic value ...


Это магическое число ввёл сам Джеймс Гослинг:
Мы раньше ходили обедать в одно место под названием St Michael's Alley. По местной легенде, в далёком прошлом там выступали Grateful Dead, ещё до того, как стали знаменитыми. Это было довольно странное, но атмосферное заведение – определённо «место в стиле Grateful Dead». Когда Джерри (Гарсия) умер, там даже сделали что-то вроде маленького буддийского алтаря.

Когда мы туда ходили, мы называли это место Cafe Dead. В какой-то момент кто-то заметил, что это ещё и HEX-число. Я как раз перерабатывал код для одного файлового формата и мне нужны были парочка «магических чисел»: одно – для формата файлов с объектами, другое – для классов.

Я использовал CAFEDEAD для объектного формата. А потом, просматривая четырёхсимвольные HEX-слова, которые подходят после CAFE (тема показалась удачной), я наткнулся на BABE и решил использовать его.

Тогда это не казалось чем-то особенно важным или судьбоносным – скорее временным решением, которому место на свалке истории. Но в итоге CAFEBABE стало «магическим числом» для class-файлов, а CAFEDEAD – для объектного формата. Правда, объектный формат со временем исчез, и вместе с ним ушло использование CAFEDEAD – его в итоге заменил RMI.


Вот такая пасхалка.

Читать полностью…

Backend Interviewer

На удивление, многие не знали про существование LongAdder.

Почему же он такой быстрый на запись, но такой медленный на чтение?

В основе LongAdder лежит Striped64.

Зачем он вообще нужен?

Когда мы используем AtomicLong, то под высокой нагрузкой все потоки дерутся за один атомарный long.

Striped64 делит счётчик на ячейки, каждая из которых может инкрементироваться независимо.

Каждый поток работает в своей ячейке, снижая конкуренцию.

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

Внутреннее устройство

volatile long base – основное значение (используется, если конкуренции нет).

volatile Cell[] cells – массив ячеек, каждая хранит long value. Каждая ячейка расширена так, чтобы не было false sharing (об этом позже).

probe (через ThreadLocalRandom) определяет, в какую ячейку будет писать поток.

cellsBusy – CAS-замок на инициализацию/расширение массива cells.

Алгоритм обновления

Попробовали сделать CAS на base. Если получилось – готово.

Если CAS на base не удался:
– Берём cells. Если они ещё не созданы – пробуем создать массив на 2 ячейки.
– Вычисляем индекс ячейки. Пробуем сделать CAS в ячейку cells[index].

Если CAS в ячейку не удался (потоки попали в одну и ту же ячейку):
– Меняем probe (перемешиваем хэш потока).
– При сильной конкуренции пробуем расширить массив ячеек в 2 раза.

Размер cells может расти до числа ядер процессора. Так достигается баланс: чем больше конкуренция, тем больше ячеек.

false sharing

У процессоров память кешируется не побайтно и не по переменной, а блоками фиксированного размера – cache line (обычно 64 байта).

Когда поток читает или пишет переменную, процессор подгружает в кеш всю линию (64 байта).

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

В итоге кеш постоянно инвалидируется, даже если логически данные не связаны.

Это и есть false sharing – ложное совместное использование данных, которых на самом деле не должно быть.

В Striped64 разные потоки пишут в разные ячейки.

Если бы ячейка была просто volatile long value, то несколько ячеек массива подряд оказались бы в одной кеш-линии.

Поэтому Striped64 использует аннотацию Contended, которая дополняет поля внутри классов пустыми байтами.

То есть каждая ячейка раздута, чтобы точно не пересекаться с соседями по кеш-линии. Это убирает false sharing и позволяет потокам работать независимо.

Читать полностью…

Backend Interviewer

Делаем свой ThreadPool

Создавать новый поток под каждую задачу – всё равно что покупать новый ноутбук каждый раз, когда нам нужно открыть вкладку в браузере.

Дорого, долго и бессмысленно.

Поэтому существует пул потоков.

Он состоит из четырёх ключевых компонентов:

1. Очередь задач: в неё кладутся новые задачи от пользователей, а потоки берут их в работу.

2. Потоки: каждый поток в пуле работает в бесконечном цикле. Берёт задачу из очереди -> выполняет -> берёт следующую. Если задач нет, поток ждёт.

3. Механизм управления потоками: нужно решать, сколько потоков поддерживать - фиксированное количество или динамически расширяемое.

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

Зачем вообще делать свой пул, когда есть готовые решения? Ответ простой. Чтобы разобраться как он работает под капотом.

Полную версию с кодом, объяснениями, стратегиями остановки и скейлинга выложил здесь

Читать полностью…

Backend Interviewer

hashCode() по умолчанию

Мы привыкли думать, что если не переопределяем hashCode(), то по умолчанию он равен адресу объекта в памяти.

Но так ли это?

Может ли hashCode быть равен рандомному числу или вообще единице?

В этой статье провел расследование.

Читать полностью…

Backend Interviewer

Очередная причина любить Питер

Читать полностью…

Backend Interviewer

Как я учился говорить "нет" менеджерам и не чувствовать себя мудаком

Менеджер пишет:

Там мелкая доработка, надо просто поле добавить. Сможешь сегодня?


И я говорю "да".

Через полчаса:

Срочно надо пофиксить расчёт формулы, иначе отчёты не сходятся


Я снова говорю "да", потому что умею в многозадачность.

Но вечером я понимаю, что:

– не сделал свою задачу из спринта
– злюсь на всех
– и сам виноват, потому что никому не сказал "нет"

Почему говорить "нет" сложно

– Хочешь быть надёжным
– Боишься показаться не командным
– Боишься, что скажут: «он токсичный»
– Или просто не хочешь конфликта

Но в итоге ты теряешь фокус, скорость, мотивацию.

И постепенно превращаешься в человека, на которого всё скидывают на всякий случай.

Что я начал делать

1. Спрашивать про приоритеты

Я не говорил "нет". Я говорил: «У меня сейчас задача Х. Это важнее?»

Обычно это ставит менеджера в режим выбора, а не давления.

Он либо говорит «ладно, потом», либо помогает снять что-то из текущего.

2. Выводить цену вопроса

«Ок, но это займёт полдня. Мы сдвигаем релиз фичи?»
«Ок, но тогда не будет покрытия тестами – норм?»

Менеджер начинает думать. А не просто кидать хотелки.

3. Заранее обозначать зону ответственности

«Я не буду поддерживать это в проде – давайте назначим владельца»
«Я могу помочь, но не беру это на себя полностью»

Ты просто честно обозначаешь, что можешь, а что нет.

Что поменялось

– Я стал спокойнее
– Мой день стал предсказуемым
– Я наконец начал делать то, что запланировал
– И, что неожиданно: менеджеры стали уважать это, потому что у них тоже есть задачи, сроки и нервы

Вывод

Говорить "да" – легко.
Говорить "нет" – трудно.

Но трудное "нет" сегодня – спасает тебя от сгоревшего "я увольняюсь" завтра.

Это не про конфликты. И не про борьбу с бизнесом.

Это про выгорание, здравый смысл и то, что никто, кроме тебя, не поставит границы.

Читать полностью…

Backend Interviewer

Что происходит, когда мы пишем new в Java: от байткода до оптимизаций

Кажется, мы просто создаём объект:

var user = new User();


Но под этой строчкой скрыт целый каскад операций внутри JVM: от байткода и выделения памяти до вызова конструктора, настройки объекта и запуска JIT-оптимизаций.

Разберём весь процесс пошагово.

Шаг 1. Компиляция в байткод

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

Строка var user = new User() превращается во что-то вроде:

0: new #2
3: dup
4: invokespecial #1
7: astore_1


new – резервирует место в heap, но не вызывает конструктор.

dup – после new ссылка на объект оказывается на вершине стека. dup нужен, чтобы использовать её дважды: и для вызова конструктора, и для присваивания переменной.

invokespecial – вызывает <init>-метод (конструктор).

astore_1 – сохраняет ссылку в переменную user.

Шаг 2. Выделение памяти в heap

Когда JVM встречает команду new, она просит менеджер памяти выделить место под объект. Обычно объект попадает в Eden (часть Young Generation).

Есть два пути.

Быстро: через TLAB (Thread Local Allocation Buffer)

Если у потока есть TLAB (специальный буфер в Eden для быстрой аллокации), объект создаётся без синхронизации.

Просто смещается указатель: nextFree += objectSize.

Почти так же, как malloc в C, только безопаснее.

Медленно: с синхронизацией

Если буфер заполнен, происходит более дорогая аллокация: с блокировками и участием GC (если мало места). Возможно, произойдёт Minor GC.

Шаг 3. Инициализация объекта: header и zeroing

Перед тем как конструктор выполнится, JVM инициализирует внутренние структуры объекта.

Mark Word – служебная информация: hashCode, флаг GC, состояние блокировки.

Class Pointer – ссылка на метаданные класса (Class<?>).

Zeroing – все поля объекта инициализируются значениями по умолчанию (0, null, false).

В этот момент объект уже существует, но он ещё "пустой". Конструктор не выполнялся.

Шаг 4. Вызов конструктора (<init>)

Теперь JVM вызывает конструктор через invokespecial.

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

Порядок важен: сначала родитель, потом поля, потом логика конструктора.

Шаг 5. Присваивание переменной

После вызова конструктора ссылка на объект остаётся на вершине стека.

Её можно:

– сохранить в переменную (var user = ...)
– передать в метод (someMethod(new User()))
– или не использовать вовсе (будет кандидатом на GC)

Шаг 6. Оптимизации от JIT

На этом этапе JVM может вмешаться и оптимизировать создание объекта, особенно если мы много раз создаём один и тот же тип объектов в горячем методе.

Escape Analysis

JVM анализирует: уходит ли объект за пределы метода?

Если нет – можно не выделять память в heap и положить объект в стек (stack allocation).

Если JVM докажет, что объект локален – он даже не будет существовать в памяти как полноценный объект.

Scalar Replacement

Если объект используется как набор примитивов, JVM может вообще разложить его на переменные и не создавать объект вовсе.

Inlining конструктора

JVM может встроить код конструктора прямо в место вызова. Это снижает накладные расходы вызова и помогает следующим оптимизациям (например, loop unrolling, constant folding).

Почему всё это важно

Мы можем думать, что просто вызываем new, но на деле:

– JVM аллоцирует память
– происходит инициализация внутренних структур
– вызывается цепочка конструкторов
– активируются JIT-оптимизации
– включается GC и мониторинг

Мы не должны бояться заглядывать под капот – мы обязаны это делать. Потому что именно там находятся ответы на вопросы "почему прод тормозит" или "откуда утечка памяти".

Читать полностью…

Backend Interviewer

Как учиться, когда у тебя работа, семья, дела и вообще ни на что не хватает времени

Учиться легко, когда тебе 18, ты живешь в общаге, ешь пельмени и всё свободное время твое.

Но если тебе за 25-30, ты работаешь full-time, у тебя семья, дети, ипотека, собака, кошка, больная спина и прод упал – то любая мысль о том, чтобы ещё учиться, вызывает злость, вину и выгорание.

Я устал.
У меня нет времени.
У меня нет сил.
Но я хочу лучшую работу.


Это самый частый конфликт у взрослых разработчиков.

Ты знаешь, что тебе нужно расти.

Ты понимаешь, что хочешь уйти с текущей работы.

Ты листаешь вакансии и мечтаешь: вот бы в нормальный проект, на нормальную зарплату, с нормальным стеком.

Но когда доходит до действий – тебя просто не хватает.

И ты начинаешь себя грызть:

Я слабак. У других же получается.
Наверное, не так уж и хочу.
Надо просто собраться и начать.


Спойлер: не получится.

Потому что ты всё ещё мыслишь по старому: думаешь, что нужно "выделить время", "настроиться", "войти в режим".

Это невозможно, если ты живёшь взрослой жизнью с обязанностями, семьёй и реальными делами.

Тебя всегда будет что-то или кто-то отвлекать.

Что же делать?

Главное, не нужно вгонять себя в жесткие рамки.

Это вызывает только стресс.

Никакого "по 2 часа каждый вечер".

Никакого "дойду до конца курса".

Возьми ответственность только за одно маленькое действие каждый день.

Открой один вопрос.
Подумай, как бы ты ответил.
Посмотри разбор.
Всё. На сегодня достаточно.

Не нужно "прокачать java".
Нужно разобраться с вопросами, которые тебе точно зададут на собесе.

Не нужно "поднять уровень".
Нужно разобраться, чем отличается твой ответ от сеньора и понять разницу.

Далее, встраивай обучение в повседневную жизнь.

Пока едешь в машине – слушаешь аудио разбор собеса.

Ждешь ребёнка с кружка – повторяешь карточки.

Сидишь в очереди к врачу – читаешь полезные тг-каналы на тему.

5 минут, 10 минут. Это не мало. Это небольшой шаг в развитии тебя.

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

Поэтому вместо "я сам справлюсь", найди среду, в которой тебя поддерживают, тебе напоминают, с тебя спрашивают и ты не один.

Именно такую среду я и создаю в групповом практикуме по подготовке к java собеседованиям.

Встречи в команде единомышленников. Максимум практики. Растёшь быстрее, чем при самостоятельной подготовке.

Ты не должен всё успевать.

Ты просто можешь делать один шаг в день.

В правильной среде. С правильной целью. С поддержкой.

Читать полностью…

Backend Interviewer

Практикум по Java собеседованиям

Весной я решил попробовать новый для себя формат подготовки разработчиков к собеседованиям.

Вместо индивидуального менторства – встречи в небольшой группе.

Я опасался, что это будет не так эффективно, как личная работа 1 на 1.

Но по завершении группового практикума, я могу с уверенность сказать, что это не так.

Нас было 7 человек, включая меня.

Мы встречались 2 раза в неделю по вечерам на протяжении месяца и обсуждали разные вопросы: от внутрянки jvm до системного дизайна и оформления резюме.

Трое уже нашли новую работу с повышением должности и дохода. Двое получили контр-офферы на текущей работе и приняли их. Один в процессе прохождения собесов. Я уверен и у него все получится.

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

Пример.

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

Когда ты один – трудно заставить себя тратить на это время.

Ментор тебя тоже не заставит. Он же не будет бить тебя палкой.

А когда ты в группе – возникает азарт.

Петя решил 30 задач, Ваня решил 40 задач, а я решил всего лишь одну – непорядок, я что ленивее других?

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

Плюс коллективный разум. Кто-то может задать такой вопрос, о которым ты даже не подозревал. Это очень сильно расширяет кругозор.

Поэтому я считаю, что групповая работа ни чуть не уступает по эффективности личному менторству.

Кроме того, она намного выгоднее по деньгам.

Менторство предполагает пред-оплату 10/20/30 тысяч и пост-оплату в процентах от полученного оффера – 50/100/150% в зависимости от уровня человека.

В группу же можно залететь за несколько тысяч рублей в месяц при использовании рассрочки.

Суммы отличаются в разы.

Что в итоге?

Я решил продолжить развивать формат групповой подготовки к собеседованиям:

– Переработал программу
– Увеличил количество групповых встреч до 10
– Включил проверку домашнего задания
– Добавил одну личную встречу в конце практикума для большей уверенности, что человек усвоил весь материал и готов выйти на рынок

Какие темы изучаем?

– Java (от core до virtual threads)
– Spring (от бинов до hibernate)
– SQL/NoSQL (от индексов до explain)
– Kafka (от устройства до гарантий доставки)
– Docker, Kubernetes
– Задачи на код-ревью и алгоритмы
– Паттерны проектирования
– Системный дизайн
– Soft skills (от оформления резюме до рассказа о себе)

Короче, суть в том, чтобы ты:

– Закрыл все вопросы по собеседованиям
– Чувствовал себя уверенно в любых ситуациях
– Нашел новую интересную работу, на которой сможешь расти
– Увеличил свой доход

Если тебе интересен такой формат подготовки, пиши мне в личку @principal_swe

Созвонимся, обсудим все вопросы и решим подойдет ли это для тебя.

Стартуем в начале августа.

Так как это небольшая группа, то, конечно, количество мест ограничено.

Поэтому выбор – действовать сейчас или отложить – только за тобой.

Я уверен это отличный буст для любого разработчика.

Читать полностью…

Backend Interviewer

Я шёл на собеседование, а попал на допрос

Вот какую цель ставит перед собой любой адекватный интервьюер, проводя собеседование?

Правильно. Найти человека к себе в команду.

Должен ли этот человек знать 100500 фреймворков? Нет. Любой технологии можно обучить.

Должен ли этот человек молниеносно и оптимизировано решать алгоритмические задачи? Нет. Любой алгоритм требует неспешных размышлений.

Должен ли этот человек вызубрить методы класса Object? Нет. Он не энциклопедия.

Что же должен этот человек?

Да ничего никому он не должен.

Как говорят психологи, долженствование – это когнитивное искажение.

Кстати, почитайте про когнитивные искажения. Очень познавательно.

Так вот. Если тебе комфортно общаться с этим человеком, если ты видишь его рост, если ты видишь его стремления, то этого достаточно, чтобы работать с ним.

Соответственно и вопросы интервьюера должны быть направлены на это.

На опыт человека, на то как он принимает решения, как подходит к компромиссам, к решению проблем, какую ответственность он готов брать.

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

Он не спрашивает, он соревнуется.

Он не слушает, он выискивает момент, когда ты ошибешься.

Он не хочет понять, он хочет почувствовать себя крутым.

Отсюда и вопросы «А если я сделаю вот такую дичь, где будет NPE?».

Это не собеседование.

Это допрос с элементами показательного унижения.

А после – его ехидная улыбка.

Каждый день мы разгребаем дерьмо:

– запутанное легаси
– ошибки прома
– тонны фич без полной аналитики
– отсутствие архитектуры и нормального техдолга
– бесконечные митинги

И ты ещё приходишь на собеседование, чтобы расти, менять проект, доход – а тебе: «Что делает GC, если у тебя в finalize вылетело исключение?».

Серьёзно? Это то, по чему ты судишь про мой опыт?

Если ты когда-нибудь выходил с собеса с мыслью: «Я вроде сеньор… но чувствую себя идиотом» – это не ты глупый. Это интервьюер сделал глупость – не смог раскрыть тебя. А иногда – и не хотел.

В предыдущем посте вы ставили 🔥 чтобы узнать как застать интервьюера врасплох на вопросах про прокси.

Адекватного интервьюера вам и не захочется заставать врасплох. Он не даст причин.

А на неадекватного – не тратьте свои силы, время и нервы. Он того не стоит. Поблагодарите судьбу за то, что работать вы с ним не будете.

❤️ если согласен

Читать полностью…

Backend Interviewer

Что будет с твоим кодом через 250 лет?

На выходных с девушкой посетил Екатерининский дворец в Царском Селе.

Во-первых, это безумно красиво!

Благодаря колоннам и пальмам складывается ощущение будто ты в Древней Греции.

Во-вторых, это архитектурная медитация.

Ты смотришь на детали: колонны, золото, лепнину, геометрию.

Каждый зал – как модуль.

Каждая анфилада – как правильно выстроенный маршрут пользователя.

И в какой-то момент ловишь себя на мысли:

Чёрт, так ведь и с системным дизайном то же самое!


Архитектура – это не просто «как расставить сервисы»

Это про видение. Про цель, ради которой всё построено.

Про гармонию между удобством, безопасностью и масштабом.

В Екатерининском дворце:

– каждая деталь подчинена композиции
– всё выдержано в стиле и логике
– даже "излишество" не выходит за рамки структуры

И это очень похоже на хорошую продакшн-систему:

– микросервисы, которые не мешают, а поддерживают друг друга
– чёткая навигация между модулями
– читаемость, ясность, масштабируемость: всё на своих местах

А теперь интересная мысль

Этот дворец стоит более 250 лет.

Наш код – часто не доживает и до 3 лет.

Но...

Кто сказал, что код не может быть "дворцом"?

Просто мы часто лепим фичу на бегу, строим архитектуру на авось, а потом удивляемся, почему всё трясёт от лёгкого рефакторинга.

Как мы можем улучшить ситуацию

Давайте относиться к проектированию кода, как к проектированию дворца!

Будем уважать долговечность. Писать код, как будто его будут поддерживать через 10 лет. Даже если это не так.

Будем прививать чувство масштаба. Архитектура – это не только про "сейчас работает", а про "как это будет жить в будущем".

Будем разбавлять инженерное мышление художественным. Красота в коде – это не про "а давай красиво назовём метод", а про то, чтобы всё складывалось в ясную, логичную структуру.

Архитектура – это про ответственность


Про уважение к тем, кто придёт после тебя.

Про готовность мыслить системно и стратегически.

Даже если ты сейчас просто пишешь код и застрял в развитии, постарайся начать думать шире. Это станет отправной точкой твоего профессионального роста.

Читать полностью…

Backend Interviewer

Карточки про гарантии доставки Apache Kafka

При работе с сообщениями могут возникать различные проблемы как на стороне producer, так и на стороне consumer.

Часть проблем решается настройками на уровне Kafka, а часть - использованием определенных паттернов проектирования.

Рассмотрим:

– At most once
– At least once
– Exactly once
– Transactional Outbox
– Идемпотентный Consumer

Читать полностью…

Backend Interviewer

Как учить глубоко, а не поверхностно

Мы все хоть раз попадали в эту ловушку.

Учишься неделями, читаешь статьи, смотришь курсы – а потом на собесе спрашивают:

А почему именно так?


И в голове пустота.

Потому что одно дело – знать, а совсем другое – понимать.

Поверхностные знания дают уверенность, пока их не проверят

Когда мы читаем статью про volatile, кажется, что всё поняли:

Гарантирует видимость между потоками


Красиво звучит.

Но если задать себе вопрос "что именно гарантирует", "а почему", "а какие есть исключения" – уверенность тает.

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

Глубина начинается с вопроса "почему"

Адекватные интервьюеры не проверяют память. Они проверяют мышление.

И каждое "почему" – это способ показать, что мы понимаем причины, а не заученные факты.

Попробуй задать себе вопрос:

– Почему ConcurrentHashMap устроен иначе, чем HashMap
– Почему volatile не решает проблему атомарности
– Почему в транзакциях важен изоляционный уровень

Каждое "почему" – это как спуск на уровень ниже. Мы перестаём быть пользователями фреймворков и начинаем думать как их авторы.

Не учим темы, а строим связи

Когда мы учим по списку (сегодня JMM, завтра транзакции, послезавтра GC), знания не соединяются. А на собесе редко спрашивают одну тему.

Там спрашивают на стыке:

– Почему HashMap не потокобезопасен
– Что произойдёт при обновлении ссылки без volatile
– Как транзакция в Spring соотносится с уровнем изоляции в БД

Каждый такой вопрос не про тему, а про связь между ними. А значит, готовиться нужно не по списку, а по цепочкам.

Примеры цепочек:

– Потоки -> JMM -> synchronized -> volatile -> CAS/FAA -> ConcurrentHashMap
– БД -> транзакции -> изоляция -> Spring propagation
– Память -> ссылки -> GC

Так знания превращаются в систему.

Понимание рождается через действие

Нет способа понять, просто прочитав. Пока не сделаешь – не почувствуешь.

Реализуй свой LRUCache – и вдруг осознаешь, зачем нужны LinkedHashMap и eviction.

Попробуй написать мини-DI – и поймёшь, что такое внедрение зависимостей.

Реализуй многопоточную очередь – и разберёшься, почему volatile не спасает от гонки.

Каждый раз, когда мы воссоздаём механизм, он перестаёт быть магией.

Объясняем, чтобы понять

Понять – значит смочь объяснить просто.

Попробуй рассказать коллегам:

– Почему volatile не гарантирует атомарность
– Почему транзакция может откатиться даже без ошибок
– Почему поток может не увидеть новое значение переменной

Если можешь объяснить без терминов – ты понял. Если повторяешь слова из статьи – ты просто запомнил.

Возвращаемся назад

Понимание не приходит с первого раза.

Первый проход – обзор.
Второй – связи.
Третий – осознанность.

Каждое возвращение открывает новые уровни. Мы читаем старый материал – и вдруг видим то, чего не замечали раньше. Это и есть глубина.

Мы не обязаны знать всё – важно уметь думать

Мы не должны быть википедией. Глубокое знание – это способность рассуждать, видеть причины и следствия. Лучше знать 10 тем, но понимать каждую до сути, чем 100 – но поверхностно.

Вывод

Когда мы запрещаем себе повторять чужие ответы – мы начинаем искать свои. Мы учимся рассуждать, строить связи и задавать вопросы "почему".

🔥 и расскажу, как мы с учениками закрепляем знания через практику – мини-проекты, лайвкодинг и задачи, которые превращают "понимаю" в "умею".

Читать полностью…

Backend Interviewer

Как не утонуть в подготовке к собесам

Мы садимся готовиться.

Открываем список тем: Java Core, Collections, Concurrency, Spring, SQL, System Design, Live Coding, HR-вопросы…

И кажется, что чтобы пройти, нужно выучить всё.

В первый день – энтузиазм.
Во второй – усталость.
На третий – паника.

Кажется, знаний слишком много, а времени слишком мало.

И все вокруг будто умнее.

Кто-то уже всё прошёл, всё повторил, всё знает, а мы третий день пытаемся вспомнить разницу между ACID и BASE.

Почему мы тонем

Потому что часто готовимся по-учебному: берём список тем и начинаем учить всё подряд.

Но собеседование – это не экзамен.

Это проверка того, как мы думаем. Как мы связываем концепции, рассуждаем, понимаем причины, а не просто факты.

А тонем мы в основном потому, что:

– хватаемся за всё сразу
– читаем, но не закрепляем
– забываем повторять
– и сравниваем себя с другими, вместо того, чтобы видеть свой прогресс

Как выбраться

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

1. Определяем цель

Мы не можем готовиться "вообще".
Если мы идём на live coding – одно дело. Если на system design – другое.
Разный уровень глубины, разные акценты.
Главное – понимать, под какую цель мы выстраиваем путь.

2. Делим на блоки

Java Core -> Collections -> Concurrency -> Spring -> SQL -> System Design.
Закрываем блок – чувствуем прогресс.
Это даёт мотивацию и убирает ощущение хаоса.

3. Учимся понимать, а не запоминать

Не "что делает volatile", а "почему без него не работает".
Не "как написать Lock", а "какие проблемы он решает".
Когда мы понимаем зачем – знание остаётся.

4. Связываем темы

Например: JMM -> happens-before -> volatile -> ConcurrentHashMap.
Так мозг видит систему, а не кучу разрозненных фактов.

5. Закрепляем практикой

Реализуем свой ThreadPool, мини-Spring, LRU Cache.
Когда мы руками повторяем механику – она становится частью мышления.

6. Повторяем

Мозг забывает быстро.
Поэтому каждые выходные – день повторения: пролистали конспекты, рассказали темы вслух, закрыли пробелы.

7. Не забываем про баланс

Если мы грузим себя по 5 часов в день – выгорим за неделю.
Лучше по часу, но стабильно. И обязательно отдых – мозгу нужно время, чтобы переварить информацию.

8. Не ругаем себя

Даже если забыли, даже если не успели.
Это часть процесса.
Главное – понимать, как рассуждать, когда не знаем ответ.

Главный вывод

Подготовка – это не зубрёжка. Это сборка системы знаний.

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

🔥 и разберём как выстроить систему подготовки, чтобы не выгореть.

Читать полностью…

Backend Interviewer

Собеседование – штука стрессовая.

Реальность может подкинуть сюрпризы даже опытному разработчику:

– вопросы, которые в теории знаешь, но формулируешь сумбурно
– live coding, где залипаешь на синтаксисе и теряешь ход мысли
– code review, где кажется, что у тебя слишком мягкий взгляд на чужой код
– системный дизайн, где голова идёт кругом с чего начать, что рисовать, на что обращают внимание

Я через это проходил не раз. И понял: лучший способ подготовиться – это прожить собес до того, как он реально случится.

Особенно в текущих реалиях, когда приглашение на собес получить не так-то просто. И не хочется слить его из-за глупой ошибки.

Поэтому я со своей командой провожу мок-собесы в формате 4 секций:

1. Java/Spring/БД – классические технические вопросы. Проверим глубину знаний и умение объяснять.

2. Live Coding – решаем задачи на алгоритмы. Здесь видно, как кандидат думает и насколько чисто пишет код в стрессовой обстановке.

3. Code Review – рассмотрим реальный кусок кода, где нужно найти проблемы и предложить улучшения.

4. Системный дизайн – разберём архитектуру сервиса: от постановки требований до продакшен-решения. Здесь проверяется не только знание инструментов, но и умение мыслить на более высоком уровне.

Можно выбрать произвольное количество секций: хоть одну, хоть все.

После каждой секции мы даём подробный фидбек:

– что выглядело уверенно
– где были пробелы
– что стоит подтянуть и как именно это сделать

Это не экзамен. Цель – подсветить слабые места и дать рекомендации, чтобы на реальном собесе чувствовать себя уверенно.

Если хочешь проверить свои силы в таком формате – бронируй встречу

Читать полностью…

Backend Interviewer

Сегодня столкнулся с OOM в микросервисе, который вообще ничего не делал. Только был запущен. Трафик не принимал. Фоновые задачи не выполнял.

Прикол в том, что до этого, трафик в него поступал. Но из-за проблем в сторонней системе, трафик перевели в другой неймспейс кубера.

И через день стрельнул OOM. Контейнер перезапустился. Через сутки снова ООМ.

Размер хипа 256 МБ.

В логах пусто.

Снял хипдамп.

75% хипа забито очередью из класса org.springframework.cloud.sleuth.autoconfig.actuate.BufferingSpanReporter

Что это за класс?

Он активируется при включённом конфиге spring.sleuth.enabled и хранит трассировки.

Размер очереди ограничен 10.000 элементов. При переполнении самый старый элемент удаляется.

Все трассировки, которые мне попались при изучении хипдампа, относились к ошибкам интеграции со spring-boot-admin.

Микросервис пытался постоянно зарегистрировать себя в spring-boot-admin, но был указан невалидный урл и регистрация завершалась ошибкой.

Эта ошибка со стектрейсом и попадала в очередь BufferingSpanReporter. Что в итоге приводило к ООМ.

Почему ООМ не возникал при трафике?

Думаю из-за того, что другие трассировки были небольших размеров, их было больше и они вытесняли тяжёлые ошибочные трассировки от интеграции со spring-boot-admin.

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

Читать полностью…

Backend Interviewer

Сравнимаем производительность потокобезопасных счётчиков

Что обычно отвечает человек, когда его просят реализовать потокобезопасный счётчик?

– synchronized, как что-то стыдное
– AtomicLong, как что-то величественное

Но всегда ли synchronized проигрывает в производительности и есть ли что-то быстрее атомиков?

На самом деле потокобезопасный счётчик можно реализовать множеством разных способов:

– synchronized
– Semaphore
– ReentrantLock
– ReentrantReadWriteLock
– StampedLock
– AtomicLong
– LongAdder

Рассмотрим все эти варианты, сравним производительность в многопоточной среде и в разных сценариях использования чтения/записи.

Полная статья здесь

Читать полностью…

Backend Interviewer

Роадмап подготовки к Java собеседованиям

Цель роадмапа – предоставить список тем и источников для быстрой подготовки к собеседованиям.

Это обновлённая версия. Работая над ней, я хотел собрать в одном месте как можно больше материалов для подготовки к собесам, и, что самое главное, подтолкнуть разработчиков к развитию.

Это не конечная версия. Она будет дополняться и поддерживаться в актуальном состоянии. Поэтому следите за обновлениями.

Так же хочу заметить, что роадмап это не развернутый список вопросов и ответов с собесов. Нет смысла заучивать ответы. Нужно понимать суть.

Темы:

– Java (архитектура jvm, gc, многопоточность)
– Spring (aop, transaction, cloud)
– SQL/NoSQL (acid, base, уровни изоляций, explain)
– Kafka/Docker/Kubernetes
– Паттерны проектирования, ООП, SOLID
– Алгоритмы и структуры данных
– Системный дизайн
– Soft skils

Полная версия роадмапа со всеми темами и источниками лежит здесь

Читать полностью…

Backend Interviewer

Когда перфекционизм мешает писать хороший код

Я просто хочу, чтобы было красиво

Надо всё обобщить

Этот сервис можно сделать универсальным


…а потом 4 часа рефакторишь то, что вообще-то уже работало.

Где-то на пути своего становления учишься писать не идеальный код, а поддерживаемый:

– который можно быстро объяснить
– который не вызывает боль при доработке
– который лучше сейчас, чем идеально когда-нибудь потом

Но перфекционизм подкрадывается.

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

Примеры, которые видел и делал сам.

1. Сделаю универсальный клиент

Зачем писать под один сервис, если можно сделать generic-клиент, который подойдёт для всех?

В результате 3 уровня абстракции, интерфейсы, которые никто не понимает и обобщённый код, который никто не переиспользует.

Лучше сделать простой клиент под одну задачу. Если появится вторая такая же – тогда уже и обобщить.

2. Не могу зарелизить, пока не будет тестов на всё

Один разработчик из нашей команды тормозил исправление бага, потому что там было покрытие 60%, а не 80%.

Лучше выкатить минимально рабочее и уже после обложить тестами. Не потому что тесты не важны, а потому что приоритет – починить систему, а не обогнать сонар.

3. Надо бы зарефакторить – в самый неподходящий момент

У нас была фича, которую надо было выкатить к концу недели.

Но кто-то полез чуть-чуть подправить старый код, и в итоге нарвался на глобальный рефакторинг.

В результате 2 дня потеряно, баг в проде, фичу не выкатили, а старый код всё равно остался.

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

Что я понял

Хороший код – это не идеальный, а поддерживаемый.

Перфекционизм часто – это страх ошибиться, быть непонятым, показать плохую работу.

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

Читать полностью…

Backend Interviewer

4 ошибки, которые я делал, когда думал, что "всё понимаю"

Каждый из нас проходит через это.

Вроде бы уже не джун, копаешься в исходниках, дебажишь прод, можешь на ходу объяснить, как работает gc и зачем нужен circuit breaker.

И в какой-то момент в голове появляется опасная мысль:

Ну всё, я вроде понял, как оно работает


Вот с этого момента и начинается веселье и детские ошибки.

Ошибка 1: Прометей соберёт всё, что надо

Я считал, что если в сервисе есть метрики и алерты – значит, всё под контролем.

Но я не задал себе простой вопрос: а какие метрики вообще имеют смысл?

У нас был процессинг заказов. Всё шло по плану, но однажды мы обнаружили: все заказы старше 2 часов внезапно зависают в статусе PROCESSING.

Никто этого не заметил, потому что ошибок нет и латенси норм на новых заказах.

Но мы не мониторили время жизни заказов в разных статусах.

В итоге пришлось в панике руками чистить залипшие заказы.

Мониторинг ≠ контроль. Алерты должны быть бизнес-осмысленными, а не только технарскими.

Ошибка 2: CompletableFuture.runAsync без контроля

В какой-то момент мы начали распараллеливать вызовы к сторонним сервисам.

Казалось бы удобно:

CompletableFuture.runAsync(() -> callExternalService());


Всё летает, нагрузка падает.

Пока не словили дикие замедления на живом трафике.

Почему?

runAsync использует ForkJoinPool.commonPool()

Этот пул общий на всё приложение. Если callExternalService() тормозит и блокирует поток – пропускная способность падает. И буум.

Если используешь параллельность – контролируй её сам. Используй выделенные пулы. Ограничивай очередь. Не доверяй удобным API.

Ошибка 3: volatile – замена synchronized

Типа, если переменная volatile, то это потокобезопасно.

Реальность: volatile даёт гарантии видимости, но не атомарности.

В условиях конкуренции можно получить неконсистентное состояние.

Один раз я словил баг, где поток продолжал работать после установки флага stop. Причём в логах всё выглядело ок.

Ошибка 4: Kafka гарантирует порядок сообщений

Реальность: порядок гарантируется только внутри одной партиции.

Был случай, когда мы отправляли OrderCreated, OrderShipped, OrderDelivered, и в консьюмере они приходили не по порядку.

Потому что не было стратегии партиционирования и всё улетало в разные партиции.

Вывод

Самое опасное состояние – это не когда ничего не знаешь. А когда знаешь 80%, и думаешь, что знаешь 100%.

Вот тут и подставляешься.

Поэтому теперь, даже если я вроде бы всё понял, я иду и:

– читаю спеку
– лезу в исходники
– проверяю поведение в тестах
– спрашиваю себя: «А как это сломается?»

Читать полностью…

Backend Interviewer

Как говорить про проект на собеседовании: 3 удачные схемы

Что делает большинство?

Ну… это был корпоративный портал. Мы там использовали Spring. И делали интеграцию с 1С. Я там пилил фичи.


Скучно, без фокуса, без смысла.

Даже если человек реально писал сложные вещи – этого не видно.

Я предлагаю использовать одну из трёх схем, которые превращают рассказ в сильную демонстрацию своего уровня.

Схема 1: Боль – Роль – Архитектура

Боль: С чем пришел бизнес? В чем была проблема, вызов, ограничения?
Роль: Что делал лично ты? Какие решения принимал?
Архитектура: Как ты это реализовал технически?

Бизнес хотел ускорить расчёт скидок в корзине: было слишком медленно, 3+ секунды. Я переписал калькулятор на чистом Java, оптимизировал SQL-запросы и закешировал правила. Это был отдельный сервис на Spring Boot с Redis и асинхронной обработкой. Время ответа – 300 мс.


Звучит как реальная польза + инженерная работа + понимание архитектуры.

Схема 2: Цель – Механика – Технологии

Цель: Зачем делался проект? Какая была бизнес-задача?
Механика: Как это работало? Что происходило под капотом?
Технологии. Какие библиотеки, паттерны, фреймворки использовались?

Делали сервис нотификаций для внутренних систем. При получении события генерировались уведомления по шаблону и отправлялись через Kafka -> Email/Push. Использовали Spring Cloud Stream, FreeMarker, RetryTemplate, Prometheus, Zipkin для трейсинга.


Отлично показывает масштаб и понимание потоков данных.

Схема 3: Что было – Что стало – Как дошли

Что было: Начальное состояние, проблемы, ограничения.
Что стало: Чего добились? Метрики, улучшения, профит.
Как дошли: Какие действия, подходы, решения применялись?

Была монолитная система, которую тяжело было масштабировать. После перехода на микросервисы – катим фичи независимо, проще локализовать баги, уменьшили время отклика на 30%. Внедрили Spring Cloud, API Gateway, Eureka, и распилили 3 ключевых модуля: аутентификацию, биллинг и отчёты.


Это любимая схема интервьюеров, потому что она показывает и результат, и инженерную эволюцию.

Главное не просто что ты делал, а зачем, как и с каким эффектом

Каждый проект – это шанс показать свой уровень, если правильно его подать.

На практикуме по собеседованиям мы тренируем это постоянно: ты рассказываешь – я помогаю упаковать это в выигрышную схему. Так, чтобы было видно: ты зрелый инженер, а не просто код писал. Старт через 2 недели. 70% мест уже занято.

Читать полностью…

Backend Interviewer

Пет-проекты не нужны

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

И вот первая мысль:

А может, мне сделать пет-проект? Ну, типа… TODO-лист или трекер задач.


Но есть один нюанс.

Зачастую пет-проект только ворует твоё время и даёт ложное ощущение продуктивности.

Сколько в мире пет-проектов вида Spring Boot + CRUD + PostgreSQL + Swagger? Один контроллер, одна табличка, один сервис.

Миллионы.

Это ничего не говорит о твоём уровне.

HR и разработчики не будут вникать, если там нет уникальности, сложности, нестандартных решений.

Даже если пет-проект классный, но без README, без деплоя, без документации, без тестов, с коммитами вида "Update", "Fix", "Final version final final v2", он не вызывает доверия. Он выглядит как очередная незаконченная подделка.

Ещё хуже, если ты делаешь пет-проект, чтобы не готовиться к собеседованиям.

Это частая ловушка.

Человек боится собеседований. Чувствует себя неуверенно. И такой:
«Сначала сделаю пет-проект, потом пойду на собес»
«Сначала закончу регистрацию, потом добавлю фронт, потом… потом…»

В итоге ты 3 месяца сидишь в вечном проекте, а реального роста ноль. Ни навыков общения, ни тренировки ответов, ни практики по вопросам.

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

Например, SaaS или open-source библиотека.

То, что требует длительных, вдумчивых и компромиссных решений.

Это уже не игрушка. Это портфолио твоего инженерного подхода.

Но сколько времени ты потратишь на такой проект? Несколько месяцев? Год?

А какая была изначальная цель?

Найти работу, повысить доход.

Это не терпит целый год.

Может быть, вместо пет-проекта, тебе просто нужна структурная подготовка к собеседованиям?

Чтобы понимать, что реально спрашивают.

Как отвечать на сложные вопросы и понимать как работает под капотом.

Как показывать свой опыт и продавать себя как сеньор.

Это то, что мы тренируем на практикуме по собеседованиям.

Прокачиваем хард и софт скиллы за 6 недель. Треть мест уже занята.

Ну а вместо бессмысленного пет-проекта, делай полезный проект для людей.

Читать полностью…

Backend Interviewer

Карточки про синхронизаторы Java

Синхронизаторы позволяют управлять потоками более гибко, мощно и безопасно, чем низкоуровневые и примитивные synchronized, wait, notify и join.

Примеры использования:

– Ограничить количество одновременных действий
– Дождаться завершения нескольких потоков
– Запустить все потоки одновременно
– Обменяться данными между потоками

Виды:

– Semaphore
– CountDownLatch
– CyclicBarrier
– Phaser
– Exchanger

Читать полностью…

Backend Interviewer

Вечный Junior

С Антоном мы пересеклись на одном митапе, когда он работал джуном в аутстаффе уже 3 года.

Джуном 3 года, Карл!

Никакого роста и архитектурных задач. Ревью – формальность. Сеньоры заняты, а тимлид кидает таску и исчезает. Джуниорская вилочка и бесконечное "ещё не время".

Он чувствовал себя застрявшим.

И он был прав.

Но он боялся.

Боялся собеседований, общения с hr, рассказов о своем опыте.

«А вдруг я не пройду собес?»
«А вдруг я ничего не умею?»
«А вдруг я уйду, и окажется, что я действительно слабый?»


Правда в том, что это нормальное состояние.

Так чувствуют себя многие, кто остался без настоящей обратной связи и без возможности развиваться.

И это не их вина.

Это можно исправить.

Но нужен план, поддержка и кто-то, кто скажет: «Ты не тупой, просто ты слишком долго был один в этом болоте».

Я понимал, что могу помочь Антону, направить его в правильное русло.

Но раньше я никогда не менторил разработчиков не из своей команды.

Как и для Антона, так и для меня это был вызов.

И мы оба решили действовать.

С первого же созвона я понял – у Антона есть потенциал. Просто его никто не видел. А он сам – не знал, как его раскрыть.

Что мы сделали?

– Выявили пробелы в знания
– Прокачали многопоточку и алгоритмы
– Написали пет-проект с популярными технологиями
– Переписали резюме
– Провели мок-собеседование
– Разобрали реальные собеседования
– Обсудили его страхи

И вот спустя два месяца:

– Несколько офферов
– Один из бигтех
– Уровень мидл
– Доход x2

Главное – он обрел уверенность и понятный план развития до сеньора.

И знаешь, что самое интересное?

Ты можешь так же.

Ты можешь оставаться в болоте – или пойти по пути роста.

Ты можешь бояться – или готовиться и пробовать.

Ты можешь ждать, пока настанут времена получше – или взять и вытащить себя на новый уровень уже сейчас.

Выбор только за тобой.

Читать полностью…

Backend Interviewer

Как выделиться среди других кандидатов, когда спрашивают про Transactional в Spring

Рассказать про механизмы создания прокси: JDK Dynamic Proxy, CGLIB, Byte Buddy.

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

Поэтому разберём отличия.

JDK Dynamic Proxy

Используется, если класс реализует интерфейс.

Встроен в JDK (java.lang.reflect.Proxy), лёгкий, быстрый, прост в отладке.

Работает только с интерфейсами.

CGLIB

Используется, если класс не реализует интерфейс.

Основан на наследовании – создаёт подкласс твоего класса с переопределёнными методами.

Не требует интерфейсов.

Не работает с final классами и методами, менее прозрачен в отладке.

Byte Buddy

Используется в более гибких и низкоуровневых сценариях (Spring Actuator, Spring DevTools, Mockito, APM-агенты).

Генерирует или модифицирует байткод в рантайм, без ограничения интерфейсами или наследованием.

Безумно гибкий, работает даже с final классами и методами (если использовать javaagent).

Какой прокси используется в Transactional

В AOP используется JDK Dynamic Proxy или CGLIB.

Если есть интерфейс – JDK Dynamic Proxy.
Если нет – CGLIB.

Есть возможность всегда использовать CGLIB, указав proxyTargetClass=true.

Можно легко понять, какой механизм используется, посмотрев в дебаг:
– JDK Dynamic Proxy: com.sun.proxy.$Proxy32
– CGLIB Proxy: MyService$$EnhancerBySpringCGLIB$$abc123

Если
хочешь узнать как застать интервьюера врасплох на вопросах про прокси, ставь 🔥 и разберем в следующем посте.

Читать полностью…

Backend Interviewer

Извините, но мы не готовы сделать вам предложение

Я получал это сообщение десятки раз.

Оно всегда приходит холодным текстом. Без эмоций. Без объяснений.

И каждый раз – будто удар в поддых.

Ты читаешь и замираешь.

Ощущение, как будто тебе сказали:

Ты недостаточно хорош. Мы посмотрели на тебя – и решили, что ты не подходишь.


Но это ложь. Это не правда. Правда – другая.

Отказы – это не оценка тебя как личности или специалиста

Собеседование – это не объективный замер твоей ценности.

Это игра с кучей переменных:

– кто попался в интервьюерах
– в каком ты был состоянии
– как сформулировали вопросы
– сколько было кандидатов на ту же роль
– какой у них был "идеальный профиль" в голове

Это лотерея.

Можно готовиться. Улучшать шансы. Но 100% гарантии нет ни у кого.

У меня было 10 отказов подряд

И это были не какие-то фейковые отклики – я реально доходил до финальных этапов.

Иногда даже в компаниях, где хотел работать всем сердцем.

Где-то я сливался на кодинге.
Где-то тупил от стресса.
Где-то просто не попадал в культуру.

После 10 отказа я стал сомневаться в себе:

Может, я переоценил свой уровень?

А вдруг я уже не дотягиваю до рынка?

Может, мне стоит бросить java и уйти во что-то полегче?


Синдром самозванца лез из всех щелей.

Что помогло мне не сломаться

Я начал вести журнал собеседований.

Что спросили, что ответил, где слил, какие эмоции были.

Начал разбирать вопросы с ментором. Чтобы понимать суть.

Перестал гнаться за "оффером любой ценой". Стал искать проект и команду, подходящую именно мне.

И однажды – получил то самое "да", которое стоило всех отказов.

Что я понял после всего этого

Отказ – это не "ты плохой". Это просто "не то место".

Ты не обязан подходить всем. Это нормально.

Каждое собеседование – это опыт.

Чем больше собесов – тем лучше ты говоришь, держишься, аргументируешь.

Ты прокачиваешь не только знания, но и эмоциональный интеллект.

Это мощный навык, который пригодится тебе во всех сферах.

У тебя уже есть мужество – потому что ты продолжаешь идти.

Большинство вообще не пробует.

И вот что я скажу тебе, если ты сейчас на грани

Ты не один.
Ты не сломан.
Ты просто в середине пути.

Каждый отказ – это шаг. И ни один из них не был зря.

Ты накапливаешь скиллы.

А это, поверь, и есть путь к сильному разработчику.

Читать полностью…

Backend Interviewer

Как один Scheduled превратился в DDoS на наш собственный сервис

Есть у нас микросервис, который раз в секунду проверяет статусы задач.

Обычный @Scheduled(fixedRate = 1_000)

Работал год – вообще без проблем.

Вычитывал из бд задачи, проверял статусы и обновлял другие таблицы.

А потом мы немного добавили логики:

– запросы к бд стали тяжелее
– появились внешние вызовы
– и, внимание, время выполнения стало больше секунды

Что это значит?

Scheduler с fixedRate не ждёт окончания предыдущей задачи.

Он просто запускает новую копию метода каждую секунду.

Из-за того, что предыдущий метод не успел завершиться и внести изменения в бд, новый метод работал со старыми данными и делал всю ту же работу, что и первый метод.

И вот у нас уже 5 потоков, 10, 15…

Потребление cpu взлетает.

Внешние системы начинают отваливаться, потому circuit breaker мы не добавили, подумав «ну что тут может случиться».

По факту мы устроили себе локальный DDoS.

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

В итоге мы поменяли fixedRate на fixedDelay и проблема ушла.

Поэтому не доверяй слепо аннотациям – понимай, как они работают внутри.

Читать полностью…
Subscribe to a channel