chore: initial plan
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
# Code Style Details
|
||||
|
||||
## Extract Complex Conditions
|
||||
|
||||
When `if` conditions span multiple operands, extract into named booleans:
|
||||
|
||||
```go
|
||||
// Good — self-documenting
|
||||
isAdmin := user.Role == RoleAdmin
|
||||
isOwner := resource.OwnerID == user.ID
|
||||
hasOverride := permissions.Contains(PermOverride)
|
||||
if isAdmin || isOwner || hasOverride {
|
||||
allow()
|
||||
}
|
||||
|
||||
// Bad — wall of logic
|
||||
if user.Role == RoleAdmin || resource.OwnerID == user.ID || permissions.Contains(PermOverride) {
|
||||
allow()
|
||||
}
|
||||
```
|
||||
|
||||
**Exception:** When the last condition involves expensive processing, keep it inline to benefit from short-circuit evaluation:
|
||||
|
||||
```go
|
||||
// Good — avoid expensive operation when possible
|
||||
if isAdmin || isOwner || expensivePermissionCheck(user, resource) {
|
||||
allow()
|
||||
}
|
||||
|
||||
// Wasteful — always runs expensive check
|
||||
canOverride := expensivePermissionCheck(user, resource)
|
||||
if isAdmin || isOwner || canOverride {
|
||||
allow()
|
||||
}
|
||||
```
|
||||
|
||||
## Value vs Pointer Arguments
|
||||
|
||||
This covers **function parameters**, not method receivers (see `samber/cc-skills-golang@golang-structs-interfaces` skill for receiver rules).
|
||||
|
||||
Pass small, fixed-size types by value — strings are already a (pointer, length) pair internally:
|
||||
|
||||
```go
|
||||
// Good — value types by value
|
||||
func FormatUser(name string, age int, createdAt time.Time) string
|
||||
|
||||
// Good — pointer for mutation
|
||||
func PopulateDefaults(cfg *Config)
|
||||
|
||||
// Good — pointer when nil is meaningful (optional field update)
|
||||
func UpdateUser(ctx context.Context, id string, name *string) error
|
||||
|
||||
// Bad — pointer for no reason
|
||||
func Greet(name *string) string
|
||||
```
|
||||
|
||||
**When to use pointers**:
|
||||
|
||||
- The function **mutates** the value
|
||||
- The struct is **large** (~128+ bytes) — avoids copying overhead
|
||||
- **Nil is meaningful** (optional/nullable parameter)
|
||||
|
||||
**When NOT to use pointers**:
|
||||
|
||||
- `string`, `int`, `bool`, `float64`, `time.Time` — pass by value
|
||||
- Read-only access to small structs — pass by value (better cache locality)
|
||||
- "Just to save memory" — value copy is negligible; stack allocation is fast
|
||||
|
||||
**Memory access trade-offs when strong performance is required**:
|
||||
|
||||
- **Values (no pointer)**: Stack allocation, excellent CPU cache locality for small types, zero indirection cost. Slower only when copying large structs.
|
||||
- **Pointers**: One extra dereference (negligible on modern CPUs), but risk cache misses if pointed-to data isn't in cache. Essential for large structs (>~128 bytes) where copy cost dominates.
|
||||
- **Rule of thumb**: For structs <~128 bytes with read-only access, values are typically faster due to cache locality. For mutation or large structs, pointers win. When in doubt, benchmark.
|
||||
|
||||
-> See the `samber/cc-skills-golang@golang-structs-interfaces` skill for pointer vs value **receiver** rules.
|
||||
Reference in New Issue
Block a user