命名争论里大小写、下划线、缩写那一半交给工具判,剩下一半才要人看。
评审被点到名字才来的,直接看工具查不出的三类。
只看名字,能不能判断它属于哪个业务、做什么、返回什么?
答「不能」,说明得读完实现才懂,先改名字。改完名字还说不清它做什么,说明这个函数在做两件事,该拆开——那不是命名问题。
把规则写进配置,评审时没人再逐行看。四条命令挂进 make check,本地和持续集成跑同一条,见质量门禁。
| 拦什么 | 规则 | 工具查不到的边界 |
|---|---|---|
| 函数、变量、参数的大小写风格 | Python N802 / N803 / N806;TS naming-convention 的 variableLike;C# IDE1006;Go revive 的 var-naming | Java 没有随构建工具附带的命名检查。不装 Checkstyle,这一项只能靠评审看 |
| 类型、类用 PascalCase | Python N801;TS naming-convention 的 typeLike;C# IDE1006 | 泛型参数、枚举成员的写法各语言不同,默认规则不统一管 |
| 包名、模块名 | Go revive 的 var-naming(小写、无下划线);Python N999 | TS 与 Java 的文件名规则不在 ESLint 内置规则里,要另装插件 |
缩写整词大写,如 ScopeID 不写 ScopeId | Go revive 的 var-naming,默认表含 ID、URL、HTTP、API | 其余语言没有内置缩写表。TS 可以用 naming-convention 的 custom 正则逐条加,成本高于收益,本仓库不加 |
原先只开了 errcheck、govet 这些,它们都不看名字。revive 的 var-naming 才管包名、下划线和缩写。默认表里的 ID 与 URL 不要删。
version: "2"
linters:
enable:
- errcheck
- govet
- ineffassign
- staticcheck
- unused
- revive
settings:
revive:
rules:
- name: var-naming
severity: error
- name: exported
severity: warning
用 extend-select 在基线之上追加,不要重写 select——重写会把质量门禁里那几项静默关掉。命名只加 N 一组;复杂度相关的那两条由复杂度那一页负责。
[tool.ruff.lint] # 基线:select = ["E", "F", "I", "B", "S", "C901"] extend-select = ["N"]
recommended 不含这一条。把占位参数写成 _note 时,要加 leadingUnderscore: "allow",否则会误报。
{
rules: {
"@typescript-eslint/naming-convention": [
"error",
{ selector: "function", format: ["camelCase"] },
{
selector: "variableLike",
format: ["camelCase"],
leadingUnderscore: "allow",
},
{ selector: "typeLike", format: ["PascalCase"] },
],
},
},
不符合时诊断号是 IDE1006。要让它阻断,命令里带 --severity warn,见本节末尾。
[*.cs] dotnet_naming_rule.constants_should_be_pascal_case.severity = warning dotnet_naming_rule.constants_should_be_pascal_case.symbols = constants dotnet_naming_rule.constants_should_be_pascal_case.style = pascal_case_style dotnet_naming_symbols.constants.applicable_kinds = field dotnet_naming_symbols.constants.required_modifiers = const dotnet_naming_style.pascal_case_style.capitalization = pascal_case
golangci-lint run ruff check --select N . npm --prefix frontend run lint dotnet format csharp/App.csproj --verify-no-changes --severity warn
工具查不到的边界:这些规则只认形状,不认含义。handleData 能过全部四条检查,因为它的形状完全合规。名字说的是不是业务含义,工具看不出来——下一节有一条命令把它筛出来。
检查跑绿之后,评审时只剩三个问题。前两类用一条命令就能从改动差异里筛出来,第三类要打开调用处读一遍。
| 类别 | 可识别现象 | 评审动作 |
|---|---|---|
| 通用词占位 | 名字里出现 data、info、manager、handler、utils、helper、common、process | 在改动差异里跑一次搜索,命中的逐个要求改 |
| 缩写与领域不符 | 要打开导入的文件名才能猜出缩写含义,例如 gc()、ns() | 要求换成领域词:笔记、所属范围、检索模式 |
| 名字说的是实现不是含义 | parseJsonToList、callApiV2。调用方换一种数据来源,名字就错了 | 问一句「它返回的东西,业务上叫什么」 |
在仓库根目录执行,只扫新增行。-U0 去掉上下文,命中处不回显旁边没改的代码。命中不等于必须改,但每一条都要给出留或改的理由。
git diff main...HEAD -U0 \ | grep -nE '^\+.*\b(Data|Info|Manager|Handler|Utils?|Helper|Common|Process|Do)[A-Z_a-z]*'
make check,红了就改,不写进评审意见——写进去等于把工具的活挪回给自己。
工具查不到的边界:上面那条 grep 只抓英文通用词。汉语拼音名(yonghu、bijixiangqing)它抓不到,评审时要逐个看。
func handleData(d interface{}) interface{} { ... }
func processInfo(req *Request) (*Response, error) { ... }
type Item struct {
Data map[string]interface{} ← 什么都装得下,也就什么都不说明
}
var mgr = NewManager()
认它的办法:只看名字,能不能说出它属于哪个业务、做什么、返回什么。这四个名字形状全部合规——长度、缩写、大小写,工具一条都挑不出来,正因为它们形状合规。工具认形状,业务含义只有人能判;handleData 能过全部四条检查,这句反过来说就是:形状检查对命名里的业务含义毫无帮助。
这四条工具判不了,也不能靠「以后注意」。
scope_id 与代码里的 scopeID 算同一个词,文档里不许出现第二个说法。违反后果:后面的改动在两种写法之间漂移,检索和重构都会漏掉一半。git ls-files | grep -E '[a-z]+(yi|xiang|shu|biao)',输出应为空。违反后果:检索时得同时试两种拼法。工具查不到的边界:第二条在数学与算法代码里放宽。n、m、x 这类符号是领域惯例,改成 itemCount 反而更难读。放宽只限这些符号本身,不扩展到业务代码。
不要跨语言抄写法。Java 的 MAX_PACKET_SIZE 抄到 Go 和 C#,会同时违反这两门语言各自的检查。
| 语言 | 函数、方法 | 类型、类 | 常量 | 文件 |
|---|---|---|---|---|
| Go | Open | Note | MaxPacketSize | notes.go |
| TypeScript | label | Note | maxPacketSize | note-store.ts |
| Python | load_note | Note | MAX_PACKET_SIZE | note_store.py |
| Java | loadNote | Note | MAX_PACKET_SIZE | Note.java |
| C# | Load | NoteStore | MaxPacketSize | NoteStore.cs |
| Rust | open | Note | MAX_PACKET_SIZE | note.rs |
Rust 里把函数写成 Open,编译器报 non_snake_case,代码格式里的 cargo clippy -D warnings 会因此失败。排版工具不改名字,改名字由人负责。
下面四种是本仓库承认的例外,其余按必须死守的四条处理;用例外时在评审意见里写明属于哪一种。
i、j、k;数学与算法里的 n、x;数据库已有列名与第三方接口字段名(scope_id 在 SQL 里保持下划线写法,不为了统一改成 scopeId);测试里的占位参数 _note(TS 需配 leadingUnderscore: "allow")。
a、b、tmp);把 NoteStore 缩成 NS 只为少打字;同一概念在不同文件里用两种缩写;把「以后再改」当作例外。
工具查不到的边界:外部字段名这条例外只覆盖映射层。Note.scopeID 这个 Go 字段仍按 Go 的表写;只有写 SQL 和解析响应那一处用列名原文,并且用一处常量或结构体标签集中表达映射,不让下划线写法扩散到业务代码。