add some stuff

This commit is contained in:
Jeffrey Paul 2024-06-10 04:21:32 -07:00
parent d11270ff9d
commit e7548045be

244
README.md
View File

@ -15,23 +15,23 @@
## Golang ## Golang
1. Any project that has more than 2 or 3 modules should use the `uber/fx` DI 1. Any project that has more than 2 or 3 modules should use the `uber/fx` DI
framework to keep things tidy. framework to keep things tidy.
2. Don't commit anything that hasn't been `go fmt`'d. The only exception is 2. Don't commit anything that hasn't been `go fmt`'d. The only exception is
when committing things that aren't yet syntactically valid, which should when committing things that aren't yet syntactically valid, which should
only happen pre-v0.0.1 or on a non-main branch. only happen pre-v0.0.1 or on a non-main branch.
3. Even if you are planning to deal with only positive integers, use 3. Even if you are planning to deal with only positive integers, use
`int`/`int64` types instead of `uint`/`uint64` types. This is for `int`/`int64` types instead of `uint`/`uint64` types. This is for
consistency and compatibility with the standard library; it's better consistency and compatibility with the standard library; it's better
than casting all the time. than casting all the time.
4. Try to use zerolog for logging. It's fast and has a nice API. For 4. Try to use zerolog for logging. It's fast and has a nice API. For
smaller/quick projects, the standard library's `log` package (and smaller/quick projects, the standard library's `log` package (and
specifically `log/slog`) is fine. In that case, log structured logs specifically `log/slog`) is fine. In that case, log structured logs
whenever possible, and import `sneak.berlin/go/simplelog` to configure whenever possible, and import `sneak.berlin/go/simplelog` to configure
it appropriately. Example: it appropriately. Example:
```go ```go
package main package main
@ -46,26 +46,26 @@
} }
``` ```
5. Write at least a single test to check compilation. The test file can be 5. Write at least a single test to check compilation. The test file can be
empty, but it should exist. This is to ensure that `go test ./...` will empty, but it should exist. This is to ensure that `go test ./...` will
always function as a syntax check at a minimum. always function as a syntax check at a minimum.
6. For anything beyond a simple script or tool, or anything that is going to 6. For anything beyond a simple script or tool, or anything that is going to
run in any sort of "production" anywhere, make sure it passes run in any sort of "production" anywhere, make sure it passes
`golangci-lint`. `golangci-lint`.
7. Write a `Dockerfile` for every repo, even if it only runs the tests and 7. Write a `Dockerfile` for every repo, even if it only runs the tests and
linting. `docker build .` should always make sure that the code is in an linting. `docker build .` should always make sure that the code is in an
able-to-be-compiled state, linted, and any tests run. The Docker build able-to-be-compiled state, linted, and any tests run. The Docker build
should fail if linting doesn't pass. should fail if linting doesn't pass.
8. Include a `Makefile` with targets for at least `clean` and `test`. If 8. Include a `Makefile` with targets for at least `clean` and `test`. If
there are multiple binaries, include a target for each binary. If there there are multiple binaries, include a target for each binary. If there
are multiple binaries, include a target for `all` that builds all are multiple binaries, include a target for `all` that builds all
binaries. binaries.
9. If you are writing a single-module library, `.go` files are okay in the 9. If you are writing a single-module library, `.go` files are okay in the
repo root. repo root.
10. If you are writing a multi-module project, put all `.go` files in a 10. If you are writing a multi-module project, put all `.go` files in a
`pkg/` or `internal/` directory. This is to keep the root clean and to `pkg/` or `internal/` directory. This is to keep the root clean and to
@ -186,96 +186,97 @@
`io.Reader` instead of `*os.File`. Tailor these to the needs of the `io.Reader` instead of `*os.File`. Tailor these to the needs of the
specific function or method. Examples: specific function or method. Examples:
1. **`io.Reader`** instead of `*os.File`: 1. **`io.Reader`** instead of `*os.File`:
- `io.Reader` is a common interface for reading data, which can be - `io.Reader` is a common interface for reading data, which can be
implemented by many types, including `*os.File`, `bytes.Buffer`, implemented by many types, including `*os.File`, `bytes.Buffer`,
`strings.Reader`, and network connections like `net.Conn`. `strings.Reader`, and network connections like `net.Conn`.
2. **`io.Writer`** instead of `*os.File` or `*bytes.Buffer`: 2. **`io.Writer`** instead of `*os.File` or `*bytes.Buffer`:
- `io.Writer` is used for writing data. It can be implemented by - `io.Writer` is used for writing data. It can be implemented by
`*os.File`, `bytes.Buffer`, `net.Conn`, and more. `*os.File`, `bytes.Buffer`, `net.Conn`, and more.
3. **`io.ReadWriter`** instead of `*os.File`: 3. **`io.ReadWriter`** instead of `*os.File`:
- `io.ReadWriter` combines `io.Reader` and `io.Writer`. It is often - `io.ReadWriter` combines `io.Reader` and `io.Writer`. It is often
used for types that can both read and write, such as `*os.File` used for types that can both read and write, such as `*os.File`
and `net.Conn`. and `net.Conn`.
4. **`io.Closer`** instead of `*os.File` or `*net.Conn`: 4. **`io.Closer`** instead of `*os.File` or `*net.Conn`:
- `io.Closer` is used for types that need to be closed, including - `io.Closer` is used for types that need to be closed, including
`*os.File`, `net.Conn`, and other resources that require cleanup. `*os.File`, `net.Conn`, and other resources that require cleanup.
5. **`io.ReadCloser`** instead of `*os.File` or `http.Response.Body`: 5. **`io.ReadCloser`** instead of `*os.File` or `http.Response.Body`:
- `io.ReadCloser` combines `io.Reader` and `io.Closer`, and is - `io.ReadCloser` combines `io.Reader` and `io.Closer`, and is
commonly used for types like `*os.File` and `http.Response.Body`. commonly used for types like `*os.File` and `http.Response.Body`.
6. **`io.WriteCloser`** instead of `*os.File` or `*gzip.Writer`: 6. **`io.WriteCloser`** instead of `*os.File` or `*gzip.Writer`:
- `io.WriteCloser` combines `io.Writer` and `io.Closer`. It is used - `io.WriteCloser` combines `io.Writer` and `io.Closer`. It is used
for types like `*os.File` and `gzip.Writer`. for types like `*os.File` and `gzip.Writer`.
7. **`io.ReadWriteCloser`** instead of `*os.File` or `*net.TCPConn`: 7. **`io.ReadWriteCloser`** instead of `*os.File` or `*net.TCPConn`:
- `io.ReadWriteCloser` combines `io.Reader`, `io.Writer`, and - `io.ReadWriteCloser` combines `io.Reader`, `io.Writer`, and
`io.Closer`. Examples include `*os.File` and `net.TCPConn`. `io.Closer`. Examples include `*os.File` and `net.TCPConn`.
8. **`fmt.Stringer`** instead of implementing a custom `String` method: 8. **`fmt.Stringer`** instead of implementing a custom `String` method:
- `fmt.Stringer` is an interface for types that can convert - `fmt.Stringer` is an interface for types that can convert
themselves to a string. Any type that implements the `String() themselves to a string. Any type that implements the `String()
string` method satisfies this interface.
9. **`error`** instead of custom error types: string` method satisfies this interface.
- The `error` interface is used for representing errors. Instead of 9. **`error`** instead of custom error types:
defining custom error types, you can use the `errors.New`
function or the `fmt.Errorf` function to create errors.
10. **`net.Conn`** instead of `*net.TCPConn` or `*net.UDPConn`: - The `error` interface is used for representing errors. Instead of
defining custom error types, you can use the `errors.New`
function or the `fmt.Errorf` function to create errors.
- `net.Conn` is a generic network connection interface that can be 10. **`net.Conn`** instead of `*net.TCPConn` or `*net.UDPConn`:
implemented by TCP, UDP, and other types of network connections.
11. **`http.Handler`** instead of custom HTTP handlers: - `net.Conn` is a generic network connection interface that can be
implemented by TCP, UDP, and other types of network connections.
- `http.Handler` is an interface for handling HTTP requests. 11. **`http.Handler`** instead of custom HTTP handlers:
Instead of creating custom handler types, you can use types that
implement the `ServeHTTP(http.ResponseWriter, *http.Request)`
method.
12. **`http.HandlerFunc`** instead of creating a new type: - `http.Handler` is an interface for handling HTTP requests.
Instead of creating custom handler types, you can use types that
implement the `ServeHTTP(http.ResponseWriter, *http.Request)`
method.
- `http.HandlerFunc` is a type that allows you to use functions as 12. **`http.HandlerFunc`** instead of creating a new type:
HTTP handlers by implementing the `http.Handler` interface.
13. **`encoding.BinaryMarshaler` and `encoding.BinaryUnmarshaler`** - `http.HandlerFunc` is a type that allows you to use functions as
instead of custom marshal/unmarshal methods: HTTP handlers by implementing the `http.Handler` interface.
- These interfaces are used for binary serialization and 13. **`encoding.BinaryMarshaler` and `encoding.BinaryUnmarshaler`**
deserialization. Implementing these interfaces allows types to instead of custom marshal/unmarshal methods:
be encoded and decoded in a standard way.
14. **`encoding.TextMarshaler` and `encoding.TextUnmarshaler`** instead - These interfaces are used for binary serialization and
of custom text marshal/unmarshal methods: deserialization. Implementing these interfaces allows types to
be encoded and decoded in a standard way.
- These interfaces are used for text-based serialization and 14. **`encoding.TextMarshaler` and `encoding.TextUnmarshaler`** instead
deserialization. They are useful for types that need to be of custom text marshal/unmarshal methods:
represented as text.
15. **`sort.Interface`** instead of custom sorting logic: - These interfaces are used for text-based serialization and
deserialization. They are useful for types that need to be
represented as text.
- `sort.Interface` is an interface for sorting collections. By 15. **`sort.Interface`** instead of custom sorting logic:
implementing the `Len`, `Less`, and `Swap` methods, you can sort
any collection using the `sort.Sort` function.
16. **`flag.Value`** instead of custom flag parsing: - `sort.Interface` is an interface for sorting collections. By
- `flag.Value` is an interface for defining custom command-line implementing the `Len`, `Less`, and `Swap` methods, you can sort
flags. Implementing the `String` and `Set` methods allows you to any collection using the `sort.Sort` function.
use custom types with the `flag` package.
16. **`flag.Value`** instead of custom flag parsing:
- `flag.Value` is an interface for defining custom command-line
flags. Implementing the `String` and `Set` methods allows you to
use custom types with the `flag` package.
39. Avoid using `panic` in library code. Instead, return errors to allow 39. Avoid using `panic` in library code. Instead, return errors to allow
the caller to handle them. Reserve `panic` for truly exceptional the caller to handle them. Reserve `panic` for truly exceptional
@ -305,19 +306,72 @@ string` method satisfies this interface.
42. Use `iota` to define enumerations in a type-safe way. This ensures that 42. Use `iota` to define enumerations in a type-safe way. This ensures that
the constants are properly grouped and reduces the risk of errors. the constants are properly grouped and reduces the risk of errors.
43. Don't hardcode big lists in your code. Either isolate lists in their own Example:
module/package, or use a third party library. For example, if you need a
list of country codes, you can use ```go
type HandScore int
const (
ScoreHighCard = HandScore(iota * 100_000_000_000)
ScorePair
ScoreTwoPair
ScoreThreeOfAKind
ScoreStraight
ScoreFlush
ScoreFullHouse
ScoreFourOfAKind
ScoreStraightFlush
ScoreRoyalFlush
)
```
Example 2:
```go
type ByteSize float64
const (
_ = iota // ignore first value by assigning to blank identifier
KB ByteSize = 1 << (10 * iota)
MB
GB
TB
PB
EB
ZB
YB
)
```
43. Don't hardcode big lists of things in your normal code. Either isolate
lists in their own module/package and write some getters, or use a third
party library. For example, if you need a list of country codes, you can
use
[https://github.com/emvi/iso-639-1](https://github.com/emvi/iso-639-1). [https://github.com/emvi/iso-639-1](https://github.com/emvi/iso-639-1).
It's okay to embed a data file (use `go embed`) in your binary if you
need to, but make sure you parse it once as a singleton and don't read
it from disk every time you need it. Don't use too much memory for
this, embedding anything more than perhaps 25MiB (uncompressed) is
probably too much. Compress the file before embedding and uncompress
during the reading/parsing step for efficiency.
44. When storing numeric values that represent a number of units, either 44. When storing numeric values that represent a number of units, either
include the unit in the name, or use a type alias, or use a 3p library include the unit in the variable name (e.g. `uptimeSeconds`,
such as `delayMsec`, `coreTemperatureCelsius`), or use a type alias (that
includes the unit name), or use a 3p library such as
[github.com/alecthomas/units](https://github.com/alecthomas/units) for [github.com/alecthomas/units](https://github.com/alecthomas/units) for
SI/IEC byte units, or SI/IEC byte units, or
[github.com/bcicen/go-units](https://github.com/bcicen/go-units) for [github.com/bcicen/go-units](https://github.com/bcicen/go-units) for
temperatures (and others). The type system is your friend, use it. temperatures (and others). The type system is your friend, use it.
45. Once you have a working program, run `go mod tidy` to clean up your
`go.mod` and `go.sum` files. Tag a v0.0.1 or v1.0.0. Push your `main`
branch and tag(s). Subsequent work should happen on branches so that
`main` is "always releasable". "Releasable" in this context means that
it builds and functions as expected, and that all tests and linting
passes.
## Other Golang Tips and Best Practices (Optional) ## Other Golang Tips and Best Practices (Optional)
1. When passing channels to goroutines, use read-only (`<-chan`) or 1. When passing channels to goroutines, use read-only (`<-chan`) or