框架约定能让应用更易理解、减少冗长文档。了解约定通常覆盖的范围、何时失效,以及如何只记录那些例外。

框架约定是框架“默认的做事方式”——框架默默鼓励甚至直接要求的做法。与其让每个团队自己发明文件夹布局、命名规则或请求/响应流程,框架提供了一套共享模式。如果你遵循这些模式,其他开发者就能预测文件的位置和行为,而不需要冗长的说明文档。
大多数文档并不是因为大家热爱写文档而存在。文档用于解决几类反复出现的问题:
约定特别擅长解决前两项。当“把 X 放哪”和“把 Y 命名为何”已经被框架决定时,需要解释和争论的就少了。
“约定取代文档”并不意味着项目不再需要文档。它的意思是大量基础指引从文字转为可预测的结构。你不再需要通过阅读 wiki 来知道控制器放哪里,而是因为框架期望控制器在某个位置(工具、生成器和示例也会强化这一点),你可以直接推断出来。
结果是关于显而易见事项的文档减少,而更多精力用在记录真正与项目相关的内容:业务规则、非同寻常的架构选择和刻意的例外。
本文面向希望在不维护庞大文档站点的前提下获得更清晰代码库和更快入职速度的开发者、技术负责人和以产品为中心的团队。
你会了解框架约定如何创建“隐式文档”、约定通常规范哪些内容、约定何时失效以及哪些内容仍然值得显式记录——以便在文档减少的同时,清晰度提升。
“约定优于配置”意味着框架为你做出合理选择——前提是你遵循它的约定。与其写(和读)大量设置说明,团队依赖于每个人都能识别的共享默认值。
可以把它想像成在一个国家开车,大家都同意靠右行驶、红灯停车、遵守标准路牌。
你可以为每个路口写详细手册(“看到红色八边形就停;绿灯就走…”),但没有必要——因为约定已经被广泛认知并稳定执行。
框架约定也如此:它们把“我们这里的做法”变为可预测的行为。
当框架有默认值时,你不必为每一个小决策写文档。框架(以及你的团队)可以假定如下模式:
User 模型映射到 users 数据)这种共享基线把文档从“设置 X 的每一步”缩减为“我们遵循框架默认,除非另有说明”。它也降低了入职时的认知负荷:新开发者更常能正确猜测,因为代码与他们在其他项目中见过的匹配。
约定并非没有代价。缺点是你有时要放弃非常规的文件结构、自定义命名或高度定制的工作流。
优点是:一致性——更少争论、更少惊讶、更少只有老员工才知道的“部落知识”。团队能够更快推进,因为他们把时间花在构建而不是解释上。
约定只有在大家已经知道它们或能一次学会并到处复用时才会节省写文档的成本。
这就是为什么流行框架如此强大的原因:它们的约定被广泛教授、广泛使用并在许多代码库中重复出现。当你的项目紧贴这些共享默认时,代码会默认变得可理解,所需的书面说明也会大幅减少。
框架约定是共享的捷径。它们规范了每个新队员第一天常问的问题:“这放哪?”和“我该怎么命名?”当答案可预测时,你可以用少量一致的默认值替代大量文档。
大多数框架会推动一种可识别的项目结构:UI 所在、路由所在、数据访问所在、测试所在。这种一致性很重要,因为人们不用看指南就能区分“渲染页面的部分”和“与数据库交互的部分”。
最好的约定让常见任务成为肌肉记忆:新增屏幕时,你已经知道它属于哪个文件夹。
命名规则减少了诸如“我们的控制器在 X,需要在 Y 中注册”的解释。名字本身就暗示职责。
常见示例:
许多 Web 框架把文件映射到路由(或使路由易于推断)。如果你能从文件名猜出 URL,或者反过来,你就不需要为每个功能单独写一份路由文档。
约定还会设定对动态路由、嵌套路由和 404 处理的期望,因此“如何添加新端点”有标准答案。
约定通常定义“数据代码”所在:模型、仓库、服务、迁移、模式文件。即便应用很小,约定好的数据访问位置也能防止随意在 UI 代码里散落数据库调用。
标准命令(运行、测试、构建、lint、格式化)消除了歧义。新开发者不应该需要查 wiki 就能知道如何启动项目——npm test(或等价命令)应该是显而易见的选择。
当这五个领域保持一致时,代码库本身就能回答大多数“我们这里怎么做?”的问题。
一个试图用文字描述“如何一切工作”的 wiki 往往会随着文件夹移动、命名变化和新功能加入而逐渐过时。约定颠覆了这个思路:你不再读长篇解释,而是读结构。
当框架(和你的团队)就文件位置达成一致时,仓库就像城市街区一样易于导航。
如果你知道 UI 组件放在 components/,页面视图放在 pages/,API 处理函数放在 api/,你就不会再问“X 在哪?”因为第一次猜测通常就是对的。即便不对,你的搜索范围也被限制了:不是到处都是,而是在少数预期位置之一。
约定让文件名和符号携带含义。新人可以从位置和命名推断行为:
user.controller 的文件很可能处理请求逻辑UserService 类可能包含业务规则migrations/ 的文件夹可能包含有序且只运行一次的数据库修改这种推断把“给我讲清楚架构”这类问题拆成更小且更易回答的问题(“这个服务允许直接调用数据库吗?”),从而更容易记录。
强化地图的最快方式是脚手架。起始模板和生成器默认会以“正确”的形状创建新功能——包括文件夹、文件名、样板代码,通常还有测试。
这很重要,因为约定只有在被一致应用时才有效。模板是护栏:它把每个新路由、组件或模块推入预期结构,使代码库无需更多 wiki 页面仍然可读。
如果你维护内部脚手架,可以在短小的入职页面(例如 /docs/getting-started)中链接它们,让文件树完成其余工作。
框架约定常常像静默的内置说明。与其写一页解释“东西放哪”或“如何接入”的文档,框架已经替你做了决定——团队学会从结构中读取答案。
Rails 因“约定优于配置”而闻名。举个简单例子:如果你创建了 OrdersController,Rails 会假设存在匹配的视图文件夹 app/views/orders/。
这个单一约定可以替代一大段原本需要解释的内容:
结果:新人可以通过遵循文件夹模式添加页面,而无需询问“这个文件放哪?”
Django 鼓励一致的“app”结构。看到一个 Django 应用时,开发者会期待在 models.py 找到数据结构,在 views.py 找到请求处理,在 templates/ 找到 HTML。
你可以写很长的指南来描述项目的组成,但 Django 的默认设置已经教会了这些。当要更改页面外观时,开发者会去 templates/;当要调整存储的数据时,便从 models.py 开始。
结果:修复更快,寻找时间更少,“哪个文件控制这个?”的问题减少。
Next.js 通过让路由直接反映文件夹结构来减少文档。将文件放在 app/about/page.tsx(或旧版的 pages/about.tsx),就自动得到一个 /about 页面。
这免去了关于如何注册路由、如何一致命名路由以及如何在不破坏导航的情况下添加新页面的文档需求。
结果:入职更简单——人们可以通过扫描目录发现网站结构。
Rails、Django 和 Next.js 看起来不同,但原理相同:共享默认把项目结构变成说明。当每个人信任相同的约定时,代码库本身能回答许多“我们这里如何做这件事?”的问题——无需维护额外文档。
框架约定工作良好时感觉“隐形”。你可以猜测文件位置、命名,以及请求如何流经应用。当代码库偏离这些共享默认时,困惑会回归。
早期会出现一些模式:
UserService,另一处用 UsersManager,再一处用 user_service这些并非必然错误——但它们意味着新队员不能再依赖框架的“地图”。
大多数约定崩坏始于合理的局部优化:“这个功能特殊,所以放到这里”或“这种命名更易读”。问题是例外具有传染性。一旦第一个例外存在,下一位开发者会把它当作先例:
到那时,约定不再是约定——而变成了部落知识。
当约定模糊时,入职变慢,因为人们无法预测去哪儿找东西。日常任务耗时增加(“哪个文件夹是真正的?”),错误增加(接错模块、使用错误命名模式、重复逻辑)。团队为此补偿,开更多同步会议、写更长的 PR 说明、添加最终变成过时的“快速文档”。
只有在有明确理由时才自定义——并留下书面说明。
这个说明可以很轻量:在不寻常结构附近放简短注释,或在 /docs/decisions 页面写一段简短条目,说明发生了什么、为什么值得,以及未来应遵循的标准做法。
框架约定能删除很多解释性页面,但并不能免除责任。仍需文档记录的部分是那些项目刻意不同于多数开发者假定的地方。
跳过对标准框架行为的重复解释。相反,捕捉影响日常工作的决策:
示例:“我们在 /src/features 下使用功能文件夹而不是分层文件夹(/src/components、/src/services),因为所有权对应团队并减少跨团队耦合。”这一句能防止数周的缓慢漂移。
当例外在局部很重要时,把说明放在局部。文件夹内的短 README.md 或文件顶部的简短注释通常比中央 wiki 更有效。
合适候选:
保持这些说明简短且可操作:什么不同、为什么不同、接下来该怎么办。
放一页轻量说明(通常在 /docs/project-rules.md 或根 README),只列出 5–10 条关键选择,能帮助人们避免踩雷:
这不是完整手册——只是共享的护栏。
即便有约定,当人们无法运行应用时入职也会受阻。添加一节简短的“如何运行/测试”,与标准命令和你的实际设置一致。
如果约定命令是 npm test,但你的项目实际需要 npm run test:unit,要明确写出来。
将文档视作变更的一部分,才能保持准确。在评审时问:“这是否引入了新的例外?”如果是,要求在同一个 PR 中包含相应说明(本地 README、Project Rules 或根快速上手)。
如果约定是代码库的“共享默认”,自动化是让它们生效的方法。与其指望每个开发者记住 wiki 页面的规则,不如把规则做成可执行的——让项目自我强制执行。
一个良好设置会及早且悄然地捕获偏离:
*.spec.ts、describe/it 词语或必需断言等模式,使测试易读一致。这些检查把“请记住……”的段落替换为简单结果:代码要么符合约定,要么不符合。
自动化的优势在于快速失败:
最好的规则集是小而无聊的。从框架默认开始,只添加能保护清晰度的规则(命名、结构、边界)。每多一条规则就是多一件人们必须理解的事,所以像对待代码一样对待新检查:当它解决了反复出现的问题才添加;当它不再有用就删除。
当代码库遵循框架约定时,测试可以不只是“证明工作正常”。它们可以解释系统应如何工作,用接近实现本身的位置和普通话描述。
一个有用的规则:每个测试应描述一个端到端行为。如果有人能通过浏览测试名就理解系统承诺了什么,你就减少了单独文档的需要。
优秀测试通常遵循简单节奏:
更好的是使用反映用户意图的命名:
signing_in_with_valid_credentials_redirects_to_dashboardcheckout_fails_when_shipping_address_is_missing这些名字就是“文档”,因为失败的测试会迫使人们更新说明。
验收(或功能)测试很适合记录产品从用户角度的行为。
验收测试可以描述的行为示例:
这些测试回答了“当我做 X 会发生什么?”这类新队员首先关心的问题。
单元测试在记录“微小但重要”的规则时很有用:
当规则不是从框架约定中能直接看出来时,单元测试尤其有价值。
示例数据也可以成为活文档。一个小而命名良好的夹具(例如 user_with_expired_subscription)比 wiki 中一段文字更快地教授领域概念。
要点是克制:保持夹具最小、可读并针对单一概念,这样它们才是值得信赖的示例,而不是另一个需要维护的系统。
起始模板(和背后的生成器)是把“我们这里怎么做”变为人们实际遵循的最快方式。与其让每个团队成员记住正确的文件夹、脚本和工具,不如把这些决定烙印在起始仓库里。
三者都减少了“文档债”,因为约定被编码在起点,而非写在会漂移的 wiki 上。
在实践中,这也是像 Koder.ai 这样的工具能发挥作用的地方:当你通过聊天驱动的工作流生成一个新的 React 应用、Go 后端、PostgreSQL 模式或 Flutter 客户端时,你可以通过让默认输出匹配你的约定来把团队保持在单一的“黄金路径”上(然后把源码导出到你的仓库)。
入职困惑多数不是业务逻辑问题,而是关于东西放哪以及如何运行。一个好的模板使常见任务在各仓库间一致:相同脚本、相同文件夹名、相同检查命令、相同的 PR 期望。
如果只能做一件事,就要达成一致:
/src、/test、/docs(仅用于例外))把它做得够小以免团队跳过:
install + dev)test、lint 与 format 脚本最大风险是因为“去年好用”就复制旧模板。过时的依赖、遗留脚本或被弃用的模式会通过模板迅速传播。
把模板当作产品:给它版本、定期审查,并在约定改变时更新它。(如果平台支持快照与回滚——例如 Koder.ai 支持——使用它来安全地迭代起始模板,而不破坏每个人的基线。)
减少文档并不等于让人猜测。这意味着把“主路径”做得足够一致,让大多数问题自有答案,只有真正不寻常的部分需要书写下来。
查找人们在 Slack、PR 评论、站会或入职环节反复问的地方。一些提示:
如果某个问题被问到两次以上,你可能不需要多写文字——而是需要一个约定。
对于每个重复问题,决定:
一个有用规则:如果偏离不能节省真实时间或降低真实风险,它很可能不值得长期困惑。
保留一页简短页面(例如 /docs/conventions),列出:
限制内容为新人第一周需要的东西。如果页面开始膨胀,通常说明你应该简化代码库而不是写更多文档。
应用会演化。安排轻量的季度回顾:
尽可能优先使用框架默认,只记录不同之处——清晰、简短并集中在一处。
框架约定是框架期望你遵循的默认模式——文件夹结构、命名、路由、数据访问和常用命令。当你遵循这些约定时,其他开发者可以推断出文件在哪里以及如何工作,而无需阅读项目特定的文档。
因为随着代码库变化,用文字保持文档准确很难。文档主要用于:
约定通过让结构可预测来覆盖前两项。
不是的。约定减少了关于显而易见内容的文档(文件放哪里、路由如何接入),但你仍需记录项目特有的内容:业务规则、刻意的偏离和关键决策。想法是“更少的文档,但更高价值的文档”。
它们规范常见的“第一天”问题:
当这些都是可预测的,仓库就能自解释。
当代码遵循已知模式时,目录树和文件名就像路标。新人可以按预期导航(例如“模板在 templates/ 中”,“迁移在 migrations/ 中”),而不是阅读可能已过时的长篇架构说明。
它们把约定编码为默认输出,避免依赖记忆。好的脚手架会生成:
这防止了偏离,使“地图”在各功能间保持一致。
当开发者无法预测文件位置或命名时就能看出来。常见信号:
UserService vs UsersManager vs user_service)届时团队会用 Slack 解释、写更长的 PR 描述,或产生陈旧的“快速文档”。
只有在确有回报时才自定义,然后留下轻量说明:
README.md/docs/decisions 或类似位置记录记录应说明变了什么、为什么变、以及今后的标准做法。
即使有强约定,仍值得写的小量实用文档包括:
保持精简,并在代码评审时要求当变更引入新例外时更新相应文档。
将约定变为可执行:
当检查在本地或 PR 中失败时,开发者会立即学会规则——审查者也能把精力放到产品逻辑上。