Golang — мова програмування, відома своєю читабельністю, продуктивністю та мінімалізмом. В комʼюніті розробки про неї склалося чимало упереджень: це нішева мова, яку може опанувати будь-хто чи багатофункціональний інструмент для складних проєктів? Чи справді додавання дженериків було помилкою — вони все одно працюють обмежено, але водночас суперечать філософії мови? Чи завжди горутина покращує код? Та яке правило синтаксису розробники-початківці часто інтерпретують неправильно? Євген Кострика, Back-end Engineer в Boosters, спростував поширені міфи та упередження розробників щодо мови програмування Go.
МІФ № 1
Gо — нішева мова, якою нічого серйозного не пишуть, а для розробників мало роботи
Порівняно з мейнстримними мовами (JavaScript, Java, PHP, C# тощо) Go і справді виглядає нішевою. Здається, що вона малодосліджена, що в неї слабка бібліотека та обмежений діапазон використання. Більшість новачків переконані, що на ньому майже немає роботи та не пишуть нічого серйозного. Водночас Go вважають легким та примітивним.
Треба розуміти, що ця мова була спроєктована таким чином, щоби на початкових етапах було просто — написати, запустити, та все працюватиме. Із зануренням ця мова стає все складнішою та багатогранною. Це дало змогу вирішувати масштабні завдання та створити популярні сервіси, якими користуються більшість розробників та DevOps інженерів — Docker, Kubernetes, Prometheus, Terraform, InfluxDB, Packer, Caddy, Grafana тощо.
МІФ № 2
Дженерики суперечать філософії простоти в Go
Довгий час у комʼюніті Go тривали жваві дискусії на тему відсутності дженериків (фіча, яка дозволяє створювати шаблони, які можуть працювати з різними типами даних без дублювання коду). Розробники, що перейшли з інших мов програмування, просили додати цю функцію. Мовляв, без дженериків не обійтися у великих проєктах, адже вони зменшують дублікацію коду. Інші були переконані, що з появою цього функціоналу Go втратить свою прямолінійність і простоту. Можливість писати код різними способами там зведена до мінімуму, що робить його легким у читанні. За це Go й люблять, а дженерики навпаки ускладнюють код.
Між тим, розробники мови провели опитування, згідно з яким 88% респондентів вважали відсутність цієї фічі критичною. 18% стверджували, що саме це заважає використовувати Go.
Go — консервативна мова, для якої значні зміни, а саме додавання дженериків, є суттєвою проблемою. Розробники довго мізкували, як органічно імплементувати їх, і після кількох років обговорень нарешті знайшли компромісний варіант та в кінці 2021 запустили бета-версію.
Нині дженерики в Go — досить специфічні та обмежені. Їх не можна просто скопіювати та працювати так само як з іншими мовами. Наприклад, коли описуєш дженерик-функцію, ти не можеш задати констренти, які функція буде опрацьовувати. Розробники придумали досить суперечливий спосіб задоволення потреби: щоби описувати констренти, використовується ключове слово інтерфейс, яке вже було в мові. Виходить, тепер під інтефейсом може бути опис того, як поводить себе функція, або опис констрентів, які передає функція. Це досить дивно й може заплутати новачків.
Також у цій фічі є проблема з написанням тестів — розробники не запропонували способу тестувати дженерик-функції. Загалом цей функціонал залишається експериментальним. Команда Go спостерігатиме за реакцією комʼюніті й плануватиме розвиток цієї фічі.
МІФ № 3
Використання Goroutines завжди покращує код
Так виглядає очікування, коли вперше починаєш застосовувати горутини в коді. Реальність полягає в тому, що краще не використовувати їх без потреби, адже вони ускладнюють код, і команді в них легко заплутатись. Якщо у вас маленький проєкт на одному сервері з одним-двома інстантами, ймовірно в горутинах немає потреби.
Їх доцільно використовувати, наприклад, якщо вам треба обробити 10 000 файлів. Опрацювання кожного файлу — це досить дорога операція. Горутина дозволяє зробити це паралельно. Тоді обробка всіх 10 000 файлів займе ту кількість часу, яка потрібна, щоб обробити найбільший файл. Якби ми робили це послідовно, то така обробка зайняла б суму всіх проміжків для обробки кожного файлу.
Крім вміння розпізнавати кейси, де використання горутин доцільне, важливо також розуміти потенційні ризики — проблеми з безпекою, перформансом тощо. Наприклад, може трапитися, що горутина не закривається, зависає і відбувається витік памʼяті. У такому випадку можна побачити лінійний приріст використання оперативної памʼяті, — це можуть бути сотні мегабайтів на день, що неприпустимо! Якщо одразу не виявити цю проблему, сервер просто впаде.
Помилку в коді з горутинами складно дебажити. Для цього застосовуються складні інструменти. Наприклад, на сервер вішають профайлер, який щосекунди моніторить дані, що їх розробник має проаналізувати. Отже, горутини треба використовувати свідомо.
МІФ № 4
Garbage collector «зʼїдає» перформанс
Garbage collector автоматизує очищення пам’яті. Коли ти компілюєш програму, туди включається Garbage collector — тобто шматок великої програми зі стандартної бібліотеки Go переїжджає в бінарник. Тоді програма, яку ти компілюєш, займає багато місця, адже крім твоєї програми, Go додає чимало інструментів для автоматизації. Тому це справді впливає на перформанс, але водночас є певним компромісом.
На C або на C++ очищувати пам’ять треба вручну, і робити це регулярно: створив змінну, записав туди значення, попрацював із нею, вивільнив памʼять. Це великий біль C і C++ програмістів, адже вони часто забувають це зробити. У такому разі буде memory leakage, дебаг якого займає багато часу й ресурсів.
Garbage collector робить цю роботу за тебе. Це справді дещо уповільнює перформанс, але мінімально. Водночас розробники мови регулярно покращують швидкості роботи Garbage collector, це не є проблемою. Якщо у вас — високонавантажений проєкт з ухилом у перформанс і менеджмент памʼяті, тоді Go не підходить у принципі. На більшості проєктів це уповільнення не відчувається.
МІФ № 5
Усі назви змінних слід писати однією літерою
Якщо ви зайдете до стандартної бібліотеки Go і подивитесь код, ви не знайдете там такого правила синтаксису. У такий спосіб часто трактують загальну рекомендацію використовувати короткі назви для локальних змінних з обмеженою областю та ресіверів методів. Наприклад, c замість lineCount або i замість sliceIndex.
Водночас у документації чітко вказано, що однолітерні назви змінних циклу можуть бути корисним інструментом для мінімізації повторень, але також можуть зробити код незрозумілим для прочитання. Зрозуміле правило сформулював Ендрю Геранд, Software Engineer з команди розробки Golang в Google: чим далі від місця оголошення використовується назва, тим більш описовою має бути назва.
Ми пишемо назву змінних для людей. Комп’ютеру все одно, як воно називається, тому є сенс називати їх так, щоби коли твій колега відкриє код, він усе зрозумів. Якщо всюди використовувати одну літеру, на вас будуть косо дивитися колеги під час зустрічей на каву чи за обідом.
МІФ № 6
Go 2 is coming
Ще з 2017 року всі очікують, що от-от вийде нова версія мови. Тоді на Gophercon 2017 Расс Кокс, Go Tech Lead в Google оголосив про планування Go 2 та запросив спільноту до обговорення, якою має бути нова версія. Того ж року команда почала збирати побажання комʼюніті до Go 2.
Спершу всі були впевнені, що Go 2 вийде разом з Go-модулями. Вони зʼявилися, але у версії 1.11. Далі її чекали з дженериками (адже це суттєве оновлення), але то була версія 1.18. Фактично розробники реалізовували фічі з переліку, але продовжували випускати оновлення Go 1.
У такий спосіб вони зберігають сумісність між версіями, що є «релігією» цієї мови. Ще 2012 року команда опублікувала документ-маніфест із чітким наміром: щоби всі програми, написані на Go 1, продовжували правильно працювати в кожній специфікації. У своєму блозі вони жартують, що всі оновлення Go — нудні, адже не треба шукати несумісності, через яку ваша програма зламається: «Нудно — це добре. Нудно — це стабільно. Нудно — це можливість зосередитися на своїй роботі, а не на тому, чим відрізняється нова версія. У цьому оновленні ми виконали важливу роботу, щоби Go залишався нудним».
Водночас протягом останніх пʼяти років вони продовжували підігрівати інтерес до Go 2. Комʼюніті очікує, коли нарешті пофіксять if err!= nil, null ptr dereference та інші негаразди. 14 серпня вийшов реліз Go 1.21, в описі якого розробники загадково прокоментували питання другої версії: «Коли очікувати специфікації Go 2, яка порушує старі програми Go 1? Відповідь: ніколи. Go 2 у сенсі розриву з попередньою версією і відмови від компіляції старих програм ніколи не відбудеться. Go 2 у тому сенсі, що це основна редакція Go 1, яку ми розпочали у 2017 році, уже відбулася».
Цьому є логічне пояснення: сьогодні Go — це популярна мова, на якій написано чимало інструментів. Розробники зайняті тим, що покращують першу версію, та гострої потреби в кардинальних змінах немає. Вона виконує свої функції на відмінно.