added more stuff
This commit is contained in:
		
							parent
							
								
									dc211342c3
								
							
						
					
					
						commit
						e0ed207a8d
					
				
							
								
								
									
										309
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										309
									
								
								README.md
									
									
									
									
									
								
							| @ -1,8 +1,115 @@ | |||||||
| # My Code Styleguide | # sneak/styleguide | ||||||
|  | 
 | ||||||
|  | The following is the first released version of my personal code styleguide. | ||||||
|  | 
 | ||||||
|  | Only the Go portion is "complete". The others are mostly just | ||||||
|  | placeholders. | ||||||
|  | 
 | ||||||
|  | Feedback and suggestions are not only welcome but explicitly encouraged. | ||||||
|  | 
 | ||||||
|  | [sneak@sneak.berlin](mailto:sneak@sneak.berlin) | ||||||
|  | 
 | ||||||
|  | # My 2024 Code Styleguide | ||||||
|  | 
 | ||||||
|  | ## All | ||||||
|  | 
 | ||||||
|  | 1. Every project/repo should have a `Makefile` in the root. At a minimum, | ||||||
|  |    `make clean`, `make run`, `make fmt`, and `make test` should work. Choose | ||||||
|  |    a sane default target (`test` for libraries, `run` or `publish` for | ||||||
|  |    binaries). `fmt` should invoke the appropriate formatters for the files | ||||||
|  |    in the repo, such as `go fmt`, `prettier`, `black`, etc. Other standard | ||||||
|  |    `Makefile` targets include `deploy`, `lint`. Consider the `Makefile` the | ||||||
|  |    official documentation about how to operate the repository. | ||||||
|  | 
 | ||||||
|  | 1. If it's possible to write a `Dockerfile`, include at least a simple one. | ||||||
|  |    It should be possible to build and run the project with `docker build .`. | ||||||
|  | 
 | ||||||
|  | 1. For F/OSS-licensed software, try to include the full source code of the | ||||||
|  |    current version (and any dependencies, such as vendored dependencies) in | ||||||
|  |    the docker image. They're small and should be included with the binary. | ||||||
|  | 
 | ||||||
|  | 1. Under no circumstances should any credentials or secrets ever be | ||||||
|  |    committed to any repository, even private ones. Store secrets in | ||||||
|  |    environment variables, and if they are absolutely required, check on | ||||||
|  |    startup to make sure they are set/non-default and complain loudly if not. | ||||||
|  |    Exception, sometimes: public keys. (Public keys can still sometimes be | ||||||
|  |    secrets for operational security reasons.) | ||||||
|  | 
 | ||||||
|  | 1. Avoid nesting `if` statements. If you have more than one level of | ||||||
|  |    nesting, consider inverting the condition and using `return` to exit | ||||||
|  |    early. | ||||||
|  | 
 | ||||||
|  | 1. Almost all services/servers should accept their configuration via | ||||||
|  |    environment variables. Only go full config file if absolutely necessary. | ||||||
|  | 
 | ||||||
|  | 1. For services/servers, log JSON to stdout. This makes it easier to parse | ||||||
|  |    and aggregate logs when run under `docker`. Use structured logging | ||||||
|  |    whenever possible. You may detect if the output is a terminal and | ||||||
|  |    pretty-print the logs in that case. | ||||||
|  | 
 | ||||||
|  | 1. Debug mode is enabled by setting the environment variable `DEBUG` to a | ||||||
|  |    non-empty string. This should enable verbose logging and such. It will | ||||||
|  |    never be enabled in prod. | ||||||
|  | 
 | ||||||
|  | 1. For services/servers, make a healthcheck available at | ||||||
|  |    `/.well-known/healthcheck`. This is out of spec but it is my personal | ||||||
|  |    standard. This should return a 200 OK if the service is healthy, along | ||||||
|  |    with a JSON object containing the service's name, uptime, and any other | ||||||
|  |    relevant information, and a key of "status" with a value of "ok" if the | ||||||
|  |    service is healthy. Make sure that in the event of a failure, the service | ||||||
|  |    returns a 5xx status code for that route. | ||||||
|  | 
 | ||||||
