夜雨聆风学习资料网

ARTICLE · 1200832

没有源码:我用 AI 完成了一次系统逆向工程

没有源码:我用 AI 完成了一次系统逆向工程

一次授权范围内的工程实践:从观察界面开始,把陌生系统拆成证据、模型、代码和可验证的交付物。

开场:只有一个网页,能看懂一套系统吗?

我最近接触到一套运行多年的业务管理系统。它可以正常登录,也能搜索、查看和编辑部分记录,但完整源码、数据库文档和原始需求并不在手边。假如要重新做一套能够独立运行的新系统,第一步应该是什么?

直觉上,答案可能是“先把页面画出来”。可真正开始之后,我很快发现,页面只是最容易看见的一层。一个按钮为什么在这里出现?某个字段什么时候必填?列表里的状态是手工填写的,还是由历史动作推动的?同一个页面,换一个角色后数据范围是否变化?这些问题如果没有答案,复制出来的页面很可能只是一个漂亮的空壳。

这一次,我尝试把 AI 编程工具放进整个过程,让它同时承担系统分析助手、文档整理助手、原型开发助手和测试助手的工作。研究始终限定在已经授权的访问范围内,只通过正常页面操作、浏览器开发者工具和可观察到的业务交互来建立认知,不猜测受限接口,不绕过认证,也不复制真实业务数据。

最后得到的最大收获不是“AI 帮我写了多少代码”,而是一个更具体的工作方法:先把观察变成证据,再把证据变成假设;假设经过验证后,才进入业务模型、接口和代码。系统逆向的难点,从来不是把界面画得像,而是理解界面背后的逻辑和边界。

*图 1|没有源码,只有一个网页。图片为原创生成,画面中的设备和界面均为虚构。*

一、第一步不是写代码,而是让 AI 学会观察

面对陌生系统,我没有一上来就让 AI “生成完整项目”,而是先建立一张功能地图。入口从导航菜单开始:有哪些一级模块,菜单之间是否有明显的上下级关系,当前角色能看到什么,点击之后是列表、详情、向导还是独立表单。

接着是字段清单。列表页记录列名、排序、筛选、分页和空状态;详情页记录区块、字段、关联记录和时间线;表单页记录控件类型、默认值、必填规则、校验提示、保存后的跳转和取消行为。每次观察都尽量写成可复核的句子,例如“在角色 A 的当前菜单和已检查页面中,字段 X 出现在详情区块 Y”,而不是“系统应该有一个 X 模块”。

浏览器的 DOM 和网络面板也很有用,但它们只是一种证据来源。DOM 可以帮助我确认一个按钮是否真的渲染、控件之间的层级如何组织;开发者工具可以帮助我观察正常点击产生的请求、请求参数和返回结构。Playwright 适合把一条已经人工确认过的路径重复执行,例如打开列表、筛选一条虚构记录、进入详情、返回列表,再检查页面状态是否一致。

我会为每个功能保留一行覆盖记录,至少包含这些字段:

功能:项目详情          观察路径:菜单 → 项目列表 → 详情          已确认:标题、负责人、阶段、历史记录可见          待确认:阶段变化是否需要审批          权限范围:仅在当前授权角色下观察          证据等级:B

这张表的作用不是做项目汇报,而是防止研究被“印象”带着走。它提醒我区分看到了什么、做过什么、还缺什么。一个页面看起来很复杂,并不等于它背后的流程已经被理解;一条接口返回了 200,也不等于业务操作已经成功。

*图 2|从菜单、列表、详情和交互逐步建立功能地图。所有信息卡片均为虚构示意。*

二、AI 第一次给出的结论,可能并不可靠

AI 很擅长把零散线索组织成一段完整解释,也正因为如此,它很容易让“合理猜测”看起来像“已经验证的事实”。在这次实践中,我把所有判断分成四个等级:

·A:直接验证的事实。 在当前授权范围内亲自看到或重复操作确认过。

·B:有界面或交互证据支持。 证据比较明确,但还没有服务端或其他角色的交叉验证。

·C:合理推断。 根据命名、布局或流程习惯推测,暂时没有直接证据。

·D:仍然未知。 当前权限、工具或资料不足,不能下结论。

*图 3|把已证实、可支持、合理推测和未知放在同一张工程台账上。标签会在后期排版,避免依赖生图文字。*

我遇到过三种特别典型的误判。

第一种:没有发现入口,就认为功能不存在。

更稳妥的说法应该是:“在当前角色和已经检查的页面范围内,尚未发现该功能入口。”系统可能通过另一个流程进入,也可能只对更高权限角色开放,还可能需要先满足某个业务状态。把“没看到”写成“不存在”,会让后续设计在一开始就漏掉能力。

第二种:字段和界面相似,就认为数据库模型相同。

两类记录展示了相同的名称、负责人和时间字段,并不能证明它们在原系统里使用同一张表。它们可能来自不同业务域,只是页面层做了统一展示;也可能共享基础实体,却有不同的状态机和历史表。界面逆向得到的是逻辑模型线索,不是原数据库的真实 DDL。

