Skip to content

policy for removing GODEBUG flags #76163

Description

@griesemer

Paying off technical debt: a policy proposal for removing GODEBUG flags

This is an umbrella issue for the decommissioning of GODEBUG flags. It proposes a policy that guides their deprecation and eventual removal.

Background

The release of Go 1 in March 2012 marked a significant milestone, introducing the Go 1 compatibility guarantee. This guarantee ensures that programs adhering to the Go 1 specification will consistently compile and execute across subsequent Go 1.x releases, with rare exceptions for security patches or critical specification corrections.

Over the past 13 years, we have addressed various security and other issues which, while not violating the Go 1 compatibility guarantee, occasionally led to altered application program behavior. To facilitate smoother transitions for developers, we introduced the GODEBUG environment variable, enabling the selective activation or deactivation of new behaviors.

For instance, Go 1.21 introduced the panicnil setting, controlling whether panic(nil) is allowed; it defaults to panicnil=0, making panic(nil) a run-time error. Using panicnil=1 restores the behavior of Go 1.20 and earlier. This buys developers time: when upgrading from Go 1.20 to Go 1.21 with code that depends on the old panic(nil) behavior, one only needs to set panicnil=1 and the (unchanged) code will run as before. When time allows, the code can be adjusted to the new panic behavior and the reliance on the GODEBUG flag can be removed.

The complete list of GODEBUG flags, along with their default settings, is available here. Some flags were introduced with the intention of remaining permanent (e.g., the netdns flag). Others have an "earliest removal date"; for instance, the gotypesalias flag may be removed with Go 1.27 at the earliest. Some flags, such as x509sha1, have already been removed. Finally, some flags have neither a removal date nor do we guarantee that they will be permanent. Importantly, GODEBUG settings added for compatibility must be maintained for a minimum of two years, per the original policy.

The proliferation of these flags over time has generated a maintenance burden and increased complexity. Each GODEBUG flag represents a divergence point, thereby reducing predictability and significantly complicating comprehensive testing of the Go toolchain's behavior. Thus, we have an interest in deprecating and removing eligible GODEBUG flags as soon as possible, but not before ascertaining minimal impact on developers.

In the following we propose a policy and implementation for how to go about removing/deprecating GODEBUG flags.

Policy proposal

GODEBUG flags can be categorized as follows, from easiest to hardest to deprecate and remove:

  1. Flags that existed at some point but have since been removed.
  2. Flags that have an earliest removal date.
  3. Flags that are not called out as permanent, and which don't have an earliest removal date.
  4. Flags that are explicitly called out as permanent (no removal planned, at least when the flag was introduced).

We propose the following steps to deprecate and remove flags, according to their current status:

1. Flags that existed at some point but have since been removed.

Obviously, flags in this category require no further action, except that we may want to record their past existence for historical reasons (see Technical implementation, below).

2. Flags that have an earliest removal date.

For flags with an established earliest removal date, the process is as follows: in the release immediately preceding the expiration date, we will mark the flag as deprecated (see Technical implementation, below) and announce the intended removal of the flag in the subsequent release. The announcement is done in the release notes of the prior release (and possibly any related issues, see below). If no significant objections are raised during that cycle, the flag will be removed (see Technical implementation, below). If strong objections or demonstrated critical reliance on the old behavior emerge, the removal will be postponed by one major Go release cycle, and the process will be repeated until the removal can proceed without undue disruption. If removal is deemed impossible, the flag will be marked as permanent. Objections are raised by filling a flag-specific issue on the Go issue tracker.

3. Flags that are not called out as permanent, and which don't have an earliest removal date.

Flags lacking an explicit expiration date must first be assigned one. The Go team or the community may suggest an explicit earliest removal date that is no sooner than half a year (one major Go release cycle) in the future, and also no sooner than two years after the flag's introduction (for flags that were introduced for compatibility), whichever is later. The suggestion is recorded by filling an issue on the Go issue tracker, at which point these flags will be managed according to the process defined for Category 2.

4. Flags that are explicitly called out as permanent (no removal planned, at least when the flag was introduced).

The removal of permanent flags requires an explicit proposal that advocates for their removal and includes a concrete, well-justified target removal date which is no sooner than two years after the flag's introduction. This proposal must analyze and address the specific backward compatibility concerns and provide a robust plan for mitigating any potential negative impact on the Go ecosystem. Once this proposal is accepted, the flag will be treated similarly to those in Category 2, beginning with the announcement cycle leading up to the agreed-upon removal date.

Finally, any part of the process outlined above, including specific removal dates or deprecation cycles, can be revised or aborted if a concrete proposal is submitted and accepted. Such a proposal must be specific to the flag or group of flags in question, clearly justifying the change to the deprecation plan based on new information, community feedback, or critical technical findings that warrant a deviation from the established policy.

Policy implementation

If this proposal is accepted, we publicize it, for instance with a blog post. To maintain flag history, we document decisions by updating the original policy document (or an alternative suitable document).

Issue #75316 is an application of step 3) above: it proposes the removal of a set of crypto-related flags introduced in Go 1.23 and earlier. If accepted, the respective flags will be removed for Go 1.27.

Technical implementation

API changes

godebug.Setting (internal package godebug) provides a new mechanism to identify a flag as deprecated and removed. For instance:

type FlagStatus int  
const (  
	Active FlagStatus = iota  
	Deprecated  
	Removed  
)

func (*Status) Status() FlagStatus

or alternatively, two new methods

func (*Status) Deprecated() bool  
func (*Status) Removed() bool

(TODO: Open question for the methods approach: should a removed flag be marked as deprecated?)

Similarly, in internal/godebugs/table.go, the Info struct is extended to encode the current FlagStatus as well.

(TODO: Determine the best way to represent this information in internal/godebugs/Info.)

Marking a GODEBUG flag as deprecated

Similar to how gopls and staticcheck warn about calls to deprecated functions, we may want build tools to warn and tests to report an error to users if they set deprecated GODEBUG flags to any values other then their (final) default values. This includes:

- godebug lines in go.mod and go.work files
- use of supported GODEBUG comments in type-checker tests (such as -gotypesalias)
- use of testing.Setenv to set deprecated flags
- possibly others

To support complex systems, we may introduce a new GODEBUG flag that causes a program to panic if a deprecated GODEBUG is set to a non-default value: for instance, GODEBUG=deprecatedgodebug=0 would disallow setting a deprecated GODEBUG at all and cause a panic if such a flag is set to any value. This includes build tools (report a build error) and calls to os.Setenv or testing.Setenv that set a deprecated GODEBUG flag (cause a panic).

Removing a GODEBUG flag

A flag is removed by making the relevant internal changes and updating documentation. Except for packages dealing specifically with GODEBUG flags, the flag in question is removed from all sources of a Go distribution.

Since we cannot enforce that removal in the wild, a removed flag may still be queried and set to its (final) default value; but the value is ignored (by definition). Any attempt to set the flag to a non-default value will cause a fatal error.

Enforced documentation

To ensure that a GODEBUG flag (name) is never reused, we maintain public documentation (we can use the existing document), but also retain the flag in the (implementation) table of GODEBUG flags: internal/godebugs/table.go, which can be programmatically verified.

Metadata

Metadata

Assignees

Type

No type

Fields

No fields configured for issues without a type.

Projects

Status
Accepted

Relationships

None yet

Development

No branches or pull requests

Issue actions