initial
This commit is contained in:
		
						commit
						9a396ff8d9
					
				
							
								
								
									
										399
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										399
									
								
								README.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,399 @@
 | 
				
			|||||||
 | 
					# My Code Styleguide
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Python
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. Format all code with `black`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. 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:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ```python
 | 
				
			||||||
 | 
					    if __name__ == "__main__":
 | 
				
			||||||
 | 
					        main()
 | 
				
			||||||
 | 
					    ```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## Golang
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  Any project that has more than 2 or 3 modules should use the `uber/fx` DI
 | 
				
			||||||
 | 
					    framework to keep things tidy.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  Obviously, 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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  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:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ```go
 | 
				
			||||||
 | 
					    package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    import (
 | 
				
			||||||
 | 
					        "log/slog"
 | 
				
			||||||
 | 
					        _ "sneak.berlin/go/simplelog"
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    func main() {
 | 
				
			||||||
 | 
					        slog.Info("Starting up")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    ```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  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`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  If you are writing a single-module library, `.go` files are okay in the
 | 
				
			||||||
 | 
					    repo root.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  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
 | 
				
			||||||
 | 
					    make it easier to see what is a library and what is a binary.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  Binaries go in `cmd/` directories. Each binary should have its own
 | 
				
			||||||
 | 
					    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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  Keep the `main()` function as small as possible.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  Keep the `main` package as small as possible. Move as much code as is
 | 
				
			||||||
 | 
					    feasible to a library package.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  HTTP HandleFuncs should be returned from methods or functions that need
 | 
				
			||||||
 | 
					    to handle HTTP requests. Don't use methods or our top level functions
 | 
				
			||||||
 | 
					    as handlers.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  Provide a .gitignore file that ignores at least `*.log`, `*.out`, and
 | 
				
			||||||
 | 
					    `*.test` files, as well as any binaries.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  Constructors should be called `New()` whenever possible.
 | 
				
			||||||
 | 
					    `modulename.New()` works great if you name the packages properly.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  Don't make packages too big. Break them up.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  Don't make functions or methods too big. Break them up.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  Use descriptive names for functions and methods. Don't be afraid to make
 | 
				
			||||||
 | 
					    them a bit long.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  Use descriptive names for modules and filenames. Avoid generic names
 | 
				
			||||||
 | 
					    like `server`. `util` is banned.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  Constructors should take a Params struct if they need more than 1-2
 | 
				
			||||||
 | 
					    arguments. Positional arguments are an endless source of bugs and
 | 
				
			||||||
 | 
					    should be avoided whenever possible.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  Use `context.Context` for all functions that need it. If you don't need
 | 
				
			||||||
 | 
					    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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  Contexts are always named `ctx`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  Use `context.WithTimeout` or `context.WithDeadline` for any function
 | 
				
			||||||
 | 
					    that could potentially run for a long time. This is especially true for
 | 
				
			||||||
 | 
					    any function that makes a network call. Sane timeouts are essential.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  Avoid global state, especially global variables. If you need to store
 | 
				
			||||||
 | 
					    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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  Package-global "variables" are ok if they are constants, such as static
 | 
				
			||||||
 | 
					    strings or integers or errors.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  Whenever possible, avoid hardcoding numbers or values in your code. Use
 | 
				
			||||||
 | 
					    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)
 | 
				
			||||||
 | 
					         ...
 | 
				
			||||||
 | 
					     }
 | 
				
			||||||
 | 
					    ```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  Define your struct types near their constructors.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  Define your interface types near the functions that use them, or if you
 | 
				
			||||||
 | 
					    have multiple conformant types, put the interface(s) in their own file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  Define errors as package-level variables. Use a descriptive name for the
 | 
				
			||||||
 | 
					    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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  Explicitly specify UTC for datetimes unless you have a very good reason
 | 
				
			||||||
 | 
					    not to. Use `time.Now().UTC()` to get the current time in UTC.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  String dates should always be ISO8601 formatted. Use `time.Time.Format`
 | 
				
			||||||
 | 
					    with `time.RFC3339` to get the correct format.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  Use `time.Time` for all date and time values. Do not use `int64` or
 | 
				
			||||||
 | 
					    `string` for dates or times internally.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  When using `time.Time` in a struct, use a pointer to `time.Time` so that
 | 
				
			||||||
 | 
					    you can differentiate between a zero value and a null value.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  Use `time.Duration` for all time durations. Do not use `int64` or
 | 
				
			||||||
 | 
					    `string` for durations internally.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  When using `time.Duration` in a struct, use a pointer to `time.Duration`
 | 
				
			||||||
 | 
					    so that you can differentiate between a zero value and a null value.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  Whenever possible, in argument types and return types, try to use
 | 
				
			||||||
 | 
					    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:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					         1. **`io.Reader`** instead of `*os.File`:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					             - `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`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					         2. **`io.Writer`** instead of `*os.File` or `*bytes.Buffer`:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					             - `io.Writer` is used for writing data. It can be implemented by
 | 
				
			||||||
 | 
					               `*os.File`, `bytes.Buffer`, `net.Conn`, and more.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					         3. **`io.ReadWriter`** instead of `*os.File`:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					             - `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`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					         4. **`io.Closer`** instead of `*os.File` or `*net.Conn`:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					             - `io.Closer` is used for types that need to be closed, including
 | 
				
			||||||
 | 
					               `*os.File`, `net.Conn`, and other resources that require cleanup.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					         5. **`io.ReadCloser`** instead of `*os.File` or `http.Response.Body`:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					             - `io.ReadCloser` combines `io.Reader` and `io.Closer`, and is
 | 
				
			||||||
 | 
					               commonly used for types like `*os.File` and `http.Response.Body`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					         6. **`io.WriteCloser`** instead of `*os.File` or `*gzip.Writer`:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					             - `io.WriteCloser` combines `io.Writer` and `io.Closer`. It is used
 | 
				
			||||||
 | 
					               for types like `*os.File` and `gzip.Writer`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					         7. **`io.ReadWriteCloser`** instead of `*os.File` or `*net.TCPConn`:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					             - `io.ReadWriteCloser` combines `io.Reader`, `io.Writer`, and
 | 
				
			||||||
 | 
					               `io.Closer`. Examples include `*os.File` and `net.TCPConn`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					         8. **`fmt.Stringer`** instead of implementing a custom `String` method:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					             - `fmt.Stringer` is an interface for types that can convert
 | 
				
			||||||
 | 
					               themselves to a string. Any type that implements the `String()
 | 
				
			||||||
 | 
					               string` method satisfies this interface.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					         9. **`error`** instead of custom error types:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					             - 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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					         10. **`net.Conn`** instead of `*net.TCPConn` or `*net.UDPConn`:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					             - `net.Conn` is a generic network connection interface that can be
 | 
				
			||||||
 | 
					               implemented by TCP, UDP, and other types of network connections.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					         11. **`http.Handler`** instead of custom HTTP handlers:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					             - `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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					         12. **`http.HandlerFunc`** instead of creating a new type:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					             - `http.HandlerFunc` is a type that allows you to use functions as
 | 
				
			||||||
 | 
					               HTTP handlers by implementing the `http.Handler` interface.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					         13. **`encoding.BinaryMarshaler` and `encoding.BinaryUnmarshaler`**
 | 
				
			||||||
 | 
					             instead of custom marshal/unmarshal methods:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					             - These interfaces are used for binary serialization and
 | 
				
			||||||
 | 
					               deserialization. Implementing these interfaces allows types to
 | 
				
			||||||
 | 
					               be encoded and decoded in a standard way.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					         14. **`encoding.TextMarshaler` and `encoding.TextUnmarshaler`** instead
 | 
				
			||||||
 | 
					             of custom text marshal/unmarshal methods:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					             - These interfaces are used for text-based serialization and
 | 
				
			||||||
 | 
					               deserialization. They are useful for types that need to be
 | 
				
			||||||
 | 
					               represented as text.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					         15. **`sort.Interface`** instead of custom sorting logic:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					             - `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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  Avoid using `panic` in library code. Instead, return errors to allow
 | 
				
			||||||
 | 
					    the caller to handle them. Reserve `panic` for truly exceptional
 | 
				
			||||||
 | 
					    conditions.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  Use `defer` to ensure resources are properly cleaned up, such as closing
 | 
				
			||||||
 | 
					    files or network connections. Place `defer` statements immediately after
 | 
				
			||||||
 | 
					    resource acquisition.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1.  When calling a function with `go`, wrap the function call in an
 | 
				
			||||||
 | 
					    anonymous function to ensure it runs in the new goroutine context:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Right:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ```go
 | 
				
			||||||
 | 
					    go func() {
 | 
				
			||||||
 | 
					        someFunction(arg1, arg2)
 | 
				
			||||||
 | 
					    }()
 | 
				
			||||||
 | 
					    ```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Wrong:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ```go
 | 
				
			||||||
 | 
					    go someFunction(arg1, arg2)
 | 
				
			||||||
 | 
					    ```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. Use `iota` to define enumerations in a type-safe way. This ensures that
 | 
				
			||||||
 | 
					   the constants are properly grouped and reduces the risk of errors.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. Don't hardcode big lists in your code.  Either isolate lists in their own
 | 
				
			||||||
 | 
					   module/package, 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).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. 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
 | 
				
			||||||
 | 
					   such as
 | 
				
			||||||
 | 
					   [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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## 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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. Use `io.MultiReader` to concatenate multiple readers and
 | 
				
			||||||
 | 
					   `io.MultiWriter` to duplicate writes to multiple writers. This can
 | 
				
			||||||
 | 
					   simplify the handling of multiple data sources or destinations.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. For simple counters and flags, use the `sync/atomic` package to avoid the
 | 
				
			||||||
 | 
					   overhead of mutexes.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. When using mutexes, minimize the scope of locking to reduce contention
 | 
				
			||||||
 | 
					   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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. Design types to be immutable where possible. This can help avoid issues
 | 
				
			||||||
 | 
					   with concurrent access and make the code easier to reason about.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. Global state can lead to unpredictable behavior and makes the code
 | 
				
			||||||
 | 
					   harder to test. Use dependency injection to manage state.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. Avoid using `init` functions unless absolutely necessary as they can
 | 
				
			||||||
 | 
					   lead to unpredictable initialization order and make the code harder to
 | 
				
			||||||
 | 
					   understand.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. Provide comments for all public interfaces explaining what they do and
 | 
				
			||||||
 | 
					   how they should be used. This helps other developers understand the
 | 
				
			||||||
 | 
					   intended use.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. Be mindful of resource leaks when using `time.Timer` and `time.Ticker`.
 | 
				
			||||||
 | 
					   Always stop them when they are no longer needed.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. Use `sync.Pool` to manage a pool of reusable objects, which can help
 | 
				
			||||||
 | 
					   reduce GC overhead and improve performance in high-throughput scenarios.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. Avoid using large buffer sizes for channels. Unbounded channels can lead
 | 
				
			||||||
 | 
					   to memory leaks. Use appropriate buffer sizes based on the application's
 | 
				
			||||||
 | 
					   needs.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. Always handle the case where a channel might be closed. This prevents
 | 
				
			||||||
 | 
					   panic and ensures graceful shutdowns.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. For small structs, use value receivers to avoid unnecessary heap
 | 
				
			||||||
 | 
					   allocations. Use pointer receivers for large structs or when mutating
 | 
				
			||||||
 | 
					   the receiver.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. Only use goroutines when necessary. Excessive goroutines can lead to high
 | 
				
			||||||
 | 
					   memory consumption and increased complexity.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. Use `sync.Cond` for more complex synchronization needs that cannot be
 | 
				
			||||||
 | 
					   met with simple mutexes and channels.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. 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.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. Avoid storing large or complex data in context. Context should be used
 | 
				
			||||||
 | 
					   for request-scoped values like deadlines, cancellation signals, and
 | 
				
			||||||
 | 
					   authentication tokens.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. Use `runtime.Callers` and `runtime.CallersFrames` to capture stack traces
 | 
				
			||||||
 | 
					   for debugging and logging purposes.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. Use the `testing.TB` interface to write helper functions that can be
 | 
				
			||||||
 | 
					   used with both `*testing.T` and `*testing.B`.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. Use struct embedding to reuse code across multiple structs. This is a
 | 
				
			||||||
 | 
					   form of composition that can simplify code reuse.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. 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.
 | 
				
			||||||
		Loading…
	
		Reference in New Issue
	
	Block a user