Update go-style guide

Signed-off-by: Émile Ré <emile@probo.com>
This commit is contained in:
Émile Ré
2026-05-19 09:37:14 +04:00
parent e87b426801
commit c162e6ba20

View File

@@ -36,7 +36,38 @@ var (
)
```
## Call expressions and argument lists
## Multiline parameter and argument lists
The same single-line-or-multiline rule applies to **both** function/method **definitions** (parameter lists) and **call expressions** (argument lists). Never mix — if any parameter or argument breaks onto another line, put every one on its own line.
### Function and method definitions
- **Single-line signature** — the entire `func` line (name, parameters, return types) fits on one source line.
- **Multiline signature** — if it doesn't fit on one line, each parameter goes on its own indented line with a trailing comma. The closing `)` sits on its own line, followed by the return types.
```go
// Good — fits on one line
func (s *Service) GetFoo(ctx context.Context, id gid.GID) (*Foo, error) {
// Good — multiline: each parameter on its own line
func (s *Service) CreateFoo(
ctx context.Context,
tenantID gid.TenantID,
req CreateFooRequest,
) (*Foo, error) {
// Bad — mixed: some params on the func line, rest on the next
func (s *Service) CreateFoo(ctx context.Context, tenantID gid.TenantID,
req CreateFooRequest) (*Foo, error) {
// Bad — closing paren on the last param line
func (s *Service) CreateFoo(
ctx context.Context,
tenantID gid.TenantID,
req CreateFooRequest) (*Foo, error) {
```
### Call expressions
In the [Go spec](https://go.dev/ref/spec#Calls), a **call** is a primary expression `f(a1, a2, … an)` where `f` is the **function value** (or **method value**) and `a1` … `an` are **arguments** passed to the matching parameters.
@@ -104,13 +135,36 @@ Short receivers: usually single-letter matching the type (`s` for Service, `c` f
## Error handling
Wrap errors with `fmt.Errorf` using lowercase messages starting with `cannot`:
Always name error variables `err`. When a function can return errors from multiple call sites, every error must be wrapped so the caller can distinguish them. Wrap with `fmt.Errorf` using lowercase messages starting with `cannot`:
```go
return nil, fmt.Errorf("cannot load trust center: %w", err)
return nil, fmt.Errorf("cannot create SAML service: %w", err)
```
When multiple errors can come from the same function, each must have a distinct wrap message:
```go
func (s *Service) DoSomething(ctx context.Context) error {
foo, err := s.loadFoo(ctx)
if err != nil {
return fmt.Errorf("cannot load foo: %w", err)
}
bar, err := s.loadBar(ctx, foo.ID)
if err != nil {
return fmt.Errorf("cannot load bar: %w", err)
}
err = s.save(ctx, bar)
if err != nil {
return fmt.Errorf("cannot save bar: %w", err)
}
return nil
}
```
Sentinel errors in grouped `var ()` blocks. Custom error types implement `Unwrap() error`. Use `errors.Is` for sentinel checks. Use `errors.AsType[T](err)` (generic form) instead of `errors.As(err, &ptr)` for type assertions:
```go