cmd/furthur | ||
.gitignore | ||
go.mod | ||
readme.md |
Going Further
This project aims to showcase "intermediate" go programming -- once you've learned the language itself, what are the idioms that make for a proficient go programmer?
This is an implementation of a golinks service using only the standard library.
Structure
The go toolchain is very unopinionated about how a project is laid out, except for a few specific folder names and rules:
- packages with
internal/
in the path can only be imported by other packages that they share a "root" with. In go, any package that can be loaded by the compiler can be pulled into another project, even packages that weren't designed to be "libraries" in a traditional sense. Theinternal
convention was introduced as a way to allow the authors of public (or even closed-source) code to truly keep a package internal to its parent module. - folders named
testdata
are ignored by the toolchain, and are used to store exactly that -- files and data that tests are to use as either input or validation.
Outside of that, there are a few conventions that have become relatively common:
- For modules that generate a command-line binary -- that is, those that
have a
main
package -- it's common to place that entrypoint file in a subfolder ofcmd
named the same as the expected binary's name. In this repo, that's/cmd/furthur
. Runninggo build ./cmd/furthur
in this module results in a binary namedfurthur
. - Packages should be named after what they provide, and packages that collect
together a lot of related functionality aren't a negative thing. Package
names like
util
oretc
are signs that your code is poorly laid-out. If you find yourself needing to put a bunch of shared functionality into one utility class, it might be a sign that all of the packages that use that utility might actually be more coherent as one larger package.- See the experience of the stdlib itself with
io/ioutil
, which ended up being entirely deprecated and moved intoio
andos
.
- See the experience of the stdlib itself with
- One pattern that used to be recommended before the advent of
internal
was to explicitly place packages that were "intended" to be public in a folder calledpkg
, but the community was never uniform in that approach, and it is now generally discouraged.
HTTP Services
Generics
Go has generics now, and they're useful... but mostly for authors of libraries,
not for application developers. They have allowed for the standard library
to finally ship a bunch of generic functions in the slices
package that developers used to have to write themselves.
//go:embed
Testing and Benchmarking
Concurrency
It turns out channels are hard to reason about, but that's because concurrency is hard to reason about.
Always remember that read/write access to a shared map must be gated with a mutex.
Go Generate
Build tags
Logging
slog
package
init functions and globals
Don't use them! They're hard to reason about and until recent versions of go the order they ran in was undefined, leading to subtle bugs.
Common tools
go vet
staticcheck
Other common idioms and stumbling blocks
./...
is a common shorthand to pass to parts of the go toolchain that means
"this directory and all directories underneath it". You'll often come across
it when running tests (go test ./...
) or linters (staticcheck ./...
).
When working with closed-source code that is not cached in the public
GOPROXY
, you need to set GOPRIVATE
to match
the name of the module or else you'll get cryptic errors when go tries to build.
This can be set globally for the entire toolchain using go env
:
go env -w GOPRIVATE="https://git.yetaga.in/*"
. See
https://pkg.go.dev/cmd/go#hdr-Configuration_for_downloading_non_public_code
for more details.
Related Resources
- Rob Pike's Go Proverbs. Of particular value:
- The bigger the interface, the weaker the abstraction.
- A little copying is better than a little dependency.
- Clear is better than clever.
- Don't just check errors, handle them gracefully.
- Mat Ryer's How I write HTTP services in Go after 13 years