166 lines
5.1 KiB
Markdown
166 lines
5.1 KiB
Markdown
# 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`.
|