难点不在技术
难点不在技术
——一个工具,和我自己的两年

一、这份代码是我要来的
2024 年 10 月,我向合作单位的一位同行要了一份命令行抓包的代码。
但真要说起点,不是这份代码,是一次谈话。
在那之前不久,徐所——合作单位的一位所长,那天恰好有空——花了一下午给我讲这东西是什么、数据怎么在这套系统里流动,也讲了他对我未来职业方向的一些想法。老实说,那天我并没有全听懂,但我记住了一句:这件事值得做。
那时候,那个命令行抓包工具我已经用过一段时间了。它有本事,但用起来实在难受——几乎所有用过的人都在抱怨它。每次打开它,我心里都会冒出一句:这也能用?
抱怨久了,念头就变了:要是它有个界面呢?
我正好也想找个项目练手,于是去问那位同行:这东西有没有源代码?我想看看它是怎么实现的,自己做一个带界面的。
他给了我。所以它的内核不是我写的,这一点得说清楚:我做的,是在别人的内核上搓一个界面。
于是这个工具从一开始就是一段接力:他给了我一段代码;后来,我把自己做的第一版也还了回去。
二、在外场,从零把它搓出来
有一件事,我一开始没觉得有多重要,后来才发现它决定了后面的一切:我开始做这个东西的时候,人就在外场。
不是做完了拿去用,是一边在那儿待着,一边从一张白纸开始搓。从"什么都不能干"到"能用了",我一直在外场。
界面这东西,看着简单,做起来不是。界面要显示数据,就得先拿到数据;要拿到数据,就得先订阅;要订阅,就得先弄明白总线上流动的到底是什么。于是我回头去啃原理——读规范、读文档、读别人的实现,把 DDS(Data Distribution Service,一套发布/订阅式的数据分发标准)一点点啃下来。
从界面一层,一路挖到了底层。 这是我这两年第一次感觉到自己在长:我本来只想搓个界面,结果被迫学会了一整套通信机制。
回头看我当时的处境,有一件事现在觉得特别关键:没有人给我提过需求。
没有需求文档,没有功能清单,也没有人催进度。所有问题都是我自己问自己的。还有一点也得说清楚:这不是我的任务,也不是我的工作产出——不在我的任务清单上,没有排期,没有验收,是我自己抽空做的。
听着有点孤独,但这恰恰是它能做成的原因:只有当期待是你自己的,你才会在没有人催的时候继续做下去。

现在回头看这张图,上面那六层——DDS 总线、采集、解析、存储、服务、入口——没有一层是我一开始设计出来的。它们全都是被问题一层一层逼出来的。
那时候在外场,最不缺的就是问题。跟着用,遇到什么就记下来,回头就改。问题就在眼前,样本随手就有——这个工具后来能一点点改得动,靠的主要就是那段时间攒下的东西。

