正在加载文章,请稍候…
AI 生成的 300 行 React 能直接上线吗?前端工程师的代码评审、风险把控与核心价值
内容提要
从 300 行 AI 生成的 React 代码出发,拆解上线判断、异步竞态、类型与权限边界等十类盲区,以代码示例、风险分级、测试策略和 PR 模板,建立主动掌握验收权的工程协作方法。
AI 几分钟生成了 300 行 React 代码:页面能打开,交互能点击,样式也像模像样。此时可以直接上线吗?答案是:可以把它作为上线候选,但不能仅凭“AI 写好了”或“本地能运行”决定发布。是否上线,取决于需求是否正确、风险是否受控,以及验证证据是否足以支撑这次变更。
300 行静态展示组件和 300 行涉及支付、权限、批量删除的代码,承担的责任完全不同。代码行数既不是质量证明,也不是风险等级。人写的代码同样需要评审;AI 让生成速度提高后,我们更需要控制未经验证的假设进入生产环境的速度。
本文讨论的是工程评审方法,不评价某个模型的缺陷率。“典型盲区”指评审时值得优先检查的问题,并不意味着每次 AI 生成都会犯同样的错误。文中的场景与代码用于教学,没有对某个真实项目执行测试或完成上线认证。
一、先把“写出来了”与“能够交付”分清楚
| 看到的现象 | 能够说明什么 | 仍然不能证明什么 |
|---|---|---|
| 页面截图很好看 | 某组数据和某个视口下视觉可用 | 真实数据、移动端、键盘操作与异常状态可用 |
| TypeScript、Lint 通过 | 满足已启用的静态规则 | 业务规则正确、接口数据可信、没有越权 |
| 单元测试全部通过 | 已写断言在当前测试环境下成立 | 断言覆盖了真实风险,或生产环境与模拟一致 |
| 生产构建成功 | 项目能够按构建配置生成产物 | 路由刷新、部署路径、接口地址和缓存策略正确 |
| AI 自评“生产可用” | 模型给出了判断 | 任何独立验证已经发生 |
更实用的上线判断是:这段代码在目标用户、真实数据、预期负载和失败场景下,是否满足明确的验收条件;出现问题后,团队是否能发现、定位并控制影响。验证深度随风险增加,但“谁负责、凭什么放行”应始终明确。
静态活动页可以采取较轻的验证;订单、账户、敏感数据和不可逆操作需要更严格的接口、权限、幂等及恢复验证。不要因为代码来自 AI 就一律禁止上线,也不要因为它只有 300 行就降低标准。
二、跳出“AI 写啥我用啥”:把验收权拿回来
被动模式通常从一句“帮我写一个页面”开始。AI 自行补齐需求、接口、错误处理和依赖,你再围绕它给出的实现修修补补。最危险的地方在于:实现先出现,之后连“什么叫正确”都由这份实现反向定义。
主动协作应从行为契约开始。你先确定目标和边界,再让 AI 提供实现方案;每个重要决定都能追溯到需求、现有系统或验证结果。
1. 编码前,写出六项约束
- 用户与任务:谁在什么场景完成什么操作,成功结果是什么。
- 输入与输出:真实接口、字段类型、空值、分页、排序、错误码和权限范围。
- 状态与转换:初始、加载、成功、空结果、失败、取消、重试,以及每一步允许的动作。
- 不变量:无权用户不能获得数据;旧查询不能覆盖新查询;同一次业务意图不能重复创建订单。
- 工程边界:当前 React 和框架版本、请求封装、组件库、目录规范、允许新增的依赖。
- 验收与发布:需要哪些测试、观察什么指标、如何停止放量、由谁负责。
例如,“做一个搜索列表”过于宽泛。更可验收的描述是:“用户在当前租户内搜索资料;快速输入时只展示最后一次查询的结果;切换租户后不展示旧租户数据;401 进入统一登录流程,403 显示无权访问;空结果与请求失败必须区分;本次不新增请求库。”
2. 先比较方案,再接收实现
让 AI 先列出现有数据层复用、组件内 Effect、框架数据加载等可行方案,说明复杂度、缓存需求和接入成本。最终选型由项目约束决定。一个已有成熟请求层的项目,不应为一页搜索再引入另一套缓存体系。
要求 AI 区分“代码里能确认的事实”和“暂时假设的条件”。例如“后端会校验 tenantId”必须有接口实现、契约或联调证据支持;模型的解释不能代替服务端能力。
3. 缩小每次变更,保留拒绝和删除的权利
先做数据契约和主要状态,再做交互,最后调整样式。每个改动都能单独解释和验证。300 行不一定需要拆成十几个文件,但请求、业务决策和展示之间应有清楚的边界。对于没有需求依据的配置层、万能 Hook 和依赖,删除往往比继续补丁更好。
如果你无法用自己的话解释数据从哪里来、状态为何变化、失败后会发生什么,就还没有完成接管。可以要求 AI 解释、画出状态转换或缩小实现,但不要用“它说没问题”补上理解的缺口。
三、代码评审先看事故路径,再看代码风格
逐行从 import 看到 JSX 容易把注意力消耗在命名和排版上。更有效的顺序是先理解需求,再沿数据和副作用追踪关键路径。
| 评审顺序 | 首先追问 | 需要的证据 |
|---|---|---|
| 业务与权限 | 做的是不是正确的事,谁有权做? | 验收规则、权限矩阵、接口契约 |
| 数据与状态 | 数据可信吗,状态能否矛盾,旧结果能否串入? | 类型边界、状态转换、乱序与切换用例 |
| 副作用与一致性 | 请求会不会重复,取消意味着什么? | 清理逻辑、幂等约定、失败与重试行为 |
| 用户体验与可访问性 | 失败能否恢复,键盘是否能完成任务? | 状态覆盖、语义结构、交互验证 |
| 性能与工程接入 | 真实规模能否承受,是否融入现有项目? | 基线、依赖差异、构建与部署配置 |
| 上线与维护 | 出了问题谁能发现,怎样止损? | 监控、版本信息、功能开关和回滚方案 |
对每个问题,使用同一个推理链:触发条件 → 当前代码路径 → 用户或业务后果 → 修复方式 → 验证办法。只有“这里不优雅”很难决定是否阻断上线;“快速切换账户时旧数据仍可见”则直接指向需要修复的行为。
四、AI 生成 React 代码的十类典型盲区
1. 把顺利完成的一次操作当成全部需求
有加载动画不等于状态完整。初次加载、后台刷新、空数据、部分失败、表单提交失败和会话过期往往需要不同反馈。几个独立布尔值还可能组合出“提交成功同时显示失败”的矛盾状态。先列状态表,再确定能否用可辨识联合类型表达互斥状态;涉及多个并行任务时,也不要强塞进一个全局 loading。
2. 忽略时间顺序:竞态、旧闭包和重复提交
用户输入 A 后输入 AB,AB 的响应先回来,A 的响应后回来。如果两个回调都直接 setState,界面可能停在 A 的结果。另一个常见问题是定时器闭包持有旧状态。评审时沿时间线推演,主动交换响应顺序,检查旧结果、旧错误和旧 finally 是否影响当前任务。
防抖减少请求频率,但不能保证响应顺序。禁用提交按钮能改善交互,但不能覆盖刷新、网络重试、多个标签页等重复来源;关键写操作仍需服务端幂等与明确的业务状态约定。客户端取消请求,也不能证明服务端已经撤销写入。
3. 用 Effect 串起所有逻辑,或缺少清理
列表过滤、拼接名称等派生值通常可以在渲染期间计算;明确由点击触发的业务操作通常放在事件处理器中。Effect 主要用于与外部系统同步,减少不必要的 Effect 有助于避免额外状态同步。参见 React:You Might Not Need an Effect。
检查 Effect 的依赖及资源清理,尤其是订阅、定时器、监听器和连接。Strict Mode 在开发环境会执行额外的 setup/cleanup 检查;遇到问题应检查生命周期配对,而不是把关闭检查作为修复。请求返回乱序也需要通过取消或忽略过期结果等方式处理。参见 React:useEffect。
4. 把 TypeScript 类型当作运行时事实
“await response.json() as User[]”没有验证服务器真的返回了 User 数组。类型断言在编译时移除,不能检查真实输入。对接口、存储、URL 参数和第三方消息等外部边界,应根据风险进行运行时校验,再转换成内部可信类型。参见 TypeScript:Type Assertions。
any、非空断言和“as unknown as T”值得追问,但不能机械地见到就判错。关键是它们是否隐藏了未证明的假设。验证放在边界,避免在每个展示组件重复实现一套校验。
5. 有 try/catch,却没有真正处理失败
原生 fetch 收到 404 或 500 等 HTTP 错误状态时,通常仍会返回 Response,不会仅因这些状态自动进入 catch;需要检查 response.ok 或 status。JSON 解析失败、业务错误码、取消和超时也需要分别考虑。参见 MDN:Using the Fetch API。
catch 后只 console.log、直接 setData([]),会把服务故障伪装成“没有数据”。重试要有适用条件与上限;401、403 和参数错误不宜无限重试。写操作超时后尤其要先确认业务结果,避免把一次不确定结果变成两笔订单。项目已有请求封装时,以封装的真实行为为准。
6. 只隐藏按钮,没有建立权限边界
前端权限控制用于改善体验,决定性授权必须在服务端、网关或相应服务执行。检查每个请求对资源、操作、租户和用户身份的授权,不能只依赖客户端提交的 userId、role 或 tenantId。参见 OWASP:Authorization Cheat Sheet。
缓存也需要身份边界:用户、租户、查询条件和必要的权限上下文都可能影响缓存键。退出账户或切换租户时,要处理旧缓存、在途请求和屏幕上残留的数据。后端挡住越权请求,也不能替代前端避免显示此前账户数据的责任。
7. 误以为用了 React 就自动解决安全问题
React 的常规文本渲染提供转义,但原始 HTML 注入、不受信任链接和危险 DOM 操作仍需要单独评审。需要渲染富文本时,应采用适合场景且持续维护的净化策略;不能靠几条正则表达式代替 HTML 安全处理,也不能只依赖 CSP。参见 OWASP:Cross Site Scripting Prevention。
还要检查构建产物和日志:放进前端包的秘密无法靠变量命名保密;错误上报不应携带令牌、密码或完整敏感表单。Cookie 会话下的写请求,还需要与后端确认 CSRF 防护和跨域策略。
8. 界面可点击,却不能被所有用户使用
模态弹窗需要处理打开时的焦点、内部 Tab 导航、Escape 关闭及关闭后的合理焦点恢复。只添加 role 或 aria-modal,并不会自动实现这些行为。参见 W3C WAI:Dialog Modal Pattern。
表单需要可关联的标签、明确的错误提示和可见焦点;按钮应有正确语义及 type。评审也要覆盖长文案、窄屏、缩放、输入法组合输入和必要的读屏反馈。自动化检查能发现部分问题,不能证明整条键盘操作路径可用。
9. 在十条模拟数据上看起来很快
在渲染中反复排序、扫描大数组,整页共享频繁变化的 Context,每次输入都重绘大型列表,缓存和历史数据无限增长,都会在真实规模下暴露成本。先确定数据规模和性能基线,再决定分页、虚拟列表、代码拆分或缓存。
useMemo、useCallback 和 memo 不能自动修复错误的数据流;虚拟列表减少挂载节点,也不等于限制内存中的数据量。性能评审要问清“哪条路径慢、在什么设备和数据量下慢、优化后证据是什么”。
10. 能独立演示,接入工程后却留下长期成本
检查 AI 是否虚构了包名、API 或项目中不存在的工具函数,是否偏离当前版本,是否为了一个小功能引入重型依赖。新增依赖要看必要性、来源、版本、维护与许可要求,扫描结果也需要结合实际使用路径判断。
SSR 或混合渲染项目还要检查服务端访问 window/localStorage、首屏输出不一致、客户端与服务端职责混淆。部署方面检查基础路径、深链刷新、资源缓存与接口兼容性。漂亮组件只是完整交付链中的一段。
五、实战:十几行代码为什么值得阻断上线
下面是一个故意保留问题的搜索示例。它的结构很常见,但评审重点应落在行为上。
useEffect(() => {
setLoading(true);
fetch("/api/items?q=" + query)
.then(res => res.json())
.then(data => setItems(data as Item[]))
.catch(() => setItems([]))
.finally(() => setLoading(false));
}, [query]);| 触发场景 | 这段代码可能出现的行为 | 应当建立的约束 |
|---|---|---|
| 查询含 &、# 等字符 | 查询串含义改变或参数丢失 | 使用 URLSearchParams 或适当编码 |
| A 请求慢,AB 请求快 | A 的旧结果覆盖 AB | 只有当前查询能提交状态 |
| 旧请求在新请求期间结束 | 旧 finally 提前关闭 loading | 所有状态更新都归属对应任务 |
| 接口返回 403 或 500 JSON | 错误响应被当成业务数据 | 明确检查 HTTP 与业务状态 |
| 接口字段变更或返回畸形数据 | 断言掩盖数据结构错误 | 边界校验失败进入明确错误态 |
| 断网或解析失败 | 故障显示为空列表 | 区分成功空结果和失败 |
| 切换身份或租户 | 旧数据或请求影响新上下文 | 身份隔离、取消与显示策略 |
一条有效评审意见可以写成:“阻断:搜索结果可能与输入不一致。复现时让 A 响应晚于 AB,当前代码会把 A 写回。请让成功、错误和 loading 更新都受当前请求生命周期约束,并增加乱序响应回归用例。”这比“建议加 AbortController”更完整,因为它说明了问题、后果与验收条件。
改进示例:把当前请求、数据边界和显示归属一起处理
下面的 Hook 是教学用最小示例,约定接口返回 { items: [{ id, title }] },同源会话与服务端授权已由系统提供。实际项目应优先接入已有请求层。它演示一种正确性策略,不提供缓存、同查询手动重试、统一登录跳转、监控或完整超时机制。
import { useEffect, useState } from "react";
type Item = { id: string; title: string };
type View =
| { status: "loading" }
| { status: "success"; items: Item[] }
| { status: "error"; message: string };
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null
&& !Array.isArray(value);
}
function parseItems(value: unknown): Item[] {
if (!isRecord(value) || !Array.isArray(value.items)) {
throw new Error("Invalid response");
}
return value.items.map((item: unknown) => {
if (!isRecord(item) || typeof item.id !== "string"
|| typeof item.title !== "string") {
throw new Error("Invalid item");
}
return { id: item.id, title: item.title };
});
}
export function useItems(tenantId: string, query: string): View {
const key = JSON.stringify([tenantId, query]);
const [snapshot, setSnapshot] = useState<{
key: string;
view: View;
} | null>(null);
useEffect(() => {
const controller = new AbortController();
let active = true;
const requestKey = JSON.stringify([tenantId, query]);
setSnapshot({ key: requestKey, view: { status: "loading" } });
async function load() {
try {
const params = new URLSearchParams({ q: query });
const response = await fetch(
"/api/tenants/" + encodeURIComponent(tenantId)
+ "/items?" + params.toString(),
{ signal: controller.signal }
);
if (!response.ok) {
throw new Error("HTTP " + response.status);
}
const raw: unknown = await response.json();
const items = parseItems(raw);
if (active) {
setSnapshot({
key: requestKey,
view: { status: "success", items }
});
}
} catch {
if (active && !controller.signal.aborted) {
setSnapshot({
key: requestKey,
view: { status: "error", message: "加载失败,请稍后重试" }
});
}
}
}
void load();
return () => {
active = false;
controller.abort();
};
}, [tenantId, query]);
return snapshot?.key === key
? snapshot.view
: { status: "loading" };
}- AbortController 用于取消支持取消的请求;active 用于阻止已失效 Effect 的异步结果提交状态,两者承担不同职责。
- 状态带有查询归属键,渲染阶段只读取当前键对应的快照,避免参数变化到新 Effect 执行之间仍显示上一租户或上一查询的数据。
- loading、success 和 error 互斥;空数组只代表成功后的空结果,不承担隐藏错误的职责。
- JSON 先按 unknown 接收,再校验需要的字段;校验规则仍需按真实接口补齐,如 ID 唯一性、分页结构和长度限制。
- tenantId 出现在 URL 与归属键中,并不构成授权。接口必须独立验证当前身份;账户变化也要纳入真实项目的隔离策略。
示例统一显示通用错误,便于突出主线。真实接入时仍需按产品契约区分 401、403、429、服务错误及业务错误,并提供适当的重试入口、可观测信息和可访问状态提示。不要把一份修复示例再次当成无需评审的最终答案。
六、测试要验证需求,不能只复述实现
让同一个 AI 基于自己写的代码生成测试,可能得到“代码怎么写,测试就怎么断言”的闭环。测试应先从验收条件和事故路径中产生,再选择能覆盖该风险的测试层级。可以让 AI 补充反例,但最终要检查断言是否真的能抓到错误。
| 层级 | 适合验证 | 有价值的断言 |
|---|---|---|
| 静态检查与构建 | 类型、Hooks 规则、导入和产物生成 | 使用项目既有脚本通过检查,不靠关闭规则消除错误 |
| 逻辑与契约测试 | 转换、校验、状态转换、权限映射 | 畸形输入被拒绝,互斥状态不会同时成立 |
| 组件交互与请求测试 | 乱序、取消、空数据、失败恢复 | B 后发先到后,A 不能覆盖 B,旧失败不能替换当前成功 |
| 接口集成测试 | 真实鉴权、错误码、数据契约、幂等 | 修改资源 ID 或租户参数不能访问无权数据 |
| 端到端与人工体验验证 | 关键业务链路、键盘、真实部署与设备表现 | 用户能完成任务,深链可用,失败可恢复 |
对于搜索功能,至少覆盖:成功有数据、成功空数据、HTTP 错误、非 JSON 或结构错误、A/B 响应乱序、参数切换、卸载后旧结果失效、取消不显示为业务失败。对于写操作,再补充双击、网络超时后查询结果、重试与服务端幂等验证。
测试不必全部落在最昂贵的端到端层。纯数据校验用逻辑测试,竞态用可控制响应顺序的组件测试,真实授权由接口集成验证,关键体验再用端到端和人工操作补充。只检查函数被调用,往往不足以证明用户看到正确结果。
一条实用检查是:如果删掉防竞态保护,这个测试会失败吗?如果不会,它可能没有覆盖声称覆盖的风险。必要时做小范围故障注入或变异检查,比追求更高的表面覆盖率更有解释力。
七、风险把控:明确什么必须修,什么可以排期
可以用“后果严重性、暴露范围、发生条件、可发现性、可恢复性”组织讨论。它们是判断维度,不是能够算出精确事故概率的公式。遇到证据不足且后果重大的问题,应先降低不确定性。
| 等级示例 | 典型问题 | 放行原则 |
|---|---|---|
| 必须阻断 | 越权、敏感数据泄露、重复扣款、数据破坏 | 修复并完成针对性验证后再发布 |
| 通常阻断 | 核心流程不可用、错误订单金额、跨账户显示旧数据 | 证明关键行为正确;若降级,需明确禁用相关能力 |
| 可受控发布 | 非核心体验缺陷、已有替代路径的局部问题 | 明确影响、监控、恢复方式、负责人和修复期限 |
| 改进建议 | 不影响行为的命名、局部重复、尚无证据的微优化 | 避免与真实阻断项混杂,按维护成本安排 |
这些等级是团队可采用的示例,并非行业统一编号。无论采用 P0/P1 还是其他标签,都应先对齐含义。可修复的普通缺陷不能被无限拔高;越权和数据破坏也不能用“先灰度看看”代替修复。
灰度和回滚同样有边界:少量流量仍可能泄露真实数据;回滚前端包不能撤销已发生的扣款、删除或数据库变更。涉及写入协议和数据迁移时,需要后端兼容、幂等、审计及必要的补偿方案共同支撑。
八、一份可以直接放进 PR 的上线评审模板
变更目标
- 用户要完成什么?验收条件是什么?
- 本次修改哪些范围?明确排除哪些范围?
需求与数据
- 接口、字段、错误码、权限边界有哪些依据?
- AI 生成中仍未确认的假设是什么?
- 状态、不变量和身份隔离如何保证?
主要风险
- 严重性、触发条件、影响范围是什么?
- 修复了什么?还存在什么限制?
- 哪些问题阻断,哪些有负责人和期限?
验证证据
- 静态检查、构建、测试实际执行结果:
- 关键异常、乱序、重复提交、权限用例:
- 真实环境、键盘和设备验证:
- 未执行的检查及其影响:
发布与恢复
- 发布范围、观察窗口与负责人:
- 核心指标、停止放量条件:
- 功能开关、回滚和数据补偿方式:
结论
- 可发布 / 满足明确条件后发布 / 阻断
- 判断依据与剩余风险:测试清单不能把“已建议执行”写成“已执行通过”。命令输出、测试报告、接口验证和实际操作分别记录;无法验证的内容应直接写明。这种证据纪律,正是工程师比生成速度更重要的价值。
发布后至少关联版本、关键路径成功率、接口错误与耗时、前端异常和相关业务指标。必要时对比同类用户或设备的基线,并给停止放量条件设定合理口径;没有流量、样本不足时,“暂时没报错”不能证明安全。
九、把 AI 从“交答案的人”变成“接受约束的协作者”
需求阶段的提示词
先不要写代码。请基于以下需求:
1. 列出可验证的用户行为、状态转换和业务不变量。
2. 列出接口、身份、数据规模与错误处理方面的未知项。
3. 每项未知标注为“有证据”或“待确认假设”,不要自行补成事实。
4. 对照仓库现有版本、请求层与组件库给出两种以内方案。
5. 给出方案代价和验收用例,由我确定边界后再实现。评审阶段的提示词
请按风险评审这份 diff,不要先重写,也不要优先讨论格式。
逐项检查业务规则、授权、外部输入、异步时序、资源清理、
错误恢复、可访问性、真实数据规模和工程接入。
每条发现写明:
- 文件位置与可核实的代码事实
- 触发步骤或反例
- 用户/业务后果
- 严重性与判断理由
- 最小修复建议
- 能证明修复的测试
无法确认的问题请列为待验证,不要虚构运行结果。验证阶段的提示词
请先从验收条件设计反例,再为必要风险编写测试。
重点考虑:乱序响应、失败重试、重复操作、身份切换、
空数据、畸形数据与长时间运行。
说明每个测试在保护什么不变量。
区分已执行通过、执行失败、尚未执行和环境受限。
不要通过删除断言、关闭规则或吞掉异常来让检查变绿。这些提示词能改善协作结构,但不能提供质量保证。换一个模型复审也只是增加一种检查视角;多个回答可能共享相同误解。事实要回到代码、接口契约、官方文档、测试和真实运行证据上确认。
十、AI 时代,前端工程师的核心价值落在哪里
1. 定义正确的问题,并澄清业务语义
“提交失败”可能表示请求没到达、服务端拒绝、成功但响应丢失,三种情况的后续动作不同。理解业务后果、识别模糊需求、提出可执行规则,决定了后面的代码究竟在解决什么问题。
2. 建立系统模型,解释代码为什么正确
你需要看清状态所有权、组件边界、请求生命周期、缓存失效、权限与身份之间的关系。基础知识仍然重要,因为你必须判断一个修复是在消除原因,还是把错误隐藏得更深。
3. 在交付速度和风险之间做有依据的取舍
不为小页面搭建复杂平台,也不让涉及资金和数据的操作只靠演示验收。能选择恰当的验证深度、拒绝无依据的复杂度、提前设计恢复手段,是工程判断力的具体表现。
4. 为结果建立证据,并承担发布责任
AI 可以生成实现、列举反例、补测试、整理文档,但团队仍需要对“为什么允许这次变更接触真实用户”作出决定。责任应由理解系统、能评估证据的人承担,并通过团队流程持续分担和改进。
5. 把个人判断沉淀成团队能力
把已确认的问题转成项目规则、可复用组件、接口契约、回归测试和发布模板。将一次竞态事故沉淀成测试习惯,将一次权限遗漏沉淀成统一接口约束,能够减少以后每一次生成的评审成本。
衡量 AI 带来的收益,应同时看需求交付周期、评审返工、线上缺陷、恢复速度和后续维护成本。生成行数增加只说明产出更多代码,不能单独说明交付效率提升。
十一、日常练习:让评审能力跟上生成速度
- 拿一个 AI 生成的小组件,先自己写出三条必须成立的不变量,再逐段核对实现。
- 主动给它设计反例:乱序、空值、切换身份、重复操作,记录哪些假设被击穿。
- 对每个 Hook 说明状态所有者、依赖来源、资源开始与结束的时机。
- 读懂已有项目的一条完整链路:界面事件 → 请求封装 → 服务端授权 → 返回数据 → 缓存 → 展示。
- 每周把一个真实发现写成“触发条件、后果、修复、验证”的评审记录,逐步形成团队检查规则。
能力的提升不要求你手写全部样板代码,但要求你能解释关键路径、设计反例、读懂故障证据,并在证据不足时拒绝放行。让 AI 加速实现,把自己的注意力投入到更难外包的判断上。
十二、回到最初的问题:这 300 行 React 能上线吗
如果需求与接口有依据,关键路径和主要失败场景经过与风险匹配的验证,权限与数据边界可靠,工程接入正确,发布后可观察、可止损,就可以按项目流程发布。实现由谁生成,不改变这些条件。
如果依据只有“页面能打开”“测试看起来挺多”“AI 说已经优化”,它就仍是一份待验证的候选实现。下一步应补齐最重要的证据,而不是继续要求模型把代码润色得更像生产代码。
前端工程师需要牢牢掌握三件事:决定什么行为才算正确,证明实现符合这些行为,控制出错时的影响。AI 负责提高实现速度,你负责让速度最终转化为可靠的交付。
参考资料
以下链接用于核对文中涉及的具体机制。文章中的评审流程、风险分级示例和协作模板为基于这些机制整理的工程建议。技术选型与 API 使用仍应以项目实际版本和官方文档为准。
React:You Might Not Need an Effect
TypeScript:Everyday Types / Type Assertions
OWASP:Authorization Cheat Sheet
