尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

AI辅助编程时代,开发者如何保持批判性思维与工程判断力

AI辅助编程时代,开发者如何保持批判性思维与工程判断力 很多开发者现在都有类似的体验遇到问题先打开 AI 助手把报错粘贴进去拿到一版代码后直接复制到 IDE运行通过就觉得“完事了”。AI 确实把编程效率拉高了一大截但一个隐患也在悄悄出现——我们已经很少追问“为什么这段代码是对的”也慢慢懒得去看日志和文档脑子里只剩下“怎么把 AI 给的答案改成能跑的样子”。这篇文章想聊一个看起来偏理念、实际上非常工程化的话题如何在借助 AI 编程、排查问题、写技术方案时继续保持自己的批判性思维。文章会拆解 AI 辅助开发的完整链路给出三个可落地的实战场景以及一份可以直接使用的检查清单。适合正在重度使用 AI 工具的开发者和技术团队参考。1. 为什么 AI 用得越多思考反而可能越浅先看一个很常见的开发场景。你拿到一个需求比如“给现有 Spring Boot 项目增加 Redis 分布式锁工具类”。以前的做法是自己先理一遍锁的粒度怎么设计、key 怎么拼、超时时间设多少、怎么处理可重入、Redis 连接异常怎么兜底。现在很多人的做法是直接把需求发给 AI拿到代码后复制到项目里单测跑一遍没问题就提交。这个过程省掉的不是写代码的时间而是深度思考的时间。心理学里有一个概念叫“认知卸载”cognitive offloading简单说就是人类倾向于把复杂的认知任务外包给外部工具以节省脑力。AI 把这个趋势推到了极致。使用搜索引擎时你还需要自己从多条结果里筛选、比对、判断但使用 AI 时它给的是一个“看起来完整且确定”的答案人的大脑会天然地倾向于接受这种结论而不是重新推理一遍。这带来的直接后果有三个需求理解变浅只关心 AI 能不能给出代码不care它理解的需求是否和你一致。方案选型退化AI 给一种方案就直接用不再比较缓存框架、锁实现、事务边界等多种可能。验收变成“跑通”代码能跑、测试能过就认为正确不再做边界分析和异常场景验证。所以很多团队会发现AI 把低水平重复工作吃掉了但与此同时开发者的方案设计能力、问题定位能力和经验沉淀速度也在下降。这不是 AI 的问题而是使用方式的问题。批判性思维在 AI 时代不再是一个“软技能”它直接决定你交付的代码质量和系统的稳定性。接下来我们把它拆成可执行的动作。2. 在使用 AI 时批判性思维到底指什么谈到批判性思维很容易被理解成“AI 说的都是错的要一直怀疑它”。其实不是。批判性思维不是否定而是有根据地接受或拒绝。在 AI 辅助开发的语境里我把它拆成四个层面思维层面核心问题工程表现质疑输入我的问题描述是否完整、准确需求拆解、字段边界、异常场景说明验证输出AI 的结论是否有事实依据查文档、查源码、看日志、看报错堆栈评估推理链AI 为什么给出这个答案中间步骤是否合理要求 AI 解释分析依赖关系、时序、数据流管理不确定性哪些环节可能存在幻觉或盲区标注不确定项、加日志、补异常兜底、人工复核这四个动作并不是让你每句话都去质疑 AI而是把“AI 的使用过程”从单向的“提问-接受”改造成双向的“提问-验证-追问-决策”。举个具体的例子。你在用 AI 写一个 Python 函数要求它解析某个日志文件中的时间戳。AI 给了一段代码import re from datetime import datetime def parse_timestamp(line): pattern r(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) match re.search(pattern, line) if match: return datetime.strptime(match.group(1), %Y-%m-%d %H:%M:%S) return None看起来没毛病。但如果你直接复制就跳过了几个关键问题日志里的时间戳是本地时间还是 UTCdatetime默认不携带时区信息后续比较会有隐患。如果一行日志里出现多个时间戳比如请求开始时间和结束时间search只取第一个结果可能错。如果时间戳带毫秒或者时区后缀这个正则就匹配不上。这些就是批判性思维落地的具体位置。AI 不是不能写这段代码而是在你还没告诉它这些约束之前它只能猜。你需要判断它猜得对不对然后补充约束让它重新生成。这个“判断”和“补充约束”的动作就是批判性思维。3. 建立“AI 辅助但不替代”的工作流很多人使用 AI 是一锤子买卖提问、复制、使用。要持续保持批判性思维更有效的方法是把 AI 放进一条完整的工作流里而不是把它当成终点。完整的 AI 辅助开发工作流可以分成四个阶段3.1 需求定义阶段这一步往往是最容易被跳过的也是最关键的。把需求发给 AI 之前先自己想清楚这个功能的输入是什么输出是什么有哪些边界条件比如空值、超时、并发、重复调用。成功标准是什么怎么验证它是对的然后把这些信息组织成结构化的问题再发给 AI。这里推荐一个简单的 prompt 模板我正在开发一个【功能模块】需要你帮我分析实现方案。 业务背景 - 输入【数据来源和格式】 - 输出【期望结果】 - 约束【性能要求、并发量、部署环境】 - 异常场景【超时、空数据、非法输入、下游不可用】 请你 1. 给出至少两种实现方案并对比优缺点 2. 指出你理解中不够明确的地方 3. 每个关键步骤给出核心代码或伪代码 4. 明确标注哪些地方你只是推测需要我确认。这个模板的作用是逼着 AI 把“它不知道什么”暴露出来而不是给你一份看似确定、实际堆满了假设的答案。3.2 生成与提问阶段拿到 AI 的初步回答之后不要急着让它写完整代码先做一轮“方案评审”方案是否符合当前项目的技术栈是否考虑了项目里已有的同类封装是否引入新的依赖这个依赖是否值得有没有明显的性能或安全问题发现问题后用追问的方式让 AI 修正而不是自己动手改。追问本身也是一种思考训练。3.3 审查与验证阶段这是批判性思维最核心的环节。AI 生成的代码必须经过人工审查审查不是看“能不能编译”而是看逻辑完整吗异常分支都覆盖了吗有没有隐藏的副作用比如修改全局状态、占用连接不释放。测试用例是否覆盖了边界情况是否符合团队的代码规范审查之后必须做实际运行验证。AI 说“这段代码能跑”和它真的在日志、真实数据、边界输入下能跑是两回事。3.4 复盘与积累阶段每次使用 AI 完成一个任务后可以做一个两分钟的小复盘这次 AI 哪些回答是准确的哪些是错的我在哪个环节发现了问题是通过日志还是通过代码走查有没有什么约束条件是我一开始忘了说的这个复盘决定了你是“用 AI 提高效率”还是“被 AI 逐渐替代判断力”。4. 实战一AI 辅助代码生成时如何保持主动权下面用一个实际的 Python 示例来演示完整流程。4.1 需求描述假设我们要写一个函数从一段文本中提取所有 HTTP URL并按域名分组输出。4.2 先自己拆解需求把这个简单需求拆一下会发现有很多隐含条件URL 可能有大小写差异吗协议只有http/https还是也包含ftpURL 里可能有查询参数和锚点吗域名是否包含端口号返回结果需要去重吗需要保持出现的先后顺序吗输入是文本行还是长文本提取时是否要跳过 HTML 标签内的 URL整理后我们把约束写清楚再发给 AI。4.3 发给 AI 的 prompt请用 Python 实现一个函数 extract_urls_by_domain(text)。 输入 - text一段长字符串可能包含普通文本、标点符号和 URL。 输出 - 返回 dictkey 是 URL 的域名包含端口号value 是该域名下所有去重且保持首次出现顺序的 URL 列表。 要求 1. 只匹配 http 和 https 协议 2. 域名不区分大小写统一转小写 3. 忽略 URL 中的锚点# 后面的部分 4. 保留查询参数但要去除末尾的句号和逗号等标点 5. 需要定义一个小型正则并说明每个分组的作用。 请给出完整可运行的代码并附带三个测试用例分别覆盖普通文本、多个相同域名、大小写混合域名。4.4 AI 给出的代码假设 AI 返回了以下代码import re from collections import OrderedDict from urllib.parse import urlsplit, urlunsplit URL_PATTERN re.compile(rhttps?://[^\s\], re.IGNORECASE) def normalize_url(url: str) - str: parts urlsplit(url.strip().rstrip(.,;!?)) return urlunsplit((parts.scheme, parts.netloc, parts.path, parts.query, )) def extract_urls_by_domain(text: str): result OrderedDict() for raw_url in URL_PATTERN.findall(text): normalized normalize_url(raw_url) parts urlsplit(normalized) domain parts.netloc.lower() current_urls result.setdefault(domain, []) if normalized not in current_urls: current_urls.append(normalized) return dict(result)这段代码整体结构是清晰的但直接能用吗需要审查。4.5 人工审查与验证按批判性思维的四个层面来分析第一正则的边界。[^\s\]会把中文标点和中文字符也吞进来。比如文本里写“访问 https://example.com/a查看更多”URL 结尾的中文逗号会被包含进$path。虽然normalize_url里rstrip了一部分英文标点但对中文逗号没有处理。第二端口和域名格式。urlsplit的netloc对缺失端口、带用户信息的 URL 都能处理但如果有少量畸形 URL整个函数会在urlsplit阶段可能抛出ValueError。可以增加异常兜底。第三大小写和重复。normalized not in current_urls做的是全量字符串比对意味着协议头大小写不同但域名相同的两个 URL 会被视为不同项。虽然域名已经转小写但 path 和 query 的差异可能是业务上需要的这里可以接受。在这个基础上我们再补充一个更严谨的版本import re from collections import OrderedDict from urllib.parse import urlsplit, urlunsplit URL_PATTERN re.compile(rhttps?://[^\s\。、], re.IGNORECASE) def normalize_url(url: str) - str: cleaned url.strip().rstrip(.,;!?。、) parts urlsplit(cleaned) return urlunsplit((parts.scheme, parts.netloc, parts.path, parts.query, )) def extract_urls_by_domain(text: str): result OrderedDict() for raw_url in URL_PATTERN.findall(text): try: normalized normalize_url(raw_url) parts urlsplit(normalized) except ValueError: continue domain parts.netloc.lower() current_urls result.setdefault(domain, []) if normalized not in current_urls: current_urls.append(normalized) return dict(result)这个版本补上了中文标点和异常兜底。但即使这样我们仍然不能说它是“绝对正确”的只是在当前约束下更健壮。如果业务里出现带括号的 URL或者 URL 出现在 Markdown 链接里还需要继续调整。这就是“持续质疑”的节奏。4.6 编写测试用例验证步骤不要省略。一个简单的单元测试文件可以写成这样import unittest from url_extractor import extract_urls_by_domain class TestExtractUrlsByDomain(unittest.TestCase): def test_normal_text(self): text 访问 https://www.example.com/path 查看更多。http://api.example.org/data?id1 result extract_urls_by_domain(text) self.assertEqual(sorted(result.keys()), [api.example.org, www.example.com]) def test_same_domain_dedup(self): text https://example.com/a 和 https://example.com/a 是同一个 result extract_urls_by_domain(text) self.assertEqual(len(result[example.com]), 1) def test_case_insensitive_domain(self): text https://EXAMPLE.com/A https://example.com/B result extract_urls_by_domain(text) self.assertEqual(list(result.keys()), [example.com]) self.assertEqual(len(result[example.com]), 2) if __name__ __main__: unittest.main()这里面test_case_insensitive_domain验证的就是“域名转小写”这个约束。如果 AI 生成代码时没有加.lower()这个用例会直接失败提醒你需求没有被正确实现。5. 实战二AI 辅助编写技术方案时如何辨别事实AI 不仅能写代码还经常被用来写技术方案、架构说明和项目文档。这一场景下AI 的“幻觉”风险比写代码还要高因为它给出的内容往往结构完整、看起来非常有说服力但其中的细节可能完全经不起推敲。举个例子你让 AI 帮你写一份“订单系统缓存的选型方案”。AI 可能给出一个结构工整的对比表格列出 Redis、Memcached、本地缓存、Caffeine 的优缺点然后给出“结论推荐使用 Redis”。如果只看结果这个结论大概率是对的。但问题在于AI 在对比表里填的每个细节可能都有偏差某个版本的过期策略描述不准确某个中间件的集群模式特性过时了“社区活跃度”“公司案例”这类事实性信息完全可能编造。因此在让 AI 辅助写技术方案时我的建议是第一把它当“结构生成器”而不是“信息源”。让 AI 帮你梳理方案的章节、对比维度、需要确认的问题列表。这些结构性的内容不太容易产生事实性错误而且能帮你快速建立思考框架。第二关键数据必须自己核实。AI 给出的版本号、性能数字、开源协议、兼容性说明都要回到官方文档确认。不确认的信息不要直接写进正式文档。第三让它生成“待确认问题清单”。这是降低幻觉风险非常有效的方式。可以这样问请针对以下技术方案列出 15 个在评审时需要重点确认的问题包括 - 数据一致性问题 - 高可用部署问题 - 性能容量问题 - 安全与权限问题 - 运维监控问题。 每条问题请附带“为什么需要确认”以及“可以通过什么方式确认日志、压测、官方文档、实验”。这样的输出会引导你自己做研究而不是被动接受结论。6. 实战三AI 辅助排查线上问题时如何避免被带偏排查线上问题是最容易丢掉思考的环节。原因是报错已经出现在那里人很容易把 AI 当成“答案机器”把解决路径交出去自己只负责复制命令。但线上问题的核心特征是错误信息是结果不是原因。AI 能帮你分析“这条报错是什么意思”但很难凭一段堆栈判断“为什么你的服务会产生这个报错”。这时候你的系统知识、日志上下文、监控数据才是决定排查方向的关键。一个有效的方法是把 AI 定位为“解读器”而不是“排查者”。看一个例子。假设 Spring Boot 服务在启动时报错APPLICATION FAILED TO START Description: The Tomcat connector configured to listen on port 8080 failed to start. The port may already be in use.你可以直接问 AI“为什么我的 Spring Boot 启动失败”它大概率会回答“端口被占用”。这没错但如果你只是执行kill -9杀掉进程下次部署还是会遇到同样问题。更好的做法是把上下文充分提供给 AI我的 Spring Boot 服务在启动时遇到以下错误 【完整堆栈】 当前环境和操作 - 操作系统Linux CentOS 7 - 启动方式通过 systemd 服务启动 - 部署方式Jenkins 流水线自动部署 - 最近变更本版本新增了 Redis 配置是否可能相关 请帮我分析 1. 断案最有可能的直接原因有哪些 2. 如何通过命令或日志进一步确认 3. 每一种原因对应的解决步骤是什么 4. 哪些是需要到生产环境操作的高风险命令请明确标注。 注意请区分“确定结论”和“推测结论”。这样问完之后你会发现 AI 给的不再是一个笼统的“端口被占用”而是一个包含排查顺序的清单。即便如此你也需要一步一步去验证比如先执行netstat -tlnp | grep 8080看占用进程再确认进程归属再决定是否终止它。这个过程里真正做决策的还是你。AI 只是把你遗漏的排查步骤补全了。7. 常见误区把 AI 当“权威”而不是“助手”在 AI 工具使用上有几类典型误区和对应的纠偏方式。这里整理成一张表方便对照查看。误区表现会造成什么后果纠正方法拿到 AI 代码只要运行成功就提交边界条件没覆盖生产环境出 Bug强制自己走查异常分支补测试用例AI 给的方案和你项目技术栈不一致照样往下做引入不必要的依赖维护成本上升先做方案评审再决定是否采用让 AI 直接写 SQL不改 where 条件直接执行更新或删除大量数据只把 AI SQL 当作草稿执行前人工确认条件和影响行数把 AI 给出的版本号、依赖、性能数据写进文档文档存在虚假信息误导团队其他成员关键事实回到官方文档核实用 AI 处理生产问题拿到命令就直接跑误操作导致故障扩大所有危险命令先加 dry-run 或备份验证后执行不向 AI 提供业务背景让它猜需求代码和真实需求有偏差提供业务上下文、边界条件和验收标准这些误区的共性在于把 AI 的结论当成了决策终点。正确的姿势是AI 帮你生成候选方案你把关、选择、决策、落地和验证。8. 给开发者的 AI 批判性使用检查清单下面这份清单是我在实际项目中一直在用的按照“使用前、使用中、使用后”三个阶段组织。可以直接复制到你的笔记工具里每次使用 AI 做重要任务时过一遍。8.1 使用前[ ] 我是否已经尝试用自己的话描述问题和期望结果[ ] 我是否把边界条件、输入输出格式写清楚了[ ] 我是否说明了大致的业务背景和使用场景[ ] 我是否明确要求 AI 标注“不确定”或“推测”的部分8.2 使用中[ ] AI 给出的方案是否与项目现有架构兼容[ ] 关键代码是否清楚每一行的作用如果有不清楚的地方是否追问了[ ] 是否要求 AI 给出备选方案而不是唯一答案[ ] 是否检查了版本、依赖和兼容性问题[ ] 是否让 AI 补充了测试用例8.3 使用后[ ] 是否在真实或模拟数据上运行过[ ] 是否检查了日志和异常输出[ ] 是否让团队其他成员做了一次代码走查[ ] 是否记录了 AI 给出的错误信息或你发现的问题这份清单的核心是在给 AI 任务的每一个关键节点上都插入一个“人工确认点”。这不是降低效率而是在效率和质量之间找到平衡。9. 工程团队如何把“批判性使用 AI”落到流程里单独一个人保持批判性思维容易整个团队都做到需要流程和文化上的支持。有几个工程层面的做法可以参考。9.1 代码评审中加入“AI 使用标记”在 pull request 描述中增加一个选项本 PR 是否包含 AI 生成代码。如果包含需要在描述里注明“哪些部分由 AI 生成我对哪些逻辑做了修改和验证”。这样做不是歧视 AI 生成的代码而是让评审者在看代码时多留一个心眼。AI 生成代码的常见问题是“看起来非常规范逻辑却有问题”如果评审者知道这段代码是 AI 生成的会更有针对性地检查边界条件。9.2 把高质量 prompt 沉淀到团队知识库好的 prompt 是一次性投入、长期复用的资产。团队可以建一个ai-prompts目录把常用的 prompt 模板按场景整理好。例如你在协助一个 Java 后端团队做代码审查。 请检查下面的代码片段找出 1. 潜在的空指针和异常处理问题 2. 线程安全问题 3. 资源未关闭问题 4. 性能隐患 5. 与标准 Java 8 语法的兼容性。 输出格式每个问题一行包含【文件位置/行号】【问题描述】【改进建议】。 同时标注哪些问题是确定缺陷哪些只是潜在风险。这类 prompt 的价值在于它把团队的代码评审规范变成了 AI 的输入约束让 AI 的输出更贴近团队的工程标准。9.3 生产环境高风险操作必须有独立确认人这一点尤其重要。AI 很适合生成生产环境命令、数据库恢复脚本或者批量变更配置。但这类操作一旦出错影响面往往很大。团队可以约定AI 生成的高风险命令数据库更新、删除、生产环境配置变更必须经过至少一个未参与生成的同事确认并且记录到变更管理系统。这也是最小权限原则的一部分。9.4 信息安全和数据边界不能因为 AI 而放松使用在线 AI 工具时不要把真实的客户数据、密钥、生产数据库 IP 直接粘贴进去。团队可以约定使用脱敏后的数据构造测试用例密钥和 token 一律使用环境变量或配置中心涉及生产数据的文本先脱敏再提问在本地或私有化部署的 AI 工具中处理敏感内容。数据安全和权限控制是底线这一点上应该比“提问效率”优先得多。10. 给 AI 使用者的最后一个行动建议写到这里其实想强调一个比较朴素的观点AI 会持续进步它可以更快地给你答案但“为什么要这个答案”这件事只有你自己能回答。我现在自己的兜底原则是每次使用 AI 完成任务之后问自己一个问题——“如果明天 AI 服务不可用了我能不能凭自己的能力把这件事重新做一遍”如果答案是“不能”说明我把思考外包得太多应该回去补上那段知识。在项目里你至少可以先做三件事把上面那份检查清单打印出来放在每天都看得见的地方从今天开始AI 生成的代码必须写单元测试测试不过不允许提交每次 AI 给出不合理的答案时记录原因积累成团队的“AI 排错经验库”。AI 是很好的协作者但它不能替代你成为问题的负责人。保持批判性思维不是为了和 AI 对抗而是为了让自己的想法始终处于项目的核心位置。希望这篇文章能帮你更从容地在 AI 时代写代码、做方案、排问题。
返回列表