Опирается на правила: GO-2.1GO-2.8, GO-2.X1GO-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 orderutilsGO-2.X1package order или выделить по роли
package helper, package commonGO-2.X1пакет по доменной ответственности
order.OrderCreate()GO-2.X2order.Create()
type IOrderRepository interfaceGO-2.4type OrderRepository interface
userId, HttpClient (смешанный регистр)GO-2.3userID, HTTPClient
func BuildOrder(...)GO-2.5func NewOrder(...)
paid bool вместо isPaid boolGO-2.6isPaid, hasPaid, canPay
func TestCreateOrderSuccess(t *testing.T)GO-2.7TestNewOrder_WhenValid_ReturnsOrder
//nolint:revive без обоснованияGO-LINT-6добавить комментарий «зачем»

Куда дальше

  • Пакеты и импорты — группировка goimports, запрет dot-импорта, барьер internal/.
  • Типы и интерфейсы — малые интерфейсы, value objects, деньги как int64.
  • golangci-lint — конфиг .golangci.yml, какие линтеры ловят нарушения именования.
  • Раздел 2. Именование — нормативные формулировки — коды GO-2.* в одном месте.