Add code styleguide split into general, Go, JS, and Python files
This commit is contained in:
parent
2ab09985e0
commit
d523ff37ae
101
prompts/CODE_STYLEGUIDE.md
Normal file
101
prompts/CODE_STYLEGUIDE.md
Normal file
@ -0,0 +1,101 @@
|
||||
# sneak/styleguide
|
||||
|
||||
The following is the first released version of my personal code styleguide.
|
||||
There are many like it, but this one is mine.
|
||||
|
||||
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.
|
||||
|
||||
## 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.
|
||||
|
||||
# Author
|
||||
|
||||
[@sneak](https://sneak.berlin)
|
||||
<[sneak@sneak.berlin](mailto:sneak@sneak.berlin)>
|
||||
|
||||
# License
|
||||
|
||||
MIT. See [LICENSE](../LICENSE).
|
||||
567
prompts/CODE_STYLEGUIDE_GO.md
Normal file
567
prompts/CODE_STYLEGUIDE_GO.md
Normal file
@ -0,0 +1,567 @@
|
||||
# Golang
|
||||
|
||||
1. Try to hard wrap long lines at 77 characters or less.
|
||||
|
||||
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
|
||||
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. 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
|
||||
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. Commit at least a single test file 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. 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
|
||||
`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/` subdirectory. `internal/` is for modules used only by the
|
||||
current repo, and `pkg/` is for modules that can be consumed externally.
|
||||
This is to keep the repo root as clean as possible.
|
||||
|
||||
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, even if it's an internal one. `main` is just
|
||||
an entrypoint to your code, not a place for implementations. Exception:
|
||||
single-file scripts.
|
||||
|
||||
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. 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 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:
|
||||
- **`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`.
|
||||
|
||||
- **`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.
|
||||
|
||||
- **`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`.
|
||||
|
||||
- **`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.
|
||||
|
||||
- **`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`.
|
||||
|
||||
- **`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`.
|
||||
|
||||
- **`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`.
|
||||
|
||||
- **`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.
|
||||
|
||||
- **`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.
|
||||
|
||||
- **`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.
|
||||
|
||||
- **`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.
|
||||
|
||||
- **`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.
|
||||
|
||||
- **`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.
|
||||
|
||||
- **`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.
|
||||
|
||||
- **`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.
|
||||
|
||||
- **`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.
|
||||
|
||||
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
|
||||
)
|
||||
```
|
||||
|
||||
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 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). 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.
|
||||
|
||||
1. When storing numeric values that represent a number of units, either 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
|
||||
[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.
|
||||
|
||||
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` 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)
|
||||
|
||||
1. For any internet-facing http server, set appropriate timeouts and limits to
|
||||
protect against slowloris attacks or huge uploads that can consume server
|
||||
resources even without authentication.
|
||||
|
||||
Example to limit request body size:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func main() {
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
// Limit the request body to 10MB
|
||||
r.Body = http.MaxBytesReader(w, r.Body, 10<<20)
|
||||
if err := r.ParseForm(); err != nil {
|
||||
http.Error(w, "Request body too large", http.StatusRequestEntityTooLarge)
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(w, "Hello, World!")
|
||||
})
|
||||
|
||||
http.ListenAndServe(":8080", nil)
|
||||
}
|
||||
```
|
||||
|
||||
Example to set appropriate timeouts:
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
server := &http.Server{
|
||||
Addr: ":8080",
|
||||
ReadTimeout: 5 * time.Second,
|
||||
WriteTimeout: 10 * time.Second,
|
||||
Handler: http.DefaultServeMux,
|
||||
}
|
||||
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintf(w, "Hello, World!")
|
||||
})
|
||||
|
||||
server.ListenAndServe()
|
||||
}
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
# Author
|
||||
|
||||
[@sneak](https://sneak.berlin)
|
||||
<[sneak@sneak.berlin](mailto:sneak@sneak.berlin)>
|
||||
|
||||
# License
|
||||
|
||||
MIT. See [LICENSE](../LICENSE).
|
||||
23
prompts/CODE_STYLEGUIDE_JS.md
Normal file
23
prompts/CODE_STYLEGUIDE_JS.md
Normal file
@ -0,0 +1,23 @@
|
||||
# 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`.
|
||||
|
||||
# Author
|
||||
|
||||
[@sneak](https://sneak.berlin)
|
||||
<[sneak@sneak.berlin](mailto:sneak@sneak.berlin)>
|
||||
|
||||
# License
|
||||
|
||||
MIT. See [LICENSE](../LICENSE).
|
||||
21
prompts/CODE_STYLEGUIDE_PYTHON.md
Normal file
21
prompts/CODE_STYLEGUIDE_PYTHON.md
Normal file
@ -0,0 +1,21 @@
|
||||
# Python
|
||||
|
||||
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 in a
|
||||
function called `main` and call `main()` at the end of the script using the
|
||||
standard invocation:
|
||||
|
||||
```python
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
```
|
||||
|
||||
# Author
|
||||
|
||||
[@sneak](https://sneak.berlin)
|
||||
<[sneak@sneak.berlin](mailto:sneak@sneak.berlin)>
|
||||
|
||||
# License
|
||||
|
||||
MIT. See [LICENSE](../LICENSE).
|
||||
Loading…
Reference in New Issue
Block a user