工程思维

前端工程师的后端思维指南

2026-04-10 · JJ

AI 已经能写出大多数后端样板代码。但真正的工程能力不在于怎么写,而在于为什么这样设计——数据该怎么建模,接口该如何定义,崩溃时数据会是什么状态。这些判断力,AI 无法替你建立。

本文以一个多用户笔记应用为载体,带你建立后端工程师最核心的五块思维。

数据建模

后端开发最重要的判断发生在第一步——决定数据的形状。给你三个实体:用户、文件夹、笔记,该怎么建表?

关系设计

用户和笔记、文件夹都是一对多关系,一个用户拥有多个文件夹和笔记。笔记和文件夹的关系取决于一个业务决策:一篇笔记能同时出现在多个文件夹吗?

工程师的克制原则:不要为了"未来可能"过度设计。先做一对多,真的需要多对多再迁移。过早的灵活性是复杂性的来源。

协作编辑是多用户访问同一篇笔记,本质是多对多——但不要给 Note 表加字段,而是独立建一张权限表:

核心字段设计要点
userid, email, password_hash密码存 hash,绝不明文
folderid, name, user_iduser_id 外键,一对多
noteid, title, content, user_id, folder_idfolder_id 可为 NULL,笔记不强制在文件夹里
user_note_accessuser_id, note_id, role权限与业务数据分离,RBAC 模式

数据建模的判断力 = 克制。不是能想到多少种设计,而是知道什么时候不要过度设计,以及当需要扩展时,结构能不能平滑演进。

API 设计

前端是 API 的消费者,后端是设计者。设计一个笔记列表接口,看似简单,实则有很多值得思考的细节。

GET /api/v1/notes
Query 参数
folder_id string 可选 传入则返回该文件夹的笔记;不传则返回所有笔记
page integer 可选 页码,从 1 开始,默认 1
size integer 可选 每页条数,默认 20,最大 100

成功响应(部分字段示意):

{
  "data": [
    {
      "id": "uuid",
      "title": "我的第一篇笔记",
      "folder_id": "uuid | null",
      "created_at": "2024-01-01T00:00:00Z"
      // ⚠ 不返回 content,列表页不需要正文
    }
  ],
  "pagination": {
    "page": 1,
    "size": 20,
    "total": 142,
    "has_more": true
  }
}

两个最容易踩的坑

列表不要返回 content 正文。 列表页只展示标题,却把每篇笔记的几千字正文都传回来,流量和解析开销会放大几十倍。字段按需返回,详情页才拿 content。

错误格式要统一。 任何错误都应该返回同一个形状,前端才能写通用拦截器:

{
  "error": {
    "code": "UNAUTHORIZED",   // 机器可读
    "message": "请先登录"      // 人类可读
  }
}

认证机制

前端知道”带 token 请求,后端验证”,但后端工程师需要理解这个 token 从哪来、长什么样、为什么能验证。

JWT 认证完整流程
前端 后端 数据库 ① 登录 POST /auth/login email + password 查用户 + 验密码 bcrypt 比对 hash 返回用户记录 id, email, role… 生成 JWT 用密钥签名 收到 JWT 存入 localStorage ② 后续请求 GET /notes Authorization: Bearer <JWT> Auth 中间件 验签 + 检查过期 解析出 user_id ⚡ 不需要查数据库 ③ JWT 结构 Header 算法 HS256 类型 JWT . Payload(明文) user_id, role, email exp 过期时间 . Signature Header+Payload 服务器密钥签名 ⚠ Payload 任何人都能解码,签名不能伪造——不要存敏感信息

JWT 的天然缺陷:无法主动吊销

用户修改密码后,旧 token 在过期之前仍然有效。攻击者的 token 最多还能再用几小时。这是 JWT 的设计缺陷,三种常见应对方案:

方案原理优点适用场景
短过期时间Access Token 15min,Refresh Token 7天无需改架构,纯无状态大多数场景首选
Token 黑名单吊销时将 token ID 写入 Redis可立即失效,最安全金融、医疗等高安全场景
密码版本号User 表加 password_version,JWT 带上,每次改密 +1实现简单,改密即失效中小型项目折中方案

JWT 本质上是"无法主动撤回的授权书",过期时间是唯一的内置失效机制。任何需要主动吊销的场景,都要在无状态和安全性之间做权衡——没有完美答案,只有适合业务的答案。

数据库索引

接口越来越慢,第一个怀疑的不是并发,而是 SQL 本身。最常见的元凶:没有索引,数据库做了全表扫描

有索引 vs 无索引
❌ 无索引:全表扫描 folder_id: aaa 工作计划 folder_id: bbb 购物清单 folder_id: aaa 读书笔记 folder_id: ccc 旅行计划 folder_id: aaa 会议记录 100万行 = 扫100万次 查询时间随数据量线性增长 ✓ 有索引:直接定位 索引目录 aaa → 1,3,5 bbb → 2 ccc → 4 工作计划 读书笔记 会议记录 100万行 = 查1次索引拿到结果 查询时间几乎不随数据量变化

加索引只需要一行 SQL,但索引不是越多越好

-- 给 folder_id 加索引
CREATE INDEX idx_note_folder_id ON note(folder_id);
读(SELECT)写(INSERT/UPDATE)
无索引慢(全表扫描)
有索引快(直接定位)慢一点(同时维护索引)

索引应该加在查询频繁、写入不那么频繁的字段上。排查慢接口的顺序:先看 SQL,再看索引,并发是最后才考虑的。

事务

删除一个有 10 篇笔记的文件夹:先删笔记,再删文件夹。如果删完 5 篇后服务器崩溃,数据库里会是什么状态?

不是数据丢失,而是数据不一致——文件夹还在,但里面只剩 5 篇笔记,另外 5 篇凭空消失,数据永久损坏。事务就是为了解决这个问题。

无事务 vs 有事务
无事务:数据不一致 删除笔记 1~5 DELETE FROM note… 💥 服务器崩溃 笔记 6~10 还在,文件夹还在 残留状态 文件夹:存在 ✓ 笔记 1~5:已删 ✗ 笔记 6~10:还在 ✓ 同一文件夹 5 篇笔记凭空消失 有事务:全成功或全回滚 BEGIN TRANSACTION 删除所有笔记(暂存) 💥 服务器崩溃 自动 ROLLBACK 数据恢复原样,用户重试即可

代码层面非常简单:

// ❌ 无事务 — 崩溃后数据不一致
await db.query('DELETE FROM note WHERE folder_id = ?', [folderId])
await db.query('DELETE FROM folder WHERE id = ?', [folderId])

// ✅ 有事务 — 崩溃自动回滚
await db.transaction(async (trx) => {
  await trx('note').where({ folder_id: folderId }).delete()
  await trx('folder').where({ id: folderId }).delete()
  // 两步都成功才 commit,任何一步报错自动 rollback
})

判断标准:如果你的操作需要改两张表以上,或同一张表改多行,且这些操作必须同时成功,就必须用事务。

结语:AI 时代的工程判断力

AI 可以替你写出正确的 SQL,但不能替你决定 folder_id 该不该可为空;AI 可以生成 JWT 验证中间件,但不能替你权衡 token 吊销方案;AI 会写事务代码,但不知道你的业务里哪些操作需要被包裹在事务里。

你需要学的不是更少的编程,而是更高层次的编程思维。写代码的动作可以交给 AI,但理解代码、判断代码、设计系统的能力反而变得更值钱——因为这正是 AI 目前做不好的部分。

JJ

关于作者

专注于内容创作、产品策略与设计实践。欢迎交流合作。

← 上一篇