Module compatibility
Prefer compatibility-preserving evolution and understand import versioning before breaking APIs.
Canonical guidance
- preserve compatibility when you can
- breaking API changes deserve deliberate version boundaries
- import compatibility and semantic import versioning are core constraints, not paperwork
Use when
- evolving public libraries
- planning major-version changes
- reviewing whether a change is actually breaking
Avoid
- silent breaking changes in minor releases
- using modules without understanding import compatibility
- treating versioning as only a package manager concern
Preferred pattern
type Client struct{}
func (c *Client) Do(ctx context.Context, req *Request) (*Response, error) {
return nil, nil
}
func (c *Client) DoWithRetry(ctx context.Context, req *Request, retries int) (*Response, error) {
return nil, nil
}
Anti-pattern
- changing exported contracts in place and expecting downstream users to absorb it
Explanation: This anti-pattern is tempting during rapid iteration, but silent breakage turns every downstream upgrade into manual triage.
Why
- compatibility is a large part of Go’s developer experience promise
Sources
- Keeping Your Modules Compatible - Go Team
- vgo: Import Compatibility Rule - Russ Cox