2025年4月29日

在大型团队中处理数据

当多个团队共享一个数据库时,问题就会出现。迁移冲突、查询变得臃肿,或者所有权变得模糊不清。

本文将详细阐述大型团队如何构建有弹性的数据工作流程——无论是使用SQL、ORM还是两者结合——同时保持速度和自主性。我们将探讨实际模式,如“扩展-收缩”模式、Schema CI、查询约定以及何时使用原生SQL。

如果你正在扩展团队和数据库,这篇文章正是为你准备的。

Illustration showing a large team of diverse people collaborating on a shared data dashboard. Individuals are interacting with charts, tables, and code snippets displayed on floating UI panels. The environment suggests an organized, collaborative workspace with digital tools and version control systems. Arrows and icons represent data flows, edits, and teamwork across roles like engineers, analysts, and product managers.

为什么数据工作流程在规模化时会崩溃

随着团队的壮大,他们常常在共享数据库环境中发生冲突。结果呢?草率的迁移、意外的表更改,以及没有人真正清楚谁拥有什么。

当所有权模糊时,即使是善意的更改也可能导致系统崩溃。这在没有明确定义数据库协议的快速发展的产品团队中尤为常见。

一些常见的故障发生在以下情况:

  • 一个团队发布了一个迁移,删除了另一个团队仍然需要的列
  • 表中充斥着服务于相互冲突目的的字段
  • 单一的Schema文件有五个所有者,却无人负责

有时,规模化更多地关乎清晰度,而非技术本身。

选择一个工作流程,并使其简单乏味

允许团队以多种方式管理同一任务可能看起来很灵活——但这往往导致不一致和混乱。一个单一、简单乏味的工作流程可以减少思维负担,促进更顺畅的协作。

团队管理Schema变更的常见方法有三种

方法示例描述迁移优先Prisma, Rails, Knex在代码中编写Schema迁移并提交到版本控制模型优先Django, Sequelize通过应用程序模型定义Schema,并自动生成数据库变更DDL优先原生SQL, Liquibase, Atlas直接编写和审查SQL语句——提供完全的控制和透明度

提示:DDL代表数据定义语言(Data Definition Language)——定义数据库结构的SQL语句,如CREATEALTERDROP

在所有团队中坚持一种方法。没有规范地混用风格会使你的工作流程更难调试,几乎无法扩展。

将Schema视为应用程序代码

你会不经过拉取请求(pull request)就发布应用程序代码吗?可能不会。数据库Schema也应受到同样严格的审查。

务必遵循以下实践:

  • 在Git中对每个Schema变更进行版本控制
  • 通过拉取请求审查Schema变更
  • 使用Prisma Migrate、Atlas或Liquibase等差异工具,在Schema变更上线前将其可视化

这有助于提高透明度,及早发现错误,并建立每次演进的历史记录。

使用“扩展-收缩”模式

大型Schema变更无需承担风险。“扩展-收缩”模式将变更分解为非破坏性步骤,从而最大限度地减少停机时间。

  • 扩展:添加新字段、表或结构
  • 迁移:回填并更新任何引用或依赖
  • 收缩:仅在一切安全后才移除或重命名旧部件

这可以避免破坏生产环境,支持滚动部署,并帮助团队即使在复杂变更的情况下也能保持正常运行时间。

Diagram showing how a database table for a customer evolves using the "expand and contract" pattern. It starts with a single name field, then adds firstname and lastname alongside name in the expand phase, and finally removes name in the contract phase, keeping only firstname and lastname.

无论你使用原生SQL、Prisma、Rails还是任何其他系统,这都是一个可靠的模式。访问我们的数据指南,了解更多关于“扩展-收缩”模式的信息。

定义何时使用SQL以及何时使用ORM

SQL和ORM是互补而非竞争的工具。关键在于根据你的目标选择更适合的工具:开发速度、查询控制和长期可维护性。

在以下情况使用ORM:

  • 你希望快速进行产品开发
  • 你偏好可读、可维护且类型安全的代码
  • 你的团队可以通过抽象化样板代码和重复任务而受益
  • 你希望通过不要求每位开发者都成为SQL专家来降低开发风险

在以下情况使用原生SQL:

  • 你正在优化复杂的连接或性能关键路径
  • 你需要对查询、索引或执行计划进行细粒度控制
  • 你正在构建依赖自定义逻辑的重报告内部工具