|  | 1. If possible, for services/servers, include a /metrics endpoint that | ||||||
|  |    returns Prometheus-formatted metrics. This is not required for all | ||||||
|  |    services, but is a nice-to-have. | ||||||
|  | 
 | ||||||
|  | ## Bash / Shell | ||||||
|  | 
 | ||||||
|  | 1. Use `[[` instead of `[` for conditionals. It's a shell builtin and | ||||||
|  |    doesn't have to execute a separate process. | ||||||
|  | 
 | ||||||
|  | 1. Use `$( )` instead of backticks. It's easier to read and nest. | ||||||
|  | 
 | ||||||
|  | 1. Use `#!/usr/bin/env bash` as the shebang line. This allows the script to | ||||||
|  |    be run on systems where `bash` is not in `/bin`. | ||||||
|  | 
 | ||||||
|  | 1. Use `set -euo pipefail` at the top of every script. This will cause the | ||||||
|  |    script to exit if any command fails, and will cause the script to exit if | ||||||
|  |    any variable is used before it is set. | ||||||
|  | 
 | ||||||
|  | 1. Use `pv` for progress bars when piping data through a command. This makes | ||||||
|  |    it easier to see how much data has been processed. | ||||||
|  | 
 | ||||||
|  | 1. Put all code in functions, even a main function. Define all functions | ||||||
|  |    then call main at the bottom of the file. | ||||||
|  | 
 | ||||||
|  | ## JavaScript / ECMAScript / ES6 | ||||||
|  | 
 | ||||||
|  | 1. Use `const` for everything. If you need to reassign, use `let`. Never | ||||||
|  |    use `var`. | ||||||
|  | 
 | ||||||
|  | 1. Use yarn for package management, avoid using npm. | ||||||
|  | 
 | ||||||
|  | 1. Use LTS node versions. | ||||||
|  | 
 | ||||||
|  | 1. Use `prettier` for code formatting, with four spaces for indentation. | ||||||
|  | 
 | ||||||
|  | 1. At a minimum, `npm run test` and `npm run build` should work (complete | ||||||
|  |    the appropriate scripts in `package.json`). The `Makefile` should call | ||||||
|  |    these, do not duplicate the scripts in the `Makefile`. | ||||||
|  | 
 | ||||||
|  | ## Docker Containers (for services) | ||||||
|  | 
 | ||||||
|  | 1. Use `runit` with `runsvinit` as the entrypoint for all containers. This | ||||||
|  |    allows for easy service management and logging. In startup scripts | ||||||
|  |    (`/etc/service/*/run`) in the container, put a `sleep 1` at the top of | ||||||
|  |    the script to avoid spiking the cpu in the case of a fast-exiting process | ||||||
|  |    (such as in an error condition). This also limits the maximum number of | ||||||
|  |    error messages in logs to 86400/day. | ||||||
| 
 | 
 | ||||||
| ## Python | ## Python | ||||||
| 
 | 
 | ||||||
| 1. Format all code with `black`. | 1. Format all code with `black`, with four space indents. | ||||||
| 
 | 
 | ||||||
| 2. Put all code in functions. If you are writing a script, put the script | 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 |    in a function called `main` and call `main()` at the end of the script | ||||||
| @ -15,19 +122,75 @@ | |||||||
| 
 | 
 | ||||||
| ## Golang | ## Golang | ||||||
| 
 | 
 | ||||||
| 1.  Any project that has more than 2 or 3 modules should use the `uber/fx` DI | 1.  Try to hard wrap long lines at 77 characters or less. | ||||||
|     framework to keep things tidy. |  | ||||||
| 
 | 
 | ||||||
| 2.  Don't commit anything that hasn't been `go fmt`'d. The only exception is | 1.  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 | 1.  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 | 1.  Any project that has more than 2 or 3 modules should use the | ||||||
|  |     `go.uber.org/fx` dependency injection framework to keep things tidy. | ||||||
|  | 
 | ||||||
|  | 1.  If you have to choose between readable and clever, opt for readable. | ||||||
|  |     It's ok to make the code less concise or slightly less idiomatic if you | ||||||
|  |     can keep it dead simple. | ||||||
|  | 
 | ||||||
|  | 1.  Embed the git commit hash into the binary and include it in startup logs | ||||||
|  |     and in health check output. This is to make it easier to correlate | ||||||
|  |     running instances with their code. Do not include build time or build | ||||||
|  |     user, as these will make the build nondeterministic. | ||||||
|  | 
 | ||||||
