Будь готов к техническому интервью
Andoird | Михаил Белый
Подключился на первый техсобес и быстро понял, что не готов. Знания оказались поверхностными, ответы – невнятными, а формулировки расплывчатыми. Итог – клеймо Middle– и изгнание с позором. Чтобы подобное не повторилось – будем основательно готовиться. В этой статье разберем, что нужно знать для прохождения собеседования на синьора. Определим грань, когда информации достаточно и пора переходить к практике.
Интервьюер приходит с опытом, сотнями собеседований и экспертизой в рамках своей компании. На нашей стороне – ограниченный тайминг и возможность подготовиться. На этом и сосредоточимся.
Статистика
- Техсобесов: 25. Вторых этапов – 6.
- Продолжительность: средняя – 01:08:21, минимальная – 38:06, максимальная – 02:04:15.
- Конфигурации участников: только техлид – 6, только старший разработчик – 9, техлид + разраб – 7, техлид + разраб + hr – 2, техлид + 3 разраба + менеджер – 1.
- Формат интервью: только теория – 11, только лайвкодинг – 2, теория + лайвкодинг – 4, теория + алгоритмы – 5, теория + систем дизайн – 1, теория + лайвкодинг + систем дизайн – 1, алгоритмы + лайвкодинг + систем дизайн – 1.
Формат взаимодействия
- Теория топ-100 вопросов. Интервьюер задает вопросы по ключевым темам. Иногда показывает код и спрашивает, что напечатается в консоль. Одна тема следует за другой, вопросы могут возникать на основе твоих ответов.
- Лайвкодинг. Написать код в онлайн-редакторе с подсветкой синтаксиса, но без автоподсказок. Реализовать небольшие классы, провести рефакторинг спагетти-кода или исправить баги. Если времени мало, допустимо использовать псевдокод.
- Алгоритмическая задача. Уровень сложности – изи с литкода. Написать решение, оценить его эффективность (Big О) и объяснить выбор подхода. Не рекомендуется использовать библиотечные функции.
- Системный дизайн. Набросать архитектуру клиент-серверного приложения, например, корзины интернет-магазина или клона инстаграм. Чаще это устный формат, но иногда используют визуальный редактор для схем.
Предварительная подготовка
Проснулись, потянулись, умылись, причесались, приоделись. Говорить придется не меньше часа – запасись водичкой, чтобы не охрипнуть. Проверь интернет и микрофон. Протри камеру. Включи запись экрана.
Прекратить тряску
Бывает, что присутствует тряска: колени дрожат, ладошки потеют, голос сбивается, все забывается. Как можно пофиксить ситуацию:
- Опыт. Наработать количество интервью. Тряски не будет на десятом собеседовании.
- Алкоголь (осуждаю). Бахнуть пинту пива или бокал винишка для расслабления.
- Спорт (одобряю). Анжуманя, пробежка или силовая тренировка. Кровь разгонит кислород, что-то достанется и мозгам.
Теория
Опытным путем выявлены темы, которые интересны интервьюеру. Они охватывают аспекты разработки Android-приложений, от базовых знаний до сложных архитектурных решений.
- Android SDK.
- Android UI (View).
- Сборка Android-приложений.
- Архитектура Android-приложений.
- Паттерны проектирования.
- Работа с сетью.
- Dependency Injection.
- Android Jetpack.
- Jetpack Compose.
- Kotlin.
- Java.
- Многопоточность, асинхронность и синхронизация.
- Алгоритмы и структуры данных.
- DevOps.
- Git.
Раскроем каждую тему подробно:
Android SDK
Основные компоненты. Разбираться, что за компоненты, почему основные, чем отличаются от других классов SDK, где используются и как вызываются.
Activity. Один из главных компонентов. Знать методы жизненного цикла и понимать, как приложение ведет себя при сворачивании, смене конфигурации и отображении диалогов. Разбираться во всех launchMode и объяснять, как они влияют на стек и вызов onNewIntent. Понимать, как сохраняется состояние.
Service. Знать, как менялось поведение сервисов с новыми версиями Android. Различать background и foreground сервисы, понимать, как запустить сервис в отдельном процессе. Что такое bound-сервис и его жизненный цикл. Как сервис взаимодействует с другими компонентами.
BroadcastReceiver. Знать определение, назначение, способы регистрации. Помнить, что это единственный компонент, который можно не указывать в манифесте. Различия между ресивером, указанным в манифесте, и созданным из кода. Отличия явного и неявного ресивера. Примеры системных ресиверов.
ContentProvider. Наименее используемый компонент. Знать определение и системные примеры (мультимедиа, контакты, календарь).
Манифест. Назначение, важные атрибуты, время считывания системой. Важно понимать, что происходит с манифестами при сборке многомодульного проекта.
Context. Определение и основные области применения. Знать типы (Application, Activity) и различия между ними. Следовать бестпрактисам и избегать утечек.
Bundle. Знать, для чего используется, поддерживаемые типы данных, ограничения и роль в транзакциях.
Parcelable. Знать преимущества, отличия от Serializable и как работает ручная настройка.
Intent. Знать назначение. Различать явные и неявные интенты. Зачем нужен PendingIntent и его особенности безопасности.
Fragment. Знать, зачем появился, жизненный цикл, методы при транзакциях add и replace. Разбираться в FragmentManager, работе со стеком, синхронных и асинхронных операциях, управлении состоянием.
Permissions. Знать, на кой введены и на какие типы делятся. Как декларировать и запрашивать. Помнить про отзыв разрешений и поведение диалога.
Handler и Looper. Знать, как используются, как работает очередь сообщений (MessageQueue) и что такое Message. Уметь обновлять UI из фоновых потоков.
Android UI (View)
XML. Знать особенности тегов <merge/> и <include/>. Помнить, что большая вложенность замедляет рендеринг, а плоская структура ускоряет.
View. Разбираться в жизненном цикле и методах onMeasure, onLayout и onDraw. Понимать особенности onDraw (не создавать объекты внутри). Знать, как работают invalidate и requestLayout. Уметь сохранять состояние View.
ViewGroup. Понимать работу метода onLayout. Знать популярных наследников (FrameLayout, LinearLayout, ConstraintLayout). У LinearLayout помнить про атрибут weight (двойной проход при отрисовке).
ViewStub. Знать про отложенную инициализацию.
ScrollView. Помнить, что он содержит только один элемент.
RecyclerView. Знать причины появления. Помнить методы адаптера. Роль DiffUtil и алгоритм обновления списка. Понимать назначение ItemDecoration. Причины возможных тормозов при скролле и способы оптимизации.
Анимации. Перечислить классы для анимаций. Помнить про MotionLayout. Разбираться Choreographer и его методах.
Жесты и тачи. Знать про onTouchEvent и его особенности (буферизация касаний). Разбираться в классе GestureDetector и типах жестов.
Сборка Android-приложений
Gradle. Знать, как подключаются зависимости и плагины, что такое applicationId. Разбираться в разнице между implementation и api. Понимать, что такое product flavors и build types, зачем нужны minSdk, compileSdk и targetSdk. Важно уметь объяснить, почему нам дают настраивать targetSdk.
R8 и ProGuard. Понимать, что такое обфускация и минификация кода.
Модули. Знать преимущества и недостатки многомодульности. Уметь объяснить, как делить приложение на модули, избегая циклических зависимостей. Понимать разницу между jar и aar.
APK. Знать структуру APK: что такое .dex файлы, ресурсы, манифест. Описать процесс сборки APK. Перечислить способы уменьшения размера APK. Объяснить, зачем появился формат AAB и его преимущества.
Процесс приложения. Уметь объяснить, как создается процесс, от клика на иконку в лаунчере до вызова Application.onCreate. Знать, что такое Zygote и как она ускоряет запуск приложений. Понимать приоритеты процессов в Android.
Профилирование и отладка. Знать инструменты для отладки и профилирования.
Архитектура Android-приложений
Декларации. Знать определения класса, абстрактного класса и интерфейса. Понимать различия между абстрактным классом и интерфейсом.
Чистая архитектура. Знать, зачем нужна, плюсы, минусы и подводные. Перечислить слои, их ответственность и взаимосвязь. Уметь объяснить, на каком слое происходит маппинг.
MV-паттерны. Разбираться в MVVM, MVP и MVI, знать значение каждой буквы и примеры в Android. Объяснить, чем MVVM отличается от MVP и как слои MVVM соотносятся с чистой архитектурой.
UDF (Unidirectional Data Flow). Понимать концепцию и где используется в Android.
ООП. Знать принципы ООП: инкапсуляцию, полиморфизм, наследование и как применяются в Android.
SOLID. Знать определение каждой буквы. Где и как эти принципы применяются и нарушаются в Android.
Отношения классов. Объяснить разницу между ассоциацией, композицией и агрегацией, привести примеры из Android.
Паттерны проектирования
Понимать, зачем нужны паттерны. Знать категории: порождающие, структурные и поведенческие. Разбираться в следующих паттернах: Adapter, Decorator, Facade, Composite и Singleton.
Работа с сетью
Сериализация. Понимать формат JSON: какие типы данных поддерживаются, как выглядят объекты и массивы. Уметь преобразовать JSON в data-классы. Разбираться в библиотеках GSON и KotlinX.Serialization, знать их особенности и настройки для безопасного парсинга.
REST. Разбираться в заголовках и методах. Понимать разницу между GET и POST. Разбираться в кодах ответа и уметь корректно обрабатывать ошибки.
Retrofit. Знать, как работает и настраивается Retrofit. Уметь обрабатывать ошибки и управлять асинхронными запросами.
OkHttp. Знать, для чего нужны интерсепторы. Уметь настраивать повтор запросов при сетевых сбоях. Разбираться в работе с Access/Refresh токенами и настройке авторизации.
Сокеты. Понимать особенности работы с WebSockets в Android. Настраивать соединение и обрабатывать события.
SSL. Знать, что такое SSL и зачем он нужен для защиты данных. Уметь настраивать HTTPS в Android. Понимать разницу между HTTP и HTTPS и как избежать атаки «Man-in-the-Middle».
Dependency Injection
Знать, зачем нужен DI. Понимать разницу между DI и Service Locator, между Dagger и Koin. Разбираться в понятиях: скоупы, компоненты и сабкомпоненты, модули, квалифаеры, Binds и Provides, BindsInstance.
Android Jetpack
ViewModel: Понимать назначение и принцип работы. Разбираться, как сохраняются данные при смене конфигурации.
WorkManager: Понимать, зачем появился и как использовать. Какие есть типы задач (OneTimeWorkRequest, PeriodicWorkRequest), ограничения и настройки. Что использует под капотом.
Paging: Разбираться Paging Library. Различать виды пагинации (page, limit/offset, lastId).
Room: Знать основные компоненты (Entity, DAO, Database). Как работает каскадное удаление и как настраиваются связи между таблицами.
Jetpack Compose
Отличие от XML. Понимать, что такое декларативный подход. Разбираться в жизненном цикле Composable-функций. Знать разницу между remember и rememberSaveable.
Рекомпозиции. Уметь минимизировать и профилировать. Помнить, что Composable-функции вызываются не по порядку. Знать про объект Composer и параметры skippable и restartable.
Стабильность типов. Разбираться в понятиях стабильности (Stable, Immutable). Знать, какие типы считаются стабильными и как это влияет на рекомпозицию. Понимать, что такое Strong Skipping Mode.
derivedStateOf. Знать для чего используется.
Snapshot: Понимать, что такое SnapshotStateList и как с ним работать. Уметь обновлять списки без перерисовки всех элементов.
Side Effects. Знать сайд-эффекты (LaunchedEffect, DisposableEffect, SideEffect). Понимать назначение, особенности работы и порядок вызова. Разбираться, как работают ключи и когда вызывается onDispose у DisposableEffect.
CompositionLocal. Понимать, для чего используется. Уметь создавать локальные переменные. Знать системные примеры.
Layouts. Знать основные контейнеры (Box, Row, Column). Разбираться в особенностях FlowRow. Понимать, как работает LazyColumn, и зачем указывать ключ для элементов. Уметь обновлять списки, избегая лагов.
Modifier. Знать, как работают операторы и как порядок вызова влияет на поведение и производительность.
Kotlin
Система типов. Чем отличается от Java, почему нет примитивов и есть поддержка null-safety. Понимать разницу между val и const val, особенности lateinit. Знать про Any (методы), Unit и Nothing (сценарии использования).
Data-классы. Знать определение и ограничения. Разбираться в особенностях лямбд в конструкторе. Понимать, как работают все методы и благодаря чему реализовано сопоставление с образом.
Sealed-классы. Понимать зачем нужны и какие преимущества использования. Отличать от enum и sealed interface.
Inner-классы. Понимать зачем нужны, к каким полям имеют доступ. Отличать от вложенных классов (nested).
Value-классы. Понимать зачем нужны и какие имеют ограничения. Помнить про аннотацию @JvmInline.
Object. Знать определение и сценарии использования. Понимать, как companion object связан с родительским классом.
Inline-функции. Разбираться в назначении. Знать про noinline, crossinline, reified и его роль при стирании типов. Понимать, почему не стоит инлайнить все функции.
Extension-функции. Знать определение, устройство под капотом и поведение при совпадении сигнатур.
Scope-функции. Перечислить все известные (let, run, with, apply, also), разбираться в примерах использования и возвращаемом типе.
Функции высшего порядка и анонимные функции. Знать определение и примеры. Понимать, как они выглядят в коде и зачем нужны.
Модификаторы доступа. Знать отличие от Java. Уметь рассказать про open и internal.
init и constructor. Разбираться, как работает блок init и вторичные конструкторы. Знать порядок вызова.
Дженерики. Понимать разницу между ковариантностью (out), контрвариантностью (in) и инвариантностью.
Делегаты. Разбираться в видах делегатов (на класс и на поле). Знать, как работает lazy.
Исключения. Понимать, как работает try-catch-finally и что его результат присваивается переменной. Знать иерархию исключений в Kotlin и ее отличие от Java.
Коллекции. Понимать разницу между мутабельными и иммутабельными коллекциями. Разбираться в работе последовательностей (Sequence), знать, в каких случаях их использование предпочтительней.
Java
Система типов. Помнить про деление на примитивные и ссылочные типы. Понимать, как работает боксинг и анбоксинг.
Строки. Разбираться в устройстве String и работе StringPool.
Модификаторы доступа. Понимать особенности static: что такое статический класс, поле и метод, в чём разница с обычными.
Исключения. Различать проверяемые и непроверяемые исключения. Понимать, почему в Java есть checked-исключения и в каких случаях их использовать.
Дженерики. Понимать концепцию стирания типов (type erasure), зачем нужно и к чему приводит.
Serializable. Разбираться в интерфейсе Serializable и роли модификатора transient. Знать, чем отличается от Parcelable в Android.
Коллекции. Уметь объяснить, как работают ArrayList, LinkedList, HashSet и HashMap. Сравнивать структуры ArrayList и LinkedList, знать их плюсы и минусы. Понимать устройство HashMap: добавление и удаление элементов, вычисление бакетов, решение коллизий. Знать, что HashSet — это обёртка над HashMap.
Object. Знать методы класса Object. Понимать контракт между equals и hashCode. Помнить диапазон значений хэшкода.
Сборщик мусора. Знать, что такое сборщик мусора и зачем нужен. Понимать, когда триггерится сборка. Что такое GC Roots и каки есть виды ссылок. Разбираться в причинах утечек памяти и способах предотвращения. Понимать, что такое stop the world и когда происходит.
Модель памяти. Объяснять различия между стеком и кучей: какие объекты там хранятся.
Многопоточность, асинхронность и синхронизация
Thread. Понимать, как работает класс потока. Знать методы. Помнить, что потоки занимают ресурс рандомно.
ThreadPool. Разбираться в принципе работы. Отличать от цикла потоков.
synchronized. Знать, когда использовать. Понимать, что блокировка происходит на уровне монитора объекта.
volatile. Знать определение и принцип работы. Понимать концепцию Happens Before. Уметь инкрементить счетчик из разных потоков.
Мьютекс, монитор и семафор. Разбираться в определениях.
Локи. Уметь работать с ReentrantLock и ReadWriteLock.
Deadlock. Знать, что такое взаимная блокировка и как ее предотвратить. Описать ситуацию возникновения взаимной блокировки.
Race condition. Знать определение, в каких случаях возникает и как предотвратить.
Атомарность. Знать, зачем нужны атомарные переменные. Разбираться в операторе compareAndSet (CAS). Помнить разницу между a++ и ++a в многопоточном контексте.
Многопоточные коллекции. Знать примеры. Понимать, как работают ConcurrentHashMap и synchronizedMap.
RxJava. Знать, как строится цепочка операторов. Понимать, что делают subscribeOn и observeOn. Разбираться в типах стримов (Observable, Single, Completable, Maybe, Flowable) и планировщиках (Schedulers). Знать про Subjects и операторы объединения (merge, concat, zip). Разбираться в различиях между map, flatMap, concatMap и switchMap.
Kotlin Coroutines. Понимать отличие корутин от потоков, как работает синхронизация и что такое Mutex. Знать, что такое Structures cuncurrency. Разбираться в билдерах launch (возвращает Job) и async (возвращает Deferred). Знать про контекст корутины, скоупы и диспетчеры. Уметь рассказать про механизм приостановки (suspend) и объект Continuation. Знать, как работает runBlocking. Про обработку ошибок через CoroutineExceptionHandler. Уметь отменять корутины.
Kotlin Flow. Понимать концепцию потоков (Hot и Cold). Разбираться в StateFlow и SharedFlow, различиях и настройках. Знать примеры билдеров и операторов. Уметь объединять потоки (zip, combine).
Kotlin Channels. Понимать, зачем нужны каналы. Знать типы каналов и сценарии их использования.
Алгоритмы и структуры данных
Структуры данных. Знать популярные: массив, связный список, стек, очередь, дерево, граф, хеш-таблица. Понимать особенности и сложность основных операций. Уметь объяснить, когда использовать каждую из структур и в чём их преимущества.
Анализ сложности алгоритмов. Понимать, как растет сложность алгоритма при увеличении объема данных. Знать, что такое асимптотическая сложность (Big O), и уметь объяснить разницу между O(1), O(n), O(n log n), O(n²).
Алгоритмы сортировки. Знать алгоритмы и их сложность.
DevOps
Качество кода. Разбираться в инструментах анализа кода: Detekt и Lint.
Тестирование. Знать разницу между Unit-тестами и UI-тестами. Понимать, что стоит покрывать Unit-тестами.
Сборка и доставка. Разбираться в инструментах автоматизации. Понимать этапы CI/CD – сборка, тестирование, публикация.
Бэкенд. Уметь настраивать интеграцию с Firebase: Messaging (уведомления), Analytics (события), Crashlytics (ошибки), Remote Config (параметры).
Мобильные сервисы. Знать, как разделить архитектуру приложения на GMS (Google) и HMS (Huawei), чтобы поддерживать оба стека в одном коде и минимизировать дублирование логики.
Git
GitFlow. Понимать, как организована работа в разных ветках: main, develop, feature, release, bugfix. Знать, когда и зачем создаются новые ветки и как происходит слияние.
Команды. Различать merge и rebase. Знать, что делают revert и cherry-pick.
Файл .gitignore. Знать, для чего используется и что туда добавлять.
Лайвкодинг
Интервьюер проверит способность распознавать и исправлять сломанный код. Оценивается не только умение писать код, но и способность рефакторить и улучшать существующий.
Корутины. Задачи на асинхронную обработку данных. Показать знание диспетчеров (Dispatchers.IO, Dispatchers.Main) и умение управлять многопоточностью. Стремиться к минимизации блокировок и избегать неправильного переключения контекста.
Compose. Снижать количество рекомпозиций. Использовать remember, derivedStateOf и key для оптимизации производительности. Знать, когда и где применять rememberSaveable.
Алгоритмическая задача
Интервьюер проверит понимание базовых алгоритмов и структур данных, а также умение анализировать их эффективность.
Бинарный поиск. Знать принцип работы и когда использовать.
Скользящее окно. Знать технику, когда используется, и её преимущества для задач на подмассивы и подстроки. Уметь решать задачи с использованием двух указателей, сохраняя линейную сложность.
Оценка сложности. Уметь рассчитывать временную и пространственную сложность. Разбираться в понятиях O(1), O(n), O(n log n), O(n²) и знать примеры алгоритмов с каждой из них.
Массивы и строки. Уметь работать с базовыми операциями (доступ, вставка, удаление). Знать методы обработки строк: разворот, палиндромы, поиск подстрок.
Системный дизайн
Интервьюер проверит умение мыслить стратегически, проектировать архитектуру приложения с нуля, учитывать будущий рост и изменения.
Архитектура и слои. Понимать разделение на слои, выбирать подходящую архитектуру, проектировать интерфейсы API и взаимодействие с бэкендом.
Нестабильный интернет. Знать, как проектировать приложение для работы при плохом соединении: использование WorkManager, кэширование, повтор запросов с экспоненциальной задержкой.
Фоновые сервисы. Уметь планировать фоновые задачи для синхронизации данных, загрузки файлов, обновления контента.
Работа с картинками. Оптимизация загрузки и отображения картинок, кэширование, обработка больших изображений.
Пагинация. Знать стратегии, уметь использовать библиотеку Paging, учитывать задержки и ошибки при загрузке данных.
Экран поиска. Уметь проектировать поиск с поддержкой автодополнения, дебаунсинга запросов, кэширования результатов и обработки ошибок.
Обработка ошибок. Разработать стратегию обработки сетевых и внутренних ошибок, показывать пользователю понятные сообщения, использовать глобальный обработчик ошибок.
Безопасность данных. Хранение с шифрованием, защита ключей и безопасная работа с токенами.
Работа оффлайн. Кэширование, локальное хранение, синхронизация при восстановлении соединения.
Выводы
Все знать невозможно. Поэтому роляет широкий кругозор, насмотренность, экспертиза и подготовка. Как подготовиться:
- Работать с опытными коллегами и перенимать практики.
- Разбирать исходный код чужих проектов на гитхабе.
- Читать статьи, смотреть видосики.
Формулировки должны быть: сказал как отрезал. Большинство неприятных вопросов появляются из-за того, что интервьюер не принял невнятный ответ. Плохие формулировки:
- Корутины – это облегченные потоки.
- Sealed-класс – это enum-класс на стероидах.
Хорошие:
- Корутины – это инструмент для асинхронного программирования. Позволяет приостанавливать и возобновлять выполнение функций без блокировки потока. Корутины управляются диспетчером и переключаются между потоками, эффективно обрабатывая фоновые задачи.
- Sealed-класс – это специальный класс c ограниченным числом наследников. Они известны на этапе компиляции. Упрощает хранение значений и обработку в выражениях when. Снижает вероятность ошибок, и делает код безопасным и читаемым.
На примере корутин – в первом случае разговор свернет в плоскость Java, придется погрузиться в дебри потоков и их работы. Во втором случае беседа спокойно переключится на диспетчеры.
Пройти алгоритмическую секцию помогут системная подготовка и тренировки на литкоде. Уже после 10 решений нарабатывается навык быстрого распознавания типовых задач.
С этапом лайвкодинга поможет практический опыт. Разбор чужого кода научит быстро находить проблемы и оценивать качество решений.
С этапом систем дизайна – практика говорить на языке бизнеса, внимание на проблемы и требования. Умение предлагать решения с учётом пользовательских сценариев.