被问得多了,有两个需求自己浮了出来。一个是"这条数据是谁发的",一个是"能不能把录到的东西重放一遍"。
后面这个最后长成了一套闭环:客户端把总线上的数据收下来、留下来;生成器把留下来的东西重新做出来、发回总线。 两者之间只传一样东西——结构定义加样本,结构跟着样本一起走,拿过去不用再配一遍,粘上就能发。
它的价值在于:现场偶发的问题,不必再等它出现第二次——录下来,当场就能重放,一遍遍重放,直到找到根因、把问题解决为止。
先让它能跑,比先想清楚怎么跑得漂亮重要得多。
三、并进工具箱,又搬了出来
故事还有另一半背景。
它原本是独立的一个项目——就是前面说的那份命令行代码,我自己给它搓界面。
后来我想把自己平时散着的小工具都收进一个工具箱,于是把它也并了进来。工具箱里还有几件别的小工具:有的模拟设备,有的转发数据,有的盯着链路。它们都只解决一个小问题,谁也不比谁大。
也就是在那次并入的时候,它之前的提交记录丢了。
并进来之后,我继续在这个工具箱里给它添东西:统一编码、加打包脚本、加 IP 筛选和主机列表、让网卡能手动绑定……还一度支持过 Windows,后来又觉得不值当,放弃了。
也是在那段时间,我干了一件现在想起来很好笑的事:把界面的操作逻辑整个重构了一遍,然后一路回退,六天之后回到了重构前。 代码几乎没变。
当时觉得是白干了。现在回头看,那六天其实是后面所有日子的预演:大部分时间不是往前冲,而是在确认哪条路是对的。
再后来,它不再只是我一个人用的东西了。于是我把它又从工具箱里搬了出来,重新做成一个独立的工程。
四、一件事,最后变成一个功能点
用起来之后,麻烦一件接一件地来。现在我几乎记不住每一个功能是怎么设计出来的,但那几件事记得很清楚——因为功能是那件事的结果。
第一件:那天网里一下子冒出几千个主题,界面直接僵住了。
没有人在等我,也没有人催我——那会儿我还是个没几个人认识的小透明,坐在边上自己用。可界面就是不动,点一下要等好几秒。回来看代码才知道,每次找一个节点,都在把整棵树从头翻一遍,几千个节点就是几百万次比较。
那一次之后,才有的哈希索引、批量插入,还有"同时冒出来的主题先攒 100 毫秒一起插进去"。同样的场景,从几秒降到半秒以内。
第二件:有人跟我说,"我明明筛了,怎么什么都没有"。
我以为是筛选写错了。折腾半天才发现,表格那个"最大行数"(默认 100)不是显示上限,是按到达顺序裁行的——你一开筛选,匹配的那几条早被后面涌进来的数据挤掉了,翻遍全表也找不回来。
于是才把它拆成两件事:能看见的行留 100 条,看不见的额外多留一段专门给筛选搜,淘汰的时候先丢看不见的。
第三件最费劲:两个数字对不上。 页面上方说这批数据有三百六十多万条,下面的列表却说少了 70 条。
差 70 条,放在三百多万里,谁都会当成四舍五入。我没放过它,去逐桶对账,最后定到具体的一分钟:那个桶里记了 30 条,明细里却有 100 条——因为时间片是从"最早一条"开始切的,边界正好落在那分钟中间,相邻两片各写了一次,后写的半截把整分钟覆盖掉了。
修法最后浓缩成三条:片边界对齐到整分钟、写失败不许丢要留着重试、启动时自动对一遍账。
这件事给了我一个后来一直用的习惯:凡是"差不多"的地方,都要去对一遍。 差的不是 70 条数据,是我对这套东西的信心。
五、现场说"没做",其实是"没收到"
有一类事,我在外场听得最多。
"这个功能是不是没做?"第一次我信了,去补功能。第二次我又信了,又去补。第三次我才反应过来——它不是没做,是那帧数据根本没收到。
这两件事在现象上一模一样:界面上什么都没有。但一个原因在我这边,一个在链路那边,方向完全相反。前两次我都在自己这边找,越补越乱。
后来我做了一件事:把"排查"变成工具的一部分。 一条命令下去,它自己去把散在各处的证据收齐——服务在不在、域开没开、主题有没有被发现、订阅有没有建上、库里到底有没有数据、插件又收到了多少——然后按一棵固定的判定树给出结论和下一步,而不是丢一堆原始数据让人自己看。
做这个接口的时候我定了三条规矩,一直守到现在:取不到的数字不许编成 0;每个数字都要说明它是从哪儿来的;拿不出证据就不许下结论。
(后来这套东西还接上了本地部署的模型,让结论讲成人话。但规矩没变:模型只负责翻译,结论永远来自那棵树;模型连不上,规则照给。)
这段之后我明白了一件事:给出结论很容易,给出证据很难。 而后者才是有用的那个。
六、用的人不少,但他们要的很简单
到后来,用它的地方其实已经不少了。
我一度以为问题会是"没人用",结果不是。真正的问题是我后来才看清的:他们要的,和我给的,不是一回事。
他们要的很简单——把数据收下来,看一眼,导出。就这么几件事。而我在往上加别的东西:各种专门用途的分析、报表、插件。加得越多,需要维护、需要教、需要解释的东西就越多。
有一段时间我还挺得意,觉得自己把功能做"全"了。后来才发现,大家翻来覆去用的永远是那几个最基础的功能,我新加的那些,他们连点都没点过。
不是他们不识货,是那本来就不是他们的需求。
不过我没有干等着。
我慢慢养成了一个习惯:一到卡住的时候,就去找那些用得最勤的人——尤其是那位一直帮我推它的同行,问一句"还有哪儿不好用"。
后来很多使用上的细节,都是这么来的。他会把平时忍着没说的地方一条条讲给我听。奇怪的是,那些地方我自己天天都在用,却从来没觉得是个问题——离自己太近的东西,看不见。
早期没有人给我提需求,是因为还没有人用;后来有人用了,需求也不会自己送上门——得去问。
七、我另起过一次炉灶,它没烧起来
在动手拆它之前,我先试过另一条路:另起一个炉灶,重写一个。
我给它起了新名字,重新画了架构——核心抽成一个独立的库,采集、模拟、存储、网络各成一个模块,界面和命令行共用同一个后端,前后端彻底分开。还配了新的日志模块、WebSocket 推送、新的筛选方式。也是在那一次,我第一次让 AI 帮我写了几个模块。
那六个星期它长得很快,我自己挺得意。
然后就是变慢。
到了七月和九月,那个仓库里只剩两次提交,说明是同一句话:同步主项目的变更。
新灶没烧起来。它变成了老项目的一个跟班。
那阵子挺打击我的:我花了六个星期,画了一套自认为更漂亮的架构,结果主线还是原来那个。
后来我慢慢想明白一点:老项目的价值不在架构漂不漂亮。 那些让它在现场站得住的东西——一个莫名其妙对不上的数字、一个只有换台机器才暴露的毛病、一句"这个功能是不是没做"——都不是重新设计一遍就能长出来的,是一天一天磨出来的。我那次重写,把磨出来的东西全都留在了原地。
于是我回到了原来的项目上。这一次不重写,改造。
八、回到原地,把它拆开
真正逼着我动手的,是一件很具体的事:那套通信中间件不是每台机器都装。谁想用这个工具,就得先在那台机器上把一整套环境配齐——在现场,这几乎不可能。
于是我花了几个月把它拆开:一部分变成一个常驻的服务,另一部分变成可以远程连的界面,再给不想开界面的人留一条命令行的路。
技术上不算特别难,心理上那一步才难:承认之前那条路是对的,但现在不够了。
拆的过程中还有一条硬要求:客户端必须能在没装那套中间件的机器上跑起来。我把客户端对引擎的依赖一层层剥掉,从结构定义转换,到配置,到界面里一个又一个页面,直到它变成一个干净的目标。验的时候我不看代码,看链接:客户端依赖列表里那几个库,一个都不剩。
顺手还解决了一堆"只有换台机器才会暴露"的问题:换了编译器版本,旧的中间文件不会重编,链出来一屏完全不相干的错;从 Windows 拷到 Linux,脚本里的换行变成 CRLF,现场直接起不来。这些坑的共同点是——在我自己机器上永远是对的。
这一段是我成长最快的时候。 不是技术难,是我第一次被迫站在"用的人"那边想问题:他要怎么拿到它、怎么装、出错的时候能看到什么。之前我只关心"我这边跑通"。
还有一件事是那时候一起做的:老的单机版没有随着拆分下线。 主线转成服务和客户端之后,我把它单独捋成一个长期维护的版本——功能冻结,只收崩溃、数据错误、现场阻塞这几类问题,以及主线把引擎修好之后顺带带过来的收益。
一个炉灶没烧起来,另一个留着不停火。
九、我加了很多插件,后来又拿掉了一些
拆开之后,主程序干净了,我就动了另一个念头:让它能长出更多用途。