第三种:页面可访问,就认为拥有完整数据和操作权限。

权限至少要拆成页面权限、数据范围权限、操作权限和服务端权限。一个用户能打开详情页,可能只代表有读权限;能看到按钮,可能只代表前端没有隐藏;真正能否写入,仍要由服务端规则决定。研究时,我会分别记录“看得到”“点得到”“数据覆盖到哪里”和“服务端是否接受”,不把它们混成一个“有权限”。

证据分级的价值在于让不确定性可见。AI 可以继续提出假设,但假设必须带着等级和验证路径,不能悄悄变成需求。

三、从界面逆向走向业务模型逆向

当功能地图逐渐完整后,我开始画逻辑数据模型。这里的“模型”不是复刻旧数据库,而是回答新系统需要理解的业务问题。

用一个完全虚构的项目管理场景举例:一个项目可能关联客户、负责人、阶段、待办、历史记录和资金计划。项目与客户是关联关系,项目有阶段变化,阶段变化会产生历史记录;待办有负责人和完成状态;资金计划可能按时间分段,并需要保留来源和核对状态。

如果只复刻页面,可能会把所有字段塞进一张大表,再用几个字符串表示状态。这样做的短期效果很快,后续却难以解释历史变化,也难以支持权限过滤和数据追踪。更稳妥的做法是先区分实体、属性、关系和事件,再决定新系统如何持久化。

我会把每个关键字段写成一条带来源的说明:

字段:项目阶段          来源:详情页状态区 + 阶段变更操作后的时间线          语义:项目当前所处阶段          已确认:当前值可显示,变化后会留下历史记录          未确认:是否存在服务端审批节点          新系统设计:阶段表 + 项目阶段历史表

这类记录可以帮助开发者避开一个常见陷阱:把原系统的技术债一起搬进新系统。新系统可以选择更清晰的表结构、统一的时间字段、可追踪的来源字段和显式的审计记录,但必须保证业务语义连续,而不是为了“长得像”而复制旧系统的每个缺陷。

界面逆向的终点,是证据支持的逻辑模型;它不是原数据库的物理结构。

*图 4|从页面结构、字段和历史动作,逐步抽象出业务实体与关系。画面中的界面和卡片均为虚构。*

四、用 AI 从零构建一个独立系统

完成初步模型后,我才开始让 AI 参与编码。这里最重要的变化,是把“做一个系统”拆成一组有验收门槛的小任务。

第一阶段只做可点击的前端原型。用虚构数据把列表、详情、表单和主要导航串起来,先验证用户能不能走通主流程。第二阶段固定接口契约,明确请求参数、返回结构、错误格式和分页方式。第三阶段再用 NestJS 等后端框架实现 REST API,引入 PostgreSQL 持久化。第四阶段补上角色、数据范围和服务端权限。最后才是把 Mock 替换成真实接口,并准备容器化和部署。

这是一套可选的新系统技术栈示例:Vue 3 + TypeScript 负责前端,NestJS 负责后端,PostgreSQL 负责数据,REST API 作为接口边界,RBAC 作为权限基础,Docker 作为交付方式。它们是新系统的自主选择,不能倒推成原系统已经证实采用的技术架构。

我不会让 AI 一次性生成所有模块,原因很实际:任务过大时,错误会同时出现在页面、接口、数据模型和权限里,最后很难定位责任。分阶段之后,每一步都有可以运行的结果:原型阶段看流程,接口阶段看契约,数据库阶段看重启恢复,权限阶段看角色对照,集成阶段看真实请求。

AI 最适合做的是把明确的工程任务快速展开:生成重复的表单骨架、补齐类型定义、整理接口文档、编写测试样例、检查字段命名的一致性。人必须持续做的是确定业务含义、确认边界、选择取舍,以及决定什么结果才算完成。

*图 4|一侧研究陌生系统,一侧构建独立新系统,中间用接口契约和验收记录连接。*

五、真正复杂的是数据,而不是页面

页面通常能在几天内做出大致样子,数据却会把所有模糊之处放大。尤其是多来源 Excel 导入:两份表里的名称可能相似,金额、阶段和负责人却不同;同一条业务记录可能在不同来源中使用不同编码;某个空值可能代表“未知”,也可能代表“尚未发生”。

因此,新系统不能简单把两份文件拼在一起,也不能让后一份数据无条件覆盖前一份。我会把数据融合拆成几层:保留来源记录,建立字段映射,做格式规范化,生成去重候选,标出冲突,等待人工确认,再写入统一的业务主数据。每个统一字段都要能追溯到来源和导入批次。

假设有两份虚构资料:来源甲把“星河项目”写成“星河”,来源乙写成“星河项目”;两边的阶段分别是“推进中”和“待确认”。系统可以把它们标记为同一条记录的候选,但不能自动把两个阶段覆盖成一个“最终真相”。更稳妥的结果是:保留两条来源记录,生成一条待人工核对的合并候选,并记录最终采用的值、确认人和确认时间。

