跳到正文
awesomego.org

Naming

See the naming section within the core style guide for overarching guidance on naming. The following sections provide further clarification on specific areas within naming.

Underscores

Names in Go should in general not contain underscores. There are three exceptions to this principle:

  1. Package names that are only imported by generated code may contain underscores. See package names for more detail around how to choose multi-word package names.
  2. Test, Benchmark and Example function names within *_test.go files may include underscores.
  3. Low-level libraries that interoperate with the operating system or cgo may reuse identifiers, as is done in syscall. This is expected to be very rare in most codebases.

Note: Filenames of source code are not Go identifiers and do not have to follow these conventions. They may contain underscores.

Package names

In Go, package names must be concise and use only lowercase letters and numbers (e.g., k8s, oauth2). Multi-word package names should remain unbroken and in all lowercase (e.g., tabwriter instead of tabWriter, TabWriter, or tab_writer).

Avoid selecting package names that are likely to be shadowed by commonly used local variable names. For example, usercount is a better package name than count, since count is a commonly used variable name.

Go package names should not have underscores. If you need to import a package that does have one in its name (usually from generated or third party code), it must be renamed at import time to a name that is suitable for use in Go code.

An exception to this is that package names that are only imported by generated code may contain underscores. Specific examples include:

  • Using the _test suffix for unit tests that only exercise the exported API of a package (package testing calls these “black box tests”). For example, a package linkedlist must define its black box unit tests in a package named linkedlist_test (not linked_list_test)

  • Using underscores and the _test suffix for packages that specify functional or integration tests. For example, a linked list service integration test could be named linked_list_service_test

  • Using the _test suffix for package-level documentation examples

Avoid uninformative package names like util, utility, common, helper, model, testhelper, and so on that would tempt users of the package to rename it when importing. See:

When an imported package is renamed (e.g. import foopb "path/to/foo_go_proto"), the local name for the package must comply with the rules above, as the local name dictates how the symbols in the package are referenced in the file. If a given import is renamed in multiple files, particularly in the same or nearby packages, the same local name should be used wherever possible for consistency.

See also: Go blog post about package names.

Receiver names

Receiver variable names must be:

  • Short (usually one or two letters in length)
  • Abbreviations for the type itself
  • Applied consistently to every receiver for that type
  • Not an underscore; omit the name if it is unused
Long Name Better Name
func (tray Tray) func (t Tray)
func (info *ResearchInfo) func (ri *ResearchInfo)
func (this *ReportWriter) func (w *ReportWriter)
func (self *Scanner) func (s *Scanner)

Constant names

Constant names must use MixedCaps like all other names in Go. (Exported constants start with uppercase, while unexported constants start with lowercase.) This applies even when it breaks conventions in other languages. Constant names should not be a derivative of their values and should instead explain what the value denotes.

// Good:
const MaxPacketSize = 512

const (
    ExecuteBit = 1 << iota
    WriteBit
    ReadBit
)

Do not use non-MixedCaps constant names or constants with a K prefix.

// Bad:
const MAX_PACKET_SIZE = 512
const kMaxBufferSize = 1024
const KMaxUsersPergroup = 500

Name constants based on their role, not their values. If a constant does not have a role apart from its value, then it is unnecessary to define it as a constant.

// Bad:
const Twelve = 12

const (
    UserNameColumn = "username"
    GroupColumn    = "group"
)

Initialisms

Words in names that are initialisms or acronyms (e.g., URL and NATO) should have the same case. URL should appear as URL or url (as in urlPony, or URLPony), never as Url. As a general rule, identifiers (e.g., ID and DB) should also be capitalized similar to their usage in English prose.

  • In names with multiple initialisms (e.g. XMLAPI because it contains XML and API), each letter within a given initialism should have the same case, but each initialism in the name does not need to have the same case.
  • In names with an initialism containing a lowercase letter (e.g. DDoS, iOS, gRPC), the initialism should appear as it would in standard prose, unless you need to change the first letter for the sake of exportedness. In these cases, the entire initialism should be the same case (e.g. ddos, IOS, GRPC).
English Usage Scope Correct Incorrect
XML API Exported XMLAPI XmlApi, XMLApi, XmlAPI, XMLapi
XML API Unexported xmlAPI xmlapi, xmlApi
iOS Exported IOS Ios, IoS
iOS Unexported iOS ios
gRPC Exported GRPC Grpc
gRPC Unexported gRPC grpc
DDoS Exported DDoS DDOS, Ddos
DDoS Unexported ddos dDoS, dDOS
ID Exported ID Id
ID Unexported id iD
DB Exported DB Db
DB Unexported db dB
Txn Exported Txn TXN

Getters

Function and method names should not use a Get or get prefix, unless the underlying concept uses the word “get” (e.g. an HTTP GET). Prefer starting the name with the noun directly, for example use Counts over GetCounts.

If the function involves performing a complex computation or executing a remote call, a different word like Compute or Fetch can be used in place of Get, to make it clear to a reader that the function call may take time and could block or fail.

Variable names

The general rule of thumb is that the length of a name should be proportional to the size of its scope and inversely proportional to the number of times that it is used within that scope. A variable created at file scope may require multiple words, whereas a variable scoped to a single inner block may be a single word or even just a character or two, to keep the code clear and avoid extraneous information.

Here is a rough baseline. These numeric guidelines are not strict rules. Apply judgement based on context, clarity, and concision.

  • A small scope is one in which one or two small operations are performed, say 1-7 lines.
  • A medium scope is a few small or one large operation, say 8-15 lines.
  • A large scope is one or a few large operations, say 15-25 lines.
  • A very large scope is anything that spans more than a page (say, more than 25 lines).

