diff options
| author | 2026-01-31 20:38:50 +0300 | |
|---|---|---|
| committer | 2026-01-31 23:38:53 +0300 | |
| commit | 49458f5ffd5a48c465117ec27f6437683f75acc1 (patch) | |
| tree | a99ee68116d10c2b2e5a70c442cdadec95ba793c /content/pages/gostyleguide/google/guide.md | |
| download | blog-49458f5ffd5a48c465117ec27f6437683f75acc1.tar.gz blog-49458f5ffd5a48c465117ec27f6437683f75acc1.tar.bz2 blog-49458f5ffd5a48c465117ec27f6437683f75acc1.tar.xz blog-49458f5ffd5a48c465117ec27f6437683f75acc1.zip | |
initial
Diffstat (limited to 'content/pages/gostyleguide/google/guide.md')
| -rw-r--r-- | content/pages/gostyleguide/google/guide.md | 495 |
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 |
