summaryrefslogtreecommitdiff
path: root/content/pages/gostyleguide/google/guide.md
diff options
context:
space:
mode:
Diffstat (limited to 'content/pages/gostyleguide/google/guide.md')
-rw-r--r--content/pages/gostyleguide/google/guide.md495
1 files changed, 495 insertions, 0 deletions
diff --git a/content/pages/gostyleguide/google/guide.md b/content/pages/gostyleguide/google/guide.md
new file mode 100644
index 0000000..31b3b33
--- /dev/null
+++ b/content/pages/gostyleguide/google/guide.md
@@ -0,0 +1,495 @@
+---
+order: 3
+title: Google Go Style Guide — Руководство
+---
+
+# Руководство по стилю Go (Go Style Guide)
+
+Оригинал: https://google.github.io/styleguide/go/guide
+
+[Обзор](https://neonxp.ru/pages/gostyleguide/google/) | [Руководство](https://neonxp.ru/pages/gostyleguide/google/guide) | [Решения](https://neonxp.ru/pages/gostyleguide/google/decisions) |
+[Лучшие практики](https://neonxp.ru/pages/gostyleguide/google/best-practices)
+
+
+**Примечание:** Это часть серии документов, описывающих [Стиль Go (Go
+Style)](https://neonxp.ru/pages/gostyleguide/google/) в Google. Данный документ является **[нормативным
+(normative)](https://neonxp.ru/pages/gostyleguide/google/#normative) и [каноническим (canonical)](https://neonxp.ru/pages/gostyleguide/google/#canonical)**.
+Подробнее см. [в обзоре](https://neonxp.ru/pages/gostyleguide/google/#about).
+
+<a id="principles"></a>
+
+## Принципы стиля
+
+Существует несколько основополагающих принципов, которые суммируют подход к
+написанию читаемого кода на Go. Ниже перечислены атрибуты читаемого кода в
+порядке важности:
+
+1. **[Понятность (Clarity)]**: Цель и обоснование кода ясны читателю.
+1. **[Простота (Simplicity)]**: Код достигает своей цели наиболее простым
+ способом.
+1. **[Лаконичность (Concision)]**: Код имеет высокое отношение сигнала к шуму.
+1. **[Поддерживаемость (Maintainability)]**: Код написан так, чтобы его было
+ легко поддерживать.
+1. **[Согласованность (Consistency)]**: Код согласуется с более широкой
+ кодобазой Google.
+
+[Понятность (Clarity)]: #clarity
+[Простота (Simplicity)]: #simplicity
+[Лаконичность (Concision)]: #concision
+[Поддерживаемость (Maintainability)]: #maintainability
+[Согласованность (Consistency)]: #consistency
+
+<a id="clarity"></a>
+
+### Понятность (Clarity)
+
+Основная цель удобочитаемости — создание кода, который понятен читателю.
+
+Понятность достигается в первую очередь за счет эффективного именования,
+полезных комментариев и продуманной организации кода.
+
+Понятность следует рассматривать с точки зрения читателя, а не автора кода.
+Важнее, чтобы код был легок для чтения, а не для написания. Понятность кода
+имеет два аспекта:
+
+* [Что именно делает код?](#clarity-purpose)
+* [Почему код делает то, что он делает?](#clarity-rationale)
+
+<a id="clarity-purpose"></a>
+
+#### Что именно делает код?
+
+Go разработан таким образом, чтобы относительно легко было понять, что делает
+код. В случаях неопределенности или когда читателю могут потребоваться
+предварительные знания для понимания кода, стоит потратить время, чтобы сделать
+цель кода более ясной для будущих читателей. Например, может помочь:
+
+* Использование более описательных имен переменных
+* Добавление дополнительных комментариев
+* Разделение кода пробелами и комментариями
+* Рефакторинг кода в отдельные функции/методы для повышения модульности
+
+Здесь нет универсального решения, но при разработке кода на Go важно отдавать
+приоритет понятности.
+
+<a id="clarity-rationale"></a>
+
+#### Почему код делает то, что он делает?
+
+Обоснование кода часто достаточно ясно передается именами переменных, функций,
+методов или пакетов. Если это не так, важно добавить комментарии. "Почему?"
+особенно важно, когда код содержит нюансы, с которыми читатель может быть не
+знаком, например:
+
+* Нюанс языка, например, замыкание захватит переменную цикла, но само
+ замыкание находится далеко от него
+* Нюанс бизнес-логики, например, проверка прав доступа, которая должна
+ различать реального пользователя и того, кто выдает себя за пользователя
+
+API может требовать осторожного использования. Например, фрагмент кода может
+быть сложным и трудным для понимания из-за соображений производительности, или
+сложная последовательность математических операций может использовать
+преобразования типов неожиданным образом. В этих и многих других случаях важно,
+чтобы сопровождающие комментарии и документация объясняли эти аспекты, чтобы
+будущие сопровождающие не допустили ошибку и чтобы читатели могли понять код без
+необходимости его обратной разработки.
+
+Также важно осознавать, что некоторые попытки повысить понятность (например,
+добавление лишних комментариев) могут фактически затуманить цель кода, добавляя
+беспорядок, пересказывая то, что код уже говорит, противореча коду или добавляя
+нагрузку по поддержке актуальности комментариев. Позвольте коду говорить самому
+за себя (например, делая имена символов самодокументированными), а не добавляйте
+избыточные комментарии. Зачастую лучше, чтобы комментарии объясняли, *почему*
+что-то сделано, а не *что* делает код.
+
+Кодовая база Google в значительной степени единообразна и согласована. Часто
+бывает, что код, который выделяется (например, использованием незнакомого
+шаблона), делает это по уважительной причине, обычно для производительности.
+Поддержание этого свойства важно, чтобы дать читателям понять, куда им следует
+направить внимание при чтении нового фрагмента кода.
+
+Стандартная библиотека содержит множество примеров реализации этого принципа.
+Среди них:
+
+* Комментарии сопровождающих в [`package
+ sort`](https://cs.opensource.google/go/go/+/refs/tags/go1.19.2:src/sort/sort.go).
+* Хорошие [запускаемые примеры в том же
+ пакете](https://cs.opensource.google/go/go/+/refs/tags/go1.19.2:src/sort/example_search_test.go),
+ которые полезны как пользователям (они [отображаются в
+ godoc](https://pkg.go.dev/sort#pkg-examples)), так и сопровождающим (они
+ [запускаются как часть тестов](https://neonxp.ru/pages/gostyleguide/google/decisions/#examples)).
+* [`strings.Cut`](https://pkg.go.dev/strings#Cut) — это всего четыре строки
+ кода, но они повышают [понятность и корректность мест вызова
+ (callsites)](https://github.com/golang/go/issues/46336).
+
+<a id="simplicity"></a>
+
+### Простота (Simplicity)
+
+Ваш код на Go должен быть простым для тех, кто его использует, читает и
+поддерживает.
+
+Код на Go должен быть написан наиболее простым способом, достигающим его целей,
+как с точки зрения поведения, так и производительности. Внутри кодовой базы Go в
+Google простой код:
+
+* Легко читается сверху вниз
+* Не предполагает, что вы уже знаете, что он делает
+* Не предполагает, что вы можете запомнить весь предшествующий код
+* Не имеет ненужных уровней абстракции
+* Не имеет имен, которые привлекают внимание к чему-то обыденному
+* Делает передачу значений и принятие решений понятными для читателя
+* Имеет комментарии, которые объясняют *почему*, а не *что* делает код, чтобы
+ избежать будущих отклонений
+* Имеет самодостаточную документацию
+* Имеет полезные ошибки и полезные сообщения об ошибках в тестах
+* Часто может быть взаимоисключающим с "умным" кодом
+
+Могут возникать компромиссы между простотой кода и простотой использования API.
+Например, может иметь смысл сделать код более сложным, чтобы конечному
+пользователю API было легче правильно его вызывать. И наоборот, также может быть
+целесообразно оставить немного дополнительной работы конечному пользователю API,
+чтобы код оставался простым и легким для понимания.
+
+Когда коду требуется сложность, она должна добавляться обдуманно. Обычно это
+необходимо, если требуется дополнительная производительность или если у
+конкретной библиотеки или сервиса есть несколько различных клиентов. Сложность
+может быть оправдана, но она должна сопровождаться документацией, чтобы клиенты
+и будущие сопровождающие могли понять и ориентироваться в ней. Это должно
+дополняться тестами и примерами, демонстрирующими ее правильное использование,
+особенно если существует как "простой", так и "сложный" способ использования
+кода.
+
+Этот принцип не означает, что сложный код нельзя или не следует писать на Go,
+или что коду на Go не разрешено быть сложным. Мы стремимся к кодовой базе,
+которая избегает ненужной сложности, чтобы, когда сложность появляется, это
+указывало на то, что рассматриваемый код требует внимания для понимания и
+поддержки. В идеале должны быть сопроводительные комментарии, объясняющие
+обоснование и указывающие на меры предосторожности. Это часто возникает при
+оптимизации кода для производительности; это часто требует более сложного
+подхода, например, предварительного выделения буфера и его повторного
+использования в течение времени жизни горутины. Когда сопровождающий видит это,
+это должно быть сигналом, что рассматриваемый код критичен для
+производительности, и это должно влиять на осторожность при внесении будущих
+изменений. С другой стороны, если эта сложность применена без необходимости, она
+становится обузой для тех, кому нужно читать или изменять код в будущем.
+
+Если код оказывается очень сложным, в то время как его цель должна быть простой,
+это часто сигнал пересмотреть реализацию, чтобы увидеть, есть ли более простой
+способ достичь того же.
+
+<a id="least-mechanism"></a>
+
+#### Наименьшая механизация (Least mechanism)
+
+Если есть несколько способов выразить одну и ту же идею, предпочтите тот,
+который использует наиболее стандартные инструменты. Сложные механизмы часто
+существуют, но не должны применяться без причины. Легко добавить сложность в код
+по мере необходимости, тогда как гораздо сложнее удалить существующую сложность
+после того, как выяснилось, что она не нужна.
+
+1. Стремитесь использовать базовую конструкцию языка (например, канал, срез
+ (slice), мапу (map), цикл или структуру (struct)), если этого достаточно для
+ вашего случая использования.
+2. Если такой нет, ищите инструмент в стандартной библиотеке (например,
+ HTTP-клиент или механизм шаблонов (template engine)).
+3. Наконец, рассмотрите, есть ли в кодовой базе Google основная библиотека,
+ которой достаточно, прежде чем вводить новую зависимость или создавать свою
+ собственную.
+
+В качестве примера рассмотрим production-код, содержащий флаг, привязанный к
+переменной со значением по умолчанию, которое должно быть переопределено в
+тестах. Если не предполагается тестирование самого интерфейса командной строки
+программы (скажем, с помощью `os/exec`), проще и поэтому предпочтительнее
+переопределить привязанное значение напрямую, а не с помощью `flag.Set`.
+
+Аналогично, если фрагменту кода требуется проверка принадлежности к множеству
+(set membership check), часто достаточно мапы с булевыми значениями (например,
+`map[string]bool`). Библиотеки, предоставляющие типы и функциональность, похожие
+на множества (set), следует использовать только в том случае, если требуются
+более сложные операции, которые невозможны или чрезмерно сложны с мапой.
+
+<a id="concision"></a>
+
+### Лаконичность (Concision)
+
+Лаконичный код на Go имеет высокое отношение сигнала к шуму. Легко различить
+соответствующие детали, а именование и структура направляют читателя через эти
+детали.
+
+Многое может помешать выделению наиболее важных деталей в любой момент:
+
+* Повторяющийся код
+* Лишний синтаксис
+* [Непонятные имена](#naming)
+* Ненужная абстракция
+* Пробелы
+
+Повторяющийся код особенно затмевает различия между каждым почти идентичным
+разделом и требует от читателя визуального сравнения похожих строк кода, чтобы
+найти изменения. [Табличное тестирование (Table-driven testing)] — хороший
+пример механизма, который может лаконично вынести общий код за рамки важных
+деталей каждого повторения, но выбор того, какие части включить в таблицу,
+повлияет на то, насколько легко таблицу понять.
+
+При рассмотрении нескольких способов структурирования кода стоит подумать, какой
+способ делает важные детали наиболее очевидными.
+
+Понимание и использование общих конструкций кода и идиом также важны для
+поддержания высокого отношения сигнала к шуму. Например, следующий блок кода
+очень распространен при [обработке ошибок (error handling)], и читатель может
+быстро понять его назначение.
+
+```go
+// Хорошо:
+if err := doSomething(); err != nil {
+ // ...
+}
+```
+
+Если код выглядит очень похоже, но имеет тонкое отличие, читатель может не
+заметить изменение. В таких случаях стоит намеренно ["усилить" сигнал] проверки
+ошибки, добавив комментарий, чтобы привлечь к нему внимание.
+
+```go
+// Хорошо:
+if err := doSomething(); err == nil { // если ошибки НЕТ
+ // ...
+}
+```
+
+[Табличное тестирование (Table-driven testing)]:
+ https://github.com/golang/go/wiki/TableDrivenTests
+[обработке ошибок (error handling)]: https://go.dev/blog/errors-are-values
+["усилить" сигнал]: best-practices#signal-boost
+
+<a id="maintainability"></a>
+
+### Поддерживаемость (Maintainability)
+
+Код редактируется гораздо чаще, чем пишется. Читаемый код не только понятен
+читателю, который пытается понять, как он работает, но и программисту, которому
+нужно его изменить. Ключевое значение имеет понятность.
+
+Поддерживаемый код:
+
+* Легко модифицируется будущим программистом правильно
+* Имеет API, структурированные таким образом, что они могут элегантно
+ развиваться
+* Четко указывает предположения, которые он делает, и выбирает абстракции,
+ которые соответствуют структуре проблемы, а не структуре кода
+* Избегает ненужной связности и не включает неиспользуемые функции
+* Имеет комплексный набор тестов, чтобы гарантировать сохранение заявленного
+ поведения и корректность важной логики, а тесты предоставляют четкие,
+ действенные диагностические сообщения в случае неудачи
+
+При использовании абстракций, таких как интерфейсы и типы, которые по
+определению удаляют информацию из контекста, в котором они используются, важно
+обеспечить, чтобы они приносили достаточную пользу. Редакторы и IDE могут
+напрямую подключаться к определению метода и показывать соответствующую
+документацию при использовании конкретного типа, но в противном случае могут
+ссылаться только на определение интерфейса. Интерфейсы — мощный инструмент, но
+они имеют свою цену, поскольку сопровождающему, возможно, потребуется понять
+особенности базовой реализации, чтобы правильно использовать интерфейс, что
+должно быть объяснено в документации интерфейса или в месте вызова (call-site).
+
+Поддерживаемый код также избегает скрытия важных деталей в местах, которые легко
+упустить из виду. Например, в каждой из следующих строк кода наличие или
+отсутствие одного символа имеет критическое значение для понимания строки:
+
+```go
+// Плохо:
+// Использование = вместо := может полностью изменить эту строку.
+if user, err = db.UserByID(userID); err != nil {
+ // ...
+}
+```
+
+```go
+// Плохо:
+// Символ ! в середине этой строки очень легко пропустить.
+leap := (year%4 == 0) && (!(year%100 == 0) || (year%400 == 0))
+```
+
+Ни один из этих примеров не является неверным, но оба могут быть написаны в
+более явной форме или могут иметь сопровождающий комментарий, привлекающий
+внимание к важному поведению:
+
+```go
+// Хорошо:
+u, err := db.UserByID(userID)
+if err != nil {
+ return fmt.Errorf("invalid origin user: %s", err)
+}
+user = u
+```
+
+```go
+// Хорошо:
+// Григорианские високосные годы — это не просто year%4 == 0.
+// См. https://en.wikipedia.org/wiki/Leap_year#Algorithm.
+var (
+ leap4 = year%4 == 0
+ leap100 = year%100 == 0
+ leap400 = year%400 == 0
+)
+leap := leap4 && (!leap100 || leap400)
+```
+
+Таким же образом вспомогательная функция, скрывающая критическую логику или
+важный крайний случай (edge-case), может облегчить ситуации, когда будущее
+изменение не учитывает их должным образом.
+
+Предсказуемые имена — еще одна особенность поддерживаемого кода. Пользователь
+пакета или сопровождающий фрагмента кода должны иметь возможность предсказать
+имя переменной, метода или функции в данном контексте. Параметры функций и имена
+получателей (receiver) для идентичных концепций обычно должны иметь одно и то же
+имя, как для понятности документации, так и для облегчения рефакторинга кода с
+минимальными затратами.
+
+Поддерживаемый код минимизирует свои зависимости (как явные, так и неявные).
+Зависимость от меньшего количества пакетов означает меньше строк кода, которые
+могут повлиять на поведение. Избегание зависимостей от внутреннего или
+недокументированного поведения делает код менее обременительным для поддержки
+при изменении этого поведения в будущем.
+
+При обдумывании того, как структурировать или писать код, стоит потратить время
+на размышления о том, как код может развиваться с течением времени. Если данный
+подход способствует более легким и безопасным будущим изменениям, это часто
+является хорошим компромиссом, даже если это означает несколько более сложный
+дизайн.
+
+<a id="consistency"></a>
+
+### Согласованность (Consistency)
+
+Согласованный код — это код, который выглядит, ощущается и ведет себя так же,
+как и аналогичный код во всей кодовой базе, в контексте команды или пакета и
+даже в пределах одного файла.
+
+Соображения согласованности не отменяют ни один из вышеперечисленных принципов,
+но если необходимо принять решение, часто полезно решить его в пользу
+согласованности.
+
+Согласованность внутри пакета часто является наиболее важным уровнем
+согласованности. Может быть очень неприятно, если одна и та же проблема решается
+несколькими способами в пределах пакета или если одна концепция имеет много имен
+в пределах файла. Однако даже это не должно перевешивать задокументированные
+принципы стиля или глобальную согласованность.
+
+<a id="core"></a>
+
+## Основные рекомендации (Core guidelines)
+
+Эти рекомендации собирают наиболее важные аспекты стиля Go, которым, как
+ожидается, следует весь код на Go. Мы ожидаем, что эти принципы будут изучены и
+соблюдены к моменту получения права на ревью кода (readability). Они не должны
+часто меняться, и новые дополнения должны преодолеть высокий барьер.
+
+Приведенные ниже рекомендации расширяют рекомендации из [Effective Go], которые
+обеспечивают общую основу для кода на Go во всем сообществе.
+
+[Effective Go]: https://go.dev/doc/effective_go
+
+<a id="formatting"></a>
+
+### Форматирование (Formatting)
+
+Все исходные файлы Go должны соответствовать формату, выводимому инструментом
+`gofmt`. Этот формат обеспечивается проверкой перед отправкой (presubmit check)
+в кодовой базе Google. [Сгенерированный код] также обычно должен
+форматироваться (например, с использованием [`format.Source`]), так как он также
+просматривается в Code Search.
+
+[Сгенерированный код]:
+ https://docs.bazel.build/versions/main/be/general.html#genrule
+[`format.Source`]: https://pkg.go.dev/go/format#Source
+
+<a id="mixed-caps"></a>
+
+### MixedCaps
+
+Исходный код Go использует `MixedCaps` или `mixedCaps` (верблюжий регистр, camel
+case) вместо подчеркиваний (змеиный регистр, snake case) при написании составных
+имен.
+
+Это применимо даже тогда, когда это нарушает соглашения в других языках.
+Например, константа называется `MaxLength` (не `MAX_LENGTH`), если она
+экспортируемая (exported), и `maxLength` (не `max_length`), если
+неэкспортируемая (unexported).
+
+Локальные переменные считаются [неэкспортируемыми (unexported)] для целей выбора
+начального регистра.
+
+<!--#include file="/go/g3doc/style/includes/special-name-exception.md"-->
+
+[неэкспортируемыми (unexported)]: https://go.dev/ref/spec#Exported_identifiers
+
+<a id="line-length"></a>
+
+### Длина строки (Line length)
+
+Не существует фиксированной длины строки для исходного кода Go. Если строка
+кажется слишком длинной, предпочтительнее рефакторинг, чем ее разделение. Если
+строка уже настолько короткая, насколько это практично, ей следует позволить
+оставаться длинной.
+
+Не разделяйте строку:
+
+* Перед [изменением отступа (indentation
+ change)](https://neonxp.ru/pages/gostyleguide/google/decisions/#indentation-confusion) (например, объявление функции,
+ условие)
+* Чтобы длинная строка (например, URL) поместилась в несколько более коротких
+ строк
+
+<a id="naming"></a>
+
+### Именование (Naming)
+
+Именование — это скорее искусство, чем наука. В Go имена, как правило, несколько
+короче, чем во многих других языках, но применяются те же [общие принципы].
+Имена должны:
+
+* Не казаться [избыточными (repetitive)](https://neonxp.ru/pages/gostyleguide/google/decisions/#repetition) при
+ использовании
+* Учитывать контекст
+* Не повторять концепции, которые уже ясны
+
+Более конкретные рекомендации по именованию можно найти в [решениях
+(https://neonxp.ru/pages/gostyleguide/google/decisions/)](https://neonxp.ru/pages/gostyleguide/google/decisions/#naming).
+
+[общие принципы]:
+ https://testing.googleblog.com/2017/10/code-health-identifiernamingpostforworl.html
+
+<a id="local-consistency"></a>
+
+### Локальная согласованность (Local consistency)
+
+Если в руководстве по стилю ничего не сказано по конкретному вопросу стиля,
+авторы могут свободно выбирать предпочтительный стиль, если только код в
+непосредственной близости (обычно в том же файле или пакете, но иногда в
+пределах каталога команды или проекта) не занял последовательную позицию по
+этому вопросу.
+
+Примеры **допустимых** соображений локального стиля:
+
+* Использование `%s` или `%v` для форматированного вывода ошибок
+* Использование буферизованных каналов вместо мьютексов
+
+Примеры **недопустимых** соображений локального стиля:
+
+* Ограничения на длину строк для кода
+* Использование библиотек тестирования на основе утверждений (assertion-based)
+
+Если локальный стиль противоречит руководству по стилю, но влияние на читаемость
+ограничено одним файлом, это обычно будет отмечено в ревью кода, для которого
+согласованное исправление выходит за рамки данного CL (change list). В этом
+случае уместно завести задачу (bug) для отслеживания исправления.
+
+Если изменение ухудшит существующее отклонение от стиля, выставит его в большем
+количестве API, увеличит количество файлов, в которых присутствует отклонение,
+или внесет фактическую ошибку, то локальная согласованность больше не является
+допустимым обоснованием для нарушения руководства по стилю в новом коде. В этих
+случаях автору уместно либо очистить существующую кодовую базу в том же CL, либо
+выполнить рефакторинг перед текущим CL, либо найти альтернативу, которая, по
+крайней мере, не усугубляет локальную проблему. \ No newline at end of file