广州总部电话:020-85564311
广州总部电话:020-85564311
20年
互联网应用服务商
请输入搜索关键词
知识库 知识库

优网知识库

探索行业前沿,共享知识宝库

【第3507期】基本属实的命名规则

发布日期:2025-05-13 18:13:41 浏览次数: 863 来源:前端早读课


给事物起一个恰当的名字非常困难,所以作为程序员,我们会想出一些小规则来帮助自己。 这些规则常常是不一致的。

比如:

  • 在 Python 中,变量应该使用蛇形命名(例如 last_name),而在 JavaScript 中则用驼峰命名(例如 lastName
  • 在 Python 中,除了类名和异常名用驼峰命名,其他都用蛇形命名
  • 在 Java 中常量全大写,但在 Scala 中就不是这样

而这些语言其实还算类似。一旦涉及 SQL、HTML 或 COBOL 这种风格差异巨大的语言,命名约定就开始大相径庭了。

通情达理的人都明白,这些规则更像是指导方针,最重要的其实是保持一致性。但我自己有一条非常喜欢的命名规则,几乎适用于所有地方:

不要给同一个概念取多个名字。

和所有规则一样,它常常与其他规则发生冲突,但我发现这条规则通常占上风。

案例分析:户外活动系统

我维护着一个用于管理大学户外旅行项目的线上应用。应用中有一张 SQL 表,记录了学生请求借用的装备(如背包、头灯等),这张表叫 member_gear_requests。之所以这么命名,是因为还有一张表叫 trip_members,记录的是参与旅行的学生。既然是 trip members 请求装备,那么表就叫 member_gear_requests,听起来也挺合理。

不过,在开发过程中我觉得 “member gear” 这个说法怪怪的。一次旅行中有两类装备:一类是大家共享的(比如帐篷),一类是每个人自己的(比如背包)。于是我在前端把这两类列表分别叫做 “Group Gear” 和 “Individual Gear”。

你可能会问:数据库里叫 “member gear”,前端却叫 “individual gear”,中间发生了什么?

我也从没认真想过这个问题。那我们现在从 HTML 模板开始,倒着看看。

<h3>Individual Gear</h3>
<tableclass="gear">
   {% for item in member_requested_gear %}
<tr><th>{{ item.name }}<td>{{ item.quantity }}
   {% endfor %}
</table>

看起来还好,标题是 “Individual Gear”,而模板变量叫 member_requested_gear。也许整个应用里都把这个概念称作 “member gear”,只是显示给用户时用了更友好的词 “Individual Gear”。虽然不是很理想,但也说得过去。

那这个数据是从哪里来的呢?

 trip.member_requested_gear = memberRequestedGear

呃,好吧,我可能是想在 JavaScript 和 HTML 中保持各自的命名风格(JS 用驼峰,HTML…… 比较随意)。那 memberRequestedGear 是怎么来的?

const memberRequestedGear = gear.getIndividualRequestedGear(req.db, tripId)

天哪,又是 “Individual Gear”?也就是说,命名在不同地方反复切换?

当然,这个函数最终查的是名为 member_gear_requests 的 SQL 表:

exportfunctiongetIndividualRequestedGear(db, tripId){
// 查询大致如下:
// SELECT ...
// FROM member_gear_requests
}

总结一下,这个概念 —— “学生为自己申请的装备” —— 在我的代码里出现了这些不同的名称:

  • Individual Gear
  • member_requested_gear
  • memberRequestedGear
  • getIndividualRequestedGear
  • member_gear_requests

结果比我想象的还混乱(而且这段代码全是我自己写的)。

当然有些差异是必须的。前端显示给用户的信息应该更友好,比如不会在 <h3> 标签里用蛇形命名。但如果我一开始就选对了数据库表名,也不用执着于用驼峰命名变量,就可以做到:

  • 前端显示为 “Individual Gear”
  • 函数叫 getIndividualGearRequests
  • 数据库表及变量名统一为 individual_gear_requests

如果你比我更坚定,还可以坚持连函数名都用蛇形命名。但我个人觉得函数和变量在概念上已经够不同了,不必强求。不过,随你喜欢!

命名不一致背后的更深原因:建模模糊

很多时候命名混乱的背后,不是风格问题,而是建模不清晰。

“member gear” 和 “individual gear” 其实指的是同一个东西,但我当时显然没彻底想明白,导致数据库、后端、前端叫法各不相同。

我们以为是在写代码,其实是在构建一个对现实世界的抽象模型。而大多数开发者并没有接受过 “建模” 的系统训练。命名之所以难,是因为我们常常没弄清楚这个概念到底是什么。

领域驱动设计(DDD)提出 “通用语言” 原则:团队中所有人 —— 产品、设计、开发、测试 —— 对同一个业务概念要用统一的名称。这个统一的名字贯穿数据库、API、代码、文档、会议和注释。

如果你在接口里叫它 user_items,数据库表是 member_gear_requests,产品叫它 “个人装备”,那说明你们根本没有达成共识。

所以在命名前,可以自问:

  • 这是一个新概念吗?有没有和别的概念重叠?
  • 团队中每个人都理解它的含义吗?
  • 如果它是一个已经存在的概念,那我是不是在重复造词?

清晰的建模,才能带来清晰的一致命名。

命名为什么重要?

Unix 工具有一个理念:文本是程序之间的通用接口。grep、awk、find、xargs 等工具之间都是通过文本交互的。代码也一样,背后的哲学是相通的 —— grep 就是从文本编辑器 ed 演化来的命令。

随着我工作时间的增加,我越来越依赖文本搜索作为浏览代码的主要手段。这是因为文本搜索永远都可用 —— 程序一定会用文字描述。如果你能快速搜索,就能迅速熟悉任何代码库,无论语言还是范式。

如果我确定会长期使用某种语言,我会配置 LSP(语言服务器协议),这样就可以用 “跳转到定义” 等功能。但即使配置好了 LSP,也总会有它搞不定的场景,或者需要复杂配置,比如:LSP 能解析数据库结构吗?模版语言呢?各种连接关系呢?

唯一我稍微信得过的是 Java,但即便如此 —— 也并不稳妥!IntelliJ 在稍微复杂的项目里一开始就是一堆红,得调几个设置才能正常工作。

本案例用的模板引擎 Nunjucks , 据我所知没有与 LSP 的集成(也不需要)。 所以如果一个新贡献者想搞清楚 “Individual Gear” 是怎么显示出来的,如果他们搜索 “individualgear” 就能一眼看到数据库、后端、前端完整链路,那会比他们被我混乱的命名绕来绕去强太多了。

有几个正在学习 React 的学生也参与了这个项目开发,几乎没怎么需要我引导。

清晰的想法会带来清晰的命名。

如果你命名老是变来变去,说明你还需要再理清楚自己的设计思路。

团队协作中的命名一致性建议

命名一致性不仅对个人有帮助,对整个团队尤为重要。以下是我在实际开发中总结的一些小建议:

建立 “命名对照表”

针对核心业务模型,建立统一命名表,明确概念和使用范围:

概念
统一命名
用途示例
个人装备
individual_gear
SQL 表、变量名、API 字段
共享装备
group_gear
模板标题、展示组件、CSS 类名
学生
member
trip_members 表、循环变量名

这个表格可以放在 docs/naming.md,成为协作基准。

Code Review 时关注命名一致性

除了看逻辑和性能,也要检查命名是否符合统一规则。尤其在引入新功能、创建新接口时,更应重视语义是否一致。

用户界面可翻译,但要记录

展示层常常使用更人性化的名称,例如用户看到的是 “个人装备”,而变量名是 member_gear。这类 “翻译” 可以接受,但应通过注释或文档说明:它们是同一个概念。

保持一致的小贴士

代码库就像树的年轮,反映了它在不同时期的开发状态。比如这个应用:

  • 是我把一个 MongoDB + ReactJS 项目重写成 SQLite + htmx

  • 是我第一次尝试这种组合,边做边学

所以代码里难免留下各种 “结” 和 “疤”,这些其实都是成长的痕迹。以下是一些教训。

【第2843期】LocatorJS:点击React组件,进入其代码

如果你改了名字,就请在所有地方都改

本例中,“individual” 和 “member” 的混用,就是因为我想改名但又懒得做迁移和搜索替换。要改可以,不改也可以,但别一半一半。否则最后就一团糟。

(当然,用户界面展示的名称可以跟代码不同,这没问题)

很多代码库都会随着多人协作和需求变化发生 “语义漂移”。但我这个案例就是个完全可以避免的错误。

你可以嘲笑我 20 秒钟,没关系。

如果你写 SQL,就用蛇形命名

SQL 有一个令人烦恼的限制,即如果您希望表名或列名中包含大写字母,则必须将它们加上引号:

 -- 标准写法
 SELECT first_name, last_name
 FROM users
 WHERE first_name like 'Alex%';

 -- 大小写字段要用双引号
 SELECT "firstName", "lastName"
 FROM users
 WHERE "firstName" like 'Alex%';

不同 SQL 实现的引号规则略有不同,也很讨厌。总之,用蛇形命名就不会踩坑。

接下来我们要在应用里使用这些值。一开始你可能想把 first_name 转换成符合编程语言习惯的 firstName。

但最好别这么做。

数据库里是 first_name,代码里也是 first_name,这样就不会因为命名不一致而出现难查的问题。这样改命名只是为了满足 “代码整洁强迫症”,但代价很高。能不改就不改。

很多 ORM(对象关系映射库)会自动帮你做大小写转换(比如 Hibernate、Sequelize),但这也容易出错。有时查询失败就是因为你或 ORM 的转换逻辑不一致。错误看似明显,但你得去读 ORM 自动生成的 SQL 才能找出问题(如果堆栈信息里还能看到那些 SQL)。

我选 Hibernate 和 Sequelize 当例子,是因为我在它们身上都踩过坑。

当然,如果你打算从不手写 SQL,完全依赖 ORM 自动转换,或者你用的数据库不受大小写限制,那这些建议可以忽略。

遵循命名规范是好的,但如果只是为了遵守规范而强行改名,那就不值得了。

【第1439期】React 项目结构和组件命名之道

不要缩写

这个建议在本例中没出现,但很重要:

别随便缩写。

如果某个地方叫 start_time,那就不要在别的地方简写成 start。你代码只写一次,但你要搜索很多次。简写会导致搜索不到所有用法,还得担心各种拼写和变体。

特别是在应用生命周期很长、可能被不同人接手的项目里,这一点尤为重要。

Java 特别容易让你想缩写变量名,因为 “名词之国” 实在太大了。但 Java 的工具链也很强大,容易让你误以为搜索功能永远能帮你找齐所有定义。还是老老实实写完整名吧,你将来在 IntelliJ 里用 CMD+SHIFT+F 搜索时会感谢自己的。

最后

软件工程基本是抽象构建的实践,所以两个概念是否为同一个,有时并不明显 —— 它们本质上都是人类构想的概念。要深入探讨这个问题可能得引入一整套哲学体系,我现在也没法立刻展开,可能得从柏拉图开始?

我最近遇到一个情况:我在 SQL 里一般喜欢给主键命名成唯一字段名(比如 users.user_id 而不是 users.id),这样可以用 USING 语法。但在我做的一个 ActivityPub 项目里,这条规则输给了命名一致性,因为 ActivityPub 的规范要求所有对象都用 id 字段。我后来意识到这一点,还专门做了迁移,结果证明这个决定是对的。

另一个项目里我用了 Go 和 Templ(因为在 Big Sky Dev Con 上见到了 Adrian,他人很好)。Templ 的 LSP 支持非常棒,我花时间配置了一下。但后来我发现跳转定义功能总是跳到 Templ 自动生成的代码,而不是我实际想改的地方。太烦了!最后我干脆放弃了配置,直接用文本搜索反而更快。

如你所见,命名是一件小事,但也是一件大事。清晰的命名源于清晰的思考。不要给同一个概念取多个名字,让未来的你和你的队友少掉头发。

优网科技,优秀企业首选的互联网供应服务商

优网科技秉承"专业团队、品质服务" 的经营理念,诚信务实的服务了近万家客户,成为众多世界500强、集团和上市公司的长期合作伙伴!

优网科技成立于2001年,擅长网站建设、网站与各类业务系统深度整合,致力于提供完善的企业互联网解决方案。优网科技提供PC端网站建设(品牌展示型、官方门户型、营销商务型、电子商务型、信息门户型、DIY体验、720全景展厅及3D虚拟仿真)、移动端应用(手机站APP开发)、微信定制开发(微信官网、微信商城、企业微信)、微信小程序定制开发等一系列互联网应用服务。


我要投稿

姓名

文章链接

提交即表示你已阅读并同意《个人信息保护声明》

专属顾问 专属顾问
扫码咨询您的优网专属顾问!
专属顾问
马上咨询
联系专属顾问
联系专属顾问
联系专属顾问
扫一扫马上咨询
扫一扫马上咨询

扫一扫马上咨询

和我们在线交谈!