Чистий код і патерни проєктування: Як пройти технічну співбесіду з першого разу
Підготовка, запитання і варіанти відповідей
Технічні співбесіди далеко не найлегша річ, особливо для новачків. Окрім суто технічних знань є також чимало теоретичних, в яких потрібно орієнтуватись. У цій статті говоримо саме про них. А щоб додати експертності, ми поставили ряд важливих запитань Олегу Фокіну — Principal Software Engineer у SoftServe і лектору курсу «Чистий код і патерни проєктування».

Про підготовку
Які 3–5 найважливіших патернів проєктування варто знати напам'ять перед співбесідою?
Strategy — втілює принцип Open/Closed із SOLID, дозволяючи змінювати поведінку runtime та уникнути розлогих умовних операторів.
Bridge — також відповідає Open/Closed та ідеально втілює Dependency Inversion Principle, дозволяючи розділяти абстракції та реалізації, що сприяє гнучкості й масштабованості, рятує від «вибуху класів» та чудово демонструє важливий принцип Composition over Inheritance.
Builder — відокремлює логіку конструювання об'єкта від самого класу об'єкта, чим втілює принцип Single Responsibility й, що важливо, повністю інкапсулює в собі всю логіку створення, чим ідеально підходить для створення складних незмінних (immutable) об'єктів та розділення складної логіки створення на окремі кроки.
Звісно, що цей патерн також відповідає принципу Open/Closed, дозволяючи реалізувати створення нових типів об'єктів без зміни наявного коду «Клієнта» і «Директора».
Як ви рекомендуєте балансувати вивчення теорії (книжки, курси) і практику (coding challenges) при підготовці?
Без практики не здобути якісних знань, а прогалини завжди можна закрити навіть запитуючи пояснення в ШІ. І це, до речі, гарний спосіб вчитись. Єдине слабке місце в цій схемі — якісні матеріали, на яких можна ефективно практикуватись. Їх можна взяти з прикладів у книжках, статтях, на курсах або також запитати в ШІ.
Вже коли ви побудуєте своє комплексне розуміння на практичних прикладах, можна починати втілювати все на якомусь практичному проєкті: pet, open source або навіть робочому.
Чому не навпаки? У прикладів та на реальних проєктах різний фокус і мета. Реальні рішення все ж сфокусовані на простому та ефективному розв’язанні конкретної проблеми, що, за певних умов, може навіть не відповідати всім принципам ідеально.
Чи достатньо знати/розуміти патерни теоретично, чи треба вміти їх швидко імплементувати з нуля?
Ні, я навіть чув, що за теорію та практичні навички відповідають різні зони мозку. Можна знати всі необхідні слова іноземної мови, але мати складнощі зі спілкуванням нею, не практикуючи це окремо.
Про live coding
Які принципи чистого коду найкритичніші під час live coding сесії, коли час обмежений?
Я б рекомендував розкладати все за Single Responsibility / Separation of Concerns, щоб сформувати необхідні мінімалістичні компоненти з обовʼязковою інкапсуляцією їхніх даних та внутрішніх деталей. За Open/Closed принципом реалізувати базові механізми розширення або внесення передбачених змін без істотного переписування коду. Подумати, як краще реалізувати це через правильний баланс компонування та наслідування за принципом Composition over Inheritance.
Цього може бути достатньо, але можна й застосувати патерни, якщо доречно. Патерни згруповані за категоріями:
- Породжувальні
- Структурні
- Поведінкові
Ви так чи інакше будете розв’язувати питання створення, організації та поведінки ваших класів та їхніх обʼєктів, тож можете застосувати патерни для цих викликів. Вони чудово працюють разом.
Чи варто згадувати патерни під час coding interview, якщо завдання можна розв’язати простіше? Коли це додає балів, а коли виглядає як overengineering?
Згадати, як покращити ваше рішення, завжди варто! Цим ви демонструєте багато речей: від гарного технічного розуміння теми та вміння ухвалювати зважені рішення, що застосувати, що ні, та коли зупинитись, — до демонстрації софт-скілів, неупередженості й відкритості до покращення ваших рішень.
Це навіть бажана та очікувана інтервʼюером поведінка. До того ж ви страхуєте себе від того, що у своєму рішенні, можливо, не врахували якісь важливі аспекти. Варто мати на увазі, що в реальних проєктах ваш код — це частина проєкту, тож може використовуватись доволі різноманітно та має еволюціонувати відповідно до викликів проєкту. Якщо якісь патерни цьому допоможуть — це вагомо додасть «балів» в очах інтерв'юера, який, дуже ймовірно, є експертом-практиком у проєкті зі своїми, дуже подібними «болями».
А щодо оverengineering з патернами — це їхнє хаотичне використання та нагромадження. Якщо у вас є певна логіка застосування, завʼязана на реальних проблемах, гадаю, з overengineeringʼом ви не стикнетесь.
Які найпоширеніші помилки ви бачите у кандидатів, які пишуть код на співбесіді?
Некоректне застосування парадигми програмування — невдалі мікси процедурної парадигми з ООП, коли в класах, замість контрольованого внутрішнього стану з його продуманими змінами, з'являються важкі методи обчислень із купою параметрів, які ще й розпорошують щось логічно внутрішнє назовні. Також порушення Single Responsibility та Open/Closed принципів.
Про system design
Як демонструвати знання патернів на system design інтерв'ю, не виглядаючи занадто академічно?
Насправді відштовхуватись від проблеми. Патерни ж самі структуровані за шаблоном — у них є імʼя, проблема яку вони розв’язують, «мотивація». Спершу показуйте приклади успішного застосування, а вже потім структуру та особливості реалізації.
Також, як я вже згадував, вони згруповані як Породжувальні, Структурні та Поведінкові, і вам знадобиться узгодити використання принаймні по одній з цих категорій разом.
Які архітектурні патерни найчастіше спливають на співбесідах для middle/senior-позицій?
Builder, Bridge, Flyweight, State, Visitor. Усі вони доволі складні та обʼєднують у собі декілька концепцій.
Про досвід
Чи можете поділитися реальним прикладом з вашої практики, коли знання конкретного патерну допомогло вам на співбесіді?
В моєму випадку є очікування, що я знаю все, але те, що я вмію просто і логічно пояснити зв'язок між принципами, проблемами з їхнього порушення та патернами, мабуть, додає плюсів.
Які запитання про рефакторинг і технічний борг найчастіше ставлять на співбесідах і як на них краще відповідати?
Доцільність рефакторингу та управління технічним боргом — це доволі повʼязані процеси, які ще й демонструють вашу зрілість як спеціаліста. Будь-який старий код має певну цінність — він якось працює та є джерелом знань про реальну бізнес-логіку та edge-cases.
Тому бажання все переробити несе не тільки перспективу, що стане краще, але й реальні ризики та додаткові витрати. Тож рішення варто приймати на основі фактів:
- Метрик зі швидкості впровадження фіч
- Регресії та кількості багів
- Крихкості коду та кількості code smells у ньому
Можна постійно застосовувати прості правила щодо рефакторингу, як-от:
- Правило бойскаута: «залишай код чистішим, ніж він був до тебе» (дрібний рефакторинг під час роботи над завданням).
- Виділяти 20% спринту на рефакторинг і технічний борг.
- Планувати окремі спринти виключно на погашення технічного боргу.
Але без наявності тестів неможливо порушувати тему рефакторингу. Так само як і без пріоритизованого Backlog та чітких цілей неможливо спланувати погашення технічного боргу взагалі.
Про типові сценарії
Якщо кандидат не знає відповіді на технічне запитання про патерн — як краще поводитися? Зізнатися чи спробувати вивести логіку?
Мабуть, не проблема чогось не знати — не бійтеся про це сказати, так ви демонструєте чудові софт-скіли, якщо ж до того ви ще й розумієте, що саме ви не знаєте і можете окреслити границю між знанням та прогалиною. Наприклад:
- «Я знаю, яку проблему цей патерн розв’язує, але забув цю особливість в його схемі»
Або ж:
- «Я використовував всі породжувальні патерни, крім Builder»
Втім, все ж важливо розуміти логіку за ними: «Як і всі породжувальні патерни, Builder має відділяти створення обʼєктів від їхнього використання з тим, щоб можна було динамічно змінювати типи створюваних обʼєктів без зміни коду, що ці обʼєкти використовує».
Так ви майже повністю нівелюєте мінуси від незнання, банально переформатувавши їх у прогалину, яку знаєте як заповнити.
Чи є різниця в тому, як треба демонструвати знання чистого коду для різних рівнів (junior/middle/senior)?
Звісно, рівень вимог усюди різний, але здебільшого від junior-позицій очікується знання конкретних технік та загальне уявлення головних принципів. Помилки сприймаються не дуже критично.
Від middle очікується розуміння всіх принципів і технік, нехай і з помилками.
Від senior очікують, що кандидат розбирається в усьому, бажано без базових помилок. Але, звісно, як я вже вказував, помилка не означає «кінець» -– це навіть можливість для вас перетворити її на демонстрацію ваших сильних сторін!