[teamai] Push 87 resource(s) from XingfenD
This commit is contained in:
@@ -0,0 +1,116 @@
|
||||
# Functions, Methods & Options
|
||||
|
||||
## Functions and Methods
|
||||
|
||||
Functions returning a value are named like **nouns** (what they return). Functions performing actions are named like **verbs** (what they do).
|
||||
|
||||
```go
|
||||
// Noun-like: returns something
|
||||
func UserName() string { ... }
|
||||
func DefaultConfig() Config { ... }
|
||||
|
||||
// Verb-like: performs an action
|
||||
func WriteFile(name string, data []byte) error { ... }
|
||||
func SendNotification(user *User) error { ... }
|
||||
```
|
||||
|
||||
NEVER repeat the package name in function names:
|
||||
|
||||
```go
|
||||
// Good: users call http.Get(), not http.HTTPGet()
|
||||
package http
|
||||
func Get(url string) (*Response, error)
|
||||
|
||||
// Bad: stutters at the call site
|
||||
package http
|
||||
func HTTPGet(url string) (*Response, error)
|
||||
```
|
||||
|
||||
Functions that accept a format string and variadic args (like `fmt.Sprintf`) MUST end with **`f`**:
|
||||
|
||||
```go
|
||||
// Good
|
||||
func Errorf(format string, args ...any) error
|
||||
func Wrapf(err error, format string, args ...any) error
|
||||
func Logf(format string, args ...any)
|
||||
|
||||
// Bad
|
||||
func Error(format string, args ...any) error // looks like it takes a plain string
|
||||
func WrapError(err error, format string, args ...any) error
|
||||
```
|
||||
|
||||
### Getters and Setters
|
||||
|
||||
Getters MUST NOT use the `Get` prefix. The getter is simply the field name, capitalized.
|
||||
|
||||
```go
|
||||
// Good
|
||||
func (u *User) Name() string { return u.name }
|
||||
func (u *User) SetName(name string) { u.name = name }
|
||||
|
||||
// Bad
|
||||
func (u *User) GetName() string { return u.name }
|
||||
```
|
||||
|
||||
Only use `Get` when the underlying concept inherently uses "get" (e.g., HTTP GET). For expensive or blocking operations, use `Fetch` or `Compute` to signal that the call is not trivial.
|
||||
|
||||
**Exception — boolean predicates keep the `Is`/`Has`/`Can` prefix.** The no-Get rule applies to value getters, not boolean predicates. A method returning `bool` SHOULD use `Is`/`Has`/`Can` to read naturally as a question — this follows the standard library pattern (`reflect.Type.IsVariadic()`, `net.IP.IsLoopback()`, `big.Int.IsInt64()`).
|
||||
|
||||
```go
|
||||
// Good — boolean predicate keeps Is prefix
|
||||
func (s *Server) IsHealthy() bool { return s.healthy }
|
||||
|
||||
// Good — value getter omits Get prefix
|
||||
func (s *Server) Port() int { return s.port }
|
||||
|
||||
// Good — richer return type, bare name is fine (different semantics)
|
||||
func (s *Server) Healthy() (HealthStatus, error)
|
||||
|
||||
// Bad — bare adjective for bool is ambiguous
|
||||
func (s *Server) Healthy() bool { return s.healthy }
|
||||
|
||||
// Bad — Get prefix on value getter is redundant
|
||||
func (s *Server) GetPort() int { return s.port }
|
||||
```
|
||||
|
||||
### Constructors
|
||||
|
||||
Name constructors `New` when the package exports a single primary type, or `NewTypeName` when there are multiple types.
|
||||
|
||||
```go
|
||||
// Single primary type — New is unambiguous
|
||||
package ring
|
||||
func New(size int) *Ring
|
||||
|
||||
// Multiple types — qualify with the type name
|
||||
package http
|
||||
func NewRequest(method, url string, body io.Reader) (*Request, error)
|
||||
func NewServeMux() *ServeMux
|
||||
```
|
||||
|
||||
### Named Return Values
|
||||
|
||||
Named return values SHOULD only be used when it improves readability — typically when multiple return values have the same type, or when the names serve as documentation.
|
||||
|
||||
```go
|
||||
// Good — names clarify which int64 is which
|
||||
func Copy(dst Writer, src Reader) (written int64, err error)
|
||||
func ScanBytes(data []byte, atEOF bool) (advance int, token []byte, err error)
|
||||
|
||||
// Good — single error return, no name needed
|
||||
func Write(p []byte) (int, error)
|
||||
|
||||
// Bad — names add no clarity
|
||||
func Read(p []byte) (bytes int, e error) // "bytes" shadows the package, "e" is non-standard
|
||||
```
|
||||
|
||||
NEVER use named returns just to enable bare `return` — bare returns hurt readability in anything but the shortest functions.
|
||||
|
||||
## Functional Options Pattern
|
||||
|
||||
When a constructor has 3+ optional parameters that may grow, use the **functional options pattern** for clean, extensible APIs.
|
||||
|
||||
- **Struct**: `ServerOptions`, `ClientOptions` (not `Opts`, `Params`, `Settings`, `Config`)
|
||||
- **Function type**: `ServerOption` (singular, not plural)
|
||||
- **With\* functions**: `WithPort()`, `WithTimeout()`, `WithLogger()`
|
||||
- **Factory**: `DefaultServerOptions()`
|
||||
@@ -0,0 +1,165 @@
|
||||
# Variables, Booleans, Receivers & Acronyms
|
||||
|
||||
## Variables
|
||||
|
||||
Name length SHOULD be **proportional to scope size**. Short names for small scopes, descriptive names for large scopes.
|
||||
|
||||
```go
|
||||
// Small scope (1-7 lines): short names are fine
|
||||
for i, v := range items {
|
||||
result = append(result, v.Name)
|
||||
}
|
||||
|
||||
// Medium scope: moderately descriptive
|
||||
userCount := len(users)
|
||||
|
||||
// Large scope / package-level: explicit and clear
|
||||
var defaultHTTPTransport = &http.Transport{
|
||||
MaxIdleConns: 100,
|
||||
}
|
||||
```
|
||||
|
||||
Common single-letter conventions:
|
||||
|
||||
| Letter | Meaning |
|
||||
| ------------- | ---------------------- |
|
||||
| `i`, `j`, `k` | Loop indices |
|
||||
| `n` | Count or length |
|
||||
| `v` | Value (in range loops) |
|
||||
| `k` | Key (in map ranges) |
|
||||
| `r` | `io.Reader` |
|
||||
| `w` | `io.Writer` |
|
||||
| `b` | `[]byte` or buffer |
|
||||
| `s` | String |
|
||||
| `t` | `*testing.T` |
|
||||
| `ctx` | `context.Context` |
|
||||
| `err` | Error |
|
||||
|
||||
### Avoid Type in the Name
|
||||
|
||||
The name should describe what the value represents, not its type.
|
||||
|
||||
```go
|
||||
// Good
|
||||
users := getUsers()
|
||||
count := len(items)
|
||||
|
||||
// Bad
|
||||
userSlice := getUsers()
|
||||
countInt := len(items)
|
||||
nameString := "hello"
|
||||
```
|
||||
|
||||
### Avoid Repetition with Context
|
||||
|
||||
Omit words already clear from the enclosing function, method, or type.
|
||||
|
||||
```go
|
||||
// Good — "user" is clear from the method receiver
|
||||
func (u *UserService) Create(name string) error { ... }
|
||||
|
||||
// Bad — "user" is redundant
|
||||
func (u *UserService) CreateUser(userName string) error { ... }
|
||||
```
|
||||
|
||||
### Use Predictable Names
|
||||
|
||||
The same concept MUST always use the same name across the codebase. If a user is called `user` in one function, it should not become `account`, `person`, or `u` in another. Consistency makes code searchable and reduces cognitive load.
|
||||
|
||||
```go
|
||||
// Good — same concept, same name everywhere
|
||||
func CreateUser(user *User) error { ... }
|
||||
func UpdateUser(user *User) error { ... }
|
||||
func DeleteUser(userID string) error { ... }
|
||||
|
||||
// Bad — same concept, different names
|
||||
func CreateUser(user *User) error { ... }
|
||||
func UpdateAccount(acct *User) error { ... } // why "acct"? it's a User
|
||||
func RemovePerson(id string) error { ... } // why "person"? why "remove"?
|
||||
```
|
||||
|
||||
This applies to variables, parameters, functions, and fields. Pick one name per domain concept and stick with it: `order` not sometimes `order` / sometimes `purchase`; `userID` not sometimes `userID` / sometimes `uid` / sometimes `userId`.
|
||||
|
||||
### Parameters
|
||||
|
||||
Parameters double as documentation at the call site. When the type is descriptive, keep the name short. When the type is ambiguous, use a longer name to clarify intent.
|
||||
|
||||
```go
|
||||
// Good — type is descriptive, short name is fine
|
||||
func AfterFunc(d Duration, f func()) *Timer
|
||||
func Escape(w io.Writer, s []byte)
|
||||
|
||||
// Good — type is ambiguous (int64, string), longer name documents meaning
|
||||
func Unix(sec, nsec int64) Time
|
||||
func HasPrefix(s, prefix string) bool
|
||||
|
||||
// Bad — ambiguous type with cryptic name
|
||||
func Unix(a, b int64) Time // what are a and b?
|
||||
func HasPrefix(a, b string) bool // which is the prefix?
|
||||
```
|
||||
|
||||
## Booleans
|
||||
|
||||
Boolean variables and fields MUST read naturally as true/false questions. Use prefixes like `is`, `has`, `can`, `allow`, `should`. This applies to **both variables and struct fields**.
|
||||
|
||||
```go
|
||||
// Good — struct fields use is/has prefix
|
||||
type Client struct {
|
||||
isConnected bool // reads as "client is connected"
|
||||
hasPermission bool // reads as "client has permission"
|
||||
}
|
||||
|
||||
// Good — variables
|
||||
isReady := true
|
||||
hasPermission := user.CanEdit(doc)
|
||||
|
||||
// Bad — bare adjective is ambiguous
|
||||
type Client struct {
|
||||
connected bool // could be confused with a connection object
|
||||
permission bool // noun, not a question
|
||||
}
|
||||
```
|
||||
|
||||
For exported boolean methods, the prefix becomes part of the method name: `IsValid()`, `HasPrefix()`, `CanRetry()`. The unexported field keeps the prefix too: `isConnected` field → `IsConnected()` method.
|
||||
|
||||
## Receivers
|
||||
|
||||
Receivers MUST be **1-2 letter abbreviations** of the type name. Use the same name across all methods of a type.
|
||||
|
||||
```go
|
||||
// Good — short, consistent
|
||||
func (s *Server) Start() error { ... }
|
||||
func (s *Server) Stop() error { ... }
|
||||
func (s *Server) Handle(r *Request) { ... }
|
||||
|
||||
// Bad — too long
|
||||
func (server *Server) Start() error { ... }
|
||||
|
||||
// Bad — inconsistent names across methods
|
||||
func (s *Server) Start() error { ... }
|
||||
func (srv *Server) Stop() error { ... }
|
||||
|
||||
// Bad — NEVER use "this" or "self"
|
||||
func (this *Server) Handle(r *Request) { ... }
|
||||
```
|
||||
|
||||
## Acronyms and Initialisms
|
||||
|
||||
Acronyms MUST be **all caps or all lower**, NEVER mixed. This preserves readability in MixedCaps names.
|
||||
|
||||
```go
|
||||
// Good
|
||||
URL // all caps
|
||||
url // all lower
|
||||
HTTPServer // HTTP is all caps
|
||||
xmlParser // xml is all lower
|
||||
userID // ID is all caps
|
||||
newHTTPSURL // both HTTPS and URL all caps
|
||||
|
||||
// Bad
|
||||
Url // mixed case acronym
|
||||
HttpServer // mixed case
|
||||
userId // mixed case for ID
|
||||
```
|
||||
|
||||
When exporting a lowercase acronym, capitalize the whole thing: `url` → `URL`, `grpc` → `GRPC`, `ios` → `IOS`.
|
||||
@@ -0,0 +1,96 @@
|
||||
# Packages, Files & Import Aliasing
|
||||
|
||||
## Packages
|
||||
|
||||
Package names MUST be **lowercase, single-word**, with no underscores or MixedCaps. They should be short, concise, and evocative of their purpose. Numbers are allowed (`oauth2`, `k8s`).
|
||||
|
||||
```go
|
||||
// Good
|
||||
package json
|
||||
package http
|
||||
package tabwriter
|
||||
package oauth2
|
||||
|
||||
// Bad
|
||||
package httpServer // no MixedCaps
|
||||
package http_server // no underscores
|
||||
package util // too generic
|
||||
package common // meaningless
|
||||
package helpers // what does it help with?
|
||||
package base // says nothing
|
||||
package model // too vague
|
||||
```
|
||||
|
||||
NEVER use generic package names like `util`, `helper`, `common`, `base`, `model`. They fail to communicate purpose and cause import collisions. If you reach for `util`, the function probably belongs in a more specific package.
|
||||
|
||||
Package names SHOULD be **singular**, not plural — `net/url` not `net/urls`, `go/token` not `go/tokens`.
|
||||
|
||||
### Directory vs Package Name
|
||||
|
||||
Directory names SHOULD **match the package name** when possible. Multi-word directories use **hyphens**, but since package names cannot contain hyphens, the package drops them.
|
||||
|
||||
```
|
||||
// Good — directory matches package
|
||||
httputil/ → package httputil
|
||||
middleware/ → package middleware
|
||||
auth/ → package auth
|
||||
|
||||
// Good — hyphenated directory, package drops hyphens
|
||||
user-service/ → package userservice
|
||||
rate-limit/ → package ratelimit
|
||||
go-chi/ → package chi
|
||||
|
||||
// Good — special directories
|
||||
cmd/api/ → package main (cmd/ subdirectories are always main)
|
||||
internal/auth/ → package auth (internal/ restricts visibility)
|
||||
|
||||
// Bad
|
||||
user_service/ → package user_service (underscores in both)
|
||||
UserService/ → package UserService (no MixedCaps in directories)
|
||||
myPackage/ → package mypackage (directory has caps, package doesn't)
|
||||
```
|
||||
|
||||
Special directories have Go toolchain meaning and don't follow normal naming:
|
||||
|
||||
- `cmd/` — entry points, each subdirectory is `package main`
|
||||
- `internal/` — restricts import visibility to parent module
|
||||
- `testdata/` — ignored by the Go tool
|
||||
- `vendor/` — vendored dependencies
|
||||
|
||||
Package names SHOULD NOT duplicate exported names — users see `bufio.Reader`, not `bufio.BufReader`. Think about the call site.
|
||||
|
||||
## Files
|
||||
|
||||
File names MUST be **lowercase** with words separated by **underscores**.
|
||||
|
||||
```
|
||||
user_handler.go
|
||||
string_converter.go
|
||||
http_client_test.go
|
||||
```
|
||||
|
||||
Special suffixes:
|
||||
|
||||
- `_test.go` — test files (excluded from production builds)
|
||||
- `_linux.go`, `_amd64.go` — OS/architecture-specific (build constraints)
|
||||
|
||||
## Import Aliasing
|
||||
|
||||
Import aliases SHOULD only be used on name collision. When an alias is necessary, use a descriptive short name.
|
||||
|
||||
```go
|
||||
// Good — no alias needed
|
||||
import "github.com/go-chi/chi/v5"
|
||||
|
||||
// Good — alias resolves collision
|
||||
import (
|
||||
"crypto/rand"
|
||||
mrand "math/rand"
|
||||
)
|
||||
|
||||
// Good — conventional alias for generated code
|
||||
import pb "myapp/proto/userpb"
|
||||
|
||||
// Bad — unnecessary alias
|
||||
import f "fmt"
|
||||
```
|
||||
@@ -0,0 +1,37 @@
|
||||
# Test Naming
|
||||
|
||||
## Test Functions
|
||||
|
||||
Test functions follow `Test` + the name of what is being tested. Use underscores for subcases.
|
||||
|
||||
```go
|
||||
func TestParseToken(t *testing.T) { ... }
|
||||
func TestServer_Handle(t *testing.T) { ... } // method test
|
||||
func TestParseToken_InvalidInput(t *testing.T) { ... } // subcase
|
||||
```
|
||||
|
||||
## Table-Driven Tests
|
||||
|
||||
Table-driven test case names SHOULD be **fully lowercase, descriptive phrases** — including acronyms. Use `input` for inputs and `expected` for expected outputs to make the data flow clear:
|
||||
|
||||
```go
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
expectedCode int
|
||||
expectedErr bool
|
||||
}{
|
||||
{name: "empty input", input: "", expectedCode: 400, expectedErr: true},
|
||||
{name: "valid token", input: "abc123", expectedCode: 200},
|
||||
{name: "expired token", input: "exp", expectedCode: 401, expectedErr: true},
|
||||
{name: "invalid id", input: "???", expectedCode: 400, expectedErr: true}, // "id" not "ID"
|
||||
}
|
||||
|
||||
// Bad — mixed case in test names
|
||||
{name: "valid ID", ...} // should be "valid id"
|
||||
{name: "Empty Input", ...} // should be "empty input"
|
||||
```
|
||||
|
||||
## Test Helpers
|
||||
|
||||
Test helper functions that panic on failure conventionally use the `must` prefix: `mustLoadFixture()`, `mustParseURL()`.
|
||||
@@ -0,0 +1,159 @@
|
||||
# Types, Constants & Errors
|
||||
|
||||
## Interfaces
|
||||
|
||||
### Single-Method Interfaces
|
||||
|
||||
Name them with the **method name + `-er`** suffix:
|
||||
|
||||
```go
|
||||
type Reader interface {
|
||||
Read(p []byte) (n int, err error)
|
||||
}
|
||||
|
||||
type Stringer interface {
|
||||
String() string
|
||||
}
|
||||
|
||||
type Closer interface {
|
||||
Close() error
|
||||
}
|
||||
```
|
||||
|
||||
### Multi-Method Interfaces
|
||||
|
||||
Use a descriptive **noun** or compose from single-method interfaces:
|
||||
|
||||
```go
|
||||
type ReadWriteCloser interface {
|
||||
Reader
|
||||
Writer
|
||||
Closer
|
||||
}
|
||||
|
||||
type Handler interface {
|
||||
ServeHTTP(ResponseWriter, *Request)
|
||||
}
|
||||
```
|
||||
|
||||
### Canonical Method Names
|
||||
|
||||
Honor established Go method names and their signatures. If your type implements `Read`, it MUST match `io.Reader`'s signature. NEVER invent variations like `ReadData` or `ToString` — use `String`.
|
||||
|
||||
| Method name | Expected interface |
|
||||
| ----------- | ------------------ |
|
||||
| `Read` | `io.Reader` |
|
||||
| `Write` | `io.Writer` |
|
||||
| `Close` | `io.Closer` |
|
||||
| `String` | `fmt.Stringer` |
|
||||
| `Error` | `error` |
|
||||
| `Len` | `sort.Interface` |
|
||||
| `ServeHTTP` | `http.Handler` |
|
||||
|
||||
## Structs
|
||||
|
||||
Name structs with **MixedCaps nouns** describing the entity. Fields follow exported/unexported rules.
|
||||
|
||||
```go
|
||||
type Server struct {
|
||||
Addr string // exported
|
||||
Handler http.Handler // exported
|
||||
timeout time.Duration // unexported
|
||||
}
|
||||
```
|
||||
|
||||
NEVER suffix struct names with `Struct`, `Object`, or `Data` — they add no information.
|
||||
|
||||
## Constants
|
||||
|
||||
Constants MUST use **MixedCaps**, NEVER `ALL_CAPS`. The name should explain the **role**, not the **value**.
|
||||
|
||||
```go
|
||||
// Good — MixedCaps, name explains purpose
|
||||
const MaxRetries = 3
|
||||
const defaultTimeout = 30 * time.Second
|
||||
const DefaultPort = 8080
|
||||
|
||||
// Bad — ALL_CAPS is not idiomatic Go
|
||||
const MAX_RETRIES = 3
|
||||
const DEFAULT_TIMEOUT = 30
|
||||
|
||||
// Bad — name is the value, not the purpose
|
||||
const Three = 3
|
||||
const Port8080 = 8080
|
||||
```
|
||||
|
||||
### Enums (iota)
|
||||
|
||||
Prefix enum values with the **type name** to avoid collisions and improve readability at the call site.
|
||||
|
||||
```go
|
||||
type Status int
|
||||
|
||||
const (
|
||||
StatusUnknown Status = iota // zero value = unknown/invalid
|
||||
StatusReady
|
||||
StatusRunning
|
||||
StatusDone
|
||||
)
|
||||
|
||||
type Color int
|
||||
|
||||
const (
|
||||
ColorRed Color = iota + 1 // skip zero to catch uninitialized values
|
||||
ColorGreen
|
||||
ColorBlue
|
||||
)
|
||||
```
|
||||
|
||||
**Always protect the zero value.** A `var s Status` will silently be 0 — if that maps to a real state like `StatusReady`, code can behave as if a status was deliberately chosen when it wasn't. Either place an explicit `Unknown` sentinel at iota 0, or start at `iota + 1`. This is not optional — uninitialized enums are a common source of silent bugs.
|
||||
|
||||
## Errors
|
||||
|
||||
### Sentinel Errors
|
||||
|
||||
Sentinel error variables use the `Err` prefix. Error strings SHOULD include the package name as prefix to identify the origin when errors are wrapped:
|
||||
|
||||
```go
|
||||
// Good — package prefix identifies origin
|
||||
var ErrNotFound = errors.New("mypackage: not found")
|
||||
var ErrPermissionDenied = errors.New("mypackage: permission denied")
|
||||
var ErrTimeout = errors.New("mypackage: operation timed out")
|
||||
|
||||
// Bad — bare strings lose origin when wrapped
|
||||
var ErrNotFound = errors.New("not found")
|
||||
```
|
||||
|
||||
### Error Types
|
||||
|
||||
Custom error types use the `Error` suffix:
|
||||
|
||||
```go
|
||||
type PathError struct {
|
||||
Op string
|
||||
Path string
|
||||
Err error
|
||||
}
|
||||
|
||||
type SyntaxError struct {
|
||||
Offset int64
|
||||
msg string
|
||||
}
|
||||
```
|
||||
|
||||
### Error Strings
|
||||
|
||||
Error strings MUST be **fully lowercase — including acronyms** — and MUST NOT **end with punctuation**, because they are often printed following other context (`fmt.Errorf("parsing config: %w", err)`). Acronyms that would normally be capitalized in identifiers (`ID`, `URL`, `HTTP`) become lowercase in error strings.
|
||||
|
||||
```go
|
||||
// Good — lowercase including acronyms, no punctuation
|
||||
errors.New("image: unknown format")
|
||||
errors.New("mypackage: invalid message id") // "id" not "ID"
|
||||
errors.New("mypackage: invalid url") // "url" not "URL"
|
||||
fmt.Errorf("decoding config: %w", err)
|
||||
|
||||
// Bad — capitalized, acronyms, punctuation
|
||||
errors.New("Image: Unknown format.")
|
||||
errors.New("mypackage: invalid message ID") // ID should be lowercase in error strings
|
||||
fmt.Errorf("Failed to decode config: %w.", err)
|
||||
```
|
||||
Reference in New Issue
Block a user