数据融合的最小审计信息可以包括:来源名称、原始行号、导入批次、字段映射版本、规范化结果、冲突类型、处理状态和确认人。这样,问题出现时可以回到原始来源,而不是只剩一张被改过很多次的结果表。

*图 5|来源保留、冲突标记、人工核对和统一主数据形成一条可追踪链路。*

数据融合不是覆盖原始数据,而是在保留来源的前提下建立统一认知。

六、AI 写出的代码,为什么必须反复测试

这次实践让我反复确认几件事:TypeScript 检查通过,不等于业务功能正确;前端显示正常,不等于接口正确;HTTP 返回 200,不等于业务结果符合预期;前端隐藏按钮,不等于服务端权限安全;数据库能写入,不等于重启后还能恢复;单次测试通过,也不等于连续操作可靠。

最典型的一次问题出现在 Excel 导入。第一次上传的表格包含“项目名称、负责人、阶段”三列,系统根据表头建立了映射。第二次上传另一份结构不同的表格时,页面看起来已经更新,但旧的映射对象没有完全清空,导入逻辑仍然尝试读取第一次出现的字段。结果是第二轮导入失败,错误信息却只显示成一个并不直观的字段缺失。

定位时,我先保存两次上传的原始表头,再对比前端状态、请求体和服务端解析结果,确认问题不是文件内容,而是组件状态在新一轮操作前没有重置。修复方式不是增加一个“如果为空就跳过”的判断,而是把导入流程拆成明确的生命周期:选择文件时创建新的解析上下文,解析完成后生成当前批次的映射,提交后销毁临时状态;如果用户更换文件,必须重新计算映射。

修复后,我没有只再上传一次相同文件,而是做了重复操作测试:上传结构甲,上传结构乙,取消后重新上传结构甲,再刷新页面继续上传结构乙,并检查每一批的字段映射和入库结果是否只依赖当前文件。问题消失的证据来自这些连续操作的结果,而不是 AI 说“已经修复”。

*图 6|两种抽象表头在连续导入时产生映射残留,问题通过状态生命周期和重复操作测试定位。*

我通常会把验收拆成四层:

1.静态检查: 类型、构建、格式和依赖是否通过。

2.接口检查: 参数校验、错误返回、权限拒绝和数据写入是否符合契约。

3.流程检查: 从登录、筛选、编辑到返回列表的连续操作是否成立。

4.回归检查: 更换角色、重复导入、刷新页面、重启服务后,结果是否仍然正确。

测试的目标不是证明代码没有任何错误,而是让错误尽早暴露,并留下足够线索。AI 可以生成测试用例,但必须由人确认测试是否覆盖真正的风险。

*图 7|运行成功只是信号,验收还要看业务结果、权限边界和连续操作。*

七、部署才是最后一关

本地能运行,不代表 NAS 或服务器能运行。开发机上的 Node 版本、数据库地址、文件权限、端口空闲情况和网络条件,到了部署环境都可能变化。

部署准备至少要考虑:依赖和镜像能否在目标环境取得,数据库卷是否持久化,重启后数据是否能恢复,端口是否与已有服务冲突,密钥是否通过环境变量管理,备份能否定期执行,低内存设备是否承受得住构建和运行开销。对于已经存在的业务环境,还要先确认资源边界和回滚方案。

在这次工作中,本地开发和流程验证属于已经完成的阶段;离线依赖准备、容器配置和部署检查属于后续准备工作。它们必须分开写,不能因为“本地页面打开了”就把生产部署写成已经成功。

八、这次实践让我重新理解了 AI 编程

AI 确实让个人可以推动更复杂的软件工程:它能快速整理陌生系统,批量生成结构化文档,协助搭建原型、接口、数据库和测试骨架,也能在反复修改中承担大量机械实现工作。

但 AI 的价值不在于替人做出所有判断。它会根据现有证据给出解释,也会在证据不足时补上一段听起来顺滑的推理。越是陌生、越是缺少文档的系统,越需要把“事实、支持、推断、未知”分开保存。

从旧系统走向新系统,也不是一次页面搬家,而是一次重新建模:哪些业务实体值得保留,哪些历史记录必须追踪,哪些权限需要在服务端落实,哪些数据必须保留来源,哪些流程应该借此机会变得更清晰。原型能帮助我们发现问题,工程纪律才能让系统长期工作。

我现在更愿意把 AI 编程理解成一种“工程放大器”。它可以把一个人的观察、判断和设计快速展开成更多文件、更多代码和更多测试;但如果输入的是未经验证的假设,放大的也会是错误。真正决定质量的,仍然是需求理解、证据意识、权限边界、数据质量和验收纪律。

AI 让个人具备了推动更复杂软件工程的能力,但真正决定系统质量的,仍然是需求理解、证据意识和工程纪律。

*图 9|清晨的工作台:从观察一个系统,到真正理解并重建系统。设备和界面均为虚构。*

如果让你接手一套没有源码、没有文档的老系统,你会从哪里开始?

*本文只讨论授权范围内的观察、建模、开发和验证方法。文中系统、数据、角色、名称和示例均已抽象或虚构,不对应任何真实单位或业务项目。*

相关学习资料