go-lib-design

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Go Library Design

Go库设计

Core Principles

核心原则

  • Work backwards from usage. Write pseudo-code and documentation before implementation. Design the API you want callers to see, then build it.
  • Minimize surface area. Smaller API = more internal freedom. When in doubt, leave it out. You can always export later; you can never unexport.
  • Accept, don't instantiate. Take dependencies as arguments (preferably interfaces). Never instantiate what you don't own.
  • Plan for growth. Signatures are frozen at v1.0. Use parameter objects, result objects, or functional options so APIs can evolve without breaking callers.
  • 从使用场景倒推。 在实现前编写伪代码和文档。先设计你希望调用者看到的API,再进行构建。
  • 最小化接口面。 更小的API意味着更多的内部自由度。拿不准的时候就不要对外暴露。你可以后续再导出,但一旦导出就无法撤销。
  • 依赖注入,而非实例化。 将依赖作为参数传入(优先使用接口)。永远不要实例化你不拥有的对象。
  • 为增长做规划。 签名在v1.0版本后即固定。使用参数对象、结果对象或函数式选项,让API在演进时不破坏现有调用者的代码。

Reference Index

参考索引

ReferenceTopics
design-processFour design axes, work backwards, minimize surface area
surface-areaInternal packages, no global state, unknown outputs, mutation guards
dependenciesAccept don't instantiate, accept interfaces, return structs
evolutionBreaking changes, param objects vs functional options, result objects
testability
TimeNow
function type,
*rand.Rand
injection,
WithX
options, deterministic outputs, DST readiness
参考链接主题
design-process四个设计维度、从使用场景倒推、最小化接口面
surface-area内部包、无全局状态、未知输出、变更防护
dependencies依赖注入而非实例化、接受接口、返回结构体
evolution破坏性变更、参数对象vs函数式选项、结果对象
testability
TimeNow
函数类型、
*rand.Rand
注入、
WithX
选项、确定性输出、夏令时适配

When to Apply

适用场景

Apply when:
  • Designing a new Go library or shared package
  • Reviewing a library's public API before v1.0
  • Deciding between functional options and parameter objects
  • Auditing a package's export surface
Do NOT apply to application-level code that won't be imported by other modules.
适用场景:
  • 设计新的Go库或共享包时
  • 在v1.0版本前审查库的公共API时
  • 在函数式选项与参数对象之间做抉择时
  • 审计包的导出接口面时
不适用于不会被其他模块导入的应用级代码。

Quick Reference

快速参考

Design process: Write usage pseudo-code first. Optimize for usability, readability, flexibility, testability — in that order.
Surface area: Export only what you'll support forever. Use
internal/
for non-public helpers. No global state — use struct methods.
Dependencies: Accept
io.Reader
, not
*os.File
. Accept interfaces, return structs. Provide convenience constructors (
NewFromFile
) as wrappers.
Evolution: Cannot add params (use param objects), cannot add returns (use result objects), cannot add interface methods (use upcasting). Functional options for optional-only params with few required args; param objects when there are required fields or many options.
Naming: No
FooManager
— find the real noun. No
util
/
common
/
helpers
packages. Qualify short generic names with parent (
httptest
, not
test
).
Docs: Write for users, not maintainers. Don't bury the lede. Provide runnable examples. Keep a changelog separate from commit log.
Testability: Accept
TimeNow
function type and
*rand.Rand
via
WithX
options with real defaults. Use
fs.FS
for I/O. No global state. Deterministic output ordering. Match abstraction to need — function types over interfaces. Simulation is opt-in.
设计流程:先编写使用场景的伪代码。按易用性、可读性、灵活性、可测试性的优先级进行优化。
接口面:仅导出你将永久支持的内容。使用
internal/
目录存放非公共辅助代码。不要使用全局状态——使用结构体方法。
依赖:接受
io.Reader
而非
*os.File
。接受接口,返回结构体。提供便捷构造函数(如
NewFromFile
)作为封装。
演进:不能直接添加参数(使用参数对象),不能直接添加返回值(使用结果对象),不能直接添加接口方法(使用向上转型)。当必填参数少、可选参数多时使用函数式选项;当存在必填字段或大量选项时使用参数对象。
命名:不要使用
FooManager
这类名称——找到真正对应的名词。不要使用
util
/
common
/
helpers
这类包名。对简短的通用名称加上父级限定(如
httptest
,而非
test
)。
文档:为用户而非维护者编写文档。不要隐藏关键信息。提供可运行的示例。将变更日志与提交日志分开存放。
可测试性:通过
WithX
选项接受
TimeNow
函数类型和
*rand.Rand
,并提供真实默认值。使用
fs.FS
处理I/O。不要使用全局状态。输出结果的顺序要确定。根据需求选择抽象方式——优先使用函数类型而非接口。模拟功能为可选启用。