Тестові і лайв-кодинг для Java-інженерів: приклади завдань від IT-компаній

Які тестові завдання дають Java-інженерам для перевірки технічних навичок? Ми отримали приклади тестових від IT-компаній і попросили їх розповісти, чим відрізняються перевірки Junior-кандидатів від тестових Middle і Senior. І ще — про те, де більше шансів потрапити на онлайн-кодинг і чому варто звернути увагу на Java Stream API та багатопотоковість.

«У наших завданнях немає навмисних пасток»

Олександр Сутягін, Senior Java Software Engineer в N-iX

Майже в кожному інтерв’ю я використовую лайв-кодинг. Клієнти можуть попросити дати домашнє завдання, але у мене таких випадків не було. Те, як людина пише код наживо, дає побачити, як вона мислить. Домашнє завдання може показати, як кандидат вміє користуватися гуглом, але лайв-кодинг цікавіший у плані алгоритмічного мислення. А коли людина використовує багато гарячих клавіш, видно, що вона має досвід. Наприклад, у три кліки нагенерував собі гетерів і сетерів.

Я маю п’ять завдань на знання Java Stream API. Для Junior достатньо розв’язати три. Четверте — це вже із зірочкою. Якщо розв’яже, великий молодець. Middle має завершити всі п’ять задач, а Senior запропонувати ще й альтернативні варіанти розв’язку. З Senior можна поспілкуватись про те, що буде ефективніше за використанням пам’яті та швидкодією.

package com.company;

import java.util.HashMap;

import java.util.List;

import java.util.Map;

import java.util.function.Function;

import java.util.stream.Collectors;

public class InterviewTest {

    public static void main(String[] args) {

        Student studentFirst = new Student(1, "John", List.of("Math", "History", "Ukrainian"));

        Student studentSecond = new Student(2, "Albert", List.of("Math", "English", "Astronomy"));

        List<Student> students = List.of(studentFirst, studentSecond);

        List<Integer> studentIds = List.of(1, 5);

        //1. Знайти студентів зі списку students, які мають id зі списку studentIds

        List<Student> filteredStudents = students.stream().filter(student -> studentIds.contains(student.id)).toList();

        //2. Знайти унікальні предмети, які вивчають студенти зі списку students

        List<String> uniqueSubjects = students.stream().flatMap(student -> student.subjects.stream()).distinct().toList();

        //3. Створити мапу, де ключ - це id студента, а значення - об'єкт студента зі списку students

        Map<Integer, Student> studentsById = students.stream().collect(Collectors.toMap(Student::id, Function.identity()));

        //4. Відсортувати студентів за предметами відповідно до наведеного списку

        List<String> subjects = List.of("Math", "English", "History", "Ukrainian", "Astronomy");

        // Варіант рішення через лямбди

        List<Student> list = students.stream().sorted((student1, student2) ->

                        Integer.compare(

                                student1.subjects.stream().mapToInt(o -> subjects.size() - subjects.indexOf(o)).sum(),

                                student2.subjects.stream().mapToInt(o -> subjects.size() - subjects.indexOf(o)).sum()))

                .toList();

        // Варіант рішення через мапу

        Map<String, Integer> subjectPriority = new HashMap<>();

        for (int i = 0; i < subjects.size(); i++) {

            subjectPriority.put(subjects.get(i), i);

        }

        List<Student> sortedStudents = students.stream()

                .sorted((student1, student2) -> {

                    int priority1 = student1.subjects.stream().mapToInt(subjectPriority::get).sum();

                    int priority2 = student2.subjects.stream().mapToInt(subjectPriority::get).sum();

                    return Integer.compare(priority1, priority2);

                })

                .toList();

        System.out.println(list);

        System.out.println(sortedStudents);

    }

    private record Student(Integer id, String name, List<String> subjects) {

    }

}

Візьмемо завдання на фільтрацію. Насамперед я звертаю увагу на те, як кандидат створює об’єкти. Цікаво, як називає змінні. Як робить списки. Це залежить від версії Java, і використання новіших методів буде плюсом. Дуже важливо, як людина розуміє задачу. В наших завданнях немає навмисних пасток, але інколи кандидат починає писати код раніше, ніж зрозуміє, що від нього очікують.

Завжди уточнюю, що це завдання на стрими і його не потрібно розв’язувати через цикли. Є люди, які не знають стримів, і це можуть бути навіть сеньйори. Вони використовували старішу версію Java, де стримів не було. Але ця версія була актуальною років 15 тому, і без знання Java Stream API я не можу вважати людину навіть джуном. З іншого боку, кандидату можна пробачити, якщо він не пам’ятає окремих функцій. Можливо, код він писав давно, а займався архітектурою.

Сучасні тестові завдання мало відрізняються від тих, які були раніше, але може додатися інфраструктура. Для сеньйора це інколи обов’язково, бо стає все більше проєктів, які передбачають роботу з інфраструктурою.