你无需只选择一种。大多数现代应用程序会两者结合使用:使用ORM来快速开发并保持类型安全,当性能或灵活性有要求时,则回退到原生SQL——理想情况下,将这些查询封装在类型化的辅助函数中以保持可维护性。

例如,Prisma ORM中的TypedSQL允许你使用带有完整TypeScript类型的原生SQL,就在你的ORM调用旁边。这让你拥有SQL的灵活性,同时不牺牲类型安全性和可维护性。

一些流行的工具包括:

Kysley (TypeScript), Sequelize (Node.js), Django ORM (Python), Knex (SQL builder), 通过SQLX或pgx使用的原生SQL (Rust),甚至与Atlas或Liquibase等迁移工具结合的SQL脚本。

使用CI来强制执行数据库安全

如果没有保障措施,Schema变更可能会悄无声息地破坏生产环境。持续集成(CI)通过在合并每个变更之前对其进行验证来防止这种情况发生。

一个健壮的数据库CI流水线应该:

  • 预览Schema差异使用Prisma Migrate或Atlas等工具检测破坏性变更
  • 运行集成测试对临时或影子数据库运行集成测试,以端到端验证行为
  • 如果检测到破坏性操作(例如删除列、数据丢失),则使构建失败

使用GitHub ActionsDocker和你选择的迁移工具来设置。例如,使用一个作业来:

  1. 启动一个干净的PostgreSQL容器
  2. 应用迁移
  3. 运行你的测试套件
  4. 解析迁移计划以查找潜在不安全的操作

这可以在回归问题影响生产环境之前捕获它们,并使Schema演进成为工作流程中可共享、可审查的一部分——而不是部署的副作用。

使用OpenAPI和Swagger定义边界

当团队通过API交换数据时,清晰的契约对于避免误解和错误至关重要。OpenAPI是一个被广泛采用的规范,用于以结构化格式描述RESTful API,这种格式人类和工具都能理解。

Swagger是一套围绕OpenAPI标准构建的流行工具集。它允许团队以最小的摩擦文档化、可视化和测试API。

使用Swagger等工具可以帮助团队:

  • 明确定义API边界和预期行为
  • 为前端和后端自动生成类型和客户端代码
  • 验证变更,及早发现破坏性更新
  • 通过交互式文档浏览和测试API端点
  • 通过模拟响应和集成类型化客户端,帮助前端团队更快地行动
  • 通过使API自解释和可发现来改善开发者入职体验

这种方法在微服务架构或多个团队依赖共享内部API时特别有效。

Screenshot of Swagger UI displaying a REST API for Prisma examples. It shows endpoints for common actions like getting posts, deleting posts, signing up, publishing posts, and viewing drafts. Each HTTP method is color-coded (e.g., GET in blue, POST in green, DELETE in red, PUT in orange), and the interface is hosted locally at localhost:3000.

开发者无需依赖“部落知识”或Slack消息来解释API的工作方式,而是可以自信地探索、原型设计和集成——这一切都得益于准确、始终保持最新的文档。

共同定位查询,标准化结构

分散的逻辑难以扩展。一致的文件结构使你的代码库更容易导航、理解和扩展——特别是随着团队的壮大。

与其将数据访问逻辑分散在models/utils/services/中,不如按领域对查询进行分组。这反映了CLEAN架构模块化单体设计的模式,其中特性边界而非技术层驱动结构。

为什么这有效

  • 鼓励每个领域清晰的所有权和封装
  • 通过使查询位置可预测来加速入职过程
  • 减少跨领域关注点和不一致的抽象
  • 让你的逻辑更接近使用它的地方——更容易测试、文档化和重构

CLEAN基于功能的架构这样的设计模式不仅是学术理论,它们能使协作更顺畅,系统随时间推移更具弹性。一个良好的结构会随着你的团队和产品的成长而扩展。

跨系统共享类型

前端和后端之间的类型不匹配是常见的bug来源——例如将number视为string,或缺少可选字段。这些问题通常发生在团队在多个地方定义相同的类型时,导致重复和偏差。

代码生成通过直接从数据库Schema或查询定义生成类型来解决这个问题。这为你提供了一个单一的真理来源,在你的技术栈中保持同步。

Diagram showing how a shared type system can be generated from a source schema. The flow goes from a "Source Schema" file to a "Types Generator", which produces a "Types" file. These types are then used by both the "Frontend" and "Backend" systems, promoting consistency across the stack.

