From ebfe57a1d102127bf7e7ea8ccbca6a62ec5d1f9e Mon Sep 17 00:00:00 2001 From: Sumedha Basak Date: Sat, 12 Sep 2026 22:33:36 +0530 Subject: [PATCH 1/2] docs: add go-size-analyzer, module version, and reflection --- docs/v3/binary-size.md | 124 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 124 insertions(+) diff --git a/docs/v3/binary-size.md b/docs/v3/binary-size.md index 0677a23b79..15de0f7e51 100644 --- a/docs/v3/binary-size.md +++ b/docs/v3/binary-size.md @@ -51,3 +51,127 @@ only import `github.com/urfave/cli/v3` do not pull in that package. Shell completion support is part of the core package. Leave `EnableShellCompletion` disabled unless the application needs shell completion, then measure the result with the commands above. + + +## Deeper Analysis with go-size-analyzer + +The commands above show symbol-level sizes, but they do not clearly show which +packages contribute most to the binary. [go-size-analyzer](https://github.com/Zxilly/go-size-analyzer) +provides a package-level breakdown. + +Install it with: + + +```sh-session +go install github.com/Zxilly/go-size-analyzer/cmd/gsa@latest +``` + +Then analyze a binary: + +```sh-session +gsa myapp +``` + +For example, a minimal `urfave/cli/v3` binary showed: + +``` +21.58% runtime 1.0 MB + 6.57% reflect 315 kB + 6.25% .rodata 300 kB + 5.87% github.com/urfave/cli/v3 282 kB + 5.74% text/template 275 kB +``` + +This helps identify which packages are worth investigating. Packages such as +`runtime` are fundamental to a Go binary and are not normally removable. +For an interactive view, use: + +```sh-session +gsa --tui myapp +``` + +> **Note:** analyze an unstripped binary when possible. Stripping debug +> information can make package attribution less accurate. + +## Dependency Versions and Multiple Modules + +A repository may contain multiple Go modules, each with its own `go.mod`. +Check the modules and their selected versions with: + +```sh-session +go list -m all +``` + +For a specific dependency: + +```sh-session +go list -m -json github.com/urfave/cli/v3 +``` + +Different `go.mod` files do not automatically cause multiple versions of the +same module to be included in a binary. Go normally selects a single version +of a module path for a build. + +However, when comparing builds across multiple modules, it is important to +check the actual selected version rather than relying only on the version +written in `go.mod`. For example, the documentation module uses: + +```go +replace github.com/urfave/cli/v3 => ../ +``` + +so it builds against the local `urfave/cli/v3` checkout. + +## Reflection and Templates + +Reflection and runtime type inspection can contribute to binary size because +additional type information and supporting code may remain reachable. + +In the example binary, `go-size-analyzer` reported: + +``` +reflect 315 kB +text/template 275 kB +``` + +These numbers show their contribution to the analyzed binary, but do not by +themselves prove that all of this code comes from a single feature. +`urfave/cli/v3` uses templates for help rendering, making `text/template` one +of the packages worth investigating when analyzing binary size. + +## Hiding Built-in Help + +Disabling built-in help does not currently reduce the binary size. + +Two otherwise identical binaries were built: + +```go +&cli.Command{} +``` + +and: + +```go +&cli.Command{ + HideHelp: true, + HideHelpCommand: true, +} +``` + +Both were approximately 4.6 MB, and `go-size-analyzer` reported the same +sizes for the relevant packages: + +| Package | Normal | Hidden | +|------------------------------|--------|--------| +| `reflect` | 315 kB | 315 kB | +| `text/template` | 275 kB | 275 kB | +| `github.com/urfave/cli/v3` | 282 kB | 282 kB | + +`HideHelp` and `HideHelpCommand` are runtime options, so hiding help does not +remove the help implementation from the compiled binary. If reducing this +overhead is important, a compile-time approach — such as separate build +configurations or changes to the library — would be required. + + + + From 625ef955900211ca72709a9070c6e5daa2f5b654 Mon Sep 17 00:00:00 2001 From: Sumedha Basak Date: Sun, 13 Sep 2026 13:20:03 +0530 Subject: [PATCH 2/2] doc:add go-size-analyzer, module version, and reflection notes --- docs/v3/binary-size.md | 56 ++++++++++++++++-------------------------- 1 file changed, 21 insertions(+), 35 deletions(-) diff --git a/docs/v3/binary-size.md b/docs/v3/binary-size.md index 15de0f7e51..89d37ab3d8 100644 --- a/docs/v3/binary-size.md +++ b/docs/v3/binary-size.md @@ -40,19 +40,6 @@ If a specific `urfave/cli` feature appears to keep unexpected code reachable, build command, a minimal reproduction, and the `go tool nm -size` output that shows the largest symbols. -## Current v3 Build Tags - -The v3 module does not currently define build tags such as -`urfave_cli_no_docs`, `urfave_cli_no_completion`, or `urfave_cli_minimal`. -Documentation generation lives outside the core module in -[`urfave/cli-docs`](https://github.com/urfave/cli-docs), so applications that -only import `github.com/urfave/cli/v3` do not pull in that package. - -Shell completion support is part of the core package. Leave -`EnableShellCompletion` disabled unless the application needs shell completion, -then measure the result with the commands above. - - ## Deeper Analysis with go-size-analyzer The commands above show symbol-level sizes, but they do not clearly show which @@ -61,7 +48,6 @@ provides a package-level breakdown. Install it with: - ```sh-session go install github.com/Zxilly/go-size-analyzer/cmd/gsa@latest ``` @@ -108,15 +94,15 @@ For a specific dependency: go list -m -json github.com/urfave/cli/v3 ``` -Different `go.mod` files do not automatically cause multiple versions of the -same module to be included in a binary. Go normally selects a single version -of a module path for a build. - -However, when comparing builds across multiple modules, it is important to -check the actual selected version rather than relying only on the version -written in `go.mod`. For example, the documentation module uses: +Go's module resolution (Minimal Version Selection) picks a single version of +each module path per main module build, so differing `go.mod` files do not by +themselves cause duplicate versions of the same module in a given binary. +This mainly matters when comparing binaries built from *different* main +modules, since each one resolves versions independently — so check the +actual selected version rather than relying only on the version written in +`go.mod`. For example, the documentation module uses: -```go +``` replace github.com/urfave/cli/v3 => ../ ``` @@ -124,20 +110,12 @@ so it builds against the local `urfave/cli/v3` checkout. ## Reflection and Templates -Reflection and runtime type inspection can contribute to binary size because -additional type information and supporting code may remain reachable. - -In the example binary, `go-size-analyzer` reported: +Reflection and templates can contribute noticeably to binary size because additional type information and supporting code may remain reachable. In the analyzer output, `reflect` and `text/template` are among the larger contributors. -``` -reflect 315 kB -text/template 275 kB -``` - -These numbers show their contribution to the analyzed binary, but do not by -themselves prove that all of this code comes from a single feature. `urfave/cli/v3` uses templates for help rendering, making `text/template` one -of the packages worth investigating when analyzing binary size. +of the packages worth investigating when analyzing binary size. These numbers +show their contribution to the analyzed binary, but do not by themselves +prove that all of this code comes from a single feature. ## Hiding Built-in Help @@ -172,6 +150,14 @@ remove the help implementation from the compiled binary. If reducing this overhead is important, a compile-time approach — such as separate build configurations or changes to the library — would be required. +## Current v3 Build Tags +The v3 module does not currently define build tags such as +`urfave_cli_no_docs`, `urfave_cli_no_completion`, or `urfave_cli_minimal`. +Documentation generation lives outside the core module in +[`urfave/cli-docs`](https://github.com/urfave/cli-docs), so applications that +only import `github.com/urfave/cli/v3` do not pull in that package. - +Shell completion support is part of the core package. Leave +`EnableShellCompletion` disabled unless the application needs shell completion, +then measure the result with the commands above. \ No newline at end of file