A styleguide for the go-simpler
organization.
It should be treated as an extension to Effective Go, Go Code Review Comments and Uber Go Style Guide.
Use the var
form when zero value is enough.
var builder strings.Builder // NOT builder := strings.Builder{}
Use the :=
form when initialization is required.
replacer := strings.NewReplacer(...)
This pattern highlights ready-to-use zero values via the Go syntax.
By default, initialize your variables with values, not pointers.
foo := Foo{...} // NOT &Foo{...}
When you need a pointer, explicitly take the address via &
.
// 100+ LoC later...
fn(&foo) // foo is 100% a value, no need to revisit its declaration to clarify.
Thus, there is no need to remember what a variable holds, no matter how big its scope is.
Prefix errors with the name of their package.
It helps to quickly identify the producer of the error.
package foo
var ErrFoo = errors.New("foo: something went wrong")
It's ok to panic if the problem is caused by incorrect usage of the library, not by user input.
For example, strings.NewReplacer
panics on odd number of arguments.
Better to fall faster and louder if we're absolutely sure that the program is not working correctly.
Prefer a separate package for tests.
If you need to test unexported functions, do it in a separate *_internal_test.go
file.
By doing so, you become the first user of your API, dogfooding helps to design it better.
// foo_test.go
package foo_test // NOT foo
import "foo"
func TestFoo(t *testing.T) {
foo.Foo()
}
// foo_internal_test.go
package foo
func Test_foo(t *testing.T) {
foo()
}
Use testing helpers or an assertion package (e.g. go-simpler/assert) when you need to write a lot of tests.
Sticking to the standard library is great but sometimes vanilla
testing
is too verbose.
func TestFoo(t *testing.T) {
// too verbose for big tests.
if got := foo.Foo(); got != want {
t.Errorf("got %v; want %v", got, want)
}
// less LoC, reads better, behaves the same.
got := foo.Foo()
assert.Equal[E](t, got, want)
}
When writing doc comments, do not wrap the text. Instead, prefer one sentence per line.
Git diffs are much smaller this way. See semantic linefeeds for the rational.
Avoid using pkg
.
Use internal
for non-public API.
Treat any non-internal package as importable.
Unlike
pkg
,internal
is officially supported by thego
tool.
Prefer smaller interfaces when possible. Remove unused interface methods on the consumer's side.
A little copying is better than a little dependency.
// producer.go
package producer
type T struct {}
func (T) Foo() {}
func (T) Bar() {}
// consumer.go
package consumer
type I interface {
Foo()
// Bar() is obsolete here.
}
func F(i I) { i.Foo() } // F only uses Foo(), it does not need Bar().