08.08.2022

Сергій Немчинський: Як вирішувати завдання як програміст?

Сергій Немчинський
10 хвилин перегляду
Сергій Немчинський: Як вирішувати завдання як програміст?

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

Є якесь завдання, яке не зовсім зрозуміле. З чого починати? Я намагатимусь дати повноцінний алгоритм, як і що робити.

Етап 1. Аналіз задачі

Перше, що ви повинні зробити після отримання завдання, — почати його аналізувати. Чи всі деталі завдання вам зрозумілі? Дуже часто на наших курсах бачу ситуацію, що щойно студент отримує завдання, він одразу хапається за консоль і нумо щось педалити. Можу впевнено сказати, що ви робите не те, що потрібно. Прочитайте текст завдання щонайменше 5 разів. Запевняю, що на 3-4 рази прочитання ви помітите нюанси, на які відразу не звернули увагу. Можливо, ви з’ясуйте, що якийсь шматок завдання взагалі не описаний. Найчастіше завдання пише не програміст, а людина з боку бізнесу. Звичайно, він мислить не як програміст, тому він не знає, які аспекти вам потрібно описувати, щоб ви могли виконати завдання.

Етап 2. Уточнення задачі

Іноді, якщо завдання описане не в повному обсязі, програмісти самі його допридумують. І це груба помилка. Якщо ви не знаєте детально предметну область, з великою ймовірністю ви додумаєте завдання зовсім не так, як замислювалося спочатку. Багато разів бачив ситуацію, коли програміст додумував якусь деталь завдання, потім здавав завдання замовнику, а замовник давав фідбек, що це зовсім не те, що він просив. Тому, якщо щось не описано, не потрібно вигадувати і робити щось на власний розсуд, потрібно уточнювати вимоги завдання у того, хто його поставив.

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

Наприклад, ви пишете якесь веб-додаток. Що робити, якщо у вас відвалилася база? Що має відобразитись? Або що робити, якщо якогось користувача не знайдено? Яким чином мають виглядати повідомлення про помилки? У яких випадках їх видавати? Писати їх тільки в лог, а назовні видавати, що сталася помилка? Чи назовні користувачеві видавати повний опис? Це все дуже важливі аспекти і здебільшого у завданні про них не сказано.

Етап 3. Аналіз поточного стану

Більшість програмістів новачків цей момент просто ігнорують. При цьому ситуація, коли програміст починає працювати з чистого аркуша, просто унікальна. Навіть якщо проект робиться з 0, є низка моментів: досвід роботи команди з певними фреймворками; певні оточення, на яких зручніше піднімати сервери, і ще безліч інших аспектів, які можуть позначитися на тому, що ви писатимете. Але найчастіше у вас уже є якийсь код. Наприклад, ваше завдання — просто фіксинг багів. І для того, щоб пофіксити цей баг, потрібно добре зануритися в код і зрозуміти, як там усе влаштовано. Не просто знайти один рядок, а подивитися, на що ще впливатиме ваша зміна. Дуже правильно, коли ви спочатку знайшли, де виправити, потім приблизно уявили це виправлення, а потім погуляли в навколишньому коді і подивилися, що ще залежить від шматка коду, який ви змінюєте. Досить часто з’ясовується, що, змінивши шматок коду, ви змусите відвалитися шматок іншого коду. І тоді треба думати, як зробити зміну, щоб усе працювало коректно. Тому аналіз поточного стану — це дуже обов’язковий пункт.

Етап 4. Декомпозиція задачі

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

Етап 5. Побудова архітектури вирішення саб-завдання

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

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

Я завжди раджу, щоб програмісти вміли користуватися UML, але це не обов’язково. Тому що в даному випадку вам потрібно не стільки намалювати конкретні класи, скільки потоки даних. Як ви їх малюватимете — залежатиме від вас. Можна користуватися стандартними діаграмами, але можна використовувати просто якусь кастомну хрень. Головне – робіть це.

Етап 6. Реалізація

Коли ви підготували архітектуру, розумієте, що робити, врахували всі аспекти, ваше завдання зрозуміле, розділене на частини і так далі, тоді ви й сідаєте за написання коду. Тільки на шостому етапі, а не першому, як роблять деякі новачки. Головна відмінність досвідченого розробника від новачка саме і полягає в тому, через який час після отримання завдання програміст сідає писати код. Якщо сідає відразу — це зовсім зелений Junior. Досвідчений розробник спочатку довго вчитується в текст завдання, потім починає повзати за кодом, потім щось малює, потім можливо ще раз щось уточнює у замовника. І тільки потім, коли він уже все зрозумів, сідає і швиденько все пише.

Що робити, якщо вам не вдається вирішити завдання — я розповім пізніше.

Етап 7. Тестування

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

Етап 8. Рефакторинг

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

Етап 9. Повторення попередніх пунктів для всіх підзадач

Ви декомпозували велике завдання, зробили одне з підзавдань (написали, протестували, срефакторили), тепер повторюємо це все для інших підзавдань.

Етап 10. Загальне тестування всього функціоналу задачі

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

Етап 11. Завершення задачі

На цьому етапі ви здаєте готове завдання.

Якщо завдання не вирішується

Буває таке, що ви довго б’єтесь із завданням, але нічого не виходить. І тут я поділюся своїм особистим алгоритмом, хоча його дотримуються +/- все досвідчені розробники. Алгоритм такий.

1. Зрозуміти, чи можна вирішити проблему гугленням

Як це визначити? Якщо ваша проблема пов’язана з якоюсь внутрішньокорпоративною логікою, з внутрішньокорпоративними правилами, з налаштуваннями вашого локального гіта, питаннями предметної логіки та іншими внутрішніми питаннями, то така інформація не гуглиться.

2. Якщо інформація не гуглиться — запитувати

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

3. Якщо рішення можна знайти в інтернеті – гуглим

Звертаю вашу увагу. Коли ви шукайте інформацію, потрібно спочатку продумати, що саме ви шукатимете. Тож уточнити запит. У текст запиту слід вкласти, можливо, назви фреймворків. Але якщо ви доповните текст запиту назвами всіх фреймворків, що використовуються на проекті, швидше за все ви нічого не знайдете. Тут слід думати, які фреймворки могли вплинути на появу помилки. Тому додаючи та видаляючи назви фреймворків у текст запиту, ви отримаєте різні результати пошуку. Найчастіше виходить, що проблема відбувається на стику одного чи двох фреймворків. Тоді текст запиту буде виглядати так: помилка, назва пари фреймворків. І тут ви можете отримати потрібну інфу, так як задаючи в запит просто текст про помилку, ви зазвичай нічого не отримуєте. Виняток — якщо помилка вилітає зсередини фреймворку або сервера, тоді текст помилки все сам за себе каже. Такі помилки – дійсно щастя для програміста, бо їх легко нагуглити та вирішити.

4. Покрутіть проблему в руках

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

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

5. Сила слабких зв’язків

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

Завжди ваш Сергій Немчинський.