|  |     Example relevant Makefile sections: | ||||||
|  | 
 | ||||||
|  |     Given a `main.go` like: | ||||||
|  | 
 | ||||||
|  |     ```go | ||||||
|  |     package main | ||||||
|  | 
 | ||||||
|  |     import ( | ||||||
|  |         "fmt" | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     var ( | ||||||
|  |         Version   string | ||||||
|  |         Buildarch string | ||||||
|  |     ) | ||||||
|  | 
 | ||||||
|  |     func main() { | ||||||
|  |         fmt.Printf("Version: %s\n", Version) | ||||||
|  |         fmt.Printf("Buildarch: %s\n", Buildarch) | ||||||
|  |     } | ||||||
|  |     ``` | ||||||
|  | 
 | ||||||
|  |     ```make | ||||||
|  |     VERSION := $(shell git describe --always --dirty) | ||||||
|  |     BUILDARCH := $(shell uname -m) | ||||||
|  | 
 | ||||||
|  |     GOLDFLAGS += -X main.Version=$(VERSION) | ||||||
|  |     GOLDFLAGS += -X main.Buildarch=$(BUILDARCH) | ||||||
|  | 
 | ||||||
|  |     # osx can't statically link apparently?! | ||||||
|  |     ifeq ($(UNAME_S),Darwin) | ||||||
|  |             GOFLAGS := -ldflags "$(GOLDFLAGS)" | ||||||
|  |     endif | ||||||
|  | 
 | ||||||
|  |     ifneq ($(UNAME_S),Darwin) | ||||||
|  |             GOFLAGS = -ldflags "-linkmode external -extldflags -static $(GOLDFLAGS)" | ||||||
|  |     endif | ||||||
|  | 
 | ||||||
|  |     ./httpd: ./pkg/*/*.go ./internal/*/*.go cmd/httpd/*.go | ||||||
|  |         go build -o $@ $(GOFLAGS) ./cmd/httpd/*.go | ||||||
|  |     ``` | ||||||
|  | 
 | ||||||
|  | 1.  Avoid obvious footguns. For example, use range instead of for loops for | ||||||
|  |     iterating. | ||||||
|  | 
 | ||||||
|  | 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 |     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 | ||||||
| @ -46,79 +209,118 @@ | |||||||
|     } |     } | ||||||
|     ``` |     ``` | ||||||
| 
 | 
 | ||||||
| 5.  Write at least a single test to check compilation. The test file can be | 1.  Commit at least a single test file to check compilation. The test file | ||||||
|     empty, but it should exist. This is to ensure that `go test ./...` will |     can be empty, but it should exist. This is to ensure that `go test | ||||||
|     always function as a syntax check at a minimum. | ./...` will always function as a syntax check at a minimum. | ||||||
| 
 | 
 | ||||||
| 6.  For anything beyond a simple script or tool, or anything that is going to | 1.  Full TDD and coverage isn't that important, but when fixing a specific | ||||||
|  |     bug, try to write a test that reproduces the bug before fixing it. This | ||||||
|  |     will help ensure that the bug doesn't come back later, and crystallizes | ||||||
|  |     the experience of discovering the bug and the resulting fix into the | ||||||
|  |     repository's history. | ||||||
|  | 
 | ||||||
