5 причин грядущего господства Go в мире программирования

Язык программирования Go, созданный более 10 лет, с завидной скоростью набирает популярность. Вот 5 причин, которые, возможно, убедят вас написать свой следующий проект на Go.

1. Оптимальный объем среды выполнения

“Среда выполнения” языка программирования  —  это код, который поставляется вместе с ним и необходим для поддержки функций самого языка.

Среда выполнения в Go значительно больше, чем, например, в C++, но на то есть веские причины.

При анализе функциональных возможностей среды выполнения, включенных в язык Go, просматривается одна закономерность. Но сперва назовем эти возможности.

  • Сборка мусора  —  вам не нужно вручную выполнять размещение данных и высвобождение ресурсов, Go позаботится об этом за вас.
  • Горутины  —  обеспечение эффективной многопоточности и ввода/вывода данных.
  • Каналы  —  средства передачи данных между горутинами.
  • Интерфейсы  —  минималистичное, но эффективное утверждение динамических типов.
  • Слайсы  —  используемые подобно массивам в других языках, они предназначены для более эффективного (и ленивого во многих случаях) распределения данных.
  • Карты  —  старые добрые хэш-карты “ключ-значение”, встроенные в язык с чистым синтаксисом.
  • Указатели  —  как и в C, только без адресной арифметики, значения и указатели на них четко обозначены, но не содержат ничего такого, что вызывает ошибки сегментации и прочие сбои.
  • Безопасность типов с выводом типов  —  все имеет свой тип, но есть сокращения, позволяющие не указывать тип в каждом случае (например, “a := b” делает “a” типом “b”).
  • Язык также заметно лишен некоторых концептов, таких как исключения и тернарный условный оператор “?:”.

Каков же паттерн? С одной стороны, язык включает в себя паттерны, необходимые практически каждому приложению, и поэтому они были сочтены достаточно важными для включения в среду выполнения. С другой  —  чувствуется некая сдержанность в добавлениях и сосредоточение на тех функциях, которые дают “максимальную отдачу”.

Большинство языков имеют странные комбинации малопонятных функций, появившиеся со временем, но полезные в очень специфических случаях и поэтому обычно находящиеся в сторонних библиотеках (вспомните операторы Javascript, такие как >>>=, и миллионы устаревших методов, связанных с различными встроенными типами).

В Go, на мой взгляд, среда выполнения и соответствующие возможности языка, а также стандартная библиотека гораздо больше соответствуют тому, что действительно нужно разработчикам приложений.

2. Более детализированный код

Да, вы правильно прочитали: это хорошо, что код Go более детализирован. Чтобы доказать это, приведу не только причины детализации кода Go, но и покажу, к чему приводят попытки сделать код менее подробным.

На первый взгляд кажется, чем короче  —  тем лучше. Зачем писать function, если можно ограничиться символом =>? Зачем писать Object, если можно просто использовать {}?

Ответ прост: когнитивные затраты. Иначе говоря, всякое добавление новой функции заставляет вас помнить и думать о ней.

Этот эффект постепенно, по мере добавления новых возможностей, усиливается. И код языка, который раньше был актуальным, со временем становится устаревшим из-за кучи новых фич, которые были добавлены. Код на C++, написанный сегодня, выглядит совсем иначе, чем десять лет назад. В данном случае речь идет не столько о самом языке, сколько о практическом опыте и общепринятых библиотеках. Однако суть та же: хотя это хорошо, что все совершенствуются и поиск путей оптимизации не прекращается, изобретение новых способов для прежних действий добавляет ненужную сложность.

Еще одно удобство, которое я считаю сомнительным компромиссом в других языках,  —  это исключения. В Java (и JS) вы можете сопроводить огромный блок кода оператором try, а затем создать блок кода для обработки любой ошибки, которая там произойдет. Но зачем большинство начинающих разработчиков используют try и catch? Правильно: чтобы “заглушить” ошибку в каком-то блоке кода, который выдает сбой.

Go решает эту проблему, позволяя функциям возвращать несколько значений, и по соглашению одно из них часто является ошибкой. Тогда вы просто проверяете ошибку с помощью оператора if. Это многословно, но все же работает очень хорошо. К счастью, в Google можно найти способы сократить все эти строки, набрав “сниппеты [название вашего редактора кода]”.

3. Наличие дженериков

В течение долгого времени я неоднократно слышал о том, что программисты не хотят писать код на Go, потому что в нем нет дженериков. И я по-настоящему рад появлению этой возможности. Не потому, что слишком нуждался в дженериках, а потому, что мне неприятно слушать, как люди жалуются на их отсутствие в Go.

