简洁 TypeScript 项目蓝图
这是一份可复用的决策指南,而非必须照搬的脚手架。它保留了 Study Buddy 仓库中有效的工程做法,移除了所有产品工作流、服务、领域和凭据细节。
1. 以首个真实需求决定项目形态
当浏览器应用需要由服务端持有数据时,默认从一个 Next.js 全栈模块化单体开始:
src/
app/ Next.js 路由、页面、布局、Server Action/Route Handler
app/admin/ 仅在当前确有管理需求时,放置同域、受服务端权限保护的管理页面
components/ 当前产品实际使用的界面组件;`ui/` 可放 shadcn/ui 组件
server/ 业务服务、身份与外部集成;不被客户端组件导入
db/ 数据库连接、Schema 与查询边界
drizzle/ 选择 Drizzle 时的生成迁移
prisma/ 选择 Prisma 时的 Schema 与生成迁移
docs/ 可导航的工程事实来源
scripts/ 确有需要的本地、迁移、配置和部署操作
推荐技术基线是 Next.js、TypeScript、Tailwind CSS、shadcn/ui、PostgreSQL、Docker,以及 Drizzle/Prisma 二选一。Drizzle 适合希望贴近 SQL、显式控制查询与迁移的团队;Prisma 适合更重视 Schema 驱动 Client、工具链和快速建模的团队。Redis 只在跨实例缓存、限流、分布式协调、队列或 Pub/Sub 有真实需求时加入,不是为了凑齐技术栈而启动的默认服务。
上面的 drizzle/ 与 prisma/ 是互斥示例,项目只保留所选 ORM 对应的目录。app/admin/ 也不是预建目录:小型项目出现管理需求后,在同一 Next.js 应用、同一域名和同一认证上下文中增加受服务端权限保护的路由即可;不要仅以隐藏菜单代替鉴权。小项目无需先建立 workspace、apps/api、独立后台或 packages/*。只有出现独立部署的 API、多个客户端、独立 Worker、真实跨应用复用,或独立后台的组织/安全边界已经成立时,再演进为:
apps/
web/ Next.js 应用
api/ 仅在独立 API 边界已经成立时存在
admin/ 仅在独立管理后台边界已经成立时存在
packages/
contracts/ 真实跨应用协议上的共享 schema 与类型
db/ 确有多个服务共同使用时的数据访问基础
将因同一原因变化的代码放在一起。contracts 适合共享请求/结果约束,不应演变成杂物区;db 负责 Schema 和迁移机制,不能承接业务授权。仅当任务确实需要跨越 HTTP 请求持续执行时再增加 Worker。整体采用渐进式架构:先让单体内的业务模块承担变化,只有调用者、部署、数据安全或运行节奏确实分离时才拆包或拆服务。没有当前用途时,不要引入微服务、队列、向量库或插件系统。没有图片/文件需求时不预建对象存储;一旦需要用户上传或运行时资源,则使用 OSS/S3 兼容对象存储,不退回 Git 或应用本地目录。
不要把全栈产品做成只有界面可看的前端 Demo:凡是需要账号、业务数据、提交、审批、支付、文件处理或其他业务副作用的界面,都要接入真实服务端操作及对应开发数据源。临时样例必须明确标识,不能伪装成后端已经实现。性能优化同样不单独预留“性能模块”:先以请求数、查询耗时、首屏/交互等待或成本等证据定位瓶颈,再在所属模块减少重复工作、缩小读取范围或做局部优化;缓存、队列和额外基础设施只在证据表明必要时引入。
2. 在业务边界收拢权威性
浏览器与模型输出只能表达用户意图,不能证明身份、归属、完成状态、价格或 Tool 结果。服务端获取可信身份后,调用的业务操作应统一拥有:
- 授权与参数校验;
- 事务与版本/冲突语义;
- 可能重试的请求和任务的幂等性;
- 持久化状态变化与稳定的失败类别。
HTTP 路由、Worker handler 与 Agent Tool 只将各自协议适配到该操作,不能复制该操作的授权或写入顺序。不要让调用方协调一连串所有者检查、加锁、写入和清理步骤。数据库与文件存储的分布式写入无法原子化时,先保全已提交引用;只有确认不存在持久引用时才删除上传对象。若无法确认,则保留对象供诊断,而不能冒数据丢失风险。
3. 让本地使用可复现且无破坏性
锁定依赖并使用冻结安装。外部容器镜像固定版本或 digest。若网络或组织要求特定包/镜像源,应写入项目配置,而不是假定 Agent 的全局环境。
提供聚焦的启动、重启、停止、状态、生成迁移、执行迁移和相关 smoke 命令。本地启动器应:
- 缺失时从已批准的本地来源创建运行时配置,但绝不覆盖已有配置;
- 仅启动本项目基础设施并执行其迁移;
- 按工作目录和入口识别自身进程,而非只按端口;
- 等待真实就绪后才报告成功;
- 停止或重启应用进程时保留数据卷。
本地数据库、端口和存储前缀必须区别于其他项目及测试/生产环境。日志、浏览器测试产物、生成的 .env、本地数据、上传目录和构建产物默认加入 .gitignore,除非负责人另有明确要求。运行时可变业务数据不得写入仓库内 JSON;结构化事实进入 PostgreSQL,图片和文件进入对象存储,数据库只保留受授权控制的对象元数据。
4. 配置与密钥的所有权
写清配置契约:必填变量名、允许的环境差异、谁提供取值、部署如何消费配置。检查配置形状时不打印具体值。运行时配置不得进入 Web 构建上下文、镜像层或诊断输出。
默认策略:
| 内容 | 版本控制策略 |
|---|---|
| 变量名、说明、无害默认值 | 提交模板/示例。 |
| 真实 API 密钥、密码、签名密钥 | 使用已批准的密钥库或受保护的部署配置。 |
生成的运行时 .env、日志、本地数据 |
不提交。 |
负责人可明确选择对完整且加密/访问受控的配置文件采用不同策略。此例外必须书面记录且一致遵守:限制仓库访问、避免显示凭据、不让凭据进入浏览器或镜像构建上下文,并在部署时以受限权限安装。不得未经明确授权改变仓库的配置策略或轮换凭据。
5. 让文档持续真实
以简短的 docs/README.md 作为导航入口。适合维护的聚焦文档包括:
development.md:分支/提交模型、本地命令、验证分层、迁移规则;architecture.md:当前拓扑、归属边界、依赖和已确认限制;deployment.md:环境、发布授权、迁移/切流/回退行为、维护归属;software-design.md:项目特有的模块边界、权威性、失败语义与性能证据规则;- 有非显而易见决策、发布或验证需要长期证据时,使用带日期的记录。
明确标注事项是已实现、计划、假设还是等待外部验证。链接而非重复事实来源,并将历史报告标记为历史。不要为了看似成熟而额外添加流程文档、图表或决策日志;只有它们能长期影响决策或解释非显而易见约束时才编写。
6. 分支与提交模型
主动选择协作模型,并在 AGENTS.md 与 development.md 中写明。
新项目目录若尚未处于 Git 仓库中,先初始化 Git、建立 .gitignore,再做首次提交。若 Git 作者身份缺失,只能使用已有的仓库/组织配置或用户提供的信息,不得编造姓名和邮箱。
- 对单人、上线前的快速迭代,在
main直接进行有完整意义的本地提交,通常比仪式化的功能分支更简单。保护未提交改动;不得暂存、重置或将无关改动并入当前任务。推送和发布仍是显式操作。 - 多人协作、受保护的发布分支、外部评审或并行高风险工作,则使用短生命周期功能分支及明确的集成/测试路径。不能暗示某一种模型永远更安全;正确选择是团队能够持续一致执行的模型。
每项完整且已经执行最小必要验证的改动,默认形成一次原子本地提交;不要把每次保存或半成品状态都提交。提交前审阅暂存文件清单和差异,只暂存本任务文件。不提交二进制资源、用户数据、数据库导出、本地文件、构建产物、测试结果或密钥;迁移 SQL、迁移元数据、依赖锁文件和必要源码必须保留。提交信息简洁地说明有实际意义的行为变化即可。推送和发布仍是独立授权动作。
7. 验证分层
以最小且能验证改动风险的证据为准:
| 改动 | 最小有效证据 |
|---|---|
| 文案、CSS、局部界面交互 | 查看受影响页面,并试用改变的操作及一个必要边界。 |
| 业务规则或较大模块 | 走通关键端到端路径;对关键不变量按需补充或运行聚焦测试。 |
| 授权、持久化写入、迁移、队列任务 | 按影响验证隔离/权限、持久化结果、重复/重试、冲突/取消和迁移行为。 |
| 广泛共享改动或用户明确要求 | 运行仓库适用的全量检查。 |
| 仅文档改动 | 审阅差异、链接与事实一致性。 |
CI 可以运行更完整的确定性检查,例如类型检查、构建、在幂等性重要时重复应用迁移、集成/E2E 测试和就绪 smoke;但不必让每次本地小改动承担同等成本。真实模型/服务商检查必须显式启用,使用合成输入、说明成本,默认不作为发布门禁。
8. 面向生产的发布设计
构建和发布应锁定某个已提交 SHA,不能打包任意工作区改动。测试/预发布与生产须在域名、配置、数据库、私有存储、认证/Cookie 上下文以及公开索引策略上相互隔离。
服务需要低中断切换时,使用发布锁,其临界区同时覆盖配置安装及其后的所有消费者。一个可靠的发布顺序是:
获得授权的已提交 SHA
-> 校验环境/配置身份
-> 构建不可变应用产物
-> 执行前向兼容的数据库与队列迁移
-> 在健康服务旁启动候选版本
-> 验证容器/进程、API 就绪和相关 Web 响应
-> 切换私有反向代理流量
-> 验证公网 HTTPS/版本
-> 保留一个已知可用的旧产物;只清理安全的旧产物
蓝绿槽适合确有低中断切换需求的应用,简单服务不必强上。应用端口绑定 loopback/私有网络,由反向代理终止 HTTPS 并路由同源 API;不能只为方便就暴露数据库或 Worker 端口。
迁移必须允许旧应用与新应用并存。使用短锁超时,并以多次发布完成“扩展 → 应用切换 → 收缩”。不要做自动 down migration。候选启动或切流验证失败时,保持健康服务继续运行。代码回退不得伪装成已应用数据迁移也被逆转。
发布确认后,只清理可证明属于本项目且不再使用的产物:旧发布目录和无引用镜像版本。保留当前与回退候选版本及容器引用镜像。不得对共享构建缓存做全局清理,也不得将数据库、数据卷、对象存储、用户文件或演示数据作为常规发布清理目标。
9. 完成交付记录
新项目或基础工程改动完成时,应简洁、如实地记录:最终拓扑,所选开发/分支/配置策略,本地命令,新增文档,验证及结果,以及刻意延期事项。不要因为存在 Dockerfile、未实际演练的发布脚本或替身通过结果,就认定已经生产验收。