做法是留一道口子——主程序里什么用途都不写,插件只需要回答三个问题:我要哪些域和主题、工具栏上叫什么名字、数据交给我怎么用是我的事。
那段时间我加了不少插件,一个一个往里塞。后来我又把其中一部分拿掉了。
原因不复杂:它们不是现场的需求,是我自己的兴趣。
留一道口子的本质,是把"我来做"换成"谁都能做"。
十、我没能留住它长大的过程
有一件事,我一直觉得遗憾。
这个工具从"什么都不能干"到现在还算结实,中间隔了两年。但这两年没有完整地留下来。
最早那份代码,提交记录在并进工具箱的那次弄丢了。之后的两年里,它又搬了好几次家:并进工具箱,再从工具箱里搬出来;起先只是往一个远端仓库推,后来又整体迁到自己搭的服务上。
我的机器其实不多:一台 Windows 主机,上面装了麒麟 V10,还有一个 Ubuntu 24.04 server 专门跑 GitLab,东西都在这几台系统的盘里。可我这人见不得乱——旧的目录、过期的包、去年建的仓库,看到就顺手清掉,腾出空间心里才舒服。
于是这些搬迁,一次完整备份都没留下——我的整理,就是删除。
只是我清掉的,正是这些记录。
等到真想回头看,已经找不回来了。还有些判断只存在于我当时的脑子里:为什么这么写、为什么不那么写、那个奇怪的绕法是绕开哪个坑。
前一阵我把手上的机器重新翻了一遍,也问过当年给我代码的那位同行,想把它最早的样子找回来。翻的时候才反应过来:当初每一次清理,我都觉得自己是在做对的事。
这不是技术问题,是"从混乱里建立秩序"要付的代价。一开始哪有什么秩序:能跑就行,改完就往前走,不写文档、不记原因、不建版本库。而且那时候也谈不上"值得"——一个自己抽空做的小东西,谁会想到它会活到两年、会有人靠它定位问题?
可秩序偏偏就是这么长出来的:先有东西,才有要求;先能跑,才谈得上规范。
所以这个遗憾换个角度看,反而是它"真的长起来了"的证明。
从那以后我改了几件事:关键的判断写进文档,而不是留在脑子里;每修一个问题,就补一条会自动检查的断言;重要的节点另外存一份。这些事都不难,难的是在没有遗憾之前就去做。
十一、从"希望很多人用"到"担心没有收益"
有一件事我不太好意思写,但它很真实。
刚开始做的时候,我特别希望很多人用它。每多一个用户,我就觉得这件事更值一点。那时候我甚至会把"有多少人在用"当成自己做得对不对的标准。
后来用的人确实多了,可我慢慢发现:他们用的永远是那几个最基础的功能。我花力气加的那些,没人在意。那种期待落空的感觉,不是"没人用"——是没人在意我在意的那部分。
再往后,另一种担心冒出来了,而且比前一种更沉:我投了两年进去,它到底给了我什么?
它不是我任务清单上的事,不在考核里,也不会因为多做一点就多拿一分。这些时间如果花在别的地方,会不会更"划算"?
这个念头,我在外场等数据的时候冒出来过,在改一个没人反馈的毛病时也冒出来过。我没法说它不对——它太对了,所以难受。
我没有立刻的答案。到现在,也不能说自己彻底想通了。
如果一定要给一个说法,我大概会说:这两年让我从一个只会搓界面的人,变成能对一整条链路负责的人。这笔账不在任何表上,但它在我身上。
十二、这两年,我变了什么
写到这里,我想把"成长"这件事说得具体一点。
第一,从"能跑就行"到"差不多就得对一遍"。 差 70 条数据那件事之后,我再也没法把"看起来没问题"当成没问题。
第二,从"我来排查"到"让它自己给证据"。 以前出问题,我凭经验猜;现在我更愿意花时间做一件当时看不出效果的事——让工具把证据摆齐。猜一次很快,但猜错一次更慢。
第三,从"我这边跑通"到"别人拿到手能用"。 这是拆客户端那段逼出来的,也是我这两年最值钱的转变。
第四,从"多加一点"到"拿掉一点"。 我原来总觉得功能越全越好,后来才明白:别人不需要的功能,加进去也是债。
第五,从"等人提"到"去问人"。 没人提需求的时候,我以为是没人需要;后来才明白,是没人有义务替我想。需求不会自己送上门。
第六,也是最晚才明白的:留下痕迹。 这一条是最贵的一条——它是用那个遗憾换来的。
要说这两年最大的变化,其实是我不再指望一开始就想清楚。第一版难看没关系,先让它跑起来;跑起来之后,问题会自己把路指出来。真正难的从来不是"会不会做",而是没人要求的时候,你还愿不愿意做第二十次。
十三、还在继续
现在这个工具已经交出去用了,但它没有"做完"的那一天。
发布之后冒出来的是一整类新问题:运行环境不对、依赖缺失、升级要怎么升、配置怎么迁移、出了故障现场怎么自己判断……这些和写功能完全是两回事——写功能是"让它能做",这些是"让它活得下去"。
我现在就在做这些,持续调优,把发布之后的使用问题一个一个磨掉。
回头看这两年我到底做成了一件什么事——大概就是:把一个没人愿意用的命令行工具,做成了一个能自己说清问题在哪的伙伴。 而我自己,从"想找个项目练练手",变成了"知道一个东西要长起来,需要的是什么"。
技术只是门票。想要,才是起点;反复,才是方法。
文 / nxbskl

浙公网安备 33010602011771号