(Some of advices are implemented in go-critic)
interface{} says nothing.Author: Rob Pike See more: https://go-proverbs.github.io/
Author: Dave Cheney See more: https://the-zen-of-go.netlify.com/
go fmt your code.Community uses the official Go format, do not reinvent the wheel.
Try to reduce code entropy. This will help everyone to make code easy to read.
// NOT BAD
if foo() {
// ...
} else if bar == baz {
// ...
} else {
// ...
}
// BETTER
switch {
case foo():
// ...
case bar == baz:
// ...
default:
// ...
}
chan struct{} instead of chan bool.When you see a definition of chan bool in a structure, sometimes it's not that easy to understand how this value will be used, example:
type Service struct {
deleteCh chan bool // what does this bool mean?
}
But we can make it more clear by changing it to chan struct{} which explicitly says: we do not care about value (it's always a struct{}), we care about an event that might occur, example:
type Service struct {
deleteCh chan struct{} // ok, if event than delete something.
}
30 * time.Second instead of time.Duration(30) * time.SecondYou don't need to wrap untyped const in a type, compiler will figure it out. Also prefer to move const to the first place:
// BAD
delay := time.Second * 60 * 24 * 60
// VERY BAD
delay := 60 * time.Second * 60 * 24
// GOOD
delay := 24 * 60 * 60 * time.Second
// EVEN BETTER
delay := 24 * time.Hour
time.Duration instead of int64 + variable name// BAD
var delayMillis int64 = 15000
// GOOD
var delay time.Duration = 15 * time.Second
const declarations by type and var by logic and/or type// BAD
const (
foo = 1
bar = 2
message = "warn message"
)
// MOSTLY BAD
const foo = 1
const bar = 2
const message = "warn message"
// GOOD
const (
foo = 1
bar = 2
)
const message = "warn message"
This pattern works for var too.
Stringer interface for integers const valuesdefer func() {
err := ocp.Close()
if err != nil {
rerr = err
}
}()
checkErr function which panics or does os.Exitpackage main
type Status = int
type Format = int // remove `=` to have type safety
const A Status = 1
const B Format = 1
func main() {
println(A == B)
}
_ = f() to this f()a := []T{}for i := 3; i < 7; i++ {...} prefer for _, c := range a[3:7] {...}func f(a int, _ string) {}
time.Before or time.After. Don't use time.Sub to get a duration and then check its value.ctx namefunc f(a int, b int, s string, p string)
func f(a, b int, s, p string)
var s []int
fmt.Println(s, len(s), cap(s))
if s == nil {
fmt.Println("nil!")
}
// Output:
// [] 0 0
// nil!
var a []string
b := []string{}
fmt.Println(reflect.DeepEqual(a, []string{}))
fmt.Println(reflect.DeepEqual(b, []string{}))
// Output:
// false
// true
<, >, <= and >=value := reflect.ValueOf(object)
kind := value.Kind()
if kind >= reflect.Chan && kind <= reflect.Slice {
// ...
}
%+v to print data with sufficient detailsstruct{}, see issue: https://github.com/golang/go/issues/23440func f1() {
var a, b struct{}
print(&a, "\n", &b, "\n") // Prints same address
fmt.Println(&a == &b) // Comparison returns false
}
func f2() {
var a, b struct{}
fmt.Printf("%p\n%p\n", &a, &b) // Again, same address
fmt.Println(&a == &b) // ...but the comparison returns true
}
fmt.Errorffmt.Errorf("additional message to a given error: %w", err)range in Go:for i := range a and for i, v := range &a doesn't make a copy of afor i, v := range a doesvalue := map["no_key"] will be zero valuevalue, ok := map["no_key"] is much betteros.MkdirAll(root, 0700)os.FileModeiotaconst (
_ = iota
testvar // will be int
)
vs
type myType int
const (
_ myType = iota
testvar // will be myType
)
encoding/gob on structs you don’t own.At some point structure may change and you might miss this. As a result this might cause a hard to find bug.
// BAD
return res, json.Unmarshal(b, &res)
// GOOD
err := json.Unmarshal(b, &res)
return res, err
_ struct{} field:type Point struct {
X, Y float64
_ struct{} // to prevent unkeyed literals
}
For Point{X: 1, Y: 1} everything will be fine, but for Point{1,1} you will get a compile error:
./file.go:1:11: too few values in Point literal
There is a check in go vet command for this, there is no enough arguments to add _ struct{} in all your structs.
func typetype Point struct {
_ [0]func() // unexported, zero-width non-comparable field
X, Y float64
}
http.HandlerFunc over http.HandlerTo use the 1st one you just need a func, for the 2nd you need a type.
defer to the topThis improves code readability and makes clear what will be invoked at the end of a function.
Use json:"id,string" instead.
type Request struct {
ID int64 `json:"id,string"`
}
sync.Onceselect{}, omit channels, waiting for a signalfunc NewSource(seed int64) Source in math/rand is not concurrency-safe. The default lockedSource is concurrency-safe, see issue: https://github.com/golang/go/issues/3611deferdefer r.Body.Close()b := a[:0]
for _, x := range a {
if f(x) {
b = append(b, x)
}
}
_ = b[7]time.Time has pointer field time.Location and this is bad for go GCtime.Time, use timestamp insteadregexp.MustCompile instead of regexp.Compilefunc initfmt.Sprintf in your hot path. It is costly due to maintaining the buffer pool and dynamic dispatches for interfaces.fmt.Sprintf("%s%s", var1, var2), consider simple string concatenation.fmt.Sprintf("%x", var), consider using hex.EncodeToString or strconv.FormatInt(var, 16)io.Copy(ioutil.Discard, resp.Body) if you don't use itres, _ := client.Do(req)
io.Copy(ioutil.Discard, res.Body)
defer res.Body.Close()
ticker := time.NewTicker(1 * time.Second)
defer ticker.Stop()
func (entry Entry) MarshalJSON() ([]byte, error) {
buffer := bytes.NewBufferString("{")
first := true
for key, value := range entry {
jsonValue, err := json.Marshal(value)
if err != nil {
return nil, err
}
if !first {
buffer.WriteString(",")
}
first = false
buffer.WriteString(key + ":" + string(jsonValue))
}
buffer.WriteString("}")
return buffer.Bytes(), nil
}
sync.Map isn't a silver bullet, do not use it without a strong reasonssync.Pool allocates memory// noescape hides a pointer from escape analysis. noescape is
// the identity function but escape analysis doesn't think the
// output depends on the input. noescape is inlined and currently
// compiles down to zero instructions.
func noescape(p unsafe.Pointer) unsafe.Pointer {
x := uintptr(p)
return unsafe.Pointer(x ^ 0)
}
m := (*map[int]int)(atomic.LoadPointer(&ptr))for k := range m {
delete(m, k)
}
m = make(map[int]int)
go.mod (and go.sum) is up to date in CI
https://blog.urth.org/2019/08/13/testing-go-mod-tidiness-in-ci/go build -ldflags="-s -w" ...// +build integration and run them with go test -v --tags integration .CGO_ENABLED=0 go build -ldflags="-s -w" app.go && tar C app | docker import - myimage:latestgo format on CI and compare difftravis 1diff -u <(echo -n) <(gofmt -d .)package_test name for tests, rather than packagego test -short allows to reduce set of tests to be runnedfunc TestSomething(t *testing.T) {
if testing.Short() {
t.Skip("skipping test in short mode.")
}
}
if runtime.GOARM == "arm" {
t.Skip("this doesn't work under ARM")
}
testing.AllocsPerRungo test -test.bench=. -count=20$ claude mcp add go-advice \
-- python -m otcore.mcp_server <graph>