Enhance Go development instructions with additional best practices and guidelines

This commit is contained in:
sauran 2025-10-01 02:05:17 +05:00
parent a09e74243f
commit 7d66c285c8

View File

@ -17,6 +17,8 @@ Follow idiomatic Go practices and community standards when writing Go code. Thes
- Make the zero value useful
- Document exported types, functions, methods, and packages
- Use Go modules for dependency management
- Leverage the Go standard library instead of reinventing the wheel (e.g., use `strings.Builder` for string concatenation, `filepath.Join` for path construction)
- Prefer standard library solutions over custom implementations when functionality exists
## Naming Conventions
@ -27,6 +29,8 @@ Follow idiomatic Go practices and community standards when writing Go code. Thes
- Choose names that describe what the package provides, not what it contains
- Avoid generic names like `util`, `common`, or `base`
- Package names should be singular, not plural
- When editing an existing `.go` file, preserve the current `package` declaration and do not insert a second `package` line
- When creating a new `.go` file, match the package name used by other files in the same directory (or the directory name if new), and ensure exactly one `package` declaration at the top
### Variables and Functions
@ -105,11 +109,14 @@ Follow idiomatic Go practices and community standards when writing Go code. Thes
- Use struct tags for JSON, XML, database mappings
- Prefer explicit type conversions
- Use type assertions carefully and check the second return value
- Prefer generics over unconstrained types; when an unconstrained type is truly needed, use the predeclared alias `any` instead of `interface{}` (Go 1.18+)
### Pointers vs Values
- Use pointers for large structs or when you need to modify the receiver
- Use values for small structs and when immutability is desired
- Use pointer receivers for large structs or when you need to modify the receiver
- Use value receivers for small structs and when immutability is desired
- Use pointer parameters when you need to modify the argument or for large structs
- Use value parameters for small structs and when you want to prevent modification
- Be consistent within a type's method set
- Consider the zero value when choosing pointer vs value receivers
@ -125,7 +132,8 @@ Follow idiomatic Go practices and community standards when writing Go code. Thes
### Goroutines
- Don't create goroutines in libraries; let the caller control concurrency
- Be cautious about creating goroutines in libraries; prefer letting the caller control concurrency
- If you must create goroutines in libraries, provide clear documentation and cleanup mechanisms
- Always know how a goroutine will exit
- Use `sync.WaitGroup` or channels to wait for goroutines
- Avoid goroutine leaks by ensuring cleanup
@ -143,8 +151,11 @@ Follow idiomatic Go practices and community standards when writing Go code. Thes
- Use `sync.Mutex` for protecting shared state
- Keep critical sections small
- Use `sync.RWMutex` when you have many readers
- Prefer channels over mutexes when possible
- Choose between channels and mutexes based on the use case: use channels for communication, mutexes for protecting state
- Use `sync.Once` for one-time initialization
- WaitGroup usage by Go version:
- If `go >= 1.25` in `go.mod`, use the new `WaitGroup` pattern
- If `go < 1.25`, use the classic `Add`/`Done` pattern
## Error Handling Patterns
@ -172,6 +183,9 @@ Follow idiomatic Go practices and community standards when writing Go code. Thes
- Use middleware for cross-cutting concerns
- Set appropriate status codes and headers
- Handle errors gracefully and return appropriate error responses
- Router usage by Go version:
- If `go >= 1.22`, prefer the enhanced `net/http` `ServeMux` with pattern-based routing and method matching
- If `go < 1.22`, use the classic `ServeMux` and handle methods/paths manually (or use a third-party router when justified)
### JSON APIs
@ -181,6 +195,15 @@ Follow idiomatic Go practices and community standards when writing Go code. Thes
- Consider using `json.RawMessage` for delayed parsing
- Handle JSON errors appropriately
### HTTP Clients
- Keep the client struct focused on configuration and dependencies only (e.g., base URL, `*http.Client`, auth, default headers). It must not store per-request state
- Do not store or cache `*http.Request` inside the client struct, and do not persist request-specific state across calls; instead, construct a fresh request per method invocation
- Methods should accept `context.Context` and input parameters, assemble the `*http.Request` locally (or via a short-lived builder/helper created per call), then call `c.httpClient.Do(req)`
- If request-building logic is reused, factor it into unexported helper functions or a per-call builder type; never keep `http.Request` (URL params, body, headers) as fields on the long-lived client
- Ensure the underlying `*http.Client` is configured (timeouts, transport) and is safe for concurrent use; avoid mutating `Transport` after first use
- Always set headers on the request instance youre sending, and close response bodies (`defer resp.Body.Close()`), handling errors appropriately
## Performance Optimization
### Memory Management
@ -191,6 +214,33 @@ Follow idiomatic Go practices and community standards when writing Go code. Thes
- Preallocate slices when size is known
- Avoid unnecessary string conversions
### I/O: Readers and Buffers
- Most `io.Reader` streams are consumable once; reading advances state. Do not assume a reader can be re-read without special handling
- If you must read data multiple times, buffer it once and recreate readers on demand:
- Use `io.ReadAll` (or a limited read) to obtain `[]byte`, then create fresh readers via `bytes.NewReader(buf)` or `bytes.NewBuffer(buf)` for each reuse
- For strings, use `strings.NewReader(s)`; you can `Seek(0, io.SeekStart)` on `*bytes.Reader` to rewind
- For HTTP requests, do not reuse a consumed `req.Body`. Instead:
- Keep the original payload as `[]byte` and set `req.Body = io.NopCloser(bytes.NewReader(buf))` before each send
- Prefer configuring `req.GetBody` so the transport can recreate the body for redirects/retries: `req.GetBody = func() (io.ReadCloser, error) { return io.NopCloser(bytes.NewReader(buf)), nil }`
- To duplicate a stream while reading, use `io.TeeReader` (copy to a buffer while passing through) or write to multiple sinks with `io.MultiWriter`
- Reusing buffered readers: call `(*bufio.Reader).Reset(r)` to attach to a new underlying reader; do not expect it to “rewind” unless the source supports seeking
- For large payloads, avoid unbounded buffering; consider streaming, `io.LimitReader`, or on-disk temporary storage to control memory
- Use `io.Pipe` to stream without buffering the whole payload:
- Write to `*io.PipeWriter` in a separate goroutine while the reader consumes
- Always close the writer; use `CloseWithError(err)` on failures
- `io.Pipe` is for streaming, not rewinding or making readers reusable
- **Warning:** When using `io.Pipe` (especially with multipart writers), all writes must be performed in strict, sequential order. Do not write concurrently or out of order—multipart boundaries and chunk order must be preserved. Out-of-order or parallel writes can corrupt the stream and result in errors.
- Streaming multipart/form-data with `io.Pipe`:
- `pr, pw := io.Pipe()`; `mw := multipart.NewWriter(pw)`; use `pr` as the HTTP request body
- Set `Content-Type` to `mw.FormDataContentType()`
- In a goroutine: write all parts to `mw` in the correct order; on error `pw.CloseWithError(err)`; on success `mw.Close()` then `pw.Close()`
- Do not store request/in-flight form state on a long-lived client; build per call
- Streamed bodies are not rewindable; for retries/redirects, buffer small payloads or provide `GetBody`
### Profiling
- Use built-in profiling tools (`pprof`)
@ -214,7 +264,7 @@ Follow idiomatic Go practices and community standards when writing Go code. Thes
- Name tests descriptively using `Test_functionName_scenario`
- Use subtests with `t.Run` for better organization
- Test both success and error cases
- Use `testify` or similar libraries sparingly
- Consider using `testify` or similar libraries when they add value, but don't over-complicate simple tests
### Test Helpers
@ -238,7 +288,7 @@ Follow idiomatic Go practices and community standards when writing Go code. Thes
- Use standard library crypto packages
- Don't implement your own cryptography
- Use crypto/rand for random number generation
- Store passwords using bcrypt or similar
- Store passwords using bcrypt, scrypt, or argon2 (consider golang.org/x/crypto for additional options)
- Use TLS for network communication
## Documentation
@ -265,7 +315,7 @@ Follow idiomatic Go practices and community standards when writing Go code. Thes
- `go fmt`: Format code
- `go vet`: Find suspicious constructs
- `golint` or `golangci-lint`: Additional linting
- `golangci-lint`: Additional linting (golint is deprecated)
- `go test`: Run tests
- `go mod`: Manage dependencies
- `go generate`: Code generation
@ -288,5 +338,5 @@ Follow idiomatic Go practices and community standards when writing Go code. Thes
- Not understanding nil interfaces vs nil pointers
- Forgetting to close resources (files, connections)
- Using global variables unnecessarily
- Over-using empty interfaces (`interface{}`)
- Over-using unconstrained types (e.g., `any`); prefer specific types or generic type parameters with constraints. If an unconstrained type is required, use `any` rather than `interface{}`
- Not considering the zero value of types