Gin — Structure#

Layout pattern#

Flat Library with Focused Sub-packages

Gin is a library, not a runnable service, so it has no cmd/ directory and no main.go. The core framework code lives directly in the root package (package gin), which is the most common pattern for Go libraries. Domain-specific concerns are delegated to focused sub-packages rather than a sprawling internal hierarchy. This is similar to the net/http stdlib approach: the root package is the public surface, sub-packages handle specializations.

Directory map#

gin/
├── *.go                  # Core framework (package gin): Engine, Context, RouterGroup,
│                         #   radix tree router, middleware, auth, recovery, logger, etc.
├── binding/              # Request body binding: JSON, XML, YAML, TOML, form, query,
│                         #   URI, header, multipart, protobuf, msgpack, BSON
├── codec/
│   └── json/             # Pluggable JSON backend selection: chooses between
│                         #   sonic, goccy/go-json, json-iterator, or stdlib encoding/json
├── render/               # Response rendering: JSON, XML, HTML templates, text, binary,
│                         #   protobuf, msgpack, TOML, YAML, redirect, PDF, SSE
├── ginS/                 # Singleton convenience wrapper — wraps gin.Default() in a
│                         #   package-level instance for quick scripts / simple apps
├── internal/
│   ├── bytesconv/        # Zero-copy string↔[]byte helpers (unsafe, package-private)
│   └── fs/               # Filesystem abstraction (OnlyFilesFS) for Static serving
├── examples/             # Usage examples (not imported, for documentation only)
├── testdata/
│   ├── certificate/      # TLS cert/key for HTTPS tests
│   ├── protoexample/     # Proto-generated types for binding tests
│   └── template/         # HTML template files for render tests
├── docs/                 # Minimal docs: doc.md (package godoc overview)
└── .github/
    └── workflows/        # CI: gin.yml (test matrix), goreleaser.yml, codeql.yml, trivy-scan.yml

Entry points#

Gin has no binary entry points. There is no cmd/ directory and no main.go. The library is consumed by importing github.com/gin-gonic/gin. Users call gin.New() or gin.Default() to create an engine, register routes, and call engine.Run() or use it with http.ListenAndServe.

The ginS/ package provides a package-level singleton (var engine = sync.OnceValue(...)) as a convenience for minimal programs that want to skip explicit engine creation.

Package organization#

  • Internal packages:

    • internal/bytesconv — unsafe string-to-bytes conversions that avoid allocations; used in hot paths in context.go and tree.go
    • internal/fsOnlyFilesFS wrapper that prevents directory listing when serving static files
  • Public packages (pkg/): No pkg/ directory. All exported packages live as top-level sub-packages:

    • binding — request body/form/query/URI parsing and validation; exposes Binding and StructValidator interfaces
    • render — response serialization; exposes Render interface implemented by ~12 format-specific types
    • codec/json — compile-time/runtime-switchable JSON encoder; abstracts encoding/json, json-iterator, sonic, goccy/go-json
    • ginS — singleton wrapper; depends only on the root gin package
  • Layering:

    • internal/*gin (root) ← binding, render, codec/json (sub-packages used by the root)
    • ginSgin (root)
    • The layering is intentionally shallow: no clean-architecture layers, no separate domain/service/repository tiers. Gin is infrastructure, not a domain application.

Build system#

  • Build tool: make (Makefile at root) + goreleaser for releases (.github/workflows/goreleaser.yml)
  • Key targets:
    • make test — runs tests across gin, ginS, binding, render packages with coverage collection
    • make fmt / make fmt-checkgofmt -s formatting
    • make vetgo vet across all packages except examples
    • make lintgolint
    • make misspell / make misspell-check
    • make tools — install golint and misspell
  • Docker: No Dockerfile; gin is a library and ships no container
  • CI: GitHub Actions (gin.yml) runs the test matrix; goreleaser.yml handles tag-triggered releases; trivy-scan.yml does vulnerability scanning; codeql.yml does static security analysis

Notable structural decisions#

  1. Core in root package. All of Engine, Context, RouterGroup, HandlerFunc, tree, and built-in middleware (Logger, Recovery, BasicAuth) live in the root gin package. This makes the import path short (gin.Context, gin.Engine) and avoids package-name collisions, at the cost of a large root package (~25 source files).

  2. binding and render as sibling sub-packages, not internal. These are exported sub-packages that users can import directly (e.g., binding.JSON, render.JSON) and extend by implementing interfaces. Making them public enables custom binding backends and render targets without forking.

  3. codec/json as a build-tag-driven abstraction. The codec/json package uses a single api.go file plus multiple backend files (sonic.go, go_json.go, jsoniter.go, json.go) where build tags or blank imports select the active backend at compile time. This is a deliberate performance optimization mechanism so users can swap JSON libraries without changing application code.

  4. ginS as an optional convenience layer. Rather than exposing global functions in the root package (which would pollute the main API and cause init-order issues), gin factored singleton behaviour into its own package. This is an unusual but tidy choice that keeps the core API clean.

  5. No vendor directory. Gin uses standard module mode with no vendoring, appropriate for a library that is itself a dependency. The go.sum file provides reproducibility.

  6. Flat examples directory. Examples are not organized into sub-packages with main functions; the directory is a simple collection of standalone files or sub-directories browsed by users reading documentation. They are explicitly excluded from make vet (VETPACKAGES filters out /examples/).