| name | go-bootstrap |
| description | Guidelines and conventions for bootstrapping Go modules, project structures, and reliable production patterns (HTTP servers, database connections). Activate on module initialization, new service creation, layout scoping, or setup requests. |
Go Project Bootstrapping & Layout Conventions
This skill provides step-by-step instructions for establishing a modern, robust, and idiomatic Go module layout. It strictly follows official Go guidelines and emphasizes simplicity, compiler-safety, and production-ready architectures.
1. Layout Conventions
We adhere strictly to the official go.dev/doc/modules/layout conventions.
Flat Layout (Recommended for Simple Services & Libraries)
Keep all Go files in the root directory. It is simpler, avoids redundant packaging, and prevents import cycles.
my-service/
├── go.mod
├── go.sum
├── main.go
├── server.go
├── database.go
└── server_test.go
Rule of thumb: Start flat. Do not over-engineer with nested folders like pkg/ or internal/ until scale or the need for multiple binaries warrants it.
Nested Layout (Recommended for Multiple Binaries or Private Packages)
Use a nested structure only when creating multiple executable binaries or protecting private package APIs from external imports.
my-app/
├── go.mod
├── go.sum
├── cmd/
│ ├── api-server/
│ │ └── main.go
│ └── cli-tool/
│ └── main.go
└── internal/
├── auth/
│ └── auth.go
└── db/
└── db.go
cmd/: Contains subdirectories for each executable binary. Keep code in cmd/ minimal (only main.go and flag parsing/initialization).
internal/: Contains private application packages. Go compiler strictly blocks other modules from importing anything under internal/.
pkg/: NEVER use pkg/ in modern idiomatic Go. It is a legacy/non-standard convention that adds redundant path depth.
2. Production Patterns (Go 1.24+)
HTTP Server with Graceful Shutdown
Always implement graceful shutdown on HTTP servers to prevent dropped requests during deployments.
package main
import (
"context"
"errors"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
)
func run(ctx context.Context) error {
ctx, stop := signal.NotifyContext(ctx, os.Interrupt, syscall.SIGTERM)
defer stop()
mux := http.NewServeMux()
mux.HandleFunc("GET /health", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("OK"))
})
srv := &http.Server{
Addr: ":8080",
Handler: mux,
ReadTimeout: 5 * time.Second,
WriteTimeout: 10 * time.Second,
IdleTimeout: 120 * time.Second,
}
serverErr := make(chan error, 1)
go func() {
log.Printf("Listening on %s", srv.Addr)
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
serverErr <- err
}
}()
select {
case err := <-serverErr:
return err
case <-ctx.Done():
log.Println("Shutting down server gracefully...")
shutdownCtx, cancel := context.WithTimeout(context.Background(), 10 * time.Second)
defer cancel()
return srv.Shutdown(shutdownCtx)
}
}
func main() {
if err := run(context.Background()); err != nil {
log.Fatalf("Application error: %v", err)
}
}
Database Connection Pooling (database/sql)
Ensure connection limits and idle timeouts are configured on your *sql.DB instance to prevent leaks and handle transient failures.
package main
import (
"context"
"database/sql"
"time"
)
func initDB(ctx context.Context, dsn string) (*sql.DB, error) {
db, err := sql.Open("pgx", dsn)
if err != nil {
return nil, err
}
db.SetMaxOpenConns(25)
db.SetMaxIdleConns(25)
db.SetConnMaxLifetime(5 * time.Minute)
db.SetConnMaxIdleTime(3 * time.Minute)
pingCtx, cancel := context.WithTimeout(ctx, 3 * time.Second)
defer cancel()
if err := db.PingContext(pingCtx); err != nil {
db.Close()
return nil, err
}
return db, nil
}
3. Bootstrapping Workflow
- Use
project_init to create the target directory and run go mod init <module_path>.
- Determine if project needs a flat layout or a nested layout. Default to flat.
- Create core files (
main.go, server.go, etc.) using smart_edit.
- Add any required dependencies using
add_dependency.
- Run
smart_build to verify the entire module compiles and tests successfully.