| name | personal-dev-guard |
| description | Use this skill before or during code changes and code review to enforce readable, restrained, maintainable code with low patch smell. |
| version | 0.1.6 |
Personal Dev Guard / 个人开发守门规范
这是一个中文指令型 Skill。它的本体是这份 Markdown 文档,而不是自动评分脚本。
使用者读取本规范后,应按用户的个人高级开发标准进行代码开发和代码审查。这里的重点不是部署、排障、工具调用礼仪,也不是形式化流程检查,而是判断一份代码是否清晰、可读、克制、可维护,是否像高级开发者写出来的代码。
1. 短版核心规范
开发和审查代码时,优先遵守以下原则:
- 长期工程质量优先。不能只追求暂时能跑、暂时能实现、最小改动;方案必须符合项目既有规范,能长期维护、长期演进。
- 可读性优先。代码首先要能顺着读懂,主流程要连贯,不要让读者在一堆小函数之间来回跳转。
- 反对补丁味。不要为了修一个点硬塞特殊判断、flag、临时分支,让代码长期补丁化。
- 反对炫技抽象。抽象必须来自真实需求,不为了设计感、层级感、模式感而抽象。
- 项目规范优先。开发前要尊重现有目录结构、接口边界、命名风格、数据模型、错误处理、测试方式和发布约定;不要做一处可用、全局不一致的改动。
- 复杂度应该来自业务本身,而不是框架、helper、接口、目录层级或设计模式额外放大出来的复杂度。
- 核心流程需要中文注释。注释重点解释为什么、业务原因、设计取舍、坑点和非显然约束。
- 测试应该覆盖真实风险,并且测试代码本身也要可读,像业务行为文档,而不是复杂 mock 工程。
- Go 项目优先简单直接:少做 Java 式机械分层,不提前定义 interface,不为了 mock 污染业务结构。
- 泛型默认克制使用:能用普通函数、明确类型或具体结构表达清楚的,就不要上泛型;只有真实多类型复用能明显减少重复并保持可读时才使用。
any 不算泛型,但也要克制:边界层解析动态 JSON 可以用,业务内部优先转成明确 struct、具体类型或小结构,避免把字段契约藏进运行时。
- Review 时优先看:是否符合长期规范性和项目规范,主流程是否连贯,有没有补丁味和炫技抽象,测试/验证是否覆盖风险,改动范围是否形成完整闭环。
如果因为现实约束违反核心偏好,必须在 Review 或交付说明中说清楚:为什么违反、替代方案是什么、风险是什么、后续是否需要修正。
2. 规范定位
本规范约束的是代码开发质量,不是 AgentDock 运维流程。
它应该用于:
- 新增功能、修复 bug、重构、清理代码。
- Go 项目开发,尤其是服务端、CLI、工具类项目。
- 其他语言项目中的通用可读性、可维护性和测试判断。
- 开发后的代码 Review 和 debrief。
它不重点覆盖:
- Docker 部署、反代、证书、服务排障。
- iOS/watchOS 安装签名流程。
- 工具调用前后如何汇报的细节。
- 自动化评分、静态分析或代替测试工具。
这份规范不是要求所有代码都写成一种形状,而是帮助开发者始终回答一个问题:未来接手这段代码的人,能不能顺着读懂、放心修改、快速定位问题?
当“最小改动”和“长期正确”冲突时,不能默认选择最小改动。应该优先选择符合项目规范、能形成完整闭环、未来维护成本更低的方案;如果只能先临时处理,必须说明临时性、风险和后续正式方案。
3. 高级开发者判断标准
高级开发者不是把代码写得复杂,而是把真实复杂度控制在可读、可定位、可维护的范围里。
优先级如下:
- 主流程可读,读者能顺着理解业务链路。
- 方案符合项目既有规范和长期演进方向,不为了暂时可用破坏一致性。
- 代码没有明显补丁味、临时味和硬塞特例。
- 抽象克制,不为了看起来高级而制造层级。
- 错误路径、边界条件和状态变化清楚可见。
- 测试或验证覆盖本次改动真正的风险。
- 改动范围服务当前目标,同时足以把功能、数据、接口、前端、测试和文档等相关闭环补齐。
不高级的代码通常有这些味道:
- 为了短函数把主流程拆散,读代码需要频繁跳转。
- 为了一个小需求引入过多 interface、manager、processor、adapter、factory。
- 为了快速修复硬塞特殊 case,没有说明原因、影响和退出条件。
- 错误被吞掉、被模糊包装,或者只打印日志后继续。
- 业务对象含义不清,充斥 map、any、万能 DTO、泛名 helper。
- 测试比业务代码还难读,mock 和 fixture 成为新的复杂度来源。
- 为了快或“少改点”,只修表面现象,不处理同一业务链路里的规范、一致性和闭环问题。
4. 主流程与函数组织
主流程优先连贯。能在一个函数或一段清楚代码里顺着读懂的业务链路,不要为了形式上的“短函数”拆成一堆独立小方法。
允许拆分,但拆分必须服务于可读性,而不是破坏可读性。合理拆分的理由包括:
- 多处真实复用。
- 隔离明确副作用,例如外部请求、数据库读写、文件操作。
- 隐藏局部复杂细节,让主流程更容易读。
- 形成清晰测试边界。
- 表达稳定的业务概念。
不接受的拆分理由包括:
- “这个函数太长,所以必须拆”。
- “高级项目都这样分层”。
- “以后可能会复用”。
- “这样看起来更架构化”。
- “为了套某个设计模式”。
如果确实拆分,入口或主流程仍然应该像目录一样清楚展示完整业务顺序。读者不应该为了理解主线,在多个 helper 里来回跳转。
坏例子:主流程被拆碎
func UpdateUser(ctx context.Context, req UpdateUserRequest) error {
user, err := loadUser(ctx, req)
if err != nil {
return err
}
prepareUser(user)
applyRequest(user, req)
normalizeUser(user)
fillAuditFields(user)
return saveUser(ctx, user)
}
这段代码的问题不是函数短,而是读者必须跳进多个 helper 才知道用户到底被改了什么。
好例子:主流程可见,局部复杂再封装
func UpdateUser(ctx context.Context, req UpdateUserRequest) error {
if req.UserID == "" {
return fmt.Errorf("用户 ID 不能为空")
}
user, err := repo.FindUser(ctx, req.UserID)
if err != nil {
return fmt.Errorf("查询用户失败: %w", err)
}
user.Name = strings.TrimSpace(req.Name)
user.Email = strings.ToLower(strings.TrimSpace(req.Email))
user.UpdatedAt = clock.Now()
if err := validateUserForUpdate(user); err != nil {
return err
}
return repo.SaveUser(ctx, user)
}
这里不是禁止 helper,而是把关键业务变化留在主流程里,让读者能顺着读懂。
5. 可读性与复杂度控制
代码不追求短,也不追求抽象,追求业务复杂度本身可见、可读、可定位。
要求:
- 可读性不是把代码拆碎,而是让业务判断、状态变化、错误路径清楚呈现。
- 不为了“统一架构”“短函数”“设计模式”把简单问题复杂化。
- 复杂逻辑可以存在,但必须让读者能顺着主线理解。
- 重要数据从哪里来、在哪里变、最后怎么用,应该能追踪。
- 重要字段的修改尽量直接出现在主流程中。
- 不要让很多 helper 在深处偷偷修改同一个对象。
- 如果 helper 会修改对象,函数名和中文注释必须说清楚它会改什么、为什么改。
判断一段代码是否可读,可以问:
- 我能否不跳很多文件就理解主流程?
- 我能否看出关键字段在哪里被修改?
- 我能否看出失败时会怎么返回?
- 我能否看出哪些逻辑是业务规则,哪些只是技术细节?
- 如果半年后再看,我是否还能快速定位改动点?
6. 抽象与模块边界
不反对抽象,但抽象必须来自真实需求。
允许抽象的理由:
- 多处真实复用。
- 隔离明确变化点。
- 降低当前阅读复杂度。
- 表达稳定领域概念。
- 形成清晰测试边界。
- 隔离外部系统、网络、数据库、文件系统等边界。
不接受的理由:
- “以后可能会用”。
- “这样看起来更高级”。
- “高级项目都这么分层”。
- “为了套设计模式”。
- “为了 mock 所以先定义接口”。
接口、抽象层、目录结构都应该减少理解成本,而不是制造跳转成本。
7. Go 项目偏好
Go 项目优先简单直接,尊重 Go 的工程习惯,不做 Java 式机械分层。
偏好:
- 包结构优先简单,不为了架构感拆很多目录。
- handler、service、repository 可以存在,但必须来自真实职责边界,不机械套模板。
- interface 不要提前定义;只有多实现、测试替身、外部边界隔离、依赖反转确实需要时才定义。
- 泛型不要提前使用;只有同一逻辑确实服务多种具体类型、普通函数会产生明显重复,并且泛型版本仍然容易读懂时才使用。
- 能用具体类型、简单结构体、普通函数或小范围重复表达清楚的,优先不用泛型;不要为了“通用”“优雅”把数据流和错误流藏进类型参数里。
- 如果使用泛型,类型参数数量要少,约束要直白,调用点要比非泛型方案更清楚;否则退回具体实现。
any 不是泛型,但同样不要随手使用;除非处在 MCP/HTTP/JSON/插件 manifest 这类动态边界,业务逻辑内部优先使用明确 struct、具体字段和具体类型。
- 动态边界可以先用
map[string]any 接住外部输入,但进入核心流程前应尽快校验并转成明确 request struct;不要让 any 和字符串 key 在业务链路里到处传。
- 不要用
any、万能 DTO 或 map[string]any 伪装通用性;字段契约如果对维护者重要,就应该让类型、命名或局部结构直接表达出来。
- 不要为了 mock 而污染业务代码结构。
- Go 代码优先清晰数据流、错误流、调用流,而不是层级数量。
- 错误处理保持显式直接,避免把关键失败路径藏在深层 helper 中。
- 表格测试可以使用,但不要为了表格测试牺牲测试场景的可读性。
- 小型项目或工具项目不需要强行套大型服务端分层。
Go 里的 interface 应该由消费方在真实需要时定义,而不是在实现方提前制造抽象。
8. 命名规范
命名优先服务阅读,让读者快速理解“这是什么、为什么存在、在业务里代表什么”。
要求:
- 命名直白,不炫技,不滥用缩写,不追求抽象感。
- 核心变量、函数、结构体命名必须表达业务意图。
- 短生命周期局部变量可以简短,但不能牺牲理解。
- 不要用 Manager、Processor、Helper、Util、Common 这类泛名掩盖真实职责。
- 不要为了显得通用,把具体业务名改成空泛概念。
- 布尔变量要能读出判断语义,例如
canSync、shouldRetry、hasPermission。
一个名字如果需要读实现才能知道它代表什么,通常就不够好。
9. 中文注释规范
注释默认使用中文,方便长期维护和快速理解。
要求:
- 不写无意义注释,不重复代码表面行为。
- 注释重点解释“为什么这么做”,而不是机械解释“这行做了什么”。
- 核心代码流程需要有适度中文注释,让读者快速把握主线。
- 复杂业务规则、历史原因、边界条件、兼容逻辑、非显然取舍必须注释。
- 如果某段代码必须保留看似奇怪的判断,必须用中文说明原因。
- TODO、workaround、临时兼容逻辑必须中文说明原因、影响范围、退出条件和后续正式方案。
好的注释应该降低未来阅读成本,不应该替代糟糕命名和糟糕结构。
10. 临时方案与补丁味
允许现实中的临时处理,但必须说明清楚,不能伪装成正式设计。
要求:
- 不鼓励 TODO、workaround、临时兼容分支泛滥。
- 如果确实需要临时处理,必须用中文注释说明为什么现在必须这样做。
- 必须说明影响范围、什么时候可以删除、后续正式方案是什么。
- 临时方案不能伪装成长期架构。
- 不能为了快而把特殊 case 硬塞进主流程,导致代码长期补丁化。
- 不能把“最小改动”“先能用”当成唯一理由,留下明显不符合项目规范的半成品。
- 临时方案如果会影响数据结构、接口契约、用户体验或后续维护,必须同步给出长期收敛路径。
临时处理如果没有退出条件,就很容易变成永久技术债。
11. 错误处理与边界条件
错误路径要和正常路径一样容易读懂。
要求:
- 错误处理必须显式、直接。
- 关键失败路径不能藏在很深的 helper 里。
- 边界条件尽量靠近主流程,让读者能看到什么时候失败、为什么失败、失败后怎么返回。
- 不吞错误,不返回模糊错误,不只打印日志然后继续。
- Go 代码里的错误包装要服务于定位问题,而不是制造一层层无意义包装。
- 日志和错误信息要提供定位上下文,但不能泄露隐私信息或敏感配置。
失败路径如果读不懂,代码就不可靠。
12. 依赖、框架与并发克制
高级开发不是引入更多依赖和框架,而是知道什么时候不引入。
要求:
- 小问题不要引入大依赖。
- Go 项目优先标准库和简单实现。
- 新依赖必须有明确收益:减少复杂度、提升可靠性、解决真实问题。
- 不为了标准化引入复杂框架。
- 没有证据不要提前引入缓存、goroutine、channel、锁或复杂并发结构。
- 并发代码必须有清楚的生命周期、取消机制、错误处理和资源释放。
- 性能优化要说明瓶颈和验证方式,不做无证据优化。
并发和缓存不是高级感来源,清楚可靠才是。
13. 测试规范
该测的一定测;不适合自动化测试的,必须给出替代验证和理由。
优先测试:
- 核心业务分支。
- 状态变化。
- 错误路径。
- 边界条件。
- 兼容逻辑。
- 曾经出过 bug 的路径。
测试代码本身也必须可读:
- 测试要表达业务场景,不要只围绕实现细节写。
- 测试用例命名要直白,能看出“什么条件下,期望什么结果”。
- 不要堆复杂 helper、fixture、mock,让测试比业务代码还难懂。
- 能用真实小对象、内存实现、表格测试表达清楚的,不要上复杂 mock 框架。
- 测试失败信息要能帮助定位问题,而不是只告诉人失败了。
- 不要为了测试把业务代码改得更丑。
测试不是为了覆盖率数字,而是为了覆盖这次改动真正的风险。
14. 改动范围克制
改动必须服务当前目标,不做无关美化和范围膨胀;但“克制”不等于只做最小改动,更不等于留下长期不一致。
要求:
- 不顺手重构无关代码。
- 不为了适配个人风格大面积改现有项目结构。
- 可以顺手改善明显问题,但必须和当前目标直接相关。
- 必须补齐当前目标涉及的真实闭环,不能只改一个点却让接口、状态、UI、数据或测试处于不一致状态。
- 不借一个小需求重写一大片代码。
- 如果确实需要扩大范围,必须说明原因、收益和风险。
- 删除代码前要确认没有真实调用方或保留兼容路径。
简化比新增抽象更优先,但简化也必须有边界。
如果为长期规范性需要扩大改动范围,应明确说明扩大范围的必要性、涉及面、验证方式和剩余风险,而不是假装这是一个“小修”。
15. Review 检查标准
Review 时按以下优先级判断:
- 方案是否符合项目既有规范、长期可维护性和长期演进方向。
- 代码能不能顺着读懂,主流程是否连贯。
- 有没有补丁味、临时味、炫技抽象、机械分层。
- 测试或验证是否覆盖这次改动真正的风险。
- 改动范围是否既克制又完整,有没有只图最小改动导致闭环缺失。
Review 必问:
- 这份代码是否符合项目规范,能不能长期维护?
- 这份代码像不像高级开发者写的?
- 主流程能否不用频繁跳转就读懂?
- 业务概念是否清楚?
- 重要数据变化是否可追踪?
- 错误路径和边界条件是否容易理解?
- 是否有临时补丁伪装成正式设计?
- 是否为了暂时可用或最小改动,牺牲了数据、接口、前端、测试或文档的一致性?
- 是否为了架构感引入了不必要的抽象?
- 是否用了不必要的泛型?能否用具体类型或普通函数更清楚地表达?
- 是否用了不必要的
any / map[string]any?这些动态值是否只停留在边界层,进入核心流程前是否转成了明确结构?
- Go 代码是否保持简单直接?
- 中文注释是否覆盖核心流程、坑点和非显然取舍?
- 测试是否覆盖真实风险,而不是只覆盖实现细节?
- 未来的人接手会不会骂人?
如果违反核心偏好,Review 必须说明:违反了哪条、为什么必须这样做、风险是什么、后续是否需要修正。
16. 关键示例
示例一:不要机械 interface
坏例子:
type UserServiceInterface interface {
UpdateUser(ctx context.Context, req UpdateUserRequest) error
}
type UserServiceImpl struct {
repo UserRepositoryInterface
}
如果当前只有一个实现,没有外部边界,也没有真实替身需求,这种 interface 只是增加跳转。
好例子:
type UserService struct {
repo *UserRepository
}
等出现真实边界时,再在消费方定义需要的 interface。
示例二:临时方案必须中文说明
坏例子:
if user.ID == "legacy" {
return nil
}
好例子:
if user.ID == "legacy" {
return nil
}
示例三:测试像业务行为文档
坏例子:
func TestUpdate(t *testing.T) {
mock := newComplexMockFactory().WithA().WithB().Build()
got := run(mock)
assert.Equal(t, true, got)
}
好例子:
func TestUpdateUser_邮箱为空时返回错误(t *testing.T) {
req := UpdateUserRequest{UserID: "u1", Email: ""}
err := service.UpdateUser(context.Background(), req)
if err == nil || !strings.Contains(err.Error(), "邮箱不能为空") {
t.Fatalf("期望返回邮箱为空错误,实际: %v", err)
}
}
测试名称和断言直接表达业务行为,失败时也能定位原因。
17. 给 Agent 的执行要求
当你读取本 Skill 后,应将它作为用户的个人代码开发标准执行。
要求:
- 不要把本规范降级成泛泛建议。
- 开始改代码前先理解项目现有规范:目录结构、接口契约、数据模型、命名风格、错误处理、测试方式、部署/发布约定;不要凭空另起一套。
- 写代码时优先保证长期规范性、长期可维护、主流程连贯、中文注释清楚、抽象克制、测试可读。
- 不能只图暂时可用、暂时可实现或最小改动;当最小改动会破坏长期一致性时,应选择能形成项目闭环的方案。
- Review 或总结时不要只说“已完成”,要指出改动是否符合本规范中的关键标准,尤其是项目规范、长期维护性和闭环完整性。
- 如果代码为了现实限制没有完全符合规范,必须明确说明原因、风险、替代方案和后续处理方式。
- 不要把部署、排障、工具调用规范混入本 Skill 的核心判断,除非用户另行要求。