去年对接一个跑了七年的老系统,我在同一个字段上看见了四种写法:库里是 created_time,接口文档写 CreateTime,前端类型定义里是 createTime,埋点表格里叫 Created Time。第一版代码上线后取不到值,我花了两个多小时怀疑序列化、怀疑网关、怀疑时区,最后发现是 ORM 里 mapUnderscoreToCamelCase 这个开关没打开。从那以后我把"命名风格转换"当成一件需要工具的事,而不是"顺手改一下"的事。
一、同一个词的几种长相
命名风格不是审美问题,是不同技术栈的历史约定。同一个"用户名",各处的合法写法并不相同:
| 风格 | 示例 | 主要出现在 |
|---|---|---|
| camelCase | userName | JavaScript / Java 变量与方法、GraphQL 字段 |
| PascalCase | UserName | 类名、React 组件、C# 公共成员 |
| snake_case | user_name | Python 变量与函数、数据库列名 |
| CONSTANT_CASE | USER_NAME | 常量、环境变量 |
| kebab-case | user-name | URL 路径、CSS 类名、CLI 参数 |
| Title Case | User Name | 表头、按钮文案、报表列名 |
外部 API 也没有统一标准:GitHub 的公开接口大面积用 snake_case,Google 风格的接口用 camelCase。于是同一份数据经过三层,就要转三次。手工转的风险不在慢,在于不一致——同一批 42 个字段里,只要有一个手抖写成 userName 与 username 两种,它就跟其他字段的映射规则不同,而这类差异通常到运行期才暴露。
二、命名冲突一般在这四个地方炸
- 数据库标识符大小写。PostgreSQL 对未加引号的标识符会折叠成小写,你按
CreateTime建列,实际存的是createtime,之后每次引用都得加双引号。MySQL 表名是否区分大小写取决于服务器配置与文件系统,Linux 上区分、Windows 上通常不区分,所以"我本地能跑"和"服务器报表不存在"完全可以同时成立。 - HTTP 头。头部名称按规范是大小写不敏感的,很多运行环境读出来一律小写(
Content-Type变成content-type),网关重写后拿不到值,多数人会先去查鉴权。 - 签名与校验串。参与签名的字段名要和对方约定逐字符一致。把
AppKey转成appkey之后验签失败,报错信息只会给你 "invalid signature",不会告诉你是大小写问题。 - 缩写和数字。
userID还是userId、QrCode还是QRCode、utf8mb4转驼峰成Utf8Mb4,机械转换的结果常常比原写法更难看。这类词需要一份人工白名单,而不是让规则去猜。
还有一个少有人提前防的坑:大小写转换在个别语言环境下不是可逆的字母映射。土耳其语里大写 I 转小写得到的是无点的 ı,而不是 i,Java 里 toLowerCase() 不带 Locale.ROOT 就会踩到,字段名在测试环境好好的,部署到设置了区域设置的机器上就匹配不上。
三、我现在的批量转换做法
- 先把分隔符统一。字段名里可能混着空格、下划线、连字符,我习惯先用批量查找替换加一条正则规则,把
[_\-\s]+全替成单个下划线,让后续转换有稳定的输入。 - 整列粘进大小写转换,分别取全大写、全小写、首字母大写和驼峰、下划线、连字符几种输出。42 行一次处理完,三份目标命名各取一列,比在编辑器里逐个改省掉的不只是时间,还有出错率。
- 结果先过一遍正则校验再落文档。用正则表达式测试匹配
^[a-z][a-z0-9]*(_[a-z0-9]+)*$这类模式,凡是列出不合格行的,就是转换没能识别分词的字段(多半是缩写或粘连的数字),单独人工处理。 - 把规则固化到检查里,而不是靠约定。前端加 ESLint 的
camelcase规则,Python 侧用 linter 的命名检查,数据库列名在 migration 评审清单里写死 snake_case。转换工具解决存量,lint 解决增量。 - 改名要往下游同步:视图、BI 报表、导出的模板、给运营用的对照表,都按新名字重新生成一次。老系统里最容易漏的是别人手工维护的 Excel 取数模板。
四、几个边界
大小写和命名风格只作用于英文字母。中文、全角字母、代码里的中文注释不参与转换,也不会被破坏,但混排文本整批处理时注释会跟着变格式,所以我一般只贴字段清单,不贴整段源码。
不要为了"统一"把整份源文件全转成大写或小写。代码的可读性和 diff 噪音都是代价,正确范围是标识符、表头、常量名这一层级,正文文案另按 Sentence case 处理。
对外契约改名要留过渡期。字段级别的重命名对调用方是破坏性变更,常见做法是新名字与旧名字并行输出一个版本周期,文档里注明废弃时间。
五、常见疑问
Q:camelCase 和 PascalCase 到底怎么选?
看它是不是"类型"。类、组件、接口名用 PascalCase,实例、变量、方法、函数参数用 camelCase,这是 Java、JavaScript、C# 社区共同的约定。
Q:数据库字段该用什么命名?
snake_case 更稳:跨库可移植、不依赖引号包裹、在 SQL 里可读性最好。定下来之后,让 ORM 的自动转换去负责映射到代码里的 camelCase,而不是两边手工对齐。
Q:为什么我本地正常的代码,发到服务器上就报字段不存在?
大概率是大小写敏感性不同。Windows 文件系统不区分、Linux 区分,数据库表名和列名的行为跟着配置与平台走。把标识符统一成小写加下划线,可以从根上避免这类"平台依赖"。
Q:几十行表头能一次转完吗?
可以,多行整批处理正是这类工具存在的理由。要点是先统一分隔符,再选目标风格,最后用一条正则校验输出,三步都不能省。
Q:转换会不会影响已经加密或参与签名的字符串?
会改变参与计算的原文,结果就完全不同。签名、哈希、Base64 这类字符串永远保持原样,只转换人写的标识符,两者不要放在同一次批量里。
那个老系统后来我做了一次全量重命名:库里全部 snake_case,接口出参统一 camelCase,前端类型不再手写而由接口定义生成。现在回头看,当初那两个多小时排查的根源,不是任何一层写错了,而是没有任何一处明确写下"这个字段在这一层应该长什么样"。命名风格本身没有对错,混着用才有代价。