Впечатления о Golang от разработчика на PHP
Привет! Меня зовут Андрей Литвиненко и я имею почти 15-летний опыт разработки на PHP. Уже около трех месяцев я активно пишу на Go и хочу поделиться наблюдениями, которые отметил для себя. Возможно, этот текст будет полезен тем, кто сейчас знаком с PHP и подумывает познакомиться с Go.
В разное время я работал на разных позициях, начиная от джуниор-разработчика и заканчивая Руководителем Отдела продуктовой разработки. А сейчас я принял вызов и присоединился к команде платформенного бэкенда NUT.Tech для разработки низкоуровневого проекта на Go. Про этот язык программирования я конечно же слышал, но никогда до этого мне не приходилось сколько-нибудь серьезно с ним работать.
Возможно некоторые мои наблюдения могут вызвать вопросы или даже негодование у аудитории, но я описываю свои личные первые впечатления от знакомства с этим языком программирования. И я буду очень рад, если под статьей развернется дискуссия, из которой я почерпну что-то о новом для себя языке программирования.
Строгая типизация. PHP тоже сейчас идет в сторону строгой типизации, особенно с 7 версии языка, но это все еще опционально и во многих существующих проектах до сих пор этого нет. В Go только строгая типизация. И мне это нравится. Благодаря строгой типизации, во-первых, отлавливаются все ошибки несоответствия кода на этапе компиляции, а во-вторых, это заставляет заранее думать о выборе правильного типа.
У языка достаточно простой синтаксис. Читать код легко, конструкций, типов, выражений не так много. Все можно изучить в течение одного-двух дней при помощи обширной документации и примеров из интернета.
Golang — это функциональный язык, и в нем нет полноценных средств ООП. Все ограничено интерфейсами и типами-структурами. Наследования реализации нет. И мне его не хватает. Очень непривычно, когда нет возможности расширить реализацию. Из-за этого складывается ощущение, что в программах, написанных на Go, много «копипаста».
Пакеты. Очень отдаленно похожи на неймспейсы в PHP, но есть свои особенности. Все файлы одного пакета находятся в единой области видимости. Это, с одной стороны, удобно, а, с другой стороны, может вызывать недопонимание — нужно ли разделять функционал на файлы или писать все в одном. Из-за этого часто можно увидеть файл на 1000 строк с нескольким десятком функций. А в некоторых библиотеках нередко наблюдается ситуация, когда весь код написан в файлах, которые расположены в корневой папке проекта.
В PHP я привык файлами разделять классы: один файл — один класс, и поначалу это сбивает с толку.
В Go хорошая поддержка документации публичных пакетов. Если писать документацию в правильном формате, то после публикации своего пакета, например, на GitHub.com, документация опубликуется на https://pkg.go.dev.
Нравится, что есть инструменты тестирования из коробки. Легко сразу начать писать тесты к своей программе.
Единый стандарт написания кода. Есть готовые инструменты для проверки синтаксиса (gofmt).
Многопоточность. Для разработчика на PHP непривычная штуковина. Интересная, мощная, но очень опасная. Нужно постоянно писать код с оглядкой на то, где будет выполняться та или иная функция; использовать локи, чтобы не было состояний гонки в памяти. И тут, кстати, очень хорошо могут помочь тесты, которые большинство таких проблем могут отловить и подсветить.
Горутины и каналы. Горутины — это функции, которые работают параллельно с другими функциями, а каналы — это способы общения между горутинами. Так как в PHP нет многопоточности, то и горутин с каналами тоже нет совсем.
Мне очень не нравится, что в Go нет возможности что-либо замокать для создания эффективных тестов. Очень непривычно и неудобно. Может быть, я еще не познал дзен, но иногда, чтобы протестировать работу какой-то функции, необходимо модифицировать ее таким образом, чтобы она была доступна для теста (например, когда часть логики функции зависит от результата выполнения функции, которая вызывается в тестируемой).
Иногда мне не хватает каких-то простых и таких привычных для PHP-разработчиков функций в стандартных библиотеках (или я их плохо искал). Например, определение минимального/максимального числа. Сортировка массива.
С массивами вообще, мне кажется, в PHP все намного проще. В PHP, в отличие от Golang, массив может быть и собственно массивом, и списком, и хеш-таблицей, и словарем, и коллекцией, и очередью, и стеком. И это иногда очень удобно.
И так как есть такая унификация структуры данных, есть и единые функции для получения элементов массивов, их сортировки и других манипуляций. В Go же карты несортированные: язык не гарантирует порядок элементов в карте. И об этом нужно помнить.
Примеры того, чего мне не хватает из «стандартного» функционала работы с массивами (тут я подразумеваю скорее карты): проверка вхождения элемента в карту, сортировка карты по значениям и т.п.
Также очень сильно не хватает исключений (Exception) и возможности их перехватывать и обрабатывать. Приходится городить конструкции с возвратом ошибок из функций.
Нравится возможность из функции возвращать несколько значений. Но мне почему-то кажется, что это сделано именно для того, чтобы была возможность возвращать и ошибку, и результат выполнения. Для человека, который привык к ООП, это странно. Для меня привычно правило «один метод — одно действие — один результат».
Оператор defer. Очень интересный оператор, который позволяет задать действия, которые необходимо выполнить в конце работы функции. Причем неважно в каком месте происходит возврат.
В конце этого текста могу сказать, что нужно вспомнить фразеологизм «Ехать в Тулу со своим самоваром»: PHP и Golang — это совсем разные языки программирования. У них разная философия и области применения. При изучении нового языка не нужно стараться подгонять его под свои знания и пытаться из Go сделать PHP. Если эту ситуацию рассматривать через призму прохождения пяти стадий принятия неизбежного, то я нахожусь где-то между Депрессией и Принятием ☺