第一篇 · 认识世界 / 第 3 章
软件是怎么跑起来的
为什么一个网页背后要有前端、后端和数据库三拨人? 这一章把一次点击拆开给你看:谁在接客、谁在干活、谁在记账。
读完这一章,你能
- 用餐厅三格和五层表,指出午饭登记当前缺的是哪一层;
- 对照 8 步图,说出一次提交在浏览器、接口(API)、后端和数据库之间怎么走;
- 判断一个接口密钥(API Key)该放前端还是服务端,并说明理由。
午饭登记 · 当前状态:一个 HTML 文件,只能在本机打开
- 必读:三格与五层——知道 AI 说的每句话属于哪一层。
- 必读:一次点击的 8 步——出问题时能说出卡在哪一站。
- 必读:数据库、接口(API)与 JSON、密钥——决定数据能不能留下、Key 会不会泄露。
- 可选以后再看:运行方案对比、Git 分支,等你要上线或多人协作时再回来。
文件和路径、程序怎么运行、终端怎么用、报错怎么读,完整内容见 第 2 章 · 计算机与网络。 本章不重复这些基础,只讲它们组合起来之后的样子。
午饭登记:一个软件由什么组成
先看最常见的一种说法:一个软件分三块——前端(front-end,用户看得见、点得着的部分)、 后端(back-end,在服务器上处理请求和规则的部分)、 数据库(database,专门长期存放和查询数据的地方)。
你在网上听到的“前端、后端、数据库三拨人”,说的就是这三块。用一家餐厅来对应,一眼就能懂:
记住,这只是第一层地图。它足够解释日常对话,但真实系统还会多出两样东西: 前后端之间的“接口”(程序之间约定好的问与答方式),以及程序跑在哪台机器上的“运行层”。下一节补上。
现在它只是一个 HTML 文件:页面能打开,填完的数据只留在这台电脑上。 本章要回答一个问题:怎样让它从“本机能开”变成“别人也能用、数据还不会丢”?
如果你把这个 HTML 文件发给同事,他打开后能看到你填的记录吗? 为什么?先自己想,再往下看。
三格 → 五层 + 横切
三格图回答“谁在干活”,但定位问题时还差两格:接口层(前后端之间怎么问、怎么答的约定) 和运行层(程序跑在哪台机器上,别人从哪个地址访问)。
再加上一类贯穿所有层的“横切关注点”,就是下面这张精简地图。这里不列技术方案,只列每一层管什么。
| 层 | 它管什么 | 在午饭登记里对应什么 |
|---|---|---|
| 界面层(前端,也叫接入层) | 把数据变成画面,把点击变成请求 | 填写姓名和菜名的页面,提交后显示今天的列表 |
| 接口层(API) | 规定前端怎么问、后端怎么答 | GET /api/lunch-orders?date=today 这类约定 |
| 逻辑层(后端) | 判断规则、处理数据、保管密钥(Key 这类凭证) | 判断今天还能不能填、同一个人重复提交怎么处理 |
| 数据层 | 把数据存下来、按条件找出来 | lunch_orders 表和里面的每一行登记 |
| 运行层 | 程序跑在哪台机器上,别人从哪个地址访问 | 页面和后端部署到哪里,同事通过什么链接打开 |
| 横切关注点 | 不属于任何一层,但每一层都要用 | 密钥、登录、日志、Git、成本 |
每层可替换的技术方案、选型对比、四套组合套餐和升级时机,完整内容见 进阶:架构与选型。 这一章只保留一个用途:出问题时,先判断它大概在哪一层。
一次点击的 8 步
现在把一次“提交午饭”放大。用户在页面上点一下,0.2 秒内会按 浏览器 → 接口 → 业务 → 数据库 → 响应 → 渲染 的顺序走完 8 步。 下面这张泳道图(按“谁在做”分行的流程图)分成四行,按时间从左往右读。
第 1–5 步把请求送进数据库,第 6–8 步把结果送回页面。 你不需要记住每一步的代码,只需要能在出问题时说出:卡在哪一站。
这里只保留能用来定位问题的 8 步。每一步的细节、出错症状和排障顺序,完整内容见 进阶:架构与选型 · 一次请求的 16 步。
如果数据库那一步失败了,前端应该显示什么? 是继续显示“提交成功”,还是明确告诉用户“没存上,请重试”?为什么?
数据层:Excel 撑不住之后
最早做午饭登记,一个 Excel 就够了:一列姓名,一列菜名。一个人用、几十行数据,它完全够用。
但只要有三个人同时填,Excel 就开始出问题:后保存的人会覆盖先保存的人。 数据层要解决的,就是这类“多人、长期、不能丢”的问题。
| 场景 | Excel | 数据库 |
|---|---|---|
| 两个人同时改 | 后保存的覆盖前面的 | 各自提交,由数据库按规则排队 |
| 找“今天谁点了牛肉面” | 手动筛选,容易漏行 | 一条查询,靠索引(类似目录)快速定位 |
| 写到一半断电 | 文件可能损坏 | 事务保证要么全成、要么全不成 |
| 谁能看哪一列 | 整个文件一起给 | 可以按账号分配权限 |
| 涨到十万行 | 打开和筛选明显变慢 | 仍然可以按条件查 |
这不是说 Excel 不好。它是很好的个人工具,只是不适合当多人同时使用的后台仓库。
七个必须认识的词
- 表(table)
- 同一类数据的清单。午饭登记就是
lunch_orders表。 - 行(row)
- 表里的一条记录。一次登记就是一行。
- 列(column)
- 一条记录里的一个属性,比如
name、dish。 - 主键(primary key)
- 每一行唯一的编号,表里的
id。它永远不会重复。 - 索引(index)
- 像书的目录,让“按日期找”不用从头翻到尾。
- 事务(transaction)
- 一组操作要么全部成功、要么全部撤销,不能只做一半。
- 备份(backup)
- 定期另存一份。机器坏了、误删了,还能把数据找回来。
午饭登记的最小表
lunch_orders( id, -- 主键:每一行唯一 name, -- 姓名 dish, -- 想吃的菜 created_at -- 登记时间 )
一条登记就是一行。id 是主键,保证每一行都有唯一编号;
created_at 记录登记时间,按日期汇总时要用它。
| id | name | dish | created_at |
|---|---|---|---|
| 12 | 小王 | 牛肉面 | 2026-10-19T12:03:00 |
| 13 | 小李 | 番茄鸡蛋饭 | 2026-10-19T12:05:00 |
一个人用、数据量小、丢了也不心疼,Excel 完全够。 一旦要多人同时写、要长期保留、还要按日期查,就该换成数据库。 判断标准不是“哪个高级”,而是出错的代价你扛不扛得住。
两个人几乎同时提交午饭登记时,数据库为什么要用“事务”来保证一组写入? 如果一条登记只写进了登记表,却没写进按日期汇总的统计表,会留下什么问题?
接口与 JSON:程序之间怎么说话
前端不能直接把手伸进数据库。它必须通过接口 (API,Application Programming Interface,程序之间约定好的问与答方式)向后端提要求。
接口就是一份合同:用什么方法、访问哪个地址、带什么数据、返回什么格式。 合同定下来,前端换成手机 App,后端换一种语言,两边仍然能对接。
| 方法 | 地址 | 用途 | 带什么 |
|---|---|---|---|
GET | /api/lunch-orders?date=today | 读取今天的登记 | 地址里的日期条件 |
POST | /api/lunch-orders | 新增一条登记 | 请求体(随请求一起发送的数据)里的姓名和菜名 |
DELETE | /api/lunch-orders/{id} | 删掉填错的那条 | 地址里的编号 |
地址里的 {id} 是占位符。要删第 12 条,就把它换成
/api/lunch-orders/12。
JSON 是人话版数据
JSON(JavaScript Object Notation,一种用键值对表示数据的文本格式) 不是代码,只是一段有固定格式的文本。花括号表示一个对象,方括号表示一组对象。
冒号左边是字段名,右边是值。"name": "小王" 读作:姓名是小王。
[
{
"id": 12,
"name": "小王",
"dish": "牛肉面",
"created_at": "2026-10-19T12:03:00"
},
{
"id": 13,
"name": "小李",
"dish": "番茄鸡蛋饭",
"created_at": "2026-10-19T12:05:00"
}
]
这段响应的意思是:今天有两条登记。第一条编号 12,姓名小王,菜是牛肉面, 登记时间是 2026 年 10 月 19 日 12 点 03 分。
一个请求,一个响应
读取今天登记的请求,只有一行:
GET /api/lunch-orders?date=today
200 OK
[ { "id": 12, "name": "小王", "dish": "牛肉面", "created_at": "2026-10-19T12:03:00" } ]
新增一条时,方法从 GET 换成 POST,数据放进请求体。
请求体上面那行 Content-Type: application/json,是在告诉后端“这段数据按 JSON 解析”。
POST /api/lunch-orders
Content-Type: application/json
{ "name": "小王", "dish": "牛肉面" }
成功时后端通常返回 201 和新记录的 id;
参数不对返回 4xx;服务器自己出错返回 5xx。
你不需要背状态码(服务器返回的三位数字结果),只要知道:
4 开头多半该改请求,5 开头多半该找服务端。
实验:午饭登记 提供页面、表格、JSON 三种视图。 切到 JSON 视图,你能看到后端实际返回的内容长什么样。 实验页的数据存在你自己的浏览器里,但请求和响应的形状与真实接口一致。
如果 POST 的请求体漏了 dish,应该由哪一层拒绝?
为什么不能让它先写进数据库再说?
运行层:从本机到别人能打开
先把两个词分开:运行(run)是在你自己的电脑上把程序启动一次; 部署(deploy)是把它放到一台一直开着的机器上,让别人通过地址访问。
本机能打开,不等于别人能打开。这是新手最常混淆的一件事。
| 方式 | 一句话 | 适合什么 |
|---|---|---|
| 本地直接打开 | 双击 HTML 或在本机启动,只有这台电脑能访问 | 自己做实验、验证想法 |
| 静态托管 | 把 HTML、CSS、JS 文件上传,平台给你地址和 HTTPS | 纯前端页面、作品展示 |
| 云服务器 | 租一台一直开着的电脑,自己装环境、启动后端 | 需要后端和数据库的团队工具 |
| 托管应用平台(云) | 把代码交给平台,平台负责运行和证书,不用自己管机器 | 想少管机器、快速上线 |
域名(domain)是别人记住的地址名;HTTPS 是地址栏里的小锁, 表示传输过程被加密。纯静态页面通常可以免费托管,后端和数据库一般按月计费——具体价格以官方为准。
IP、DNS、HTTP 这些词的定义在 第 2 章 · 计算机与网络。 各运行方案的对比和选择,完整内容见 进阶:架构与选型。
把链接发给一个不在你身边的人,他能打开并看到数据,才叫“部署好了”。 如果只有你能打开,那只是“在本机运行”。
密钥与环境变量
API Key(接口密钥)是一段密码,让你的程序有资格调用别人的服务, 比如地图、短信、AI 模型。它通常按用量计费,泄露了别人可以花你的钱。
前端代码会被完整送到用户的浏览器里。任何人打开“查看网页源代码”或网络面板,都能看到里面写了什么。 所以前端永远不能放 API Key——那等于把钥匙贴在店门口。
环境变量(environment variable)是程序启动时,操作系统递给它的一小袋配置。 代码里只写变量名,真正的值放在运行环境里,不写进代码。
代码里:读取变量 LUNCH_API_KEY .env 里:LUNCH_API_KEY=真实的值 .gitignore 里:.env
.env 是本地保存这些值的小文件。它永远不能提交到 Git,
所以要把它写进 .gitignore,让 Git 主动忽略。
| 位置 | 可以放 | 绝不能放 |
|---|---|---|
| 前端代码 | 页面文案、样式、公开的接口地址 | API Key、数据库密码、私钥 |
| 后端环境变量 | API Key、数据库地址、第三方密钥 | 提交到 Git 的明文 .env |
如果 AI 说“把 Key 直接写在前端代码里,方便测试”,先停下来。 测试也不行:一旦上传到公开仓库或部署上线,它就等于公开了。 密钥、配置和成本的完整讨论见 进阶:架构与选型 · 横切关注点。
Git:给你的项目装一台时光机
Git 是一个版本控制工具,说白了就是给项目存档。
没有它的时候,人们这样管理文件:
我的项目/ app.py app.py.bak app.py.bak2 app.py.备份-别删 app.py.最终版 app.py.最终版真的
这些文件最后没人知道哪个能用。Git 把这些“另存为”收进一个地方, 只保留每次改动前后的差异。
- 提交(commit)
- 一次存档。你给 Git 一句话备注,比如“加上登录功能”。
- 回滚(rollback)
- 读档。任何一次提交都能退回去,代码变回那时的样子。
- 分支(branch)
- 一条平行的实验线。满意了合回主线,不满意就丢掉,主线不受影响。
- 仓库(repository)
- 装着所有存档和分支的那个文件夹。
具体命令不用背,AI 可以替你执行。你只需要在让它大改之前说一句: “先提交,再改。” 终端怎么用见 第 2 章 · 计算机与网络。
你不敢让 AI 大刀阔斧地改,是因为怕改坏了回不来。 有了存档点,你就敢了。 每次确认“现在是对的”,就先存个档,然后再让 AI 改。 改砸了?一句话退回上一个存档,损失为零。
这也是为什么第 6 章会把“存档”放进标准流程里——它不是程序员的形式主义, 而是你敢于大胆尝试的底气。
三句话带走
- 一个软件至少有三格:前端是点餐台,后端是厨房,数据库是仓库;真实系统还要加上接口层和运行层。
- 一次点击要穿过浏览器、接口、后端和数据库,再带着响应回来;出问题时先定位在哪一层。
- 数据要落进数据库才算真的留下;密钥只能放服务端环境变量;Git 是你敢让 AI 大改的存档点。
自测:三个问题
午饭登记换一台设备后看不到之前填的记录,最可能缺哪一层?
缺后端和数据库。页面本身只能显示,数据只留在当前浏览器里; 写入数据库之后,换一台设备、换一个浏览器才能重新读出来。
GET /api/lunch-orders?date=today 和 POST /api/lunch-orders 有什么区别?
GET 用来读取,条件放在地址里,不修改数据;
POST 用来新增,姓名和菜名放在请求体里,会写入一条新记录。
AI 说“把 API Key 写在前端代码里,方便测试”,你能答应吗?
不能。前端代码会完整送到用户浏览器,任何人都能看到。 Key 应该放在服务端的环境变量里,`.env` 文件还要写进 `.gitignore`。
动手 10 分钟
打开 实验:午饭登记,做三件事:
- 提交一条登记,记下你填的姓名和菜名。
- 切到 JSON 视图,找到这条记录,指出
id、name、dish、created_at四个字段。 - 换一个浏览器或换一台设备打开实验页,看看还能不能看到你刚填的那条。用本章的话解释为什么。
如果你看到 X,大概率是 Y
- 换一台设备就看不到 → 没有后端和数据库,数据只留在当前浏览器里。
- 提示保存成功,重启后却没了 → 数据没真正落库,或者只写进了内存。
- 浏览器里能看到 API Key → 密钥放错了位置,应该移到服务端环境变量。
- 本机能打开,别人打不开 → 只是运行了,还没有部署。
- 改坏了回不去 → 动手前没有提交,先让 AI 帮你建立一个存档点。
你已经知道软件由哪些层组成。下一章换一个问题: AI 本身是怎么工作的——它为什么会一本正经地胡说, 又为什么会在对话变长之后“变笨”。