|  | 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 |     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 | 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 |     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 | 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 |     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 | 1.  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 | 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 |     `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. |     make it easier to see what is a library and what is a binary. | ||||||
| 
 | 
 | ||||||
| 11. Binaries go in `cmd/` directories. Each binary should have its own | 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 |     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 |     what is a library and what is a binary. Only package `main` files | ||||||
|     should be in `cmd/*` directories. |     should be in `cmd/*` directories. | ||||||
| 
 | 
 | ||||||
| 12. Keep the `main()` function as small as possible. | 1.  Keep the `main()` function as small as possible. | ||||||
| 
 | 
 | ||||||
| 13. Keep the `main` package as small as possible. Move as much code as is | 1.  Keep the `main` package as small as possible. Move as much code as is | ||||||
|     feasible to a library package. |     feasible to a library package, even if it's an internal one. `main` is | ||||||
|  |     just an entrypoint to your code, not a place for implementations. | ||||||
|  |     Exception: single-file scripts. | ||||||
| 
 | 
 | ||||||
| 14. HTTP HandleFuncs should be returned from methods or functions that need | 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 |     to handle HTTP requests. Don't use methods or our top level functions | ||||||
|     as handlers. |     as handlers. | ||||||
| 
 | 
 | ||||||
| 15. Provide a .gitignore file that ignores at least `*.log`, `*.out`, and | 1.  Provide a .gitignore file that ignores at least `*.log`, `*.out`, and | ||||||
|     `*.test` files, as well as any binaries. |     `*.test` files, as well as any binaries. | ||||||
| 
 | 
 | ||||||
| 16. Constructors should be called `New()` whenever possible. | 1.  Constructors should be called `New()` whenever possible. | ||||||
|     `modulename.New()` works great if you name the packages properly. |     `modulename.New()` works great if you name the packages properly. | ||||||
| 
 | 
 | ||||||
| 17. Don't make packages too big. Break them up. | 1.  Don't make packages too big. Break them up. | ||||||
| 
 | 
 | ||||||
| 18. Don't make functions or methods too big. Break them up. | 1.  Don't make functions or methods too big. Break them up. | ||||||
| 
 | 
 | ||||||
| 19. Use descriptive names for functions and methods. Don't be afraid to | 1.  Use descriptive names for functions and methods. Don't be afraid to | ||||||
|     make them a bit long. |     make them a bit long. | ||||||
| 
 | 
 | ||||||
| 20. Use descriptive names for modules and filenames. Avoid generic names | 1.  Use descriptive names for modules and filenames. Avoid generic names | ||||||
|     like `server`. `util` is banned. |     like `server`. `util` is banned. | ||||||
| 
 | 
 | ||||||
| 21. Constructors should take a Params struct if they need more than 1-2 | 1.  Constructors should take a Params struct if they need more than 1-2 | ||||||
|     arguments. Positional arguments are an endless source of bugs and |     arguments. Positional arguments are an endless source of bugs and | ||||||
|     should be avoided whenever possible. |     should be avoided whenever possible. | ||||||
| 
 | 
 | ||||||
| 22. Use `context.Context` for all functions that need it. If you don't need | 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 |     it, you can pass `context.Background()`. Anything long-running should | ||||||
|     get and abide by a Context. A context does not count against your |     get and abide by a Context. A context does not count against your | ||||||
|     number of function or method arguments for purposes of calculating |     number of function or method arguments for purposes of calculating | ||||||
|     whether or not you need a Params struct, because the `ctx` is always |     whether or not you need a Params struct, because the `ctx` is always | ||||||
|     first. |     first. | ||||||
| 
 | 
 | ||||||
| 23. Contexts are always named `ctx`. | 1.  Contexts are always named `ctx`. | ||||||
| 
 | 
 | ||||||
| 24. Use `context.WithTimeout` or `context.WithDeadline` for any function | 1.  Use `context.WithTimeout` or `context.WithDeadline` for any function | ||||||
|     that could potentially run for a long time. This is especially true for |     that could potentially run for a long time. This is especially true for | ||||||
|     any function that makes a network call. Sane timeouts are essential. |     any function that makes a network call. Sane timeouts are essential. | ||||||
| 
 | 
 | ||||||
| 25. Avoid global state, especially global variables. If you need to store | 1.  If a structure/type is only used in one function or method, define it | ||||||
|  |     there. If it's used in more than one, define it in the package. Keep it | ||||||
|  |     close to its usages. For example: | ||||||
|  | 
 | ||||||
|  |     ```go | ||||||
|  |     func (m *Mothership) tvPost() http.HandlerFunc { | ||||||
|  | 
 | ||||||
|  |         type MSTVRequest struct { | ||||||
|  |                 URL string `json:"URL"` | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         type MSTVResponse struct { | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return func(w http.ResponseWriter, r *http.Request) { | ||||||
|  |             // parse json from request | ||||||
|  |             var reqParsed MSTVRequest | ||||||
|  |             err = json.NewDecoder(r.Body).Decode(&reqParsed) | ||||||
|  |             ... | ||||||
|  | 
 | ||||||
|  |             if err != nil { | ||||||
|  |                     SendErrorResponse(w, MSGenericError) | ||||||
|  |                     return | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             log.Info().Msgf("Casting to %s: %s", tvName, streamURL) | ||||||
|  |             SendSuccessResponse(w, &MSTVResponse{}) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     ``` | ||||||
|  | 
 | ||||||
|  | 1.  Avoid global state, especially global variables. If you need to store | ||||||
|     state that is global to your launch or application instance, use a |     state that is global to your launch or application instance, use a | ||||||
|     package `globals` or `appstate` with a struct and a constructor and |     package `globals` or `appstate` with a struct and a constructor and | ||||||
|     require it as a dependency in your constructors. This will allow |     require it as a dependency in your constructors. This will allow | ||||||
| @ -127,10 +329,10 @@ | |||||||
|     graph allows for it, put it in the main struct/object of your |     graph allows for it, put it in the main struct/object of your | ||||||
|     application, but remember that this harms testability. |     application, but remember that this harms testability. | ||||||
| 
 | 
 | ||||||
| 26. Package-global "variables" are ok if they are constants, such as static | 1.  Package-global "variables" are ok if they are constants, such as static | ||||||
|     strings or integers or errors. |     strings or integers or errors. | ||||||
| 
 | 
 | ||||||
| 27. Whenever possible, avoid hardcoding numbers or values in your code. Use | 1.  Whenever possible, avoid hardcoding numbers or values in your code. Use | ||||||
|     descriptively-named constants instead. Recall the famous SICP quote: |     descriptively-named constants instead. Recall the famous SICP quote: | ||||||
|     "Programs must be written for people to read, and only incidentally for |     "Programs must be written for people to read, and only incidentally for | ||||||
|     machines to execute." Rather than comments, a descriptive constant name |     machines to execute." Rather than comments, a descriptive constant name | ||||||
| @ -149,39 +351,39 @@ | |||||||
|      } |      } | ||||||
|     ``` |     ``` | ||||||
| 
 | 
 | ||||||
| 28. Define your struct types near their constructors. | 1.  Define your struct types near their constructors. | ||||||
| 
 | 
 | ||||||
| 29. Define your interface types near the functions that use them, or if you | 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. |     have multiple conformant types, put the interface(s) in their own file. | ||||||
| 
 | 
 | ||||||
| 30. Define errors as package-level variables. Use a descriptive name for the | 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 |     error. Use `errors.New` to create the error. If you need to include | ||||||
|     additional information in the error, use a struct that implements the |     additional information in the error, use a struct that implements the | ||||||
|     `error` interface. |     `error` interface. | ||||||
| 
 | 
 | ||||||
| 31. Use lowerCamelCase for local function/variable names. Use UpperCamelCase | 1.  Use lowerCamelCase for local function/variable names. Use UpperCamelCase | ||||||
|     for type names, and exported function/variable names. Use snake_case for |     for type names, and exported function/variable names. Use snake_case for | ||||||
|     JSON keys. Use lowercase for filenames. |     JSON keys. Use lowercase for filenames. | ||||||
| 
 | 
 | ||||||
| 32. Explicitly specify UTC for datetimes unless you have a very good reason | 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. |     not to. Use `time.Now().UTC()` to get the current time in UTC. | ||||||
| 
 | 
 | ||||||
| 33. String dates should always be ISO8601 formatted. Use `time.Time.Format` | 1.  String dates should always be ISO8601 formatted. Use `time.Time.Format` | ||||||
|     with `time.RFC3339` to get the correct format. |     with `time.RFC3339` to get the correct format. | ||||||
| 
 | 
 | ||||||
| 34. Use `time.Time` for all date and time values. Do not use `int64` or | 1.  Use `time.Time` for all date and time values. Do not use `int64` or | ||||||
|     `string` for dates or times internally. |     `string` for dates or times internally. | ||||||
| 
 | 
 | ||||||
| 35. When using `time.Time` in a struct, use a pointer to `time.Time` so that | 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. |     you can differentiate between a zero value and a null value. | ||||||
| 
 | 
 | ||||||
| 36. Use `time.Duration` for all time durations. Do not use `int64` or | 1.  Use `time.Duration` for all time durations. Do not use `int64` or | ||||||
|     `string` for durations internally. |     `string` for durations internally. | ||||||
| 
 | 
 | ||||||
| 37. When using `time.Duration` in a struct, use a pointer to `time.Duration` | 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. |     so that you can differentiate between a zero value and a null value. | ||||||
| 
 | 
 | ||||||
| 38. Whenever possible, in argument types and return types, try to use | 1.  Whenever possible, in argument types and return types, try to use | ||||||
|     standard library interfaces instead of concrete types. For example, use |     standard library interfaces instead of concrete types. For example, use | ||||||
|     `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: | ||||||
| @ -227,7 +429,8 @@ | |||||||
| 
 | 
 | ||||||
|             -   `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. | 
 | ||||||
|  |     string` method satisfies this interface. | ||||||
| 
 | 
 | ||||||
|         -   **`error`** instead of custom error types: |         -   **`error`** instead of custom error types: | ||||||
| 
 | 
 | ||||||
| @ -277,15 +480,15 @@ string` method satisfies this interface. | |||||||
|                 flags. Implementing the `String` and `Set` methods allows you to |                 flags. Implementing the `String` and `Set` methods allows you to | ||||||
|                 use custom types with the `flag` package. |                 use custom types with the `flag` package. | ||||||
| 
 | 
 | ||||||
| 39. Avoid using `panic` in library code. Instead, return errors to allow | 1.  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 | ||||||
|     conditions. |     conditions. | ||||||
| 
 | 
 | ||||||
| 40. Use `defer` to ensure resources are properly cleaned up, such as | 1.  Use `defer` to ensure resources are properly cleaned up, such as | ||||||
|     closing files or network connections. Place `defer` statements |     closing files or network connections. Place `defer` statements | ||||||
|     immediately after resource acquisition. |     immediately after resource acquisition. | ||||||
| 
 | 
 | ||||||
| 41. When calling a function with `go`, wrap the function call in an | 1.  When calling a function with `go`, wrap the function call in an | ||||||
|     anonymous function to ensure it runs in the new goroutine context: |     anonymous function to ensure it runs in the new goroutine context: | ||||||
| 
 | 
 | ||||||
|     Right: |     Right: | ||||||
| @ -302,7 +505,7 @@ string` method satisfies this interface. | |||||||
|     go someFunction(arg1, arg2) |     go someFunction(arg1, arg2) | ||||||
|     ``` |     ``` | ||||||
| 
 | 
 | ||||||
| 42. Use `iota` to define enumerations in a type-safe way. This ensures that | 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. |     the constants are properly grouped and reduces the risk of errors. | ||||||
| 
 | 
 | ||||||
|     Example: |     Example: | ||||||
| @ -343,7 +546,7 @@ string` method satisfies this interface. | |||||||
|     ) |     ) | ||||||
|     ``` |     ``` | ||||||
| 
 | 
 | ||||||
| 43. Don't hardcode big lists of things in your normal code. Either isolate | 1.  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 |     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 |     party library. For example, if you need a list of country codes, you can | ||||||
|     use |     use | ||||||
| @ -355,7 +558,7 @@ string` method satisfies this interface. | |||||||
|     probably too much. Compress the file before embedding and uncompress |     probably too much. Compress the file before embedding and uncompress | ||||||
|     during the reading/parsing step for efficiency. |     during the reading/parsing step for efficiency. | ||||||
| 
 | 
 | ||||||
| 44. When storing numeric values that represent a number of units, either | 1.  When storing numeric values that represent a number of units, either | ||||||
|     include the unit in the variable name (e.g. `uptimeSeconds`, |     include the unit in the variable name (e.g. `uptimeSeconds`, | ||||||
|     `delayMsec`, `coreTemperatureCelsius`), or use a type alias (that |     `delayMsec`, `coreTemperatureCelsius`), or use a type alias (that | ||||||
|     includes the unit name), or use a 3p library such as |     includes the unit name), or use a 3p library such as | ||||||
| @ -364,7 +567,7 @@ string` method satisfies this interface. | |||||||
|     [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 | 1.  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` |     `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 |     branch and tag(s). Subsequent work should happen on branches so that | ||||||
|     `main` is "always releasable". "Releasable" in this context means that |     `main` is "always releasable". "Releasable" in this context means that | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user