Опирается на правила:
GO-2.1…GO-2.8,GO-2.X1…GO-2.X2иGO-LINT-4из Go Style Guide → раздел 2. Именование.
Важно знать
- Пакеты — одно слово,
lowercase, без_и цифр; имя совпадает с именем директории; описывает что в пакете, не что с ним делают (order, неorderutils, неmanageorders).- Экспортируемые символы —
MixedCaps; неэкспортируемые —mixedCaps; неsnake_caseнигде.- Аббревиатуры — целиком в одном регистре:
userID,HTTPClient,URLParser; смешанный регистр (UserId,HttpClient) запрещён.- Интерфейсы с одним методом — суффикс
-er:Reader,Stringer,Handler; с несколькими — по роли:OrderRepository.- Конструкторы —
New<Type>(экспортируемый) илиnew<Type>(неэкспортируемый); неCreate, неBuild.- Булевые —
isX,hasX,canX,ok; не голые существительные.- Имена тестов —
TestFuncName_WhenCondition_ExpectedBehavior; подтесты черезt.Run.- Метод не повторяет имя типа:
order.Create(), неorder.OrderCreate().
Именование — первый и самый дешёвый инструмент документации. В Go оно устроено компактнее, чем в Java: нет пространств имён вне пакетов, нет final-классов, нет IRepository-конвенций. Стандарт Effective Go и Google Go Style Guide — один, и он строже, чем кажется.
Пакеты
GO-2.1: имя пакета — это часть API. Вызов order.Create(...) в одном файле и product.List(...) в другом — понятно. orderutils.CreateNewOrder(...) — избыточно и нарушает GO-2.X2.
package order // ✓ — одно слово, описывает контент
package product // ✓
package customer // ✓
package orderutils // ✗ — суффикс util → GO-2.X1
package manageorders // ✗ — глагольная форма
package Order // ✗ — заглавная буква
package order_svc // ✗ — underscore
Имя директории и пакета совпадают. Если директория называется order, пакет — order. Если нужно разделить бизнес-логику от адаптера — отдельные директории: order/ и adapters/out/order/.
Пакеты util, helper, common, misc — признак нарушения разбиения (GO-2.X1). Перенести в пакет по домену или роли:
internal/util/ → internal/order/validator/ (если про заказы)
internal/common/ → internal/sberauth/ (если про аутентификацию)
Экспортируемые и неэкспортируемые символы
GO-2.2: видимость в Go — регистр первой буквы. Никакого public/private в объявлениях.
type Order struct { ... }
type orderEvent struct { ... }
func NewOrder(...) *Order { ... }
func validate(...) error { ... }
const MaxItems = 100
const defaultTimeout = 5
Переменные в узком скоупе — короткие: i, n, ok, err (GO-2.8). В широком скоупе — говорящие:
for i, item := range items { // ✓ — i и item достаточно в теле цикла
processItem(item)
}
func (r *orderRepository) findByCustomer(ctx context.Context, customerID string) ([]Order, error) {
// customerID — широкий скоуп, говорящее имя обязательно
}
Аббревиатуры
GO-2.3: аббревиатуры не «капитализируются» по первой букве — они либо целиком заглавные, либо целиком строчные, в зависимости от позиции.
// ✓ — в середине идентификатора: аббревиатура целиком заглавная
userID // не userId
productURL // не productUrl
HTTPClient // не HttpClient
URLParser // не UrlParser
// ✓ — в начале неэкспортируемого: целиком строчная
httpClient
urlParser
// ✗ — смешанный регистр нарушает GO-2.3
userId // неправильно
HttpClient // неправильно
Полные примеры в структурах:
type CustomerProfile struct {
CustomerID string
ProfileURL string
HasSberID bool
}
type HTTPTransport struct {
BaseURL string
maxRetries int
}
Интерфейсы
GO-2.4: интерфейс с одним методом именуется <Method>er — это конвенция стандартной библиотеки Go.
type Reader interface {
Read(p []byte) (n int, err error)
}
type Stringer interface {
String() string
}
type Handler interface {
ServeHTTP(w http.ResponseWriter, r *http.Request)
}
Интерфейсы с несколькими методами — по роли, существительное:
type OrderRepository interface {
Save(ctx context.Context, order *Order) error
FindByID(ctx context.Context, id string) (*Order, error)
FindByCustomer(ctx context.Context, customerID string) ([]Order, error)
}
type PaymentGateway interface {
Charge(ctx context.Context, req ChargeRequest) (*ChargeResult, error)
Refund(ctx context.Context, chargeID string, amount int64) error
}
Префикс I — C#-конвенция, в Go не применяется. Суффикс Interface тоже лишний.
type IOrderRepository interface { ... } // ✗ — не Go-идиома
type OrderRepositoryInterface interface { ... } // ✗ — лишний суффикс
type OrderRepository interface { ... } // ✓
Конструкторы
GO-2.5: конструктор — это обычная функция New<Type>. Go не имеет new-ключевого слова для пользовательских типов (только встроенный new() для аллокации), поэтому конвенция однозначная.
func NewOrder(customerID, productID string, quantity int) (*Order, error) {
if quantity <= 0 {
return nil, &ValidationError{Field: "quantity", Msg: "must be positive"}
}
return &Order{
id: uuid.New().String(),
customerID: customerID,
productID: productID,
quantity: quantity,
status: StatusPending,
}, nil
}
func NewOrderRepository(db *pgxpool.Pool) *orderRepository {
return &orderRepository{db: db}
}
Конструктор с валидацией инварианта — единственная точка создания агрегата. Build, Create, Make не используются:
func BuildOrder(...) *Order // ✗ — не конвенция Go
func CreateOrder(...) *Order // ✗
func NewOrder(...) (*Order, error) // ✓
Булевые переменные и поля
GO-2.6: булевые идентификаторы описывают состояние через префикс-вопрос.
type Order struct {
isPaid bool
hasShipped bool
canRefund bool
}
func (o *Order) IsPaid() bool { return o.isPaid }
func (o *Order) HasShipped() bool { return o.hasShipped }
func (o *Order) CanRefund() bool { return o.canRefund }
В узком скоупе — ok для результата операции:
order, ok := cache[orderID]
if !ok {
return nil, ErrNotFound
}
if err := validate(req); err != nil {
return nil, err
}
Голое существительное как булевое — нарушение:
type Product struct {
Available bool // ✗ — не ясно, это bool или количество
Stock bool // ✗
isAvailable bool // ✓
inStock bool // ✓
}
Имена тестов
GO-2.7: структура TestFuncName_WhenCondition_ExpectedBehavior — пространство имён через _, не camelCase.
func TestNewOrder_WhenQuantityZero_ReturnsValidationError(t *testing.T) {
_, err := NewOrder("cust-1", "prod-1", 0)
require.ErrorAs(t, err, &ValidationError{})
}
func TestOrderRepository_FindByCustomer_WhenCustomerHasNoOrders_ReturnsEmpty(t *testing.T) {
t.Parallel()
// ...
}
Подтесты через t.Run с snake_case-именем:
func TestNewOrder(t *testing.T) {
tests := []struct {
name string
customerID string
quantity int
wantErr bool
}{
{name: "valid_order", customerID: "cust-1", quantity: 1, wantErr: false},
{name: "zero_quantity_returns_error", customerID: "cust-1", quantity: 0, wantErr: true},
{name: "negative_quantity_returns_error", customerID: "cust-1", quantity: -1, wantErr: true},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
_, err := NewOrder(tc.customerID, "prod-1", tc.quantity)
if tc.wantErr {
require.Error(t, err)
} else {
require.NoError(t, err)
}
})
}
}
Что запрещено
| Антипаттерн | Правило | Что взамен |
|---|---|---|
package orderutils | GO-2.X1 | package order или выделить по роли |
package helper, package common | GO-2.X1 | пакет по доменной ответственности |
order.OrderCreate() | GO-2.X2 | order.Create() |
type IOrderRepository interface | GO-2.4 | type OrderRepository interface |
userId, HttpClient (смешанный регистр) | GO-2.3 | userID, HTTPClient |
func BuildOrder(...) | GO-2.5 | func NewOrder(...) |
paid bool вместо isPaid bool | GO-2.6 | isPaid, hasPaid, canPay |
func TestCreateOrderSuccess(t *testing.T) | GO-2.7 | TestNewOrder_WhenValid_ReturnsOrder |
//nolint:revive без обоснования | GO-LINT-6 | добавить комментарий «зачем» |
Куда дальше
- Пакеты и импорты — группировка
goimports, запрет dot-импорта, барьерinternal/. - Типы и интерфейсы — малые интерфейсы, value objects, деньги как
int64. - golangci-lint — конфиг
.golangci.yml, какие линтеры ловят нарушения именования. - Раздел 2. Именование — нормативные формулировки — коды
GO-2.*в одном месте.