This article has been machine-translated from Chinese. The translation may contain inaccuracies or awkward phrasing. If in doubt, please refer to the original Chinese version.
This lesson covered how to write cleaner and clearer code. Each language has its own characteristics and unique coding standards. For Go, the available performance optimization techniques and handy tools were also introduced.
High-quality code needs to be correct, reliable, concise, and clear:
- Correctness: Have all edge cases been considered? Can incorrect calls be handled?
- Reliability: Are exception handling and error handling clear? Can dependent service anomalies be handled promptly?
- Conciseness: Is the logic simple? Can new features be quickly supported?
- Clarity and readability: Can other people clearly understand the code when reading it? Will refactoring not cause unpredictable situations? This requires coding standards.
Coding Standards
Formatting Tools
When it comes to coding standards, we must mention code formatting tools. It’s recommended to use the official Go formatting tool gofmt. GoLand has it built in, and common IDEs can easily configure it.
- Another tool is
goimports, which is equivalent togofmtplus dependency package management, automatically adding and removing dependency packages.

In JS, there are similar formatting tools like
Prettier, which can be used with ESLint for code formatting.
Comment Standards
Good comments should:
-
Explain what the code does
-
Explain complex or non-obvious logic
-
Explain why the code is implemented this way (these factors are hard to understand out of context)
-
Explain when the code might fail (explain some constraints)
-
Comment on public symbols (every public symbol declared in a package: variables, constants, functions, and structs)
- Exception: no need to comment methods implementing interfaces
Google Style Guide has two rules:
- Any public functionality that is neither obvious nor short must be commented.
- Any function in a library must be commented regardless of length or complexity.
Situations to avoid:
- Verbose comments on self-explanatory functions
- Direct translation of obvious flows
In summary, code is the best comment:
- Comments should provide contextual information not expressed in the code
- Clean and clear code has no requirement for flow comments, but you can supplement with comments about why things are done a certain way, relevant background, etc., to provide useful information.
Naming Conventions
Variable Names
-
Brevity over verbosity
- The scope of
ivsindexdoesn’t need the extra verbosity ofindex
- The scope of
// Bad
for index := 0; index < len(s) ; index++ {
// do something
}
// Good
for i := 0; i < len(s); i++ {
// do something
}
-
Acronyms should be all caps, but when they appear at the beginning of a variable and don’t need to be exported, use all lowercase
- Use
ServeHTTPinstead ofServeHttp - Use
XMLHTTPRequestorxmlHTTPRequest
- Use
-
The farther a variable name is from where it’s used, the more context information it needs to carry.
- For global variables, more context information is needed in the name so it can be easily identified in different places.
// Bad
func ( c *Client ) send( req *Request, t time.Time )
// Good
func ( c *Client ) send( req *Request, deadline time.Time )
Function Naming
-
Function names should not carry package name context information, because the package name and function name always appear together
- For example, the function that creates a server in the http package:
Serve>ServeHTTP, because it’s always called ashttp.Serve
- For example, the function that creates a server in the http package:
-
Function names should be as short as possible
-
When a package named
foohas a function that returns typeT(whereTis notFoo), you can include the return type information in the function name- When returning type
Foo, it can be omitted without causing ambiguity
- When returning type
Package Names
- Consist only of lowercase letters. No uppercase letters, underscores, or other characters.
- Short and containing some context information. For example:
schema,task, etc. - Don’t use the same name as standard libraries. For example, don’t use
syncorstrings. The following rules should be followed as much as possible, using standard library package names as examples: - Don’t use common variable names as package names. For example, use
bufioinstead ofbuf - Use singular instead of plural. For example, use
encodinginstead ofencodings - Use abbreviations carefully. For example,
fmtis shorter thanformatwithout breaking context
Overall, good naming reduces the cost of reading and understanding code, keeps attention on the main flow, and allows clear understanding of program functionality without frequently switching to branch details and having to explain them.
Control Flow
-
Avoid nesting, keep the normal flow clear and readable
- Handle error/special cases first, return early or continue loops to reduce nesting
// Bad
if foo {
return x
} else {
return nil
}
// Good
if foo {
return x
}
return nil
- Keep the normal code path at minimum indentation, reduce nesting
// Bad
func OneFunc() error {
err := doSomething()
if err == nil {
err := doAnotherThing()
if err == nil {
return nil // normal case
}
return err
}
return err
}
// Good
func OneFunc() error {
if err := doSomething(); err != nil {
return err
}
if err := doSomething(); err != nil {
return err
}
return nil // normal case
}
In summary, the flow handling logic in programs should go in a straight line as much as possible, avoiding complex nested branches, letting the normal flow code move downward along the screen. This improves code maintainability and readability, because most failure issues appear in complex conditional and loop statements.
Error Handling
-
Simple errors
- Simple errors refer to errors that occur only once and don’t need to be caught elsewhere
- Prefer using
errors.Newto create anonymous variables to directly represent simple errors - If formatting is needed, use
fmt.Errorf
func defaultCheckRedirect(req *Request, via []*Request) error {
if len(via) >= 10 {
return errors.New("stopped after 10 redirects")
}
return nil
}
-
Complex errors: Use error
WrapandUnwrap- Error
Wrapessentially provides the ability to nest oneerrorinside anothererror, generating anerrortrace chain - Use the
%wkeyword infmt.Errorfto associate an error with the error chain - Use
errors.Isto determine if an error is a specific error, checking all errors in the error chain (go/wrap_test.go · golang/go) - Use
errors.Asto get a specific type of error from the error chain and assign it to a defined variable (go/wrap_test.go · golang/go)
- Error
In Go, something more severe than an error is panic, which indicates that the program cannot work normally.
-
Using panic in business code is not recommended
- After a
panicoccurs, it propagates up to the top of the call stack - If none of the calling functions contain
recover, the entire program will crash - If the problem can be masked or resolved, it’s recommended to use
errorinstead ofpanic
- After a
-
When an irreversible error occurs during program startup,
paniccan be used in theinitormainfunction (sarama/main.go · Shopify/sarama)
Where there’s panic, recover naturally comes up. If a third-party library’s bug causes a panic that affects your own logic, you need recover.
recovercan only be used indeferred functions; nesting won’t work, and it only takes effect in the current goroutine (github.com/golang/go/b…)deferstatements are last-in-first-out- If more context information is needed, you can log the current call stack after recover (github.com/golang/webs…)
Summary
errorshould provide concise contextual information chains to facilitate problem identificationpanicis for truly exceptional situationsrecovertakes effect within the current goroutine’sdeferred functions
Performance Optimization Suggestions
- Prerequisite: Improve program efficiency as much as possible while meeting quality factors like correctness, reliability, conciseness, and clarity.
- Trade-offs: Sometimes time efficiency and space efficiency can be opposing; appropriate trade-offs need to be made based on importance analysis.
Based on Go language characteristics, the lesson introduced many Go-related performance optimization suggestions:
Pre-allocate Memory
When initializing slices with make(), provide capacity information whenever possible.
func PreAlloc(size int) {
data := make([]int, 0, size)
for k := 0; k < size; k++ {
data = append(data, k)
}
}
This is because a slice is essentially a description of an array segment, including the array pointer, segment length, and segment capacity (maximum length without changing memory allocation).
- Slice operations don’t copy the elements pointed to by the slice
- Creating a new slice reuses the underlying array of the original slice. So pre-setting the capacity value can avoid extra memory allocations and achieve better performance.
String Processing Optimization
Using strings.Builder for common string concatenation:
-
+concatenation (slowest) -
strings.Builder(fastest) -
bytes.BufferPrinciple: Strings in Go are immutable types with fixed memory size -
When concatenating with +, a new string is generated in a new space, with the new space being the sum of the two original strings
-
strings.Builderandbytes.Bufferallocate memory in multiples -
Both
strings.Builderandbytes.Bufferuse[]bytearrays underneathbytes.Bufferallocates a new space for the generated string variable when converting to stringstrings.Builderdirectly converts the underlying[]byteto string type and returns it
func PreStrBuilder(n int, str string) string {
var builder strings.Builder
builder.Grow(n * len(str))
for i := 0; i < n; i++ {
builder.WriteString(str)
}
return builder.String()
}
Empty Struct
-
An empty
structinstance doesn’t occupy any memory space -
Can be used as a placeholder in various scenarios
- Saves memory
- The empty struct itself has strong semantics, meaning no value is needed here, serving only as a placeholder
-
For example, when implementing a Set, use the map’s key while setting the value to an empty struct (golang-set/threadunsafe…)
Related Links
- Go pprof Practice Code experiment examples: github.com/wolfogre/go…
- Try using the test command to write and run simple tests: go.dev/doc/tutoria…
- Try using the -bench parameter to performance test your written functions: pkg.go.dev/testing#hdr…
- Go Code Review suggestions: github.com/golang/go/w…
- Uber’s Go coding standards: github.com/uber-go/gui…
Summary and Reflections
This lesson introduced common coding standards in Go and other languages, and proposed Go-specific performance optimization suggestions. A performance optimization hands-on exercise using the pprof tool followed.
Notes content from Teacher Zhang Lei’s course in the 3rd Youth Training Camp: “High-Quality Programming and Performance Tuning Practice”
Course materials: Go Language Principles and Practice Learning Materials (Part 1) - 3rd ByteDance Youth Training Camp - Backend Track
喜欢的话,留下你的评论吧~