

Also from Kynth Studios


Also from Kynth Studios


Also from Kynth Studios
1# Go Production — Cursor Rules2# Production Go: error handling, concurrency, interfaces, and clean architecture34# Project Context5You are writing production Go code (Go 1.21+). The project follows Go idioms and conventions,6uses the standard library extensively, handles errors explicitly, and leverages goroutines and7channels for concurrency. Code must be clean, testable, and maintainable.89# Project Structure10```11cmd/12 server/13 main.go # Entry point, DI wiring14 cli/15 main.go # CLI entry point16internal/ # Private application code17 handler/ # HTTP handlers18 service/ # Business logic19 repository/ # Data access20 model/ # Domain models21 middleware/ # HTTP middleware22 config/ # Configuration loading23pkg/ # Public reusable packages24 httpclient/25 logger/26migrations/ # SQL migrations27api/ # API specs (OpenAPI, proto)28```2930# Error Handling (Go's Most Important Pattern)31- Always check errors immediately. Never ignore an error return value.32- Wrap errors with context using `fmt.Errorf` and `%w`:33```go34 user, err := repo.FindByID(ctx, id)35 if err != nil {36 return fmt.Errorf("finding user %d: %w", id, err)37 }38```39- Define sentinel errors for expected conditions:40```go41 var (42 ErrNotFound = errors.New("not found")43 ErrUnauthorized = errors.New("unauthorized")44 ErrConflict = errors.New("conflict")45 )46```47- Define custom error types for errors with structured data:48```go49 type ValidationError struct {50 Field string51 Message string52 }53 func (e *ValidationError) Error() string {54 return fmt.Sprintf("validation: %s — %s", e.Field, e.Message)55 }56```57- Use `errors.Is()` and `errors.As()` for error checking — not type assertions.58- DON'T: Use `panic()` for expected errors — only for unrecoverable programmer errors.59- DON'T: Return error messages that expose internal implementation details.60- DON'T: Log and return the same error — do one or the other.6162# Interface Design63- Define interfaces where they're consumed, not where they're implemented.64- Keep interfaces small — 1-3 methods maximum:65```go66 type UserStore interface {67 FindByID(ctx context.Context, id int64) (*User, error)68 Create(ctx context.Context, user *User) error69 }70```71- Accept interfaces, return structs:72```go73 func NewUserService(store UserStore, logger Logger) *UserService { ... }74```75- Use the `io.Reader`, `io.Writer`, `io.Closer` patterns from the standard library as models.76- DON'T: Create interfaces with many methods — break them into smaller, focused interfaces.77- DON'T: Create interfaces prematurely — wait until you need the abstraction.7879# Concurrency Patterns80- Use goroutines for concurrent I/O operations. Use `sync.WaitGroup` to wait for completion:81```go82 var wg sync.WaitGroup83 for _, item := range items {84 wg.Add(1)85 go func(item Item) {86 defer wg.Done()87 process(item)88 }(item)89 }90 wg.Wait()91```92- Use `errgroup.Group` from `golang.org/x/sync/errgroup` for goroutines that can fail:93```go94 g, ctx := errgroup.WithContext(ctx)95 g.Go(func() error { return fetchUsers(ctx) })96 g.Go(func() error { return fetchProducts(ctx) })97 if err := g.Wait(); err != nil { return err }98```99- Use channels for communication between goroutines. Use `select` for multiplexing.100- Always pass `context.Context` as the first parameter for cancellation support.101- Use `context.WithTimeout` and `context.WithCancel` for deadline/cancellation control.102- Use `sync.Once` for one-time initialization.103- Use `sync.Map` only when the key set is stable — otherwise use a regular map with `sync.RWMutex`.104- DON'T: Use goroutines without a way to stop them — always accept context or a done channel.105- DON'T: Share memory between goroutines — communicate by sending data on channels.106107# HTTP Handler Patterns108- Use `http.Handler` and `http.HandlerFunc` interfaces.109- Parse and validate input at the handler level, pass typed structs to service layer:110```go111 func (h *Handler) CreateUser(w http.ResponseWriter, r *http.Request) {112 var req CreateUserRequest113 if err := json.NewDecoder(r.Body).Decode(&req); err != nil {114 h.respondError(w, http.StatusBadRequest, "invalid request body")115 return116 }117 if err := req.Validate(); err != nil {118 h.respondError(w, http.StatusUnprocessableEntity, err.Error())119 return120 }121 user, err := h.service.CreateUser(r.Context(), req)122 if err != nil {123 h.handleServiceError(w, err)124 return125 }126 h.respondJSON(w, http.StatusCreated, user)127 }128```129- Write helper methods: `respondJSON`, `respondError` for consistent response formatting.130- Use middleware for cross-cutting concerns: logging, auth, recovery, request ID.131132# Struct Design133- Use constructor functions: `func NewUser(name, email string) (*User, error)`.134- Validate in constructors — return errors for invalid state.135- Use pointer receivers for methods that modify state, value receivers for methods that don't.136- Use struct embedding for composition (not inheritance):137```go138 type TimestampedModel struct {139 CreatedAt time.Time `json:"created_at" db:"created_at"`140 UpdatedAt time.Time `json:"updated_at" db:"updated_at"`141 }142 type User struct {143 TimestampedModel144 ID int64 `json:"id" db:"id"`145 Email string `json:"email" db:"email"`146 }147```148- Use struct tags consistently: `json`, `db`, `validate`.149150# Testing151- Table-driven tests for functions with multiple input/output cases:152```go153 tests := []struct {154 name string155 input string156 want int157 wantErr bool158 }{159 {name: "valid input", input: "42", want: 42},160 {name: "empty input", input: "", wantErr: true},161 }162 for _, tt := range tests {163 t.Run(tt.name, func(t *testing.T) {164 got, err := Parse(tt.input)165 if (err != nil) != tt.wantErr { t.Fatalf(...) }166 if got != tt.want { t.Errorf(...) }167 })168 }169```170- Use `testify/assert` or `testify/require` for cleaner assertions.171- Use `httptest.NewServer` for HTTP handler testing.172- Use interfaces for dependency injection — mock interfaces in tests.173- Use `t.Parallel()` for tests that don't share state.174- Use `testcontainers-go` for integration tests with real databases.175176# Configuration177- Use environment variables for configuration. Parse at startup in main.go.178- Use a struct for configuration with validation:179```go180 type Config struct {181 Port int `env:"PORT" envDefault:"8080"`182 DatabaseURL string `env:"DATABASE_URL,required"`183 LogLevel string `env:"LOG_LEVEL" envDefault:"info"`184 }185```186- Use `github.com/caarlos0/env` or `github.com/kelseyhightower/envconfig` for parsing.187- Fail fast on missing required configuration — don't use defaults for critical values.188189# Logging190- Use structured logging with `slog` (standard library, Go 1.21+):191```go192 slog.Info("user created",193 slog.Int64("user_id", user.ID),194 slog.String("email", user.Email),195 )196```197- Include context in all log messages: request_id, user_id, operation.198- Log at appropriate levels: Error (action needed), Warn (unexpected but handled), Info (key events), Debug (development).199- DON'T: Use `fmt.Println` or `log.Println` — use structured logging.200201# Database Access202- Use `sqlx` for typed database access or `pgx` for PostgreSQL-specific features.203- Use prepared statements and parameterized queries — never concatenate SQL.204- Use connection pooling with appropriate `MaxOpenConns` and `MaxIdleConns`.205- Use transactions for multi-step operations:206```go207 tx, err := db.BeginTxx(ctx, nil)208 if err != nil { return err }209 defer tx.Rollback()210 // ... operations ...211 return tx.Commit()212```213- Use migrations (golang-migrate, goose) — never modify schemas manually.214215# Common Mistakes to Avoid216- DON'T: Ignore error return values — always check them.217- DON'T: Use `init()` functions for complex initialization — use explicit setup in main.218- DON'T: Use package-level variables for mutable state — pass dependencies explicitly.219- DON'T: Use `interface{}` / `any` when a specific type or generic constraint works.220- DON'T: Start goroutines without a cancellation mechanism.221- DON'T: Use `time.Sleep` in production code — use timers, tickers, or context deadlines.222- DON'T: Return `nil, nil` — either return a value or return an error, never neither.223
One repository carrying more than one format is the comparison this product exists for: does anyone actually write different content in each file, or is one a copy of the other?
| Repository | Format | Stack | Covers | Score | Changed |
|---|---|---|---|---|---|
| survivorforge/cursor-rulesrules/ai-ml-python/.cursorrules · 17 | .cursorrules | teststylearchdeployment+2 | 81/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/api-microservices/.cursorrules · 17 | .cursorrules | buildteststylearch+5 | 92/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/database-sql/.cursorrules · 17 | .cursorrules | styletypessecuritydatabase+3 | 65/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/devops-docker/.cursorrules · 17 | .cursorrules | setupbuildteststyle+4 | 93/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/devops-infrastructure/.cursorrules · 17 | .cursorrules | buildteststylesecurity+3 | 93/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/django-rest/.cursorrules · 17 | .cursorrules | buildteststylearch+5 | 84/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/docker-devops/.cursorrules · 17 | .cursorrules | setupteststylearch+6 | 85/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/flutter-dart/.cursorrules · 17 | .cursorrules | teststylearchtypes+5 | 89/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/go-gin/.cursorrules · 17 | .cursorrules | testlint-formatstylearch+5 | 84/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/langchain-ai/.cursorrules · 17 | .cursorrules | testlint-formatstylearch+4 | 84/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/mobile-react-native/.cursorrules · 17 | .cursorrules | teststylearchtypes+7 | 89/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/nextjs-14-app-router/.cursorrules · 17 | .cursorrules | teststyletypestesting-strategy+3 | 71/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/nextjs-app-router/.cursorrules · 17 | .cursorrules | teststylearchtypes+5 | 81/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/nextjs-typescript/.cursorrules · 17 | .cursorrules | teststylearchtypes+5 | 81/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/nextjs/.cursorrules · 17 | .cursorrules | teststylearchtypes+5 | 81/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/nodejs-express-typescript/.cursorrules · 17 | .cursorrules | setupteststylearch+7 | 81/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/nodejs-express/.cursorrules · 17 | .cursorrules | testlint-formatstylearch+7 | 92/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/performance-optimization/.cursorrules · 17 | .cursorrules | styledatabaseapiperformance+2 | 65/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/python-django/.cursorrules · 17 | .cursorrules | testlint-formatstylearch+6 | 99/100 | 13 days ago | |
| survivorforge/cursor-rulesrules/python-fastapi/.cursorrules · 17 | .cursorrules | testlint-formatstylearch+6 | 99/100 | 13 days ago |
A badge carrying the measured quality of the strongest agent config file in this repository, out of 100. It reads from this index every time somebody loads your page, so it changes when the measurement changes and there is nothing to keep up to date. Free, no account, and the value is not something you or we can set by hand.
[](https://rulestack.kynth.studio/configs/survivorforge-cursor-rules-rules-go-production-cursorrules)Would rather not hotlink us? Every badge is also served in shields.io’s endpoint schema, so shields renders the image and your readers never talk to our domain:
Published by Toolproof, the masthead over this index and eight others. The method behind the number is at toolproof.kynth.studio/methodology, and the whole thing is readable as JSON with no key at /api.