2024-06-10 11:01:47 +00:00
|
|
|
# My Code Styleguide
|
|
|
|
|
|
|
|
## Python
|
|
|
|
|
|
|
|
1. Format all code with `black`.
|
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
2. Put all code in functions. If you are writing a script, put the script
|
|
|
|
in a function called `main` and call `main()` at the end of the script
|
|
|
|
using the standard invocation:
|
2024-06-10 11:01:47 +00:00
|
|
|
|
|
|
|
```python
|
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|
|
|
|
```
|
|
|
|
|
|
|
|
## Golang
|
|
|
|
|
2024-06-10 11:21:32 +00:00
|
|
|
1. Any project that has more than 2 or 3 modules should use the `uber/fx` DI
|
|
|
|
framework to keep things tidy.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:21:32 +00:00
|
|
|
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
|
|
|
|
only happen pre-v0.0.1 or on a non-main branch.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:21:32 +00:00
|
|
|
3. Even if you are planning to deal with only positive integers, use
|
|
|
|
`int`/`int64` types instead of `uint`/`uint64` types. This is for
|
|
|
|
consistency and compatibility with the standard library; it's better
|
|
|
|
than casting all the time.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:21:32 +00:00
|
|
|
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
|
|
|
|
specifically `log/slog`) is fine. In that case, log structured logs
|
|
|
|
whenever possible, and import `sneak.berlin/go/simplelog` to configure
|
|
|
|
it appropriately. Example:
|
2024-06-10 11:01:47 +00:00
|
|
|
|
|
|
|
```go
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"log/slog"
|
|
|
|
_ "sneak.berlin/go/simplelog"
|
|
|
|
)
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
slog.Info("Starting up")
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2024-06-10 11:21:32 +00:00
|
|
|
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
|
|
|
|
always function as a syntax check at a minimum.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:21:32 +00:00
|
|
|
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
|
|
|
|
`golangci-lint`.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:21:32 +00:00
|
|
|
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
|
|
|
|
able-to-be-compiled state, linted, and any tests run. The Docker build
|
|
|
|
should fail if linting doesn't pass.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:21:32 +00:00
|
|
|
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
|
|
|
|
are multiple binaries, include a target for `all` that builds all
|
|
|
|
binaries.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:21:32 +00:00
|
|
|
9. If you are writing a single-module library, `.go` files are okay in the
|
|
|
|
repo root.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
10. If you are writing a multi-module project, put all `.go` files in a
|
2024-06-10 11:01:47 +00:00
|
|
|
`pkg/` or `internal/` directory. This is to keep the root clean and to
|
|
|
|
make it easier to see what is a library and what is a binary.
|
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
11. Binaries go in `cmd/` directories. Each binary should have its own
|
2024-06-10 11:01:47 +00:00
|
|
|
directory. This is to keep the root clean and to make it easier to see
|
|
|
|
what is a library and what is a binary. Only package `main` files
|
|
|
|
should be in `cmd/*` directories.
|
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
12. Keep the `main()` function as small as possible.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
13. Keep the `main` package as small as possible. Move as much code as is
|
2024-06-10 11:01:47 +00:00
|
|
|
feasible to a library package.
|
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
14. HTTP HandleFuncs should be returned from methods or functions that need
|
2024-06-10 11:01:47 +00:00
|
|
|
to handle HTTP requests. Don't use methods or our top level functions
|
|
|
|
as handlers.
|
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
15. Provide a .gitignore file that ignores at least `*.log`, `*.out`, and
|
2024-06-10 11:01:47 +00:00
|
|
|
`*.test` files, as well as any binaries.
|
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
16. Constructors should be called `New()` whenever possible.
|
2024-06-10 11:01:47 +00:00
|
|
|
`modulename.New()` works great if you name the packages properly.
|
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
17. Don't make packages too big. Break them up.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
18. Don't make functions or methods too big. Break them up.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
19. Use descriptive names for functions and methods. Don't be afraid to
|
|
|
|
make them a bit long.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
20. Use descriptive names for modules and filenames. Avoid generic names
|
2024-06-10 11:01:47 +00:00
|
|
|
like `server`. `util` is banned.
|
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
21. Constructors should take a Params struct if they need more than 1-2
|
2024-06-10 11:01:47 +00:00
|
|
|
arguments. Positional arguments are an endless source of bugs and
|
|
|
|
should be avoided whenever possible.
|
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
22. Use `context.Context` for all functions that need it. If you don't need
|
2024-06-10 11:01:47 +00:00
|
|
|
it, you can pass `context.Background()`. Anything long-running should
|
|
|
|
get and abide by a Context. A context does not count against your
|
|
|
|
number of function or method arguments for purposes of calculating
|
|
|
|
whether or not you need a Params struct, because the `ctx` is always
|
|
|
|
first.
|
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
23. Contexts are always named `ctx`.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
24. Use `context.WithTimeout` or `context.WithDeadline` for any function
|
2024-06-10 11:01:47 +00:00
|
|
|
that could potentially run for a long time. This is especially true for
|
|
|
|
any function that makes a network call. Sane timeouts are essential.
|
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
25. Avoid global state, especially global variables. If you need to store
|
2024-06-10 11:01:47 +00:00
|
|
|
state that is global to your launch or application instance, use a
|
|
|
|
package `globals` or `appstate` with a struct and a constructor and
|
|
|
|
require it as a dependency in your constructors. This will allow
|
|
|
|
consumers to be more easily testable and will make it easier to reason
|
|
|
|
about the state of your application. Alternately, if your dependency
|
|
|
|
graph allows for it, put it in the main struct/object of your
|
|
|
|
application, but remember that this harms testability.
|
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
26. Package-global "variables" are ok if they are constants, such as static
|
2024-06-10 11:01:47 +00:00
|
|
|
strings or integers or errors.
|
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
27. Whenever possible, avoid hardcoding numbers or values in your code. Use
|
2024-06-10 11:01:47 +00:00
|
|
|
descriptively-named constants instead. Recall the famous SICP quote:
|
|
|
|
"Programs must be written for people to read, and only incidentally for
|
|
|
|
machines to execute." Rather than comments, a descriptive constant name
|
|
|
|
is much cleaner.
|
|
|
|
|
|
|
|
Example:
|
|
|
|
|
|
|
|
```go
|
|
|
|
|
|
|
|
const jsonContentType = "application/json; charset=utf-8"
|
|
|
|
|
|
|
|
func (s *Handlers) respondJSON(w http.ResponseWriter, r *http.Request, data interface{}, status int) {
|
|
|
|
w.WriteHeader(status)
|
|
|
|
w.Header().Set("Content-Type", jsonContentType)
|
|
|
|
...
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
28. Define your struct types near their constructors.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
29. Define your interface types near the functions that use them, or if you
|
2024-06-10 11:01:47 +00:00
|
|
|
have multiple conformant types, put the interface(s) in their own file.
|
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
30. Define errors as package-level variables. Use a descriptive name for the
|
2024-06-10 11:01:47 +00:00
|
|
|
error. Use `errors.New` to create the error. If you need to include
|
|
|
|
additional information in the error, use a struct that implements the
|
|
|
|
`error` interface.
|
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
31. Use lowerCamelCase for local function/variable names. Use UpperCamelCase
|
|
|
|
for type names, and exported function/variable names. Use snake_case for
|
|
|
|
JSON keys. Use lowercase for filenames.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
32. Explicitly specify UTC for datetimes unless you have a very good reason
|
2024-06-10 11:01:47 +00:00
|
|
|
not to. Use `time.Now().UTC()` to get the current time in UTC.
|
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
33. String dates should always be ISO8601 formatted. Use `time.Time.Format`
|
2024-06-10 11:01:47 +00:00
|
|
|
with `time.RFC3339` to get the correct format.
|
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
34. Use `time.Time` for all date and time values. Do not use `int64` or
|
2024-06-10 11:01:47 +00:00
|
|
|
`string` for dates or times internally.
|
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
35. When using `time.Time` in a struct, use a pointer to `time.Time` so that
|
2024-06-10 11:01:47 +00:00
|
|
|
you can differentiate between a zero value and a null value.
|
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
36. Use `time.Duration` for all time durations. Do not use `int64` or
|
2024-06-10 11:01:47 +00:00
|
|
|
`string` for durations internally.
|
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
37. When using `time.Duration` in a struct, use a pointer to `time.Duration`
|
2024-06-10 11:01:47 +00:00
|
|
|
so that you can differentiate between a zero value and a null value.
|
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
38. Whenever possible, in argument types and return types, try to use
|
2024-06-10 11:01:47 +00:00
|
|
|
standard library interfaces instead of concrete types. For example, use
|
|
|
|
`io.Reader` instead of `*os.File`. Tailor these to the needs of the
|
|
|
|
specific function or method. Examples:
|
|
|
|
|
2024-06-10 11:21:32 +00:00
|
|
|
1. **`io.Reader`** instead of `*os.File`:
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:21:32 +00:00
|
|
|
- `io.Reader` is a common interface for reading data, which can be
|
|
|
|
implemented by many types, including `*os.File`, `bytes.Buffer`,
|
|
|
|
`strings.Reader`, and network connections like `net.Conn`.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:21:32 +00:00
|
|
|
2. **`io.Writer`** instead of `*os.File` or `*bytes.Buffer`:
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:21:32 +00:00
|
|
|
- `io.Writer` is used for writing data. It can be implemented by
|
|
|
|
`*os.File`, `bytes.Buffer`, `net.Conn`, and more.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:21:32 +00:00
|
|
|
3. **`io.ReadWriter`** instead of `*os.File`:
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:21:32 +00:00
|
|
|
- `io.ReadWriter` combines `io.Reader` and `io.Writer`. It is often
|
|
|
|
used for types that can both read and write, such as `*os.File`
|
|
|
|
and `net.Conn`.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:21:32 +00:00
|
|
|
4. **`io.Closer`** instead of `*os.File` or `*net.Conn`:
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:21:32 +00:00
|
|
|
- `io.Closer` is used for types that need to be closed, including
|
|
|
|
`*os.File`, `net.Conn`, and other resources that require cleanup.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:21:32 +00:00
|
|
|
5. **`io.ReadCloser`** instead of `*os.File` or `http.Response.Body`:
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:21:32 +00:00
|
|
|
- `io.ReadCloser` combines `io.Reader` and `io.Closer`, and is
|
|
|
|
commonly used for types like `*os.File` and `http.Response.Body`.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:21:32 +00:00
|
|
|
6. **`io.WriteCloser`** instead of `*os.File` or `*gzip.Writer`:
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:21:32 +00:00
|
|
|
- `io.WriteCloser` combines `io.Writer` and `io.Closer`. It is used
|
|
|
|
for types like `*os.File` and `gzip.Writer`.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:21:32 +00:00
|
|
|
7. **`io.ReadWriteCloser`** instead of `*os.File` or `*net.TCPConn`:
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:21:32 +00:00
|
|
|
- `io.ReadWriteCloser` combines `io.Reader`, `io.Writer`, and
|
|
|
|
`io.Closer`. Examples include `*os.File` and `net.TCPConn`.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:21:32 +00:00
|
|
|
8. **`fmt.Stringer`** instead of implementing a custom `String` method:
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:21:32 +00:00
|
|
|
- `fmt.Stringer` is an interface for types that can convert
|
|
|
|
themselves to a string. Any type that implements the `String()
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:21:32 +00:00
|
|
|
string` method satisfies this interface.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:21:32 +00:00
|
|
|
9. **`error`** instead of custom error types:
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:21:32 +00:00
|
|
|
- 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.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:21:32 +00:00
|
|
|
10. **`net.Conn`** instead of `*net.TCPConn` or `*net.UDPConn`:
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:21:32 +00:00
|
|
|
- `net.Conn` is a generic network connection interface that can be
|
|
|
|
implemented by TCP, UDP, and other types of network connections.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:21:32 +00:00
|
|
|
11. **`http.Handler`** instead of custom HTTP handlers:
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:21:32 +00:00
|
|
|
- `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.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:21:32 +00:00
|
|
|
12. **`http.HandlerFunc`** instead of creating a new type:
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:21:32 +00:00
|
|
|
- `http.HandlerFunc` is a type that allows you to use functions as
|
|
|
|
HTTP handlers by implementing the `http.Handler` interface.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:21:32 +00:00
|
|
|
13. **`encoding.BinaryMarshaler` and `encoding.BinaryUnmarshaler`**
|
|
|
|
instead of custom marshal/unmarshal methods:
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:21:32 +00:00
|
|
|
- These interfaces are used for binary serialization and
|
|
|
|
deserialization. Implementing these interfaces allows types to
|
|
|
|
be encoded and decoded in a standard way.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:21:32 +00:00
|
|
|
14. **`encoding.TextMarshaler` and `encoding.TextUnmarshaler`** instead
|
|
|
|
of custom text marshal/unmarshal methods:
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:21:32 +00:00
|
|
|
- These interfaces are used for text-based serialization and
|
|
|
|
deserialization. They are useful for types that need to be
|
|
|
|
represented as text.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:21:32 +00:00
|
|
|
15. **`sort.Interface`** instead of custom sorting logic:
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:21:32 +00:00
|
|
|
- `sort.Interface` is an interface for sorting collections. By
|
|
|
|
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:
|
|
|
|
- `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.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
39. Avoid using `panic` in library code. Instead, return errors to allow
|
2024-06-10 11:01:47 +00:00
|
|
|
the caller to handle them. Reserve `panic` for truly exceptional
|
|
|
|
conditions.
|
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
40. Use `defer` to ensure resources are properly cleaned up, such as
|
|
|
|
closing files or network connections. Place `defer` statements
|
|
|
|
immediately after resource acquisition.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
41. When calling a function with `go`, wrap the function call in an
|
2024-06-10 11:01:47 +00:00
|
|
|
anonymous function to ensure it runs in the new goroutine context:
|
|
|
|
|
|
|
|
Right:
|
|
|
|
|
|
|
|
```go
|
|
|
|
go func() {
|
|
|
|
someFunction(arg1, arg2)
|
|
|
|
}()
|
|
|
|
```
|
|
|
|
|
|
|
|
Wrong:
|
|
|
|
|
|
|
|
```go
|
|
|
|
go someFunction(arg1, arg2)
|
|
|
|
```
|
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
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.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:21:32 +00:00
|
|
|
Example:
|
|
|
|
|
|
|
|
```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
|
2024-06-10 11:06:57 +00:00
|
|
|
[https://github.com/emvi/iso-639-1](https://github.com/emvi/iso-639-1).
|
2024-06-10 11:21:32 +00:00
|
|
|
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.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
44. When storing numeric values that represent a number of units, either
|
2024-06-10 11:21:32 +00:00
|
|
|
include the unit in the variable name (e.g. `uptimeSeconds`,
|
|
|
|
`delayMsec`, `coreTemperatureCelsius`), or use a type alias (that
|
|
|
|
includes the unit name), or use a 3p library such as
|
2024-06-10 11:06:57 +00:00
|
|
|
[github.com/alecthomas/units](https://github.com/alecthomas/units) for
|
|
|
|
SI/IEC byte units, or
|
|
|
|
[github.com/bcicen/go-units](https://github.com/bcicen/go-units) for
|
|
|
|
temperatures (and others). The type system is your friend, use it.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:21:32 +00:00
|
|
|
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.
|
|
|
|
|
2024-06-10 11:01:47 +00:00
|
|
|
## Other Golang Tips and Best Practices (Optional)
|
|
|
|
|
|
|
|
1. When passing channels to goroutines, use read-only (`<-chan`) or
|
|
|
|
write-only (`chan<-`) channels to communicate the direction of data flow
|
|
|
|
clearly.
|
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
2. Use `io.MultiReader` to concatenate multiple readers and
|
2024-06-10 11:01:47 +00:00
|
|
|
`io.MultiWriter` to duplicate writes to multiple writers. This can
|
|
|
|
simplify the handling of multiple data sources or destinations.
|
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
3. For simple counters and flags, use the `sync/atomic` package to avoid
|
|
|
|
the overhead of mutexes.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
4. When using mutexes, minimize the scope of locking to reduce contention
|
2024-06-10 11:01:47 +00:00
|
|
|
and potential deadlocks. Prefer to lock only the critical sections of
|
|
|
|
code. Try to encapsulate the critical section in its own function or
|
|
|
|
method. Acquire the lock as the first line of the function, defer
|
|
|
|
release of the lock as the second line of the function, and lines 3-5
|
|
|
|
should perform the task. Try to keep it as short as possible. Avoid
|
|
|
|
using mutexes in the middle of a function. In short, build atomic
|
|
|
|
functions.
|
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
5. Design types to be immutable where possible. This can help avoid issues
|
2024-06-10 11:01:47 +00:00
|
|
|
with concurrent access and make the code easier to reason about.
|
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
6. Global state can lead to unpredictable behavior and makes the code
|
2024-06-10 11:01:47 +00:00
|
|
|
harder to test. Use dependency injection to manage state.
|
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
7. Avoid using `init` functions unless absolutely necessary as they can
|
2024-06-10 11:01:47 +00:00
|
|
|
lead to unpredictable initialization order and make the code harder to
|
|
|
|
understand.
|
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
8. Provide comments for all public interfaces explaining what they do and
|
2024-06-10 11:01:47 +00:00
|
|
|
how they should be used. This helps other developers understand the
|
|
|
|
intended use.
|
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
9. Be mindful of resource leaks when using `time.Timer` and `time.Ticker`.
|
2024-06-10 11:01:47 +00:00
|
|
|
Always stop them when they are no longer needed.
|
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
10. Use `sync.Pool` to manage a pool of reusable objects, which can help
|
|
|
|
reduce GC overhead and improve performance in high-throughput scenarios.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
11. Avoid using large buffer sizes for channels. Unbounded channels can
|
|
|
|
lead to memory leaks. Use appropriate buffer sizes based on the
|
|
|
|
application's needs.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
12. Always handle the case where a channel might be closed. This prevents
|
|
|
|
panic and ensures graceful shutdowns.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
13. For small structs, use value receivers to avoid unnecessary heap
|
|
|
|
allocations. Use pointer receivers for large structs or when mutating
|
|
|
|
the receiver.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
14. Only use goroutines when necessary. Excessive goroutines can lead to
|
|
|
|
high memory consumption and increased complexity.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
15. Use `sync.Cond` for more complex synchronization needs that cannot be
|
|
|
|
met with simple mutexes and channels.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
16. Reflection is powerful but should be used sparingly as it can lead to
|
|
|
|
code that is hard to understand and maintain. Prefer type-safe
|
|
|
|
solutions.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
17. Avoid storing large or complex data in context. Context should be used
|
|
|
|
for request-scoped values like deadlines, cancellation signals, and
|
|
|
|
authentication tokens.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
18. Use `runtime.Callers` and `runtime.CallersFrames` to capture stack
|
|
|
|
traces for debugging and logging purposes.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
19. Use the `testing.TB` interface to write helper functions that can be
|
|
|
|
used with both `*testing.T` and `*testing.B`.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
20. Use struct embedding to reuse code across multiple structs. This is a
|
|
|
|
form of composition that can simplify code reuse.
|
2024-06-10 11:01:47 +00:00
|
|
|
|
2024-06-10 11:06:57 +00:00
|
|
|
21. Prefer defining explicit interfaces in your packages rather than
|
|
|
|
relying on implicit interfaces. This makes the intended use of
|
|
|
|
interfaces clearer and the code more maintainable.
|
|
|
|
|
|
|
|
# Author
|
|
|
|
|
|
|
|
[@sneak](https://sneak.berlin) <[sneak@sneak.berlin](mailto:sneak@sneak.berlin)>
|
|
|
|
|
|
|
|
# License
|
|
|
|
|
|
|
|
[WTFPL](./LICENSE)
|
|
|
|
|
|
|
|
Do with it what you will. There is no warranty, express or implied,
|
|
|
|
including but not limited to merchantability or fitness for a particular
|
|
|
|
purpose. Use at your own risk.
|