«Якщо дуже суворо оцінювати лайв-кодинг, то нікого не наймеш»

Володимир Балицький, Senior Java Developer в Intellias

Я майже не практикую домашніх завдань. За весь досвід проведення співбесід був лише один проєкт, де давали домашні. Це був Android-застосунок, і від потенційного учасника команди очікували знання Clean Architecture. Ми давали тиждень на виконання. На жаль, це домашнє завдання не дало хороших результатів. Були кандидати, які виконали все ідеально, покрили код тестами, а на співбесіді не впоралися з технічною частиною.

Senior не буде робити домашнє, тому що він цінує свій час

Коли ринок належав кандидатові, він міг піти в іншу компанію, яка запропонує ті ж умови, але без домашніх завдань. Хіба що він хотів потрапити на конкретний проєкт, у такому разі кандидат міг погодитися. Тому ми більше практикуємо лайв-кодинг, але не завжди. Часто лайв-кодинг є запитом від замовника.

У нас є матриця компетенцій, яку заповнюємо, співбесідуючи людину в компанію. Вона охоплює алгоритми, тому ми даємо нескладні алгоритмічні задачки. Немає сенсу мучити складною задачею людину, яка не може впоратись із простою.

Задачу, яку ви побачите далі, я придумав сам, тому її неможливо ніде знайти. Вона допомагає перевірити як алгоритмічну базу, так і знання багатопотоковості в Java. Я не ставлю обмежень на фреймворки і бібліотеки. Задача має багато варіантів розв’язку. Її можна розв’язати навіть в 3-4 рядки за допомогою CompletionService. Абсолютно нормально, якщо кандидат про це не знає. Але є люди, які загалом відмовляються міркувати над цим, а є ті, хто ставить запитання та пропонує варіанти розв’язку.

Задача:

Є великий набір даних (файл, окремий рядок, окремі параметри запиту), які ми відправляємо до вебсервісу. Ми знаємо, що вебсервіс буде повертати відповідь зі статусом 200, але з певного моменту він почне повертати 404. Нам потрібно зберігати результати тих запитів, доки відповідь від сервісу 200, і припинити процес відправки, щойно починають надходити відповіді 404. Запити потрібно робити паралельно, щоб пришвидшити процес, оскільки обсяг досить великий, зупинити весь процес потрібно коректно. Цікавлять альтернативні шляхи вирішення такого завдання.

Лайв-кодинг для кандидата може бути стресовим. Якщо дуже суворо його оцінювати, то нікого не наймеш. Хіба що кандидат займався спортивним програмуванням.

Я почав працювати в IT після кризи 2009 року. Був підйом, і в Україні сформувався ринок кандидата, коли лайв-кодинг був рідкістю. В останні два-три роки його стало більше. Зокрема, лайв-кодинг частіше почали вимагати іноземні замовники.

В Європі задачі різного ступеню складності давали завжди. Одного разу в нідерландському стартапі мені запропонували задачу, розв’язати яку було нереально, і вона жодним чином не перегукувалася з роботою. Не знаю, яку ціль вони переслідували, даючи таке тестове.

«Відмовляли й тому, що не було тестів»

Іван Маглатій, Engineering Manager в Avenga

У нашій команді джавістів ми не фанатіємо від тестових завдань, зокрема лайв-кодингу. Нам цікавіше поспілкуватися з кандидатами про їхні попередні проєкти та завдання.

Завдання на кодинг краще підходять людям, які тільки починають свій шлях в IT. Ми вважаємо, що для більш досвідчених кандидатів завдання з системного дизайну краще, ніж кодинг чи теоретичні запитання, розкриває, як людина розуміється на загальних аспектах побудови програмного забезпечення. Не так багато людей знає, як побудувати систему з нуля. Ми можемо дати кандидатові готову схему і запитуємо, чи бачить він проблеми в намальованій архітектурі.

Приклад завдання з системного дизайну:

Проблема: під час пікових навантажень онлайн-система продажу товарів працює повільно або недоступна, через клієнти незадоволені.

Деталі системи. Система має монолітну архітектуру та розгорнута у власному дата-центрі (on-premises), причому база даних і застосунок містяться на одному сервері.

Ваше завдання знайти способи швидко покращити продуктивність системи без повного переписування коду. Потрібно працювати з наявною архітектурою та мінімізувати зміни.

Що потрібно зробити:

— Виявити вузькі місця, що призводять до проблем з продуктивністю.

— Запропонувати конкретні рішення для підвищення стійкості до навантажень.

— Розглянути варіанти масштабування (горизонтального або вертикального).

— Забезпечити мінімальний вплив на роботу системи.

Багато наших клієнтів мають технічне інтерв’ю на своєму боці. Цього року вони нерідко стали пропонувати онлайн-кодинг, а можуть дати й домашнє завдання.

