Мій Go-девелоперський щоденник: про навчання та виклики
Мене звати Петро Щербатюк, я Software Engineer у MacPaw. У цій статті я поділюся власним досвідом переходу на Go як Back-end розробник.
Дисклеймер: ця стаття не є мануалом або точним порівнянням PHP i Go. Скоріше, це ті враження, з якими ви стикнетесь, якщо вирішите перейти на іншу мову програмування.
Чому я вирішив перейти на Go
На 2023 рік ми в команді поставили собі ціль — вивчення додаткових мов програмування. На вибір було запропоновано Python, JS і, власне, Go, на якій ми й зупинилися.
Серед переваг Golang розглядалися такі:
Швидкодія.
Ми не збиралися писати справжній Highload, але якщо вже обирати нову мову для вивчення, чому не обрати щось більш ефективне?
Статична типізація.
Дозволяє виявляти помилки на етапі компіляції, а не в runtime.
Суворіша філософія написання коду ніж у PHP.
Як приклад, наведу досить поширену в PHP практику використання асоціативних масивів. Так, це можна замінити командними домовленостями про те, як робити можна, а як — ні, але деякі речі Go просто не дозволить зробити, і я вважаю, що це на краще.
Робота з великою кількістю паралельних задач.
Це було важливо для нас через специфіку проєкту. Так, для PHP є сторонні інструменти для роботи з воркерами, але хотілося спробувати мову, в якій парадигма паралелізму була закладена із самого початку.
Менторство від ініціативного колеги, який вже мав досвід роботи з Golang.
Як я вивчав Go
Я навчався за гайдом на go.dev і пройшов курс на Coursera, який радше розчарував. Також використовував ChatGPT (куди ж без нього) та навчався через зворотний зв’язок від колеги на початкових стадіях розробки.
Щодо застосування Golang, у нас була ціль зробити специфічний сервіс оновлень macOS-застосунків — розробка з нуля сервісу, який буде збирати дані від мільйонів користувачів, обробляти, доповнювати їх і повертати користувачам.
Особливості, з якими я стикнувся за час розробки на Golang
Відсутність null-values.
Як приклад, наведу парсинг JSON-запиту в структуру за допомогою стандартної бібліотеки. Якщо в запиті відсутнє конкретне поле, відповідна змінна в структурі буде заповнена дефолтним значенням.
І якщо для рядка (string) ми ще потенційно можемо вважати дефолтне значення (порожній рядок) не валідним значенням, то для інтеджерів і булевих змінних дефолтні значення — нуль і false відповідно, які цілком можуть бути валідними значеннями з реквестів.
Як варіант, можна використовувати вказівники на змінні. У такому разі, за відсутності конкретного поля ми отримаємо nil pointer, який легко можна перевірити. Але за таким сценарієм усе ще немає розуміння, чи ми отримали null значення у реквесті, чи поле в ньому було відсутнє.
Відсутність фреймворків на кшталт Laravel або Symfony.
Фреймворки на Go звісно є, але вони значно простіші порівняно з вищезгаданими PHP-аналогами. Це можна вважати і перевагою, і недоліком, але на це точно треба зважати.
Також розробка на Go схожа на порівняння MacOS і GNU- \ Linux-дистрибутивів: у першому випадку за вас усе вирішили розробники, просто сідайте і пишіть, а в другому — у вас є пʼять різних систем ініціалізації, десять різних DE, і вам треба самим збирати проєкт із цього конструктора.
Особисто мені дуже не вистачає аналогу Symfony DI і деякого вбудованого у фреймворки «цукру».
Наслідок з попереднього пункту — складніше структурування проєкту.
По-перше, через відсутність чіткої структури файлів у фреймворках її треба продумувати власноруч. Через те, що проєкт був для нас першим на Go, доводилося обирати архітектуру, не до кінця розуміючи, на які підводні камені наштовхнемося потім.
Звісно, із часом (і деякою кількістю рефакторингу) оптимальний варіант було знайдено.
По-друге, є особливості роботи з пакетами, бо кожна тека з кодом — це окремий пакет. І підтеки в пакетах — це теж окремий пакет. Спочатку ми час від часу зіштовхувалися з проблемою циклічного імпорту пакетів. Через це доводилося переносити логіку проєктом.
Обробка помилок.
Про це не написав, мабуть, лише лінивий. Якщо у PHP ви можете обернути код у try / catch, знаючи, що винятки буде оброблено, то в Go помилки мають повертатися у вигляді змінних і мають бути оброблені вручну. З одного боку, це дисциплінує і змушує писати більш усвідомлено, а з іншого — if err != nil
, if err != nil
, if err != nil
...
Вказівники (pointers).
Завжди стежте за вказівниками.
Компіляція.
Після змін у коді ви не можете просто зробити ще один запит, щоб перевірити, чи зміни були правильними — треба чекати, поки скомпілюється проєкт. Але, з іншої сторони, цей нюанс змушує тебе бути більш уважним і двічі перевіряти, що ти написав перед запуском.
Типізація.
Здавалося б, що тут може піти не так? Але нещодавно наткнувся на те, що int
у Go й у PostgreSQL різних розмірів: у Go він залежить від архітектури процесора (4 або 8 байтів). Звісно, RTFM, але тут каверзи я не очікував.
То чи варто переходити на Go
Перечитавши список, який я накидав вище, може скластися враження, що негативу більше, ніж позитиву. Проте це точно не так. Так, шишки регулярно набиваються, але це — цікавий і корисний досвід, який дозволяє поглянути на веб з іншої точки зору.
На рівні емоцій вивчення нової мови взагалі перенесло мене в часи написання лабораторних в університеті.
Якщо ж відкинути лірику, то скажу, що у виборі / не виборі Go для розробки варто відштовхуючись від задачі. Чи писав би я на Go просте API з невеликою кількістю бізнес-логіки? Ні, бо PHP був би тут виграшним через швидкість і простоту розробки.
Чи писав би я якийсь мікросервіс з більш-менш складною бізнес-логікою, особливо, якщо вона вимагає великої кількості обчислень? Hell, yes. А якщо логіку ще можна розпаралелити, то тут і думати не варто.
Наш внутрішній експеримент я вважаю успішним, адже сервіс зараз у стадії альфа-релізу, і сподіваюся, що він проживе довге і щасливе життя, радуючи розробників і наших клієнтів. Окремо хотів би подякувати MacPaw за можливість свічнутися, це було неочікувано навіть для самого себе.
57 коментарів
Додати коментар Підписатись на коментаріВідписатись від коментарів