A name that might be perfectly clear (e.g., c for a counter) within a small scope could be insufficient in a larger scope and would require clarification to remind the reader of its purpose further along in the code. A scope in which there are many variables, or variables that represent similar values or concepts, may necessitate longer variable names than the scope suggests.

The specificity of the concept can also help to keep a variable’s name concise. For example, assuming there is only a single database in use, a short variable name like db that might normally be reserved for very small scopes may remain perfectly clear even if the scope is very large. In this case, a single word database is likely acceptable based on the size of the scope, but is not required as db is a very common shortening for the word with few alternate interpretations.

The name of a local variable should reflect what it contains and how it is being used in the current context, rather than where the value originated. For example, it is often the case that the best local variable name is not the same as the struct or protocol buffer field name.

In general:

  • Single-word names like count or options are a good starting point.
  • Additional words can be added to disambiguate similar names, for example userCount and projectCount.
  • Do not simply drop letters to save typing. For example Sandbox is preferred over Sbx, particularly for exported names.
  • Omit types and type-like words from most variable names.
    • For a number, userCount is a better name than numUsers or usersInt.
    • For a slice, users is a better name than userSlice.
    • It is acceptable to include a type-like qualifier if there are two versions of a value in scope, for example you might have an input stored in ageString and use age for the parsed value.
  • Omit words that are clear from the surrounding context. For example, in the implementation of a UserCount method, a local variable called userCount is probably redundant; count, users, or even c are just as readable.

Single-letter variable names

Single-letter variable names can be a useful tool to minimize repetition, but can also make code needlessly opaque. Limit their use to instances where the full word is obvious and where it would be repetitive for it to appear in place of the single-letter variable.

In general:

  • For a method receiver variable, a one-letter or two-letter name is preferred.
  • Using familiar variable names for common types is often helpful:
    • r for an io.Reader or *http.Request
    • w for an io.Writer or http.ResponseWriter
  • Single-letter identifiers are acceptable as integer loop variables, particularly for indices (e.g., i) and coordinates (e.g., x and y).
  • Abbreviations can be acceptable loop identifiers when the scope is short, for example for _, n := range nodes { ... }.

Repetition

A piece of Go source code should avoid unnecessary repetition. One common source of this is repetitive names, which often include unnecessary words or repeat their context or type. Code itself can also be unnecessarily repetitive if the same or a similar code segment appears multiple times in close proximity.

Repetitive naming can come in many forms, including:

Package vs. exported symbol name

When naming exported symbols, the name of the package is always visible outside your package, so redundant information between the two should be reduced or eliminated. If a package exports only one type and it is named after the package itself, the canonical name for the constructor is New if one is required.

Examples: Repetitive Name -> Better Name

  • widget.NewWidget -> widget.New
  • widget.NewWidgetWithName -> widget.NewWithName
  • db.LoadFromDatabase -> db.Load
  • goatteleportutil.CountGoatsTeleported -> gtutil.CountGoatsTeleported or goatteleport.Count
  • myteampb.MyTeamMethodRequest -> mtpb.MyTeamMethodRequest or myteampb.MethodRequest

Variable name vs. type

The compiler always knows the type of a variable, and in most cases it is also clear to the reader what type a variable is by how it is used. It is only necessary to clarify the type of a variable if its value appears twice in the same scope.

Repetitive Name Better Name
var numUsers int var users int
var nameString string var name string
var primaryProject *Project var primary *Project

If the value appears in multiple forms, this can be clarified either with an extra word like raw and parsed or with the underlying representation:

// Good:
limitRaw := r.FormValue("limit")
limit, err := strconv.Atoi(limitRaw)
// Good:
limitStr := r.FormValue("limit")
limit, err := strconv.Atoi(limitStr)

External context vs. local names

Names that include information from their surrounding context often create extra noise without benefit. The package name, method name, type name, function name, import path, and even filename can all provide context that automatically qualifies all names within.

// Bad:
// In package "ads/targeting/revenue/reporting"
type AdsTargetingRevenueReport struct{}

func (p *Project) ProjectName() string
// Good:
// In package "ads/targeting/revenue/reporting"
type Report struct{}

func (p *Project) Name() string
// Bad:
// In package "sqldb"
type DBConnection struct{}
// Good:
// In package "sqldb"
type Connection struct{}
// Bad:
// In package "ads/targeting"
func Process(in *pb.FooProto) *Report {
    adsTargetingID := in.GetAdsTargetingID()
}
// Good:
// In package "ads/targeting"
func Process(in *pb.FooProto) *Report {
    id := in.GetAdsTargetingID()
}

Repetition should generally be evaluated in the context of the user of the symbol, rather than in isolation. For example, the following code has lots of names that may be fine in some circumstances, but redundant in context:

// Bad:
func (db *DB) UserCount() (userCount int, err error) {
    var userCountInt64 int64
    if dbLoadError := db.LoadFromDatabase("count(distinct users)", &userCountInt64); dbLoadError != nil {
        return 0, fmt.Errorf("failed to load user count: %s", dbLoadError)
    }
    userCount = int(userCountInt64)
    return userCount, nil
}

Instead, information about names that are clear from context or usage can often be omitted:

// Good:
func (db *DB) UserCount() (int, error) {
    var count int64
    if err := db.Load("count(distinct users)", &count); err != nil {
        return 0, fmt.Errorf("failed to load user count: %s", err)
    }
    return int(count), nil
}