Go
Go code should keep package boundaries clear, control flow simple, and life cycles deducible. The format and naming follow gofmt, Effective Go and Go official conventions.
Package and public API
- Use short lowercase words in package names to avoid repeating the meaning already expressed in the upper directory.
- Use MixedCaps for naming and do not use underscores to simulate the naming habits of other languages.
- Export only the symbols needed by external callers, and write clear Go docs for new or modified public packages, types, functions, and methods.
- The interface is placed at the consumer boundary and uses the methods that the caller really needs to describe capabilities; do not build an abstraction that only covers one layer of implementation in advance.
- The constructor should not hide unanticipated side effects such as starting goroutines, making network requests, or modifying global state.
- Use embedding with caution to avoid accidentally expanding the public API or creating promoted members with unclear meaning.
- Handwritten cross-package APIs use the definer's primitive type and package-qualified name directly. Do not re-export the types of other ownership packages through
type Local = otherpkg.Type; alias will hide the source of the type, so that the caller and reviewer cannot determine the real owner from the signature. Go APIs generated by the repository's own generator must also comply with this rule, and the offending output should be fixed from the generator. Alias directly generated by third-party generators (such asprotoc-gen-go,oapi-codegen) can be retained, and the generated files must not be manually modified, generator forks maintained, or output normalizers added just to meet this rule. The file name, build comments, or directory itself do not prove that a piece of code is a third-party generated output.
Functions and data
- One function handles one coherent responsibility; prefer early return to avoid unnecessary nesting.
- error must be propagated or handled. When adding context, explain the failed operation and object, and do not repeatedly stack meaningless
failed to. panicis only used for programmer errors that cannot be reasonably recovered;recovercan only be placed at clear isolation boundaries.- Clarify the ownership and mutability of slices, maps, and pointers, and handle the differences between nil, null values, capacity, append, and copy.
- The receiver's pointer/value selection should be consistent with mutability, copy cost, and method set, and remain uniform for the same type.
initis only used for necessary and predictable registration and cannot hide the business startup process.
Generate types and Protobuf
protocThe generated message belongs to the package that generated it. When the handwritten RPC, SDK, adapter or service requires this wire type, use primitive qualified types such as*rpcpb.Messagedirectly, and do not create aliases, isomorphic wrappers or DTOs for renaming only for it.- The alias and helper signature automatically generated by third-party generators such as
protoc-gen-gobelong to the third-party generated surface and can be retained; do not modify the generated files manually. If the repository has its own generator to generate alias, the generator should be modified; if the third-party generated results need to be changed, the Schema or the officially supported generation configuration should be adjusted first, and all committed output should be regenerated. - The business layer only defines its own types if it has domain semantics, lifecycle or compatibility boundaries that are independent of the wire message. The type cannot copy the protobuf message field by field just to hide the generated package.
.protois still the only source of truth for wire contracts. The generator is responsible for the protobuf output and necessary codec, and should not generate a second set of isomorphic public types to obscure ownership.
Concurrency and resources
- Each goroutine should be able to answer: who started it, when to exit, how to cancel it, and who to hand the error to.
- The channel is closed by the sender or a clear life cycle owner; the receiver should not close the channel at will to end consumption.
- The context must be propagated along the call chain, and existing cancellations and deadlines cannot be lost with the new background context.
- timer, ticker, stream, connection and worker must be released during success, failure and cancellation paths.
- Check lock granularity, blocking paths, race and goroutine leaks when shared state, callbacks or long-lived workers are involved.
Testing and Verification
- Unit tests using the smallest effective package for pure logic, boundary value, error path and regression scenarios.
- table-driven tests and subtests should only be used when it makes input, expectation, and failure messages clearer.
- HTTP, RPC, database, file system, serialization, timeout and retry behavior use integration test.
- Concurrent changes should be run according to risks
go test -race; performance-sensitive paths should be benchmarked as needed. - Run
go vetwhen it comes to generated types, Protobuf or cross-package APIs. The diagnosis in the output of the repository's own generator should be returned to the generator for processing; the source of the diagnosis in the third-party generated file should be recorded, and the output should not be manually modified to eliminate the diagnosis. Ownership issues may not be hidden through handwritten aliases, wrappers, or suppressions. - You must run
modernize ./...when modifying Go code to check for modernization suggestions provided by the current Go version. review handles at least the handwritten code diagnostics involved in this change; existing out-of-scope diagnostics should be stated in the verification results and are not required to be mixed into the current change. Diagnostics in the output of the repository's own generator are returned to the generator for processing; diagnostics in third-party generated files should not be fixed manually. modernize -fixwill directly modify the file and can only be used after confirming that the suggestion is applicable; after execution, the complete diff must be reviewed and the corresponding tests must be run. Analyzer suggestions cannot be equated to proof of behavioral correctness.- Modify Go behavior and run by default:
sh
gofmt -w <changed-files>
modernize ./...
go test ./...