Дженерики  —  одна из тех вещей, которые кажутся довольно простыми, но имеют много неочевидных нюансов и скрытую сложность. Подумайте о том, что шаблоны C++ и Java-дженерики выглядят очень похожими с точки зрения синтаксиса, но “под капотом” представляют собой совершенно разные реализации. Как разработчик, вы можете игнорировать такую сложность, но она оказывает огромное влияние на сам язык. Как бы там ни было, теперь, после добавления дженериков, разработчикам больше не на что жаловаться. Многим из них еще предстоит узнать, что только небольшой процент написанного кода обычно нуждается в дженериках или выигрывает от них.

4. Удобные инструменты для сборки

Тот факт, что только файлами в папках и одним простым файлом go.mod могут создаваться надежные сборки с абсолютно одинаковым исходным кодом на разных системах, звучит банально и очевидно, но это именно то, чего не делают другие системы сборки. Попробуйте собрать проект node.js, подождать полгода и снова запустить npm ci, а затем посмотрите, что получится. Проблемы со сборкой  —  постоянный источник головной боли разработчиков, и Go наконец-то нашел верное решение.

Ключевыми концепциями здесь являются получение одним набором совместимых API одного пути импорта и идея выбора минимальный версии (Minimal Version Selection, MVS).

Разберемся подробнее. Первая концепция является основной, и именно ее неправильно понимают большинство других версионных систем. Суть ее в том, что каждая уникальная несовместимая версия модуля должна иметь уникальный путь импорта.

Допустим, у вас есть версия 1 библиотеки X и версия 2 библиотеки X. Это не одно и то же, и поэтому им понадобятся разные пути импорта. В модулях Go появилась идея добавлять /v2 к пути импорта, чтобы соответствовать модулю версии 2, и т.д. Вы можете почитать об этом более подробно (тема определенно требует изучения), но думаю, что смысл подхода ясен: если вы вносите в библиотеку коренные изменения, она должна получить другой путь импорта. Эта простая практика имеет важные побочные эффекты, которые значительно повышают надежность и уменьшают путаницу при сборке.

Разобравшись с этой концепцией, перейдем к MVS. Суть выбора минимальной версии в том, что не нужно просто “использовать последнюю версию библиотеки 2.x”. Вместо этого, вам необходимо обновлять зависимости вручную и использовать только проверенную версию библиотеки, которая была указана в конфигурации сборки (go.mod).

Как это работает? Если вы создадите часть программного обеспечения сегодня и запустите ту же сборку завтра, то гарантированно получите тот же результат (используя ту же самую версию в качестве входных данных). Конечно, мы стремимся к поддержке обновлений, патчей безопасности и тому подобного, но идея в том, что это должно быть сделано явно, а не случайно, в качестве этапа процесса сборки.

Эти изменения значительно улучшили сборку программ на Go по сравнению со многими другими системами.

5. Безопасность типов для больших проектов  —  обязательное условие

Безопасность типов есть и во многих других языках, хотя и не во всех. Например, JavaScript и Python подходят для небольших проектов, но могут легко выйти из-под контроля из-за отсутствия безопасности типов.

Современные редакторы немного облегчают эту “боль”, предоставляя достаточно эффективные оповещения об ошибках для распространенных случаев даже в языках с отсутствием поддержки безопасности типов, таких как JS и Python. Но это не меняет того факта, что по мере роста сложности приложения, использующего язык, в котором нет никакой проверки доступа к полям и методам, кроме как во время выполнения, рано или поздно вы напишете проблемный код. Он будет только казаться нормальным, а затем внезапно покажет свою несостоятельность в производстве, причем в самое неподходящее время, из-за какой-нибудь глупой опечатки. Подобную опечатку можно было бы предотвратить с помощью компилятора и языка с безопасными типами.

То же самое преимущество вы получаете при редактировании кода. Выявление ошибки a.SomeThing() на этапе компиляции (вместо a.Something()) сэкономит ваше время: вам не придется выполнять эту строку кода, чтобы обнаружить ошибку,  —  просто нужно будет скомпилировать код.

Система типов Go, хотя и вызывает споры из-за своих отличий от других языков, обладает всеми основными преимуществами, которые вы ожидаете от любой другой системы типов. Используйте их во благо.


В целом, несмотря на то, что Go существует всего десять лет, он уже добился значительных успехов, став де-факто стандартом для решения задач бизнес-программирования во многих командах. Сможет ли он действительно “захватить власть”? Кто знает. Но думаю, что есть много убедительных аргументов в пользу того, что он будет неуклонно продолжать завоевывать позиции.

Читайте также:

Читайте нас в TelegramVK и Дзен


Перевод статьи Brad Peabody: 5 Reasons Go Will Take Over the Programming World

Предыдущая статьяДетальное исследование 3 подводных камней React, с которыми сталкиваются разработчики
Следующая статьяСсылки на методы в Java