关于 adder
面向 lwlee2608/adder 的 Go 应用配置技能,指导开发者处理 YAML 与结构体绑定、环境变量覆盖和默认值,并检查静默绑定失败。
主要功能
- 01让 YAML 键匹配小写 Go 字段名
- 02在 application.yml 中配置默认值
- 03使用自动环境变量映射
- 04按索引处理切片覆盖
- 05按包组织配置类型
- 06验证绑定并排查零值
适用场景
- 01为 Go 服务添加配置字段
- 02诊断静默加载为零值的字段
- 03设置本地默认值和部署覆盖项
- 04通过环境变量配置列表值
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ lwlee2608/ adder --skill adder安装后在 Claude Code 中输入/adder确认已加载。
详细介绍
使用 lwlee2608/adder
Adder 会将 YAML 读入 Go 结构体,并叠加环境变量。它只进行不区分大小写的键匹配,不会转换 snake_case ↔ CamelCase。以下每条规则都是为了避免字段悄无声息地绑定到零值。
规则
-
优先使用与 Go 字段名转为小写后相匹配的 YAML 键。 Adder 会查找
strings.ToLower(field.Name),因此字段SessionTtl会与 YAML 键sessionttl匹配。小写键和 camelCase 键都可用,因为 adder 会将两侧都转为小写;camelCase 只是更易读。# good — matches field name when lowercased auth: username: admin sessionTtl: 24htype AuthConfig struct { Username string SessionTtl time.Duration // no tag required }符合 Go 习惯的写法与不使用标签的绑定之间存在取舍。 Go 风格要求首字母缩写全部大写(
SessionTTL)。但strings.ToLower("SessionTTL")会得到"sessionttl",这就只能使用难看的 YAML 键(sessionttl: 24h)或mapstructure标签。自行取舍:符合 Go 习惯的字段名加标签,或者不符合 Go 习惯的字段名但不加标签。此规范倾向后者,以尽量减少标签,但两种方式都说得通——在项目内保持一致即可。这同样适用于每一层嵌套——
auth.session.ttl中的每个部分都按相同方式解析。 -
尽量不要在 YAML 中使用 snake_case。 像
session_ttl这样的 snake_case 键永远无法自动绑定到SessionTTL——adder 会比较sessionttl和session_ttl,发现不匹配,因此字段会悄无声息地保留为零值(例如time.Duration(0),对于 TTL 来说意味着“立即过期”)。如果为了可读性必须使用 snake_case,则每个这样的字段都需要显式的mapstructure标签:// only if you insist on snake_case YAML SessionTTL time.Duration `mapstructure:"session_ttl"`优先采用规则 1——标签越少,忘记添加标签的可能性也越小。
-
将默认值放在
application.yml中,而不是代码里。application.yml是默认值的规范来源。代码不应使用硬编码的回退值掩盖缺失的配置(例如if cfg.SessionTTL == 0 { cfg.SessionTTL = 24*time.Hour })——这会掩盖真正的配置错误。如果某个值是必需的,应在启动时进行验证,并给出清晰的报错后 panic。列出每个字段,并提供本地开发用的值。
application.yml是本地运行所需的配置,也是维护者查看所有可用配置的全局视图。为每个字段提供适用于本地开发的值(localhost、docker-compose 密码、10s);环境变量会在不同部署中覆盖这些值。只有没有安全开发值的密钥(第三方 API 密钥)才使用""。切勿提交真实凭据。如果非字符串字段确实没有默认值,请使用不带值的键(timeout:),不要用""——""无法解析为time.Duration。保持
application.yml中没有注释——列出每个字段后,逐字段添加注释只会淹没配置内容。auth: sessionTtl: 24h smtp: host: localhost port: 1025 password: "" from: dev@localhost如果代码调用
godotenv.Overload(),请在.env.example中说明覆盖项。 列出部署时需要设置的环境变量——密钥以及任何在生产环境中开发默认值不适用的变量——并为每个变量添加注释说明。只能使用占位值。如果没有godotenv.Overload(),则不要添加.env文件。# SMTP relay for outgoing auth emails AUTH_SMTP_HOST=smtp.example.com AUTH_SMTP_PORT=587 # App password from the mail provider, not the account password AUTH_SMTP_PASSWORD=changeme [email protected] -
标准环境变量模式依赖
AutomaticEnv()。 keyPath 是 adder 在内部使用的点分路径——结构体字段Auth.SessionTtl会变为 keyPathauth.sessionttl。使用SetEnvKeyReplacer(strings.NewReplacer(".", "_"))和AutomaticEnv()后,adder 会将strings.ToUpper(keyPath)中的点替换为下划线,从而得出环境变量名。对于符合此模式的环境变量,不要添加BindEnv调用——这只是多余内容。keyPath: openrouter.apikey → env: OPENROUTER_APIKEY (auto, no BindEnv needed) keyPath: db.url → env: DB_URL (auto) -
只有环境变量名与自动模式不一致时才使用
BindEnv。 常见原因是存在既定的外部环境变量名(例如OPENROUTER_API_KEY,其中API和KEY之间有下划线),与自动推导出的OPENROUTER_APIKEY不匹配。// Only because the external name is OPENROUTER_API_KEY, not OPENROUTER_APIKEY _ = adder.BindEnv("openrouter.apikey", "OPENROUTER_API_KEY") // Don't write this — DB_URL is already automatic from keyPath db.url // _ = adder.BindEnv("db.url", "DB_URL")如果可以将环境变量名改为符合自动模式的形式,就这样做并删除
BindEnv。 -
通过环境变量中的索引访问切片元素。 切片字段的 keyPath 会增加元素索引:
Clients []ClientConfig→clients.0.token→CLIENTS_0_TOKEN。如果索引超出application.yml中列表的长度,就会追加一个新元素,因此列表可以完全通过环境变量提供。追加会在遇到第一个缺失的索引时停止,不带索引的键(CLIENTS=...)会被忽略。不带索引的元素内字段键(CLIENTS_TOKEN=...)仍会应用于每个现有元素;两者都有值时,带索引的键优先。CLIENTS_1_TOKEN=t9 → clients[1].token (override) CLIENTS_2_NAME=baz → clients[2] appended CLIENTS_2_AUTH_ID=id-2 → clients[2].auth.id PATHS_0=/one → paths[0], no yaml entry needed对于固定结构列表中的密钥,请使用
${NAME}占位符,而不是""——yaml 仍会记录列表结构,并且命名环境变量在列表重排后仍然有效,而带索引的环境变量则不会。仅将它们用于字符串密钥:未设置的变量会展开为空值,并且不支持${NAME:-default}语法。如果项目有.env.example,请在其中列出${NAME}变量。仅在不同环境中的元素数量不固定时,才使用带索引的环境变量。 -
将配置结构体与其配置的包放在一起。 每个包管理自己的配置类型(
internal/auth/config.go→auth.Config,internal/db/config.go→db.Config)。cmd层的Config只负责组合。// cmd/myapp/config.go type Config struct { Auth auth.Config DB db.Config }两种方式都可以绑定 Adder——这样安排是为了组织代码:包负责自己的字段、屏蔽标签,以及任何
Enabled()/Validate()辅助函数,而cmd/<app>/config.go则保持简短。仅用于cmd装配的配置类型(例如LogConfig)可以留在cmd/<app>/中。
验证流程
添加或更改配置字段后(这些检查适用于每个嵌套层级——请验证最深层的字段,而不仅仅是顶层结构体):
- 小写匹配检查——
strings.ToLower(fieldName)是否与 YAML 键完全相同?如果不同,就需要添加mapstructure标签,或者重命名 YAML 键。 - 默认值检查——
application.yml中是否有该字段对应的键,且包含可正常使用的本地开发值?不设置环境变量覆盖项运行二进制程序,并确认字段的值符合预期。不要依赖记忆——请记录日志,例如log.Printf("%+v", cfg)。 - 环境变量覆盖检查——如果该字段应支持环境变量覆盖,请设置自动派生的环境变量(
UPPER_CASE_WITH_UNDERSCORES),并确认它覆盖了 YAML 默认值。同样,请记录加载后的值,不要凭假设判断。 - 零值陷阱——对于
time.Duration、int、float64或bool字段,绑定失败看起来与“配置为 0/false”完全相同。务必测试非零默认值是否确实加载。
需要留意的常见错误
- 添加多词字段时未设置
mapstructure标签,却在 YAML 中使用 snake_case。 这是最常见的陷阱:字段会静默绑定为零值。SessionTTL+session_ttl:→0,而不是24h。 - 为已符合自动模式的环境变量添加
BindEnv。 这会造成冗余,并让人难以看出哪些绑定确有必要。 - 在
application.yml中遗漏仅通过环境变量设置的字段。 它们仍可从环境变量绑定,但阅读 yaml 的维护者无法看到它们的存在。 - 使用生产环境值或
""作为默认值。application.yml应开箱即用地支持本地运行;仅对没有安全开发值的密钥使用""。 - 在
application.yml中添加注释。 保持文件简洁;如果项目使用godotenv.Overload(),请在.env.example中环境变量旁添加说明。 - 用代码默认值掩盖配置错误。
if cfg.X == 0 { cfg.X = default }会掩盖真正的问题(YAML 键没有绑定)。应修复绑定,而不是添加默认值。 - 认为不带索引的环境变量可以替换整个列表。
CLIENTS=...不起作用;切片覆盖必须带索引(CLIENTS_0_NAME)。 - 误以为大小写不敏感就能识别 snake_case。 并非如此——
caseInsensitiveLookup只会统一字母大小写,不会处理分隔符。 - 忘记
time.Duration的底层类型是int64。 缺失的时长会显示为0s,而大多数代码会将其视为“无超时”或“已过期”——这几乎总是错误的。