Приклад завдання від клієнта:

We have a coding challenge that involves processing data from a web server with multiple endpoints. The task is to develop a program that:

Reads Data from Two Sources:

— JSON Source: Emits JSON records.

— XML Source: Emits XML records.

Processes the Records:

Categorizes each record as «matched», «unmatched», or «invalid»:

— Matched: Records with matching IDs from both sources.

— Unmatched: Records without a matching ID in the other source.

— Invalid: Malformed records (these can be ignored).

Sends Results:

Sends the «matched» and «unmatched» records to the Result Endpoint.

Key Requirements:

— Concurrency: The program must read from both sources simultaneously and begin sending data before all records are received.

— Scalability: It should efficiently handle large datasets, as it will be tested with much larger volumes of data.

— Resilience: Occasionally, the endpoints may return a bad response. The program should handle this gracefully, retrying requests as necessary.

Серед причин відмови кандидату з боку клієнта є неуважність і стиль написання коду, якщо це спагеті-код. Буває, що кандидат думає лише про те, щоб воно працювало, а замовник хоче побачити хороший код. Відмовляли й тому, що не було тестів. Після виконання тестового клієнти спілкуються з кандидатами щодо технічних рішень, і багато хто не може пояснити, чому обрав саме цей підхід, а не якийсь інший.

«Дрібні неточності чи незначні помилки переважно „пробачають“»

Богдан Сергієнко, Chief Technology Officer у Master of Code Global

Ми практикуємо домашні завдання, щоб не створювати зайвого стресу кандидату та водночас перевірити якість виконання й уважність. Зазвичай тестові даємо кандидатам на позицію джуніора або на специфічні позиції. Наприклад, Solution Delivery Manager для AI має показати розуміння AI. Коли ж практикуємо онлайн-кодинг, то радше щоб швидко подивитись напрямок думок, а не перетворювати це на змагання з програмування.

Джуніору потрібно давати тестове, аби зрозуміти його набір хард-скілів. Досвідченим кандидатам рідко пропонуємо тестові, бо їм достатньо розповісти подробиці своєї роботи, це вже продемонструє їхнє мислення та підходи.

Якщо вирішуємо додавати тестове, то пропонуємо його всім кандидатам, які сподобались, щоб порівнювати їх однаково. Зазвичай це 2-3 людини, адже ми цінуємо час кандидатів і завжди даємо фідбек.

У нашій компанії є формальні критерії для оцінки тестових завдань. Основні з них охоплюють технічну точність, відповідність вимогам завдання, креативність та оптимізацію рішень, враховується і час виконання. Це допомагає об’єктивно та прозоро ухвалювати рішення. Ми також радо надаємо зворотний зв’язок після виконання тестового, щоб допомогти кандидатам зрозуміти наш підхід та вимоги.

Дрібні неточності чи незначні помилки зазвичай «пробачаємо», особливо якщо кандидат загалом демонструє відповідний рівень навичок, відповідальний підхід і потенціал до зростання.

Зазвичай наші кандидати сприймають фідбек конструктивно, оскільки даємо його з акцентом на можливості для вдосконалення. Проблеми з тестовим завданням або брак досвіду не є блокерами для подачі на майбутні вакансії для кандидата. Вони можуть пізніше відгукнутися на іншу позицію.

Буває, що людина «закопалася» в завдання глибше, ніж потрібно, але загалом продемонструвала добре мислення і підхід і може пояснити, чому вирішила все робити саме так.

Приклад завдання

Ми не маємо заготовок, але для розробника це може бути завдання «реалізувати сервіс бота для одного каналу, який би міг розповісти про погоду у твоєму місті та дати якісь рекомендації із використанням LLM». Задача може містити чіткі вимоги та бути схожою на реальну, але без реалізації тої частини логіки, яка займає найбільше часу.

Кандидат має написати це за кілька годин, а ми будемо дивитися передусім на структуру виконання та архітектуру, а також здатність передбачити додаткові сценарії. Деталі виконання задачі є важливішими, ніж формальні ознаки.

Похожие статьи:
Совмещать работу, саморазвитие, отдых, личные дела и семью — совсем не просто. Особенно если семья большая. Редакция DOU собрала...
31 березня на передовій загинув Антон Гарбуз, який працював Project Manager у компанії Syndicode. Йому було 33 роки, у героя залишилася...
Привет! Я Data Engineer в NIX, фанат обработки данных больших и маленьких, поклонник Python. В этой статье расскажу, как Data Engineer...
Capgemini Engineering (ex-Lohika), яка входить до 20 найбільших ІТ-компаній України, повідомила DOU про своє приєднання до правового...
Сразу хочу сказать, что не претендую на внимательный анализ всей выставки. Это скорее ощущения одного дня...
Яндекс.Метрика