为什么它很重要

  • 避免后端和前端之间手动复制类型
  • 防止由类型不匹配或过时契约引起的错误
  • 在编译时而非运行时强制执行正确性
  • 通过更好的IDE支持和自动补全,使重构更安全

帮助实现此目的的工具示例:Prisma ORM (TypeScript), SQLC (Go), SeaORM (Rust), Pydantic (Python)

当你的应用程序层共享相同的类型定义时,你可以减少错误,提高开发者信心,并以更少的意外更快地发布。

让AI发现显而易见(和不那么显而易见)的问题

现代AI工具帮助团队在不牺牲质量的情况下更快地行动。通过将大型语言模型(LLMs)集成到你的开发工作流程中,你可以自动化繁琐的任务,捕捉细微的问题,并专注于解决真正的问题。

Two-part diagram comparing software deployment workflows before and after continuous integration (CI) improvements. In the "Before" section, application code follows an automated path to deployment, while database code faces multiple manual steps like sending scripts to DBAs, script reviews, bundling, and manual deployment—each marked with stop signs. In the "After" section, both application and database code are integrated into a unified CI pipeline with shared steps for commit, validation, artifact build, and automatic deployment, eliminating manual bottlenecks.

使用LLM驱动的助手来:

  • 发现潜在不安全或破坏性的Schema变更
  • 总结拉取请求差异以便更快地审查
  • 生成测试数据、边界情况和种子场景
  • 直接在拉取请求中推荐改进或标记风险

帮助实现此目的的工具示例:

Windsurf, CodiumAI, Sweep.dev (schema & PR review), GitHub Copilot, Cursor (inline coding help), OpenDevin (backend automation)。

这是Laravel生态系统中的一个例子,名为Enlightn,一个扫描你的应用程序并提供性能、安全等方面的可行建议的工具。

Screenshot of a pull request code review by the enlightn-for-laravel bot on GitHub. The bot flags that 2 checks have failed, reporting 2 security vulnerabilities but no performance or reliability issues. It comments on a code snippet in UserProfileController.php, warning that using orderBy with request input could allow users to inject column names dynamically, potentially leading to security risks.

AI不仅仅是为了速度——它是你的第二双眼睛。明智地使用它,可以帮助你编写更安全的代码,减少审查疲劳,并分担日常任务,让你能够专注于大局。

优化团队速度,而非查询完美

精心调优的查询固然有价值,但可靠且一致的交付能力更为重要。优先选择那些能够帮助整个团队更快行动而不牺牲稳定性的工作流程。

专注于以下过程:

  • 鼓励安全的默认设置和合理的约定
  • 使新开发者的入职过程变得简单
  • 通过工具和CI及早发现问题
  • 平衡性能与代码清晰度和可维护性

以速度为重点的实践示例:

使用带有防护措施的ORM、代码检查工具和格式化工具、类型安全的API,以及用于查询回归的CI检查。

快速的团队交付的价值高于完美调优的查询;优化那些能随团队扩展而非仅仅数据库扩展的工作流程。

TLDR:构建工作流程,而非混乱

你已经读到这里了——太棒了!这里有一个快速回顾,帮助你巩固所学。将此表格视为一个诊断和完善当前设置的透镜,而非一份核对清单。大多数规模化数据挑战更多地关乎清晰度和一致性,而非技术本身。

问题解决方案迁移冲突使用PRs、版本控制、扩展-收缩Schema所有权混乱组织查询、共享类型脆弱的原生SQL使用类型化辅助函数、CI验证团队行动过慢选择简单乏味、快速的工作流程意想不到的API变更使用OpenAPI并在CI中验证

一个好的数据工作流程是你的团队理解、信任并能够改进的。这才是可扩展的。选择规范和清晰。当每个人都了解工作方式时,团队就能更快地协同前进。

加入对话,塑造更好的工作流程

如果这帮助了你,我们很乐意听到你的反馈。在X上标记我们并分享你正在构建的内容。或者加入我们的Discord,如果你想聊天、解决问题,或者深入探讨数据库和性能。

我们还定期在YouTube上发布视频深度解析。如果你喜欢这类内容,请点击订阅。更多示例、更多性能技巧,或许还有一些惊喜发布。我们会在那里见到你。

不要错过下一篇文章!

订阅Prisma时事通讯

© . All rights reserved.