从语法元素到设计一个应用
家人最近开始转向项目中的产品设计岗位,但是很多时候没法对一些设计方向做出合理的决策。按钮应该怎么设计、怎么放置,级联的表单怎么设计。很多都是看起来无关紧要,但是实际上和业务息息相关的。
这让我想到身边很多人(包括后来面试的候选人)的困境:学会了框架、能跟着教程搭出 demo,但就是没法独立解决现实问题。K8S 用得很溜,但问一句"为什么用这个组件不用那个",答案是"视频里这么写的"。
引言:为什么学了语法却做不出应用?
我入行算是比较早的。初中那会儿就在写球弹跳的效果、模拟 B&O Player 在显示器边缘显示一个三角拉出窗口的效果——可能你们都没听说过 B&O Player 了,它是一个有非常丰富动画的音乐播放器,我到现在都仍然痴迷于 BeoPlayer 的美学设计。那时候都是 VB 6.0 自己画,没有什么动画库、得自己算偏移,但反而因此对"怎么组织代码"有了很直观的感受。
但我观察到身边很多人后来的学习路径是这样的:
- 先学一门语言的语法(变量、循环、函数)
- 刷一些算法题(排序、查找、递归)
- 学个框架(Django、Spring、React)
- 然后就没有然后了
面对一个真实的需求,比如"做一个简单的任务管理工具",脑子一片空白。这不是智商问题,而是学习的层次出了问题。
我们把编程想得太简单了,以为学会语法就能写程序。但实际上,从"知道怎么写"到"知道写什么"之间,隔着好几层东西。
学习的层次及其关系
我觉得技术学习成长有这么几个部分:
第一层:语言语法
这是表达的基础单位。类似于语文英语的词汇,数学物理的公式表达。它们是表达的基础,你总得有一个工具去说出来。
但是有了这些你并不能组织起来。就像你背了一万个单词,还是写不出一篇像样的作文。
第二层:数据结构和算法
这是表达的一般方法。差不多就是语文英语的语法,数学的公理。它可以告诉你一些常见的方法,脱离这些方法行不行?可以,但是你说的东西会不够好。
它也是带着你到下一步的基础。要理解业务流程,总是要知道能力边界在哪里,对吧?
第三层:业务和数据关系
当你知道常见方法的表达以后,就要梳理自己要做的事情规范化以后的表达方式了。其中包括业务——事情怎么做,以及数据关系——其中包括哪些事情、之间有什么联系。
有了这一步以后就知道自己前面学的两件事情是怎么组合起来使用了。
第四层:架构、演进、全局考量
这一层和其他三层不太一样。它不是"最后才学"的东西,而是从一开始就要接触的。
你写第一个实际的小工具,就要用数据库、就要考虑缓存、就要处理错误和异常。这些中间件——数据库、Redis、消息队列——就是你手里的榔头槌子和现代仪器。你先用起来,用着用着就会问:为什么这里要快?为什么那里要持久化?数据不一致了怎么办?
架构能力是在使用这些工具的过程中,慢慢生长出来的。它不是一门课,是一堆问题的解决方案集合。
举个例子🌰
举个最实际的例子:做一个简单的博客系统。
语法层:你会用 Python/Java/Go 写函数、操作变量、连接数据库。这是最基本的。
数据结构层:你知道可以用数组存文章列表,用哈希表做用户会话,用树形结构存评论的层级关系。你知道查询文章列表用索引会更快。
业务和数据关系层:你开始想——博客系统里有"用户"和"文章"两种核心实体,一个用户可以写多篇文章,所以是一对多关系。文章可以有标签,一个文章可以有多个标签,一个标签下也有多篇文章,所以是多对多关系。这时候你需要一个关联表。
你还想到:用户注册要验证邮箱,写文章要有草稿和已发布状态,删除文章是软删除还是硬删除?评论要不要支持回复?这些不是语法问题,是业务逻辑问题。
架构层(贯穿始终):
- 刚开始: SQLite 就够了,单文件省事儿
- 用户多了:换 PostgreSQL,要考虑连接池了
- 读多写少:加 Redis 缓存热点文章
- 搜索需求: SQLite 的 LIKE 太慢了,要上 Elasticsearch
- 上传图片:从存本地变成存对象存储
你看,架构不是最后才考虑的,是需求推着你在每一步做选择。
你看,同样是"写个博客系统",不同层次的人考虑的问题完全不同。光会语法,你最多能写出个"能跑"的东西;但要写出"好用"的东西,必须四层都覆盖到。
闭环流程:从需求到实现的完整路径
刚才说的是静态的"层次",但真实开发是个动态过程。我总结了一个从需求到代码的闭环:
需求分析 → 数据关系设计 → 业务流程设计 → 算法选择 → 数据结构选择 → 代码实现 → 验证优化 → 回到需求
咱们还是拿博客系统来走一遍这个流程。
第一步:需求分析
“我想做一个博客"太笼统了。具体点:
- 谁能写文章?(注册用户)
- 谁能看文章?(所有人 / 仅限登录用户)
- 文章能分类吗?能加标签吗?
- 支持评论吗?评论能回复吗?
- 需要富文本编辑器吗?还是 Markdown 就够了?
需求越清晰,后面的设计越容易。反过来,如果需求模糊,做着做着就会发现"啊这里漏了个场景”,然后返工。
架构考量:有多少用户?这个问题直接影响后面的技术选型。
第二步:数据关系设计
根据需求,我们梳理出实体和关系:
- 用户(User):id, username, email, password_hash, created_at
- 文章(Post):id, author_id, title, content, status(draft/published), created_at, updated_at
- 标签(Tag):id, name
- 文章标签关联(PostTag):post_id, tag_id
- 评论(Comment):id, post_id, author_id, parent_id(用于回复), content, created_at
这时候你会发现一些约束:
- 文章的 author_id 必须对应一个存在的用户(外键约束)
- 用户名和邮箱必须是唯一的
- 软删除的话,得加个 deleted_at 字段
架构考量:刚开始用 SQLite,但数据关系的设计要兼容以后迁到 PostgreSQL。别用 SQLite 特有的语法。
第三步:业务流程设计
数据关系定了,但数据是怎么流转的呢?
比如"用户发布文章"这个流程:
- 用户填写标题和内容,点击发布
- 系统验证:标题不能为空、内容不能超过 XX 字
- 如果验证通过,创建 Post 记录,状态设为 published
- 如果有标签,检查标签是否存在,不存在则创建,然后建立关联
- 返回成功,或者返回错误信息
再比如"用户删除文章":
- 软删除:把 status 改成 deleted,或者填上 deleted_at
- 硬删除:直接从数据库删除,关联的评论怎么办?一起删还是留空?
这些不是技术问题,是业务决策。每个决策都会影响后面的实现。
架构考量:这里可以同步处理,也可以异步。如果"创建标签"要调用第三方 API,就得考虑异步队列了。
第四步:算法选择
博客系统好像没什么复杂算法?其实也有的。
- 文章列表怎么排序?按时间倒序?那数据库用 B+ 树索引就行。要按热度排序(阅读量+点赞数加权)?那得设计个评分算法。
- 搜索功能怎么做?简单的用 SQL LIKE,复杂的要用全文检索(Elasticsearch、Meilisearch)。
- 评论的层级结构怎么展示?用递归查询还是存路径?这是树结构的遍历问题。
架构考量:100 篇文章和 100 万篇文章,选择完全不同。前者 SQLite LIKE 就行,后者必须上倒排索引。
第五步:数据结构选择
算法定了,就要选合适的数据结构来实现。
- 用户信息经常查,存在数据库但频繁读取,加个 Redis 缓存?那数据结构就是 KV 对。
- 文章列表分页,数据库返回的是数组,但你得封装成包含"当前页、总页数、总条数"的对象。
- 热门文章排行榜,用 Redis 的 Sorted Set 很方便,直接按分数排序。
架构考量:引入 Redis 就引入了新的故障点。Redis 挂了怎么办?降级方案是什么?
第六步:代码实现
终于到写代码了。这时候你会发现,前面五步都做对了,代码写起来很顺畅;前面有任何一步模糊,代码里就会有一堆 if-else 和 TODO。
# 举个例子:发布文章的伪代码
def create_post(user_id, title, content, tag_names):
# 验证输入
if not title or len(title) > 200:
return error("标题不能为空且不能超过200字")
# 创建文章
post = Post.create(
author_id=user_id,
title=title,
content=content,
status="published",
created_at=now()
)
# 处理标签
for tag_name in tag_names:
tag = Tag.get_or_create(name=tag_name)
PostTag.create(post_id=post.id, tag_id=tag.id)
# 清除缓存(因为文章列表变了)
cache.delete("recent_posts")
# 异步任务:生成摘要(如果有队列的话)
# queue.enqueue("generate_summary", post.id)
return success(post)
你看,这段代码里包含了验证逻辑、数据库操作、缓存更新——这些都是前面几步设计好的。
第七步:验证和优化
代码写完了,但事情没完。
- 功能验证:用户真的能发布文章吗?标签关联正确吗?
- 边界情况:标题是空字符串怎么办?内容包含恶意脚本怎么办?
- 性能测试:1000 个并发用户发布文章,数据库撑得住吗?
- 用户体验:发布成功后页面怎么反馈?失败了错误信息清晰吗?
- 架构验证:Redis 挂了系统还能跑吗?数据库主从切换时有没有丢数据?
测试和实际使用中会暴露问题,然后你回到第一步重新审视需求,或者调整设计。这就是闭环。
为什么需要闭环?
很多人写代码是单向的:需求来了,直接写代码。这样很容易跑偏——写完了发现理解错了需求,或者方案有硬伤需要重写。
闭环的意义在于:每一步都有明确的产出和验证标准,发现问题可以及时回到上一步调整,而不是等到代码写完了才发现大问题。
而且这个过程是迭代的。第一个版本可能用 SQLite,第二个版本加 Redis,第三个版本上消息队列。每个版本都走一遍闭环,系统就在这个过程中成长起来。
AI 时代的产品研发
补充一个有趣的观察:现在的 AI 编程助手
最近用各种 Coding Agent 的体验,让我对这个"层次"和"闭环"有了更深的体会。
Coding Agent 可以说是天生的"语法之神"——几乎所有语言的语法、标准库、常见框架,它都"会"。你让它写个 Rust 的异步 HTTP 客户端,或者 Python 的装饰器,它能写得比大部分人都标准。
但它有两个致命的问题:
第一,它"失忆"了。 虽然知识都存在参数里,但需要你通过提示词去激活。它不知道你项目的上下文,不知道你之前的决策,不知道你的代码风格。
第二,它没有你的前提假设。 它不知道你的用户是谁,不知道业务上的约束,不知道"这里可以妥协、那里必须严格"。
所以你跟 Coding Agent 合作,更需要走完整的闭环。你不能直接说"给我写个博客系统",你得先把需求掰开揉碎讲清楚——谁用?多少数据?性能要求?然后画出数据关系、梳理业务流程,把这些写成详细的文档喂给它。
换句话说,Coding Agent 把"语法层"和"数据结构/算法层"的成本几乎降到了零,但业务层和架构层的工作反而更重要了。因为如果你自己没想明白,它只会按最常规的方式生成代码,然后你就会得到一堆——语法大致正确,也许能跑通、但很可能不符合需求的代码:你向神明许了一个愿望,神明用一个怪异的代价实现了你的愿望。
更好的办法是,你要不断的和 AI 去唠,说清楚自己要什么,让 AI 提问,把细节的边边角角都唠到,然后写成文档,并且规定研发迭代要围绕文档,然后放手让 AI 去做。不断的来回迭代文档 -> 代码 -> 文档 -> 代码。
这让我更加确信:从需求出发、走完闭环的能力,才是编程的核心。工具越来越强大,但这种思维方式的价值只会越来越大。
结语
回到开头那个问题:为什么学了语法还是做不出应用?
因为编程不只是写代码。写代码只是最后一步,前面还有理解需求、设计数据关系、梳理业务流程、选择算法和数据结构。
这就像盖房子。学会用砖头(语法)不等于能盖房子。你还得会画图纸(设计)、懂结构(算法和数据结构)、知道住户需要什么(业务理解)。
而且这些东西不是线性的,是一个循环。你写完代码会发现设计有问题,优化设计会发现需求理解有偏差,修完需求可能又得重构代码。
这就是工程的本质:没有完美的方案,只有不断迭代的闭环。
不要害怕用"低级"的工具。我高中那会儿用 SDL1.2 画界面,一个个画点,现在看起来确实很原始,但那种"一切都自己控制"的经历,让我后来理解任何框架都很快——因为我知道它们大概做了什么,只是帮我省了些体力而已。
编程不是背 API,是理解问题、设计解决方案、用合适的工具实现的能力。框架只是工具箱里的一件工具,别让它成为你的全部。