这篇不是给别人看的故事,是写给我自己的。三个月,两个项目,踩过的坑比写过的代码多。趁着还没忘,把技术细节、心理活动、犯过的蠢和想通的事都记下来。
刚拿到项目的时候,我其实什么都不敢动。
项目已经是能跑的状态——虽然有小毛病,但整体功能能用。我看着满屏几万行代码,几百个文件,连依赖关系都理不清。哪里能改、哪里不能碰、改了会不会把别的地方搞崩,我心里完全没底。
于是第一个星期,我基本只是看。看代码、看日志、看文档。鼠标在变量名上悬着,就是不敢敲键盘。我知道应该加心跳机制,但不知道加在哪里合适。打开 IDE 想写,脑子里转一圈又关掉了。
后来 mentor 可能看不下去了,找我聊了一次。原话我记不太清,大意是:
你要大胆地改,改错了回滚就行了。及时做好 commit,怕什么?如果你不敢改代码,尽早别吃这行饭了。
他说这话时语气不重,但我听进去了一句话:不敢改代码的人,做不了这行。
他接着教了我一套工作流:不要在主分支上改,先切一个新分支出去;在新分支上改完,提交,push,申请 Pull Request;如果改崩了,直接回滚到上一个 commit,什么损失都没有。分支就是给你试错用的。
我记得他当时还加了一句:“版本控制不是你交作业用的,是你犯错用的。”
这话对我来说很新鲜。在学校用 Git,就是提交作业、备份代码,从没想过它可以作为“犯错的安全网”。我之前的心理模式是“一改错了就完蛋”,mentor 给我换了一个模式:改错了只是多了一次 commit,回滚就是。
那天下午,我切了人生中第一个真正意义上的 feature 分支,开始动手改心跳的代码。
改的过程也没那么顺利。第一次提交编译报错,回滚,重来。第二次跑起来了,但心跳间隔设错了,PLC 那边日志刷屏,又回滚。来回三次,才把第一版心跳逻辑写完。
但神奇的是,我不慌了。因为我知道这条分支是我自己的,主分支干干净净。改烂了就删分支重开,没人知道,也不影响别人。
回头看,敢不敢改代码,本质不是技术问题,是心理问题。怕犯错,怕担责,怕暴露自己不懂。mentor 用“分分支 + 频繁 commit + 回滚兜底”这套流程,直接把犯错的成本降到了零。成本没了,心理包袱也就没了。
这件事我写在这里,因为它太基础、太前置了。没有这第一周的心理破冰,后面所有的排查、优化、重构都无从谈起。敢动手,才有资格谈技术。
心跳机制需要给三个硬件发命令并接收响应。海康 4K 相机没问题,调 SDK 正常工作。塔石 PLC 和致德程控电源,各有各的麻烦。
先说塔石 PLC。
这个 PLC 同时支持 Modbus RTU 和 Modbus TCP。代码里写的是走 Modbus TCP 协议,但我用第三方工具测试的时候,发现事情不对劲。
我用的工具是 Modbus Poll,先发标准的 Modbus TCP 报文——功能码 03 读保持寄存器,MBAP 头 + PDU,规规矩矩。PLC 没反应。
我换成 Modbus RTU 的格式,地址码 + 功能码 + 数据 + CRC,走串口工具发,也没反应。
两种标准格式都不响应,但程序偶尔能正常工作。这就矛盾了:如果 PLC 严格遵循协议,要么 TCP 通,要么 RTU 通,不可能两个标准都不认、却认我们程序的格式。
问题只能出在“我们程序发的东西”既不是标准 TCP、也不是标准 RTU,而是某种变体。
为了验证,我打开 Wireshark,设了过滤规则只抓发往 PLC 对应 IP 和端口 502 的包。让程序正常运行,抓了几轮通信包。
展开 Payload 的时候我愣了几秒。
外层是标准的 Modbus TCP 帧:事务标识符 00 01、协议标识符 00 00、长度、单元标识符 01。这些都对。
但长度字段后面的 Payload,不是标准的 PDU。里面直接塞了一个完整的 Modbus RTU 帧——地址码、功能码、数据、CRC 校验,原封不动。
也就是说,前任开发者在 Modbus TCP 的壳里,跑了一个完整的 Modbus RTU 帧。RTU 帧里已经带了地址码和 CRC,TCP 的外壳又套了一层自己的地址和长度字段。两层地址,两层校验。
这就像你寄快递,箱子上写了收件地址,打开箱子,里面的东西自己也贴了一张快递单。
我当时的心情很复杂。先是哭笑不得——这人到底在看什么文档?然后是不安——如果这种写法都有,代码里还有多少我没发现的奇怪实现?
技术上能解释为什么偶尔能跑通:PLC 的固件在 502 端口上同时兼容两种协议解析。它收到数据包后,发现 TCP 的 Payload 部分不符合标准 PDU 格式,fallback 到 RTU 解析,碰巧解析成功。但这个“碰巧”依赖于 PLC 固件的容错实现,换一个严格遵循标准的设备,直接丢包。
我后来重构的时候,把 TCP 的 Payload 改成了标准 PDU,去掉了冗余的地址码和 CRC,只用 MBAP 头 + 标准功能码和数据。PLC 正常响应,再也没出现协议层面的偶发异常。
这件事在我脑子里刻下了一条规则:接手别人代码,不要信注释,不要信变量名,不要信“文档里写了用 Modbus TCP”。直接抓包看实际发出的字节流。 代码可以说谎,抓包不会。
致德程控电源的偶发故障,是我整个实习里耗时最长、精神内耗最多的一次排查。
现象很明确:流程偶尔失败。查应用日志,日志显示发送命令的语句已经执行,但响应超时,没有收到返回数据。
我第一反应:日志都打印了,函数调用了,write() 返回了成功,那肯定是发出去了。问题应该在电源硬件或者通信链路上,不是软件的事。
当时的心态说白了就是自证清白——日志在手,软件没问题,你们查硬件去吧。
老板和我单独聊的时候说了句话:
“你只是软件层面发出去了,不代表电脑硬件真的发出去了。”
我嘴上是应了,心里没完全服。操作系统还能把我 write() 系统调用的数据吞了不成?协议栈不就是一层层往下传吗?
但后来我静下来想,确实没法自证。应用层 write() 返回成功,只代表数据从用户态拷贝到了内核缓冲区。内核什么时候把数据交给网卡驱动?网卡驱动什么时候把数据打包成以太网帧送出去?物理层信号有没有被网线正常传输?电源那边的网口有没有正常接收?每一层都可能掉链子,而我的日志只覆盖了最上面的应用层。
日志告诉我的是“我说了”,不是“对方听到了”。
这两者之间的差距,学校里没人讲过。学校作业里 printf 打印出来就等于程序执行了。但工程里,一个通信链路上下七层,你只在自己的用户态里证明自己做了事,离“事情完成”还有很长一段路。
带着这个意识重新排查。老板教我用控制变量法:每次只改一个东西,观察现象,确定定位再往下走。
第一步,换网线。拿了一根确定在别的设备上正常使用的网线替换现有网线,问题依然偶发。排除网线物理故障。
第二步,换软件。不用我们自己的上位机,用第三方 Modbus 工具直接给电源发相同的命令——读电压、设电流。连续发一百次,收发正常,没有无响应。说明命令本身没问题,应用层逻辑也是对的。
第三步,压力测试。写了一个小脚本,只做两件事:发读电压命令、发设电流命令。然后分别用单线程串行发和双线程并发发做对比测试。
单线程模式:每 100ms 发一条,收到响应才发下一条。跑了 500 轮,0 失败。
双线程模式:两个线程各自独立发送,时间间隔随机。跑了不到 50 轮就出现大量无响应,比例和三周前用户反馈的失败频率基本一致。
到这里,定位已经很清晰了:电源无法处理并发命令。
后补查了电源的技术手册,里面没写“不支持并发”,只写了“核心处理器为单线程架构”。这句话换个说法就是——同一时间只能处理一件事。两条命令同时过来,它随机丢掉一条。
这件事让我深刻理解了控制变量法的价值。排查最开始,我对问题的归因完全错误——上来就判断“软件没问题”,但实际上我没排除软件发送方式的问题。如果我在第一步就认定“日志都有了肯定是硬件的事”,后面所有的排查都不会发生。
真正客观的排查方法是:不预设“哪一层是好的”,一层一层验证,一次只动一个变量,用排除法逼近根因。 找出来是谁的问题不重要,重要的是找出来。
根因找到后,解决方案反倒是明确的:命令必须串行化。我用了消息队列,所有命令入队,单线程消费。收到当前命令的响应后,队列才取下一个命令发送。
单次命令响应时间:大部分 30ms,个别 80ms,平均约 40ms。
电源启动本身需要 1-2 秒的电容充电时间。整个流程有三组灯,每组灯都要走一遍“供电→开灯→拍照→关灯→断电”,总耗时 30-60 秒。
所以加上这 40ms 的串行延迟,对整体流程的影响可以忽略不计。
但我有一个要对自己诚实的地方:我当时改消息队列方案的时候,并没有提前计算延迟影响。不是因为胸有成竹,是因为脑子里只有“让命令别并发”这一件事,没顾上评估性能。事后补了测试数据,验证了方案合理。
这是经验不够的表现。如果是现在,我会在做方案决策前先拿两个数据:当前流程总耗时、消息队列引入的平均延迟。用数据对比来判断方案是否可行,而不是改完再测。
用户在工控机上连续运行软件好几天,反馈偶尔弹出数据库连接失败的报错。
看日志,MySQL 返回的是 Lost connection to MySQL server during query。查 MySQL 配置,wait_timeout 是 28800 秒,正好 8 小时。
问题很清楚:8 小时不做数据库操作,连接自动断开。用户长期不关软件,超过了这个时间窗口。
我意识到需要保活机制,但具体怎么保活,我当时没概念。
和 AI 讨论后确定了方案:开一个独立线程,每 30 分钟发一次 SELECT 1。30 分钟远小于 8 小时,足够保证连接不断。SELECT 1 是最轻量的查询,几乎没有负载。
写完给 mentor 看了,mentor 说思路对,然后加了一句:
再加一个连接池,保持 3 个连接。光靠心跳保一个连接不够,万一那个连接因为别的原因断了呢?
我一开始只想到“让连接不超时”,mentor 想的是“万一有其他原因导致断开怎么办”。两种思路的差别在于对“失效模式”的覆盖范围。我只防了超时断开这一种,mentor 想的是连接可能在任意时刻因为任意原因断开,需要有备用连接随时顶上。
最终方案是两个机制的叠加:
SELECT 1,解决空闲超时断开这算是我第一次在实际项目中接触“冗余设计”的概念。单点防护的思路是“让这一个东西不出问题”,冗余设计的思路是“假设一个东西出问题,还有另一个顶着”。后者的可靠性不在单点的质量,而在备选的数量。
这个认知后来在硬件模拟器的 DAD 问题里也起了作用——当一种方案走不通,我有意识和习惯去寻找另一种完全不同的路径。
项目有几万行代码,几百个文件。界面用 QML 写,业务逻辑用 C++,数据访问单独一层,硬件通信封装在协议层里,第三方 SDK(海康相机)又是一个独立目录。
刚拿到的时候打开一看,脑子里就四个字:无从下手。
头一个星期我试图通读代码,从 main 函数开始一路往下追。追到第三天就放弃了——太多文件,太多类之间的继承和依赖,根本理不清。
mentor 看我在那儿死磕,走过来问了一句:“你现在要解决的问题是什么?”
我说心跳机制。
他说:“那就找心跳相关的代码路径,没关系的文件先别看。”
这句话把我从“阅读全量代码”的负担里拉了出来。我之前的下意识反应是“要把所有代码看懂了才能改”,但这是学校作业的思维——作业只有几百行,能看完。工业项目几万行,等你全看完,用户投诉都堆成山了。
我换了策略:从用户反馈的 Bug 入手。心跳断连——跟踪心跳发送的函数调用——找到协议层构建命令的位置——在命令构建前后加心跳逻辑。这条路径涉及的文件不到十个,其他几百个文件暂时和我无关。
大概半个月后,我开始能自然地在代码里找到对应功能的实现位置。不是全看懂了,是建立了一个“功能-文件”的心理地图,知道哪类功能大概在哪个目录、哪几个文件里。大部分代码我至今没读过,也不需要读。
另一个让我印象很深的是分层架构。
项目里发一条命令,不是在一个函数里一条龙写完的。业务层构建命令参数,传给协议层;协议层打包成指定格式的帧(Modbus TCP 或自定义协议),加上帧头帧尾 CRC;传输层负责 Socket 发送和接收;接收回来后反过来拆包——传输层收原始数据、协议层解帧校验、业务层处理语义。
我第一次 debug 通信问题的时候,收到数据但解析报错。因为分层清楚,我直接在协议层的解帧函数里打断点,发现是 CRC 校验失败。如果所有逻辑混在一起,我得从头看到尾才能定位。分层让我几秒就缩小了排查范围。
之前学校写代码,全是平铺直叙,一个函数从头干到尾。出问题就只能一行行打日志。现在我理解每一层存在的意义了:不是为了看起来高级,是为了出问题时能快速隔离。
硬件模拟器用 Go 写的。Go 我之前完全没写过,mentor 指定让我学 Go,说他觉得 Go 适合做网络服务。
上手的方式很粗暴:对着 Go by Example 敲了两天基础语法,然后直接开始写调试工具。不懂就查,报错就搜,边写边学。
第一版模拟器的设计思路很朴素,其实就是做一个“应答机器人”。我把上位机会发的所有命令列了一张表,每条命令对应一个固定响应。模拟器收到命令后,用 switch-case 匹配命令码,把预存的字节数组原样返回。
我觉得这个设计没问题——命令就那些,响应也固定,比对就行了。
然后心跳命令的响应里包含 CPU 温度。
CPU 温度是一个实时变化的值。48 度、49 度、50 度,随便变一下,我预存的字节数组就对不上了。
这件事听起来很简单,但我当时是在测试的时候盯着日志发现的。模拟器返回的 CPU 温度一直是 46 度,而实际硬件那边已经 51 度了。上位机拿到这个固定值,触发了温度异常的假告警。
我只能把这部分逻辑拆开:心跳响应的其他部分(帧头、设备地址、功能码、版本号)用固定模板,CPU 温度那个偏移位置的四个字节单独拿出来,从系统读取实时温度然后动态填入。
改完心跳之后我突然意识到,同样的问题会出现在几乎所有命令上。
IO 状态查询命令更复杂。它返回两个 ACK 帧,第二个 ACK 里包含 3 路 PWM 的当前值。PWM 值不是固定的,可以通过设置命令修改。用户先调了 PWM 值,再查 IO 状态,模拟器如果还返回初始值,上位机就会判定状态异常。
但最让我头疼的不是 PWM 值本身会变,而是一条查询命令会触发两次回复,第二次回复里还夹着一个需要动态校验的 PWM 字段。
我原本的设计是按命令码一对一匹配响应,一条命令对应一个固定的字节数组。但现在出现了一条命令触发两条响应的情况,而且第二条响应的内容还依赖于设备当前状态。
switch-case 搞不定了。我需要让模拟器在处理查询命令时,先去读取自己维护的 PWM 状态变量,然后动态构造第一条 ACK 和第二条 ACK。这意味着模拟器不能只是一个“应答机器人”,它必须是一个有状态的设备。
后来我把所有需要动态解析的响应都重构了一遍,抽出一个公共函数,专门负责“根据命令类型和设备当前状态,构造对应的响应字节流”。不再有一个预存的固定字节数组,所有响应都在收到命令的那一刻实时生成。
这个过程让我重新认识了“协议”两个字。
以前我对协议的理解就是帧格式:帧头几个字节、地址几个字节、数据区多长、CRC 怎么算。只要格式对,协议就通。
做完模拟器才意识到,格式只是协议的骨架。协议真正的复杂性在语义层:哪个字段是动态的、哪个是静态的、什么条件下触发什么响应、一条命令对应几条回复、回复之间是否有顺序依赖、某些字段之间是否存在关联约束(比如改了 PWM 值之后,后续查询必须返回新值)。
你只看帧格式,你只能让数据不丢包。你理解了语义规则,你才能让模拟器的行为和真实硬件一样。
我在两个项目里用 AI 的方式完全不同。
上位机项目的时候,我是一句一句问。每次遇到问题就问 AI,拿到代码贴进去,跑不通再问。改 A 的时候经常不小心动了 B,然后又开始修 B。来回折腾,效率极低。
后来我发现问题不在 AI,在我。我给的指令太零碎、太随意,AI 看不到全局,只能猜我的意图。
硬件模拟器项目开始前,我决定换方式。
我从早上 10 点开始,打开一个空白文档,开始和 AI 对齐需求。
我把协议的每一个字节拆开来讲:第 0 字节是什么、取值范围、是否可变;第 1-2 字节是什么、大端还是小端、和前面字段的关系;第 3 字节的命令码枚举值、每个值对应的响应类型;响应的构造规则、哪些字段从状态变量读取、哪些固定写入。
AI 根据我的描述生成需求描述,我再读,发现不准确的就纠正。来回了不知道多少轮,很多细节我也是在描述的过程中才想清楚的。比如“IO 状态查询命令到底在什么条件下返回第一条 ACK、什么条件下返回第二条”——这个我之前没细想过,写需求的时候才被迫把它理清楚。
到下午 3 点多,文档成型了,将近 2000 字。我存成 README,当成事实来源。
然后把 README 交给工作 AI,让它生成代码框架。十几分钟就出完了。
跑之前我没直接编译。先让同一个 AI 自查代码,检查类型错误、边界条件、遗漏的处理分支。它找出几个问题,改了。然后把代码和 README 一起丢给另一个 AI,让它交叉审查逻辑是否符合需求文档。又找出一个 README 里写明白了但代码漏掉的处理逻辑,补上了。
两轮审查结束,我才编译运行。整个过程没有遇到需要大面积返工的情况。
我复盘这次和上位机项目的区别:
上次我把 AI 当代码生成器,一句一句要。它不知道上下文,我也不知道自己要什么,两个都不清楚的人凑在一起,效率不可能高。
这次我把 AI 当执行器,在让它动手之前,我先花了 5 个小时把自己要什么搞清楚了。README 成了唯一的真相来源。代码对不对,拿 README 对照一目了然。AI 审查也是审代码和 README 的一致性,而不是凭空判断。
核心变化在这里:我把脑力从“纠正代码”转移到了“定义正确性”。
纠正错误代码是无限循环——改了一个 bug 可能引入另一个。但定义清楚什么是正确的之后,验证就变成了简单的比对。5 个小时写 README,十几分钟生成代码,两轮审查。总耗时大概 6 小时。
如果是用上位机项目那种老方式做模拟器,我估计至少两三天,而且 bug 更多。
不是 AI 变强了,是我学会了怎么当一个能写清楚需求的甲方。
模拟器需要给每个虚拟硬件分配独立 IP,这样上位机才能分别给两个设备发心跳——发给 A 只有 A 收,发给 B 只有 B 收,和真实网络环境一致。
我的电脑只有一个物理网卡,配了一个 IP。怎么再搞出两个独立 IP?
所有 AI 给的都是同一个方案:在物理网卡的高级设置里添加辅助 IP。Windows 支持一个网卡绑多个 IP,Go 程序分别绑定到不同 IP 上就行。
我照做了。添加辅助 IP 192.168.1.100,子网掩码 255.255.255.0。点确定。
打开终端,ipconfig 一看,状态显示 Tentative。
Tentative 是 Windows 的 DAD(Duplicate Address Detection,重复地址检测)机制的状态标记。在 Tentative 状态下,IP 还不能被应用程序绑定使用,系统要先发 ARP 探测这个 IP 在局域网里有没有被占用。没人回应,状态才转为 Preferred,地址才可用。
问题是我的环境是两台机器直连,没有路由器,ARP 探测的响应机制可能不正常。Tentative 状态一直不转 Preferred,Go 程序绑定就报错:address already in use 或直接绑定失败。
我开始搜解决方案。主流答案是改注册表禁用 DAD:HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters 下面加两个 DWORD 值,一个叫 ArpRetryCount 设为 0,一个叫 EnableDeadGWDetect 之类。重启网卡。
试了。没用。重启电脑,又回到 Tentative。
换个思路,改子网掩码、改用不同网段的 IP、禁用再启用网卡。全试了,Tentative 纹丝不动。
折腾了两天多。AI 开始反复建议我放弃虚拟 IP 路线:
“要不改成监听 0.0.0.0,用一个 Socket 收所有数据,然后在应用层根据数据包里的设备地址字段区分是给哪个设备的。”
技术上可行,但业务逻辑不对。真实环境中两个硬件分别有独立 IP,上位机向各自的 IP 发心跳命令。如果模拟器用单一 Socket 监听所有 IP,两个虚拟硬件就会收到完全相同的数据流。心跳命令是广播语义的——上位机同时给两个设备发相同的心跳帧——如果两个模拟器实例都收到,就无法区分上位机是想单独查询 A 的状态,还是两个都要查。
这不是实现方式的问题,是需求定义的问题。模拟器要模拟的是“两个独立 IP 的独立设备”,不是“一个 IP 上跑两个逻辑实例”。需求在这个点上不能妥协。
AI 的方案一直在绕,因为它的知识库里没有在我的具体环境下能走通的路。
我放弃了问 AI,直接用浏览器搜真人帖子。关键词来回换:Windows 11 multiple IP tentative、Windows loopback adapter static IP、DAD tentative fix not working。
翻了很多页之后,在一篇讲 Windows 8 网络配置的技术博客里看到一句话:用 Microsoft Loopback Adapter 创建虚拟网卡,在虚拟网卡上配 IP,不经过物理网卡,DAD 行为不同。
我不确定 Windows 11 还有没有这个功能,但这是唯一还没试过的方案。
我把帖子发给 AI,让它帮我整理成 Windows 11 的操作步骤。AI 照做了,但态度很明确——每条指令下面都附了一句“建议还是放弃,改用应用层区分设备的方式”。
我没理。跟着步骤一步步走:打开设备管理器,添加过时硬件,手动选择网络适配器,厂商选 Microsoft,型号选 Microsoft KM-TEST Loopback Adapter。装完,网络连接里多了一个“以太网 2”。
在以太网 2 的 IPv4 设置里配静态 IP 192.168.1.100,子网掩码 255.255.255.0,网关留空。点确定。
终端里 ipconfig,以太网 2 的 IP 状态:Preferred。
那一刻的心情很复杂。不是兴奋,是一种“终于对了”的放松,混合着一点点对 AI 的重新认识。AI 不是不想帮我,它只是在我的具体环境、具体驱动、具体 Windows 版本、具体网络拓扑的组合下,给不出正确路径。它给的永远是概率最高的解,不是必然正确的解。
当概率最高的解在具体环境里失效时,能往上顶的,只有人的判断和坚持。
两个虚拟 IP 都配好之后,Go 程序成功绑定,分别监听各自的 IP 和端口。上位机发心跳到 IP A,只有模拟器 A 收到;发到 IP B,只有 B 收到。和真实硬件行为一致。
这个 DAD 问题从发现到解决,前后折腾了差不多三天。时间不算长,但给我的冲击很大。学校里习惯了“所有问题都有标准答案”,但工程里很多事情是第一次遇到——没有人踩过你的坑,没有人写你的教程,连 AI 都劝你放弃。这时候,你相不相信自己的判断,就成了唯一的分界线。
CCD 数据转换和连续采样的功能最后没有做完,因为硬件模拟器的需求来了,优先级更高,只能把调试工具的开发先搁置。这算一个遗憾。如果后面有时间,我想把这两个功能补完整——不是为了交付,是想给自己一个交代。
刚来的时候,我连切分支改代码都不敢,怕改崩了被骂。mentor 一句“不敢改代码就别吃这行饭”算是把我推过了那道心理门槛。Git 分支 + commit + 回滚这套流程,把犯错的成本降到零之后,我才真正开始动手。
动手之后发现,工程和学校最大的区别不是技术难度的差距,是问题归属的边界不同。
学校里的 Bug 一定是你的代码逻辑写错了。工程里的 Bug 可能在网线、在电源的单线程架构、在数据库的 8 小时超时策略、在 Windows 的 DAD 机制、在前任开发者塞进 TCP 壳里的那个多余的 CRC 校验码。问题在哪一层都有可能。你能做的不是猜,是一层层排查,直到找到它。
排查也不是漫无目的地试,要讲方法。老板教的控制变量法——每次只改一个变量,观察结果——已经变成了我的固定工作模式。出问题先列变量:软件、网线、发送方式、硬件固件、操作系统配置。然后一次只动一个,排除一个再动下一个。
上位机优化教会我怎么排查问题,硬件模拟器教会我怎么用 AI 和自己解决问题。上位机项目我是一句一句问 AI,效率低还容易偏。模拟器项目我花了 5 个小时写 README,把“我想要什么”定义清楚,再让 AI 执行,效率翻了几倍。但当所有 AI 都劝我放弃虚拟 IP 的时候,我用浏览器搜到了一篇 Windows 8 的教程,自己判断可行,一步步配通了虚拟网卡。
AI 给的是概率最高的解,不是必然正确的解。 当概率最高的解在具体环境里失效,你相不相信自己的判断,就成了唯一的分界线。
还有一件小事,mentor 在某次 code review 的时候跟我说:你的代码风格干净,命名也规范,这个好习惯要保持。我当时愣了下,因为没觉得自己特别在意这个——可能就是以前写作业养成的。mentor 接着说了句:“代码是写给人看的,顺便能跑而已。你能从一开始就注意这个,说明你知道以后看这段代码的人不是你一个人。”
这句话我一直记着。
最后写几条给自己的总结,是这两个月确实长在身上的东西: