This living document describes some Go design philosophies we endeavor to use/start with when working, building, or writing ipxedust
.
- Prefer easy to understand over easy to do
- First do it, then do it right, then do it better, then make it testable 14
- When you spawn goroutines, make it clear when - or whether - they exit. 2
- Packages that are imported only for their side effects should be avoided 4
- Package level and global variables should be avoided
- Magic is bad; global state is magic → no package level vars; no func init 13
- Engineer with clear and obvious layers of concern and purpose. 16
- External dependencies should be tried and fail fast or just keep trying
- For example, external connections, port binding, environment variables, secrets, etc
- Examples of "failing fast"
- Try external connections immediately
- Binding to ports immediately
- Examples of "keep trying"
- Block ingress traffic or calls until external connections are successful
- Should be accompanied by some way to check health status of external connections
- Block ingress traffic or calls until external connections are successful
- Make all dependencies explicit 11
- Naming general rules 12
- Structs are plain nouns: API, Replica, Object
- Interfaces are active nouns: Reader, Writer, JobProcessor
- Functions and methods are verbs: Read, Process, Sync
- Package names 15
- Short: no more than one word
- No plural
- Lower case
- Informative about the service it provides
- Avoid packages named utility/utilities or model/models
- Avoid renaming imports except to avoid a name collision; good package names should not require renaming 3
- Accept interfaces, return structs 5
- Small interfaces are better 6
- Define an interface when you actually need it, not when you foresee needing it 7
- Interfaces 15
- Use interfaces as function/method arguments & as field types
- Small interfaces are better
- All top-level, exported names should have doc comments, as should non-trivial unexported type or function declarations. 1
- Methods/functions 15
- One function has one goal
- Simple names
- Reduce the number of nesting levels
- Only func main has the right to decide which flags, env variables, config files are available to the user 10a,10b
context.Context
should, in most cases, be the first argument of all functions or methods- Prefer synchronous functions - functions which return their results directly or finish any callbacks or channel ops before returning - over asynchronous ones. 8
- Error Handling 15
- Func
main
should normally be the only one calling fatal errors oros.Exit
- Func
- One file should be named like the package 9
- One file = One responsibility 9
- If you only build one binary prefer a top level
main.go
, if you have more than one binary put the code in acmd/
package