소스 정보
- 저장소
- irahardianto/awesome-agv
- 최근 소스 활동
- 2026년 8월 6일 13:59
- 감지된 SKILL.md 언어
- 영어
- 스타
- 150
- 포크
- 48
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
메뉴
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/irahardianto/awesome-agv --skill go-idioms명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
SKILL.md 표시 중
| name | go-idioms |
| description | Go stdlib, error wrapping, interfaces, goroutines, table-driven tests, gofumpt. |
| paths | ["**/*.go","**/go.mod"] |
Go favors simplicity, explicitness, and readability. The language is intentionally small — resist the urge to import patterns from other languages. If it looks boring and obvious, it's probably idiomatic Go.
Scope: This file covers Go-specific coding idioms. For file layout, see
references/project-structure.md. For detailed safety, SAST, and performance patterns, seereferences/go-patterns-and-anti-patterns.md. For logging library choice, see thelogging-implementationskill. For quality commands, see thecode-idioms-and-conventionsrule.
Loading Guards: If the project has no go.mod, this skill does not apply.
| Situation | Reference to Load |
|---|---|
| Starting a new project or setting up file layout | references/project-structure.md |
Choosing packages, go.mod setup, or golangci-lint config | references/recommended-dependencies.md |
| Writing code that handles I/O, concurrency, or user input | references/go-patterns-and-anti-patterns.md |
Default to the latest stable Go release. As of August 2026, Go 1.24 is the baseline with the Go module system.
Key version milestones:
go tool for tool dependencies, swiss table map implementationrange-over-func (iterator protocol), unique packagerange-over-integer, loop variable per-iteration scoping (eliminates loop variable capture bugs), enhanced ServeMux with method-based routinglog/slog structured logging, maps and slices stdlib packages, sync.OnceFunc / sync.OnceValue / sync.OnceValues, min / max builtins, clear builtintesting.FExample go.mod configuration:
module github.com/user/project
go 1.24.0
toolchain go1.24.0
Always return errors — never panic in library or business code
panic is reserved for truly unrecoverable states (programmer errors, nil dereference)recover only at top-level goroutine boundaries (middleware, server startup)Wrap errors with context using %w
✅ Preserves the error chain for errors.Is / errors.As
return fmt.Errorf("creating task for user %s: %w", userID, err)
❌ Loses the error chain
return fmt.Errorf("creating task: %v", err)
Error string formatting
✅ Correct formatting
return fmt.Errorf("failed to open file %q: %w", path, err)
❌ Incorrect formatting
return fmt.Errorf("Failed to open file: %w.", err)
Use errors.Join for multi-error aggregation (Go 1.20+)
var errs []error
if err := doFirst(); err != nil {
errs = append(errs, err)
}
if err := doSecond(); err != nil {
errs = append(errs, err)
}
return errors.Join(errs...)
Use sentinel errors for expected branch conditions
// Define in errors.go
var ErrNotFound = errors.New("not found")
ErrUnauthorized = errors.New()
errors.Is(err, ErrNotFound) {
}
Keep interfaces small — one or two methods is ideal
✅ Focused, composable
type Reader interface { Read(p []byte) (n int, err error) }
type Writer interface { Write(p []byte) (n int, err error) }
❌ Monolithic
type FileManager interface {
Read()
Write()
Delete()
List()
Stat()
}
"Accept interfaces, return structs"
Define interfaces where they are used, not where they are implemented
✅ Defined in the consumer package (task feature)
// task/storage.go
type Storage interface {
GetByID(ctx context.Context, id string) (*Task, error)
}
// postgres.go implements Storage — it does NOT define it
Implicit satisfaction is a feature — don't use embedding to "implement" interfaces
implements keyword needed or wantedany vs interface{}
any (Go 1.18+ alias) instead of interface{}.Interface compliance verification
var _ Interface = (*Struct)(nil) to guarantee a type implements an interface at compile time.var _ Storage = (*PostgresStorage)()
For general concurrency principles (race conditions, deadlocks, message passing), see
concurrency-and-threading-principles.md. This section covers Go-specific mechanics.
Always pass context.Context as the first parameter
✅ Pass context
func (s *Service) GetTask(ctx context.Context, id string) (*Task, error)
❌ No way to cancel or propagate deadlines
func (s *Service) GetTask(id string) (*Task, error)
Never start a goroutine without knowing how it will stop (Anti-pattern: Goroutine Leak)
context.Context cancellation or channel close).✅ Goroutine is bounded by context cancellation
go func() {
for {
select {
case <-ctx.Done():
return
case item := <-ch:
process(item)
}
}
}()
Goroutine lifecycle management with sync.WaitGroup
var wg sync.WaitGroup
for _, task := range tasks {
wg.Add(1)
go func(t Task) {
defer wg.Done()
process(t)
}(task)
}
wg.Wait()
Use errgroup for concurrent fan-out with error collection
g, ctx := errgroup.WithContext(ctx)
g.Go(func() error { fetchUsers(ctx) })
g.Go( { fetchOrders(ctx) })
err := g.Wait(); err != { ... }
Receiver names: short, consistent, and the first letter of the type ✅
func (s *Service) Create(...) {}
❌
func (svc *Service) Create(...) {} // too verbose
func (self *Service) Create(...) {} // not Go
Package names: short, lowercase, no underscores, no plurals
✅ package task
❌ package tasks (plural)
❌ package task_service (underscore)
Acronyms follow Go conventions (all caps or all lowercase)
✅ userID, HTTPClient
❌ userId, HttpClient
Unexported identifiers omit the type name — if it's private, keep it terse
Don't stutter — task.Task is fine; task.TaskService is not
Error string formatting
Getter naming
Get prefix in Go. Use user.Name() not user.GetName().Test naming
TestFunctionName_Scenario_Expected pattern.func TestCalculateDiscount_NegativeInput_ReturnsError(t *testing.T) { ... }
Constructor naming
New prefix (NewService, NewClient).Range-over-func iterators (Go 1.23+)
// Definition
func Count(n int) iter.Seq[int] {
return func(yield func(int) bool) {
for i := 0; i < n; i++ {
if !yield(i) {
return
}
}
}
}
// Usage
for i := range Count(5) {
fmt.Println(i)
}
Nil slice vs empty slice semantics
var s []int (nil slice) marshals to null in JSON.s := []int{} or make([]int, 0) (empty slice) marshals to [] in JSON.Type assertions with comma-ok pattern
if str, ok := val.(string); ok {
fmt.Println(str)
}
Type switches for polymorphic dispatch
switch v := val.(type) {
case string:
fmt.Println("String:", v)
case int:
fmt.Println("Int:", v)
default:
fmt.Println("Unknown type")
}
strings.Builder for string concatenation
+ inside loops. Use .type Option func(*Service)
func WithTimeout(d time.Duration) Option {
return func(s *Service) { s.timeout = d }
}
func NewService(store Storage, opts ...Option) *Service {
s := &Service{store: store, timeout: 30 * time.Second}
for _, o := range opts { o(s) }
return s
}
defer for cleanup — always use error-checked closuresEvery deferred cleanup call that returns an error MUST check and log the error.
Never use bare defer X.Close() — the discarded error hides resource leak failures.
❌ NEVER: Error silently discarded
defer rows.Close()
✅ ALWAYS: Error-checked closure with structured logging
rows, err := db.QueryContext(ctx, query)
if err != nil { return fmt.Errorf("querying tasks: %w", err) }
defer func() {
if err := rows.Close(); err != nil {
slog.Warn("failed to close rows", "error", err, "operation", "ListTasks")
}
}()
Transaction rollback: ❌ NEVER
defer tx.Rollback()
✅ ALWAYS: Guard against sql.ErrTxDone (already committed)
defer func() {
if err := tx.Rollback(); err != nil && !errors.Is(err, sql.ErrTxDone) {
slog.Error("failed to rollback transaction", "error", err, "operation", "CreateOrder")
}
}()
HTTP response body: ❌ NEVER
defer resp.Body.Close()
✅ ALWAYS: Drain then close (prevents connection reuse issues)
defer func() {
if _, err := io.Copy(io.Discard, resp.Body); err != nil {
slog.Warn("failed to drain response body", "error", err)
}
if err := resp.Body.Close(); err != nil {
slog.Warn("failed to close response body", "error", err)
}
}()
Avoid init() functions
main or constructors.Use named return values only for documentation or defer-based cleanup
unique.Handle for string interning (Go 1.23+)
import "unique"
h := unique.Make("active")
// h.Value() returns "active"
// Two handles with the same underlying value compare as equal
h1 := unique.Make("active")
fmt.Println(h == h1) // true — same canonical representation
Test file naming and pyramid proportions are defined in
testing-strategy.md. This section covers Go-specific tooling only.
Table-driven tests are the default pattern
func TestCalculateDiscount(t *testing.T) {
tests := []struct {
name string
input float64
expected float64
wantErr bool
}{
{"zero items", 0, 0, false},
{"negative input", -1, 0, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := calculateDiscount(tt.input)
if tt.wantErr {
require.Error(t, err)
return
}
require.NoError(t, err)
assert.Equal(t, tt.expected, got)
})
}
}
Use testify for assertions
require for fatal assertions, assert for non-fatal.Run with the race detector in CI
go test -race ./...Use httptest.NewRecorder() for HTTP handler tests
Test behaviour, not implementation
Test helpers with t.Helper() and testing.TB interface
func createTestUser(t testing.TB) *User {
t.Helper()
}
| Approach | When to Use |
|---|---|
| Hand-written fake (implement interface) | Simple interface, few methods, need stateful behavior |
gomock/mockgen | Complex interface, need to verify call counts, argument matching |
testify/mock | Complex interface, prefer fluent assertion API |
| Table-driven tests | Same logic, multiple input/output pairs |
go-cmp | Deep equality comparison with custom options |
Prefer hand-written fakes for core domain traits.
All of the following must pass with zero warnings/errors before any commit. See code-idioms-and-conventions.md for the full checklist.
Feedback Loop — Development Workflow:
| Phase | Command | Purpose |
|---|---|---|
| TDD / rapid iteration | go vet ./... | Type-checks and correctness — fastest loop |
| Pre-commit | golangci-lint run | Aggregated linting — must pass zero warnings |
| Pre-commit | gofumpt -l -w . | Formatting — non-negotiable |
| Pre-commit | go test -race ./... | Unit tests with race detector |
| Coverage verification | go test -coverprofile=c.out ./... | Verify before merging |
| Security audit | govulncheck ./... | CVE scanning |
Lint suppression policy — NEVER suppress these:
| Lint | What It Signals | What To Do Instead |
|---|---|---|
errcheck | Unchecked error returns | Handle the error — use error-checked closure in defer |
govet shadow | Variable shadowing | Rename the inner variable |
staticcheck SA* | Correctness issues | Fix the underlying bug |
gosec G* (security) | Security vulnerability | Fix the vulnerability |
cyclop / gocognit | Function too complex | Decompose into smaller functions |
Acceptable suppressions (with mandatory // nolint: comment + rationale):
| Lint | When Acceptable |
|---|---|
gosec G104 (unhandled error) | In test code only, with // nolint:gosec // test-only |
revive exported | When the exported symbol is intentionally part of API design |
Rule of thumb: If you're about to write //nolint:, stop and ask if you're suppressing a real design problem.
//nolint:errcheck is NEVER acceptable. If a function returns an error, handle it — even in defer. Use an error-checked closure. This is the #1 source of audit findings.
Logging: Never use
fmt.Printlnorlog.Printfin production service code — these produce unstructured output. Uselog/slog(stdlib, Go 1.21+) or the project's chosen adapter. See@.agents/skills/logging-implementation/SKILL.mdfor the required library and patterns.
Godoc conventions
Example functions
func ExampleFunction() with // Output: comments at the end of the function body for runnable examples in Godoc.func ExampleHello() {
fmt.Println("Hello")
// Output: Hello
}
Package doc
doc.go file.Document the WHY, not the WHAT
Minimize dependency count
Commit go.sum always
go.mod and go.sum belong in version control to guarantee reproducible builds.go mod tidy before commits
govulncheck in CI
Module proxy and checksum database
GOPROXY and the checksum database. For private modules, configure GOPRIVATE or GONOSUMCHECK.Prefer stdlib over third-party
log/slog vs zap), use the stdlib.Never scatter os.Getenv() calls throughout codebase
Centralized config struct parsed at startup
Config struct once, then inject it (or its subsets) where needed.Fail fast
Example with struct tags and validation
type Config struct {
Port int `env:"PORT" envDefault:"8080"`
Database string `env:"DATABASE_URL" envRequired:"true"`
}
func LoadConfig() (*Config, error) {
// load and validate
}
defer.references/go-patterns-and-anti-patterns.md.perf-optimization skill.Use typed errors for rich domain errors
type ValidationError struct {
Field string
Message string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("validation failed on %s: %s", e.Field, e.Message)
}
// Caller unwraps with errors.As
var ve *ValidationError
if errors.As(err, &ve) {
// access ve.Field, ve.Message
}
Handle errors at the right level
Use lazy evaluation for fallback values
When NOT to use interfaces
Generic interfaces with type parameters (Go 1.18+)
type Repository[T any] interface {
FindByID(ctx context.Context, id string) (T, error)
}
Prefer channels for ownership transfer; mutexes for shared state
Close channels from the sender, never the receiver
context.AfterFunc (Go 1.21+)
stop := context.AfterFunc(ctx, func() {
cleanupResources()
})
defer stop()
Channel direction restrictions in function signatures
func producer(ch chan<- int) {} // Send-only
func consumer(ch <-chan int) {} // Receive-only
sync.OnceFunc / sync.OnceValue (Go 1.21+)
var initDb = sync.OnceValue(func() *sql.DB {
// initialize DB
return db
})
select with default for non-blocking operations
select {
case ch <- data:
// Sent
default:
// Channel is full or unbuffered with no receiver, move on
}
context.WithoutCancel (Go 1.21+)
// Request-scoped context cancelled when handler returns
bgCtx := context.WithoutCancel(ctx)
go func() {
// This work continues even after the parent request completes
auditLog(bgCtx, event)
}()
⚠️ Use sparingly — most work should respect parent cancellation. Reserve for fire-and-forget operations where partial completion is acceptable.
Structured concurrency with conc (when errgroup isn't enough)
errgroup covers fan-out with error collection. For additional needs — panic recovery within goroutines, bounded worker pools with panic safety — use github.com/sourcegraph/conc.// conc.WaitGroup recovers panics and re-panics on Wait()
wg := conc.NewWaitGroup()
wg.Go(func() { process(item1) })
wg.Go(func() { process(item2) })
wg.Wait() // Re-panics if any goroutine panicked
errgroup for simple fan-out. Reach for conc when you need panic recovery or bounded concurrency with conc/pool.-er suffix for single-method interfaces (Reader, Writer, Stringer).strings.Buildervar b strings.Builder
b.WriteString("hello")
b.WriteString("world")
return b.String()
slices and maps stdlib packages (Go 1.21+)
slices.Sort(mySlice)
if slices.Contains(mySlice, target) { ... }
Variadic append
append(s1, s2...) instead of loops.Pre-allocation
make([]T, 0, n) when the size is known to avoid reallocation overhead.sync.Pool for hot-path allocations
time.NewTicker vs time.Tick
time.Tick leaks the underlying ticker. Always prefer time.NewTicker which can be stopped via ticker.Stop().Struct embedding for composition (not inheritance)
Early returns / guard clauses
if err != nil {
return err
}
// Continue happy path
Keep function complexity low
Functional options for optional configuration
t.Cleanup() for resource teardown
func setupDB(t *testing.T) *sql.DB {
db := connect()
t.Cleanup(func() { db.Close() })
return db
}
Fuzz testing with testing.F (Go 1.18+)
func FuzzParse(f *testing.F) {
f.Add("valid_input")
f.Fuzz(func(t *testing.T, input string) {
Parse(input) // ensure it never panics
})
}
Benchmark patterns with testing.B
func BenchmarkProcess(b *testing.B) {
b.ReportAllocs() // ALWAYS report allocations
b.ResetTimer() // Ignore setup time
for i := 0; i < b.N; i++ {
Process()
}
}
Golden file testing
Test coverage non-negotiable policy
Coverage commands
go test -cover ./... and go test -coverprofile=coverage.out ./....Test Double Selection Table