技术月刊:2025年10月
重构我的个人网站
近段时间,我抽空把我的个人网站前后端完整重构了一遍,现在已经是v3.0版本。
这次重构的契机,缘于从去年开始一直以来我对编程架构和理论的思考和总结。我在沉淀和提升的过程中,也不断地对我以前写的代码——包括公司的和我自己的——进行了反思。大概到了两三个月前,我感觉我的新理论已经有了比较清晰的、经过实践验证过的框架,因此我觉得是时候将它运用到更多的地方去了。
新的思想
这次我的新思想,若要用最少的字数来概括,我认为是“数据驱动+依赖注入”。
数据驱动的理念来源于前端,特别是React+Mobx的组合,我现在已经玩的比较透彻,并不仅仅局限于前端UI,我还将这种理念拓展到不含UI的Node.js层甚至一部分后端项目中。
关注数据驱动,最大的意义在于,我逐渐彻底地将“数据”独立出来,在我眼中,只有看不见摸不着的“纯数据”和用户眼前的“纯视图”,中间的部分,不管是数据库、前端、后端,都只是数据-视图之间的临时过渡态。在架构设计的时候就考虑以数据为核心,其他逻辑都围绕数据展开。
依赖注入给我灵感的是Unity,即那个用c#语言的游戏编辑器;此外,typescript的类型、c的头文件、golang的接口设计都是我模仿和反思的对象。
关注依赖注入,其实最初的目的是为了解决多平台的代码复用问题,但是在实践中我逐渐发现,它也引导我走向DDD领域驱动设计,让我深入思考模块之间的界限和联系,让我能更优雅地组织项目工程目录结构和梳理依赖关系。
其实这些理念可以说是近些年流行的八股,也可以说是老生常谈的经典。但我并没有刻意去追求这些术语,而是在不断总结中不知不觉地向这些经典的理论靠拢了,该说是英雄所见略同吗,它们存在就有其道理啊。
具体技术细节就不谈了,同样的问题你去问chatGPT,它会回答得比我好多了。
新的前端路由
在重构过程中,我尝试更新了react-router-dom库的版本。
然后我郁闷地发现,这东西的API又变了。大概回忆一下,似乎我每次更新这玩意都会弄出不兼容接口,然后要重新写不少代码。
气恼之下我寻求它的替代品,然后我找到了wouter这个前端路由库。它非常精简,我需要的功能它都有,虽然与之前react-router-dom的接口设计略有不同,但很快也就适应了。
不禁感慨,代码库的发展就像一个螺旋,人们总是从最简单的开始,逐渐变得不满足,给它添加更多的功能,然后等它变得越来越臃肿之后,人们又重新寻求简单的替代品。
新的mongo
这次我将mongo的版本更新到了8.0。
不过其实我并没有用上它的新功能特性,或者说它本来就已经很稳定成熟了不应该有大的用法上的变化。
我想记录的,是在更新镜像时出的问题。
也许是8.0版本太新了,我在操作k8s更新镜像的时候一直失败,一直都是网络问题。
然后我尝试改变镜像服务器。一开始我改的是docker的配置,改好几次都没效果,卡住我好久。
后来我才知道,我应该操作的是containerd的配置,具体操作为:
$ vi /etc/containerd/config.toml
# 修改以下内容,使用腾讯云镜像(因为我是腾讯云服务器)
[plugins."io.containerd.grpc.v1.cri".registry.mirrors]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
endpoint = ["https://mirror.ccs.tencentyun.com"]
# 保存后重启服务
$ systemctl daemon-reexec
$ systemctl restart containerd
或者尝试用工具手动拉取,不过这并不能改变网络不通畅的问题:
$ ctr image pull docker.io/library/mongo:8.0
删掉多余的代码
在重构过程中我发现,我之前写了/玩了好多东西啊。
大概有以下内容:
- Google认证登录
- 微信认证登录、微信小程序后端
- trello回调
- 钉钉机器人
- 飞书应用机器人、飞书聊天机器人
- WCL接口(魔兽世界战斗日志工具)
- EVE市场接口+计算器
- 戴森球产线计算器
- MySQL、Redis
- DroneCI
它们中的大部分早已不再使用甚至已经长久没有维护,却依然以代码的形式留存在我的项目中。它们是我技术成长路途中的里程碑,现在已经可以说它们已经完成了其历史使命,可以安心躺进git提交记录了。
克服cpp恐惧症
c/c++,从十几年前我还是个高中生的时候就已经开始自学了,即使已经反复自学多次,即使大学必修课轻松拿下100分,但是直到去年我依然可以算是怀有“c++恐惧症”,我敢自信地说我是个多语言全栈研发工程师,但我丝毫不敢说我“会写”c++ 。
直到今年中旬,基于一个AI语音项目的契机,在AI编程插件的帮助下,我终于完成了第一个完整的、比较大的、商业运营的项目,终于敢说“会写”了。这令我十分高兴。
对我而言,c/c++的难点在于:
第一,语法晦涩。同一功能有多种写法,标准库和商业SDK很可能使用不同范式,左值右值、类型转换等语法细节众多,对于即使是多年工作经验的资深工程师来说也足够头皮发麻。还有宏、模板等功能更是看不懂的重灾区。
第二,指针管理。“没有内存回收”听起来就少这么一个特性,但对编程难度的影响太大了,特别是在业务变复杂以后更是容易出错。对RAII、std::move等概念和工具需要认真学习理解。
第三,工具链难用。之前我一直分不清各个工具的作用、替代品和分工。还有动态连接、编译等其他语言没有的问题。
第四,并发管理。这是golang的伟大之处,确实解决了巨大的痛点。
不过,幸运的是,在现在这个年代,在IDE和AI的帮助下,我终于顺利地把上述难点逐个击破,虽然不敢说精通,但说个会用还是没问题的。终于解决了我多年的一个心病。
罗列一下,我现在“会用”的语言有:
- Typescript
- golang
- python
- c++
- c#
还缺点啥?接下来再看看Kotlin和安卓吧。或者继续玩c和单片机?
羽毛球之路
在去年的一篇博客中,我提到我跟老板一起打过一次羽毛球,那时感慨,我打球根本不在乎输赢,只是偶尔锻炼一下身体。
今年实在苦于失眠,痛定思痛之下,我从7月初开始,每隔一两天早上7点打羽毛球,打完了再洗澡上班,至今已经持续了三个多月了。
这次,我就想赢。我从7月初自评中羽2级,到现在自评中羽3.5+级,固然我算是有些底子,但我这进步之明显想必我的球友们应该都是有目共睹的。从三个月前我跑步300米就双腿灌铅,到现在我连续打4局双打还能意犹未尽,体力和技术都有了明显的进步。另一个更加客观的成绩是:我的体重也减去了大约15斤。
这个成绩我觉得足以自豪。而且身体耐力的改善对于个人日常精力也是有巨大提升的。运动所带来的大量多巴胺也是支撑我心态的重要力量。
稍微扯远一点。对于所谓的“中羽等级”,因为没有专业考试和认证,在业余爱好者之中一直无法达成一个共同的标准。我也在此分享一下我的看法:
- 0级:身体很不协调,跑动击球看着非常别扭,感觉随时要摔跤或者扭伤;
- 1级:身体素质尚可,有合格的装备(拍、鞋),进入球馆打球;
- 2级:了解过技术动作但是不会,身体条件比较好,能跟同级别的人打得有来有回的;
- 2.5级:大多数业余球友的门槛,练了一些技术,但是一直打不好高远球;
- 3级:掌握高远球(准确说是最核心的内旋、鞭打、蹬转),以及附带的蹬转杀球;
- 4级:会用所有的技术动作(包括全场步伐、劈滑)但有失误,反手后场能打到中后场,有球路意识,能打多拍;
- 5级:熟练运用所有技术动作,较少失误,比赛已经有一定观赏性
- 6/7/8级:更快更猛更巧更准,参加市/省/国级比赛拿名次
用更简单说法概括一下:
- 0级:打不到球
- 1级:能打到球
- 2级:能把球打回去
- 3级:知道为什么能把球打回去
- 4级:通过长期认真的训练
- 5级:业余高手
此外我还觉得必须要说明的是,前几级评级标准都是以技术动作为准的,并不完全对等于比赛实力,输赢并不能直接作为等级评价的标准。
实力差距在哪
羽毛球还引发我另一个思考:为什么世界顶尖的羽毛球运动员,在比赛时依然看起来慢慢悠悠地“打太极球”,大多数技术动作看起来即使是经过训练的业余普通人也能做到?那么业余、专业和顶尖运动员差距到底在哪里?
道理并不难,但只有认真训练过的人才有机会亲身体会。
就以最基本的“正手高远球”为例。这个动作是“中羽三级”水平的重要标志,看起来也并不难,两个人站在后场对拉底线到底线,要求又高又远,虽然是大多数业余球友进阶路上一道巨大的门槛,但依然有大量的球友是可以通过学习和训练掌握的。那么问题来了,同样是高远球,以下两种情况:
- 情况一:你吃饱睡足刚上场,正在跟朋友热身,你站在底线摆好架势,等着球飞过来再做标准动作打出一个高远球;
- 情况二:在激烈的单打对抗中你已经精疲力竭了,此时对手做一个假动作骗到你,等你意识到时球已经飞过你的头顶,你不得不赶紧后退、后仰着身体勉强打出一个高远球;
我想即使是完全不懂羽毛球的外行也能看出,后者肯定无法发挥自己的实力。除非这个人平时只要50%的力量就能打出一个合格的高远球,那么他才有机会在如此被动的条件下依然打出一个合格的高远球。而如果对于绝大多数普通人来说,每次高远球都要100%的力量,那么在被动时打出的高远球质量一定不合格,这会让对手抓到机会,对手的下一拍可能就会有更大的威胁,这一个回合此消彼长之间就给自己造成了劣势。这就是不同级别羽毛球水平差距的关键。
很多领域都是与之类似的。比如编程。想向上发展的程序员们,肯定都是有基本功的,例如设计模式等一些思想理论肯定都是知道的,甚至很多人也意识到要重点提升这方面的能力。然而,有多少人能在真实业务中将先进理论实现落地呢?——以我的经验来看,可谓寥寥无几。能在繁杂的业务中,看透表面的业务表达,准确把握核心逻辑,选用合适的理论并以恰当的方式融入已有的项目中,打造坚固的代码框架或者说所谓架构,这种核心能力就类似于专业运动员与业余球友之间的差别。
要提升这种核心能力,必不可少同时也是触手可及的方法是:刻意训练。
业余打球往往是双打居多。平时在打球的时候,有时我会被队友说:“你这招的失误率有点大,不如把动作做小一点,把球稳定打过去再说”。这其实反应了两种不同的心态:“比赛心态”和“训练心态”。前者代表这看重比赛的结果,重视得分,在比赛时的策略是典型的扬长避短,尽量避免暴露自己的弱点,尤其是当掌握了一招杀手锏之后,便常常依赖这招杀手锏得分。而后者则完全相反,不关心比赛的输赢,而重视自己是否通过一局游戏训练了自己的技术,在比赛中往往主动暴露弱点让对手来进攻。虽然前者可能在短时间内经常赢,但长期来看,还得是后者的成长速度和上限更高。
当然,成长速度更快的是第三种人,平时很少打“比赛”,而是找朋友或者花钱请教练,用绝大多数打球时间刻意训练某一两项技术直到熟练,然后训练下一个技术,只偶尔比赛用于验证或者融会贯通。
写代码又何尝不是如此呢?第一种人,也可以说是“沉浸在舒适区内”,当熟练掌握某一两项技术框架后,长期用已经掌握的技术去完成业务迭代需求,短时间内看起来做得又快又好,但从长远来看,必然是不如第二种人,也就是主动学习、尝试和总结新技术、新思想的人,后者也许短时间内产出差一些,但等到机会来临,只有后者才能有机会突破瓶颈,开创新的业务领域方向。