SOC 직업 분류 기준
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/tomevault-io/skills-registry --skill golang-dev명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
SKILL.md 표시 중
| Use when this capability is needed.
> Use when this capability is needed.
Review architecture and API design for the vfs-s3 project. Use when the user mentions @architect, asks to review an issue's design, discuss module boundaries, API shape, or architectural decisions for vfs-s3. Also trigger when the user wants to create an ADR (Architecture Decision Record) or evaluate a technical approach for the project. Intended for dispatch from Codex automation or Claude routines; GitHub trigger phrase: @vfs-s3-bot please prepare design doc Use when this capability is needed.
| name | golang-dev |
| description | > Use when this capability is needed. |
Go development best-practices guide. Covers library choices, build/test commands, and escape analysis.
When to use: Any Go project exposing a command-line interface.
Always use github.com/spf13/cobra. Structure:
cmd/
root.go # Root command + global flags
serve.go # Subcommand: serve
migrate.go # Subcommand: migrate
main.go # Only calls cmd.Execute()
// cmd/root.go
package cmd
import (
"fmt"
"os"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var cfgFile string
var rootCmd = &cobra.Command{
Use: "myapp",
Short: "Short description of myapp",
}
func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
func init() {
cobra.OnInitialize(InitConfig)
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default $HOME/.myapp.yaml)")
}
func InitConfig() {
if cfgFile != "" {
viper.SetConfigFile(cfgFile)
} else {
home, _ := os.UserHomeDir()
viper.AddConfigPath(home)
viper.AddConfigPath(".")
viper.SetConfigName(".myapp")
viper.SetConfigType("yaml")
}
viper.AutomaticEnv()
_ = viper.ReadInConfig()
}
// cmd/serve.go
package cmd
import (
"fmt"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var serveCmd = &cobra.Command{
Use: "serve",
Short: "Start the HTTP server",
RunE: func(cmd *cobra.Command, args []string) error {
port := viper.GetInt("port")
fmt.Printf("Listening on :%d\n", port)
// start server...
return nil
},
}
func init() {
rootCmd.AddCommand(serveCmd)
serveCmd.Flags().IntP("port", "p", 8080, "server port")
viper.BindPFlag("port", serveCmd.Flags().Lookup("port"))
}
RunE (not Run) so errors propagate instead of silently failing.PersistentFlags() for flags inherited by all subcommands; Flags() for command-specific.viper.BindPFlag() in init() to unify flag and config access.If github.com/bizshuk/gosdk is available, record every CLI invocation by wiring the metric hook into the root command before Execute():
import "github.com/bizshuk/gosdk/metric"
func init() {
metric.CobraCMDHook(rootCmd)
}
Each execution emits one metric via Prometheus remote-write to the backend configured by the METRIC_URL viper key (default: VictoriaMetrics http://localhost:8428/api/v1/write; override with viper.Set("METRIC_URL", ...), the APP_METRIC_URL env var when using config.Default(), or METRIC_URL env var with viper.AutomaticEnv()):
command_line_trigger{cmd="myapp sub leaf", flag="env-verbose"} = 1
cmd — full command chain, root → leaf (cmd.CommandPath()).flag — flags the user actually set, collected across the whole chain, alphabetically sorted, joined with -; empty string when no flag was set.PersistentPreRunE on root. If any subcommand defines its own PersistentPreRunE, set cobra.EnableTraverseRunHooks = true (once, in init()), otherwise cobra skips the root hook and no metric is emitted for that subcommand.monitor sub command is used to overall monitoring for the command, with --merge is one time fetch and merge into pre defined filesWhen to use: Any Go project that needs configuration management.
IMPORTANT: If github.com/bizshuk/gosdk is available, always use its config.Default() for configuration loading. Fall back to raw viper manual setup only if the SDK is not supported or available.
For database access, use the db package. Each storage type is a service with its own global singleton and a flat <TYPE>_<FIELD> viper key:
import "github.com/bizshuk/gosdk/db"
config.Default()
if viper.IsSet("SQLITE_PATH") {
if err := db.InitSQLite(); err != nil { /* handle */ }
}
if viper.IsSet("MYSQL_DSN") {
if err := db.InitMySQL(); err != nil { /* handle */ }
}
if viper.IsSet("POSTGRES_DSN") {
if err := db.InitPostgres(); err != nil { /* handle */ }
}
// Anywhere later in the process:
gormDB := db.DefaultSQLite.DB()
defer db.DefaultSQLite.Close()
YAML (flat keys, uppercase underscore):
SQLITE_PATH: ./app.db
Env var (with APP_ prefix from config.Default()): APP_SQLITE_PATH, APP_MYSQL_DSN, APP_POSTGRES_DSN.
Why a singleton per storage type: micro-service concept forbids two of the same storage type in one process. Init<Storage>() refuses double-init and returns an error if called twice. The *gorm.DB is cached in the singleton, so any code path that needs it just reads db.DefaultSQLite.DB() instead of opening its own connection.
Always use github.com/spf13/viper. Loading precedence (highest wins):
Environment variables (viper.AutomaticEnv())CLI flags (bound via viper.BindPFlag())Config file (YAML preferred)Defaults (viper.SetDefault())For non-DB settings, unmarshal nested config (e.g., server.* blocks) into a typed struct. Database access uses the db package above; do not include DB fields here.
type Config struct {
Server ServerConfig `mapstructure:"server"`
}
type ServerConfig struct {
Port int `mapstructure:"port"`
ReadTimeout int `mapstructure:"read_timeout"`
}
func LoadConfig() (*Config, error) {
var cfg Config
if err := viper.Unmarshal(&cfg); err != nil {
return nil, fmt.Errorf("unmarshal config: %w", err)
}
return &cfg, nil
}
# .myapp.yaml
server:
port: 8080
read_timeout: 30
Env prefix: Call viper.SetEnvPrefix("MYAPP") so env vars like MYAPP_SERVER_PORT map correctly.Nested keys in env: Use viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) so server.port maps to MYAPP_SERVER_PORT.Type mismatch: viper.Unmarshal uses mapstructure tags, not json or yaml tags.| Category | Library | When to use |
|---|---|---|
| Logging | go.uber.org/zap | Structured logging. zap.NewProduction() for JSON, zap.NewDevelopment() for console. |
| Testing | github.com/stretchr/testify | assert (continue on fail), require (stop on fail). |
| Linting | golangci-lint | Meta-linter. Install: go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest. |
| HTTP | net/http (stdlib) | Prefer stdlib. Use github.com/gin-gonic/gin only when routing complexity justifies it. |
| Hot Reload | github.com/air-verse/air | Dev-time file watcher. Run air instead of go run. |
| Mocking | github.com/bytedance/mockey | Runtime monkey-patching for tests. |
| CLI | github.com/spf13/cobra | CLI scaffolding (see Section 1). |
| Config | github.com/spf13/viper | Config management (see Section 2). |
func NewLogger(isDev bool) (*zap.Logger, error) {
if isDev {
return zap.NewDevelopment()
}
return zap.NewProduction()
}
func TestGetUser(t *testing.T) {
assert := assert.New(t)
require := require.New(t)
user, err := GetUser(ctx, "123")
require.NoError(err) // stops test if err != nil
assert.Equal("Alice", user.Name) // continues even if fails
}
Use mockey to mock functions and methods. Always execute mocking code within mockey.PatchConcurrently to ensure thread safety:
func TestGetUserDiscount(t *testing.T) {
mockey.PatchConcurrently(t, func() {
// Mock a package-level function
mockey.Mock(FetchUserFromDB).Return(&User{ID: "123", Age: 65}, nil).Build()
discount, err := GetUserDiscount("123")
assert.NoError(t, err)
assert.Equal(t, 0.2, discount)
})
}
| Task | Command |
|---|---|
| Standard build | go build ./... |
| Production binary | go build -ldflags="-s -w" -o bin/app ./cmd/app |
| Static binary (no CGO) | CGO_ENABLED=0 go build -ldflags="-s -w" -o bin/app ./cmd/app |
| Version injection | go build -ldflags="-X main.version=1.2.3 -X main.buildTime=$(date -u +%Y-%m-%dT%H:%M:%SZ)" |
| Cross compile (Linux) | GOOS=linux GOARCH=amd64 go build -o bin/app-linux ./cmd/app |
| Race detector | go build -race ./... |
-s strips symbol table, -w strips DWARF debug info — ~30-40% smaller binary.CGO_ENABLED=0 produces a fully static binary (no libc dependency) — ideal for scratch/distroless Docker images.-race enables the race detector — use in CI but NOT in production (10x slowdown).DO NOT create interface for pure test or coverage
| Task | Command |
|---|---|
| Run all tests | go test ./... |
| Verbose | go test -v ./... |
| Race detector | go test -race ./... |
| Coverage | go test -cover -coverprofile=coverage.out ./... |
| View coverage HTML | go tool cover -html=coverage.out |
| Specific test | go test -run TestFunctionName ./pkg/... |
| Benchmarks | go test -bench=. -benchmem -run=^$ ./... |
| Short mode | go test -short ./... |
| Disable caching | go test -count=1 ./... |
| Timeout | go test -timeout 30s ./... |
| Disable inlining | go test -gcflags="all=-N -l" ./... |
go test -gcflags="all=-N -l" ./...
-N disables all compiler optimizations.-l disables inlining (function calls remain as actual calls, not inlined).all= applies the flags to all packages being compiled, not just the test package. Without all=, only the direct test target gets the flags — dependencies may still be inlined.Use cases:
dlv (delve) — requires non-inlined frames for accurate breakpoints and variable inspection.mockey (or other monkey-patching libraries) rewrite function machine code at runtime. If a target function is inlined or optimized, the patch will fail because there is no independent function entry point to rewrite.When to use: Investigating heap allocations, optimizing memory-sensitive hot paths.
# Basic: shows escape decisions
go build -gcflags='-m' ./...
# Detailed: shows reasoning for each decision
go build -gcflags='-m=2' ./...
# Specific package
go build -gcflags='-m=2' ./pkg/handler/...
# Filter for escapes only
go build -gcflags='-m=2' ./... 2>&1 | grep "escapes to heap"
| Output | Cause | Fix |
|---|---|---|
leaking param: x | Param stored beyond function scope | Avoid storing pointer params in long-lived structs |
moved to heap: too large | Stack frame exceeds ~10MB | Break into smaller allocations or use sync.Pool |
moved to heap: captured by closure | Variable captured in closure | Pass value explicitly as parameter |
moved to heap: interface conversion | Concrete assigned to any/interface{} | Use concrete types in hot paths |
&x escapes to heap | Returning address of local | Return value instead of pointer when possible |
go build -gcflags='-m=2' ./... 2>&1 | grep "escapes to heap" to find escaping allocations.hot paths — handlers, loops, frequently called functions. Don't optimize cold code.go test -bench=. -benchmem to measure allocs/op before and after changes.Reference: Stack allocation ~0.26ns vs heap ~10.55ns — ~40x penalty per escaped allocation.
| Task | Command |
|---|---|
| Build (dev) | go build ./... |
| Build (prod) | go build -ldflags="-s -w" -o bin/app ./cmd/app |
| Build (static) | CGO_ENABLED=0 go build -ldflags="-s -w" -o bin/app ./cmd/app |
| Test | go test ./... |
| Test (race) | go test -race ./... |
| Test (cover) | go test -cover -coverprofile=coverage.out ./... |
| Test (no inline) | go test -gcflags="all=-N -l" ./... |
| Test (bench) | go test -bench=. -benchmem -run=^$ ./... |
| Escape analysis | go build -gcflags='-m=2' ./... |
| Lint | golangci-lint run ./... |
| Vet | go vet ./... |
| Format | gofmt -w . or goimports -w . |
| Hot reload | air |
| Cross compile | GOOS=linux GOARCH=amd64 go build -o bin/app ./cmd/app |
Source: BizShuk/gosdk — distributed by TomeVault.