MoreRSS

site iconNekonull | 卢之睿修改

学生、程序员、设计师,腾讯和商汤实习。
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

Nekonull | 卢之睿的 RSS 预览

Today I Learned - 更简单更频繁的分享

2025-03-30 21:00:34

对我来说,写文章其实是心理门槛挺高的一件事,会觉得得对某个事物充分了解,完全掌握了,才有动力去下笔。(虽然大概这里的文章并没有达到这样一个状态。)但也有时候,我见到了一个有趣或者有用的事物,可能是一篇论文、一个项目、一条视频,或者只是简单的一个想法,会有分享的欲望。为此写文章有些太大动干戈了,但是不将他们分享出去又有些可惜。现实生活里我有一个小群来分享这些东西,但我认为它们值得被更多人看到。

因此,在我经常阅读的另外两位创作者 Simon Willision 和 Julia Evans 的启发下,去年年底我创建了一个名为 TIL (Today I Learned) 的分类。形态上类似于微博或者 Reddit,基本上都是一个链接 + 一些简短的介绍,意图在于让读者快速了解这些事物是否对 ta 们有帮助,并将流量引导到原作者。

但创建这个分类之后,我发现自己的分享频率并没有显著上升。后来我意识到,虽然有了单独的分类,但是发布行为本身并没有简化。即使我只是想快速分享一个链接,我依然要采用和正式文章完全相同的方式:在编辑器里新建一个 Markdown 文件,写 front matter 和文章正文,预览,推送到 Github 仓库。如果这个新分类的定位类似于微博,那为什么不能像真的发微博那样简单呢?

这个月初,我调整了下 TIL 的发布方式,做法类似于 headless CMS。具体而言,在某个地址有一个简单的页面,里面有一些基本字段(标题、URL)和一个 Markdown 编辑器,还有一个“发布”按钮。在我填充完基本信息,点击“发布”之后,会触发一个云函数,读取请求,调用 Github API 完成 Repo 内新文件的写入。虽然技术上来看这并不复杂,但却极大减少了发布的心理负担。现在当我想分享的时候,只要打开这个页面,快速把内心的感受 dump 进去,点击发布,就算完成了。

当然,考虑到 TIL 形式的分享贴所含的信息量更低,不能排除正常读者会被打扰的可能性。为此,我修改了 Hugo 的 RSS 模板,将 TIL 和正常文章拆分成了两个 RSS 源。TIL 的分享只会在单独的 TIL 订阅源中出现,以确保读者只有在明确想收到这些分享的时候才会收到。(这也是从 Simon Willison 那里学到的,他有一个名为 atom-everything 的订阅入口。)

最后是一些相关链接。希望你也能发现我的 TIL 对你有帮助。感谢阅读!

code2prompt - 满足「这是怎么做到的」好奇心的小工具

2025-03-30 15:57:05

我发现自己经常对着一个有趣的 Github Repo 思考:「这看起来真不错!但是究竟是怎么做到的?」。但很多时候我无法满足我的好奇心。要么是这个项目并没有自带一个「How it works / 工作原理」的介绍(在我看来这应该是每个项目 README 的必备),要么是我没有时间或者耐心在层层叠叠的文件树或者错综复杂的调用链里自己找到答案。得益于近来 LLM 的上下文窗口增长,让 LLM 来帮我回答这一问题似乎是个不错的选择。即使 LLM 回答给出的答案可能有错误或者不完整,但至少能给出一些探索方向,让我自己的理解更有效率。然而在通常的对话窗口里复制粘贴代码有些烦人,更别提无法在文件数多的项目里应用了。为此,我(让 LLM)帮我写了一个小工具,让我能更方便的从代码生成发送给 LLM 的 prompt。

如何使用

具体使用起来很简单:

  1. 在GitHub项目页面,点击右上角"Code"→“Download Zip"下载代码
  2. 打开工具页面,点击"选择文件"并上传刚下载的zip包
  3. 根据文件数量或token大小进行筛选(比如排除test/、vendor/或package-lock.json等无关文件)
  4. 点击"Generate"生成提示词
  5. 点击"Copy"复制到剪贴板,然后粘贴到你喜欢的LLM对话工具中

还有一些其他特性:

  1. 文件树可以一次选中/取消选中整个文件夹
  2. 每个文件旁标记了该文件的大小和 token 预估
  3. 点击文件,可以直接展示该文件的内容(monaco editor)

实现原理

  1. 解析zip文件(jszip 库)
  2. 计算每个文件的token占用(用的是 gpt-tokenizer,基于 cl100k 计算,也就是 gpt-3.5-turbo 的分词器)
  3. 展示文件列表和占用空间最大的文件,并根据选择更新
  4. 最后用文件内容填充预设的提示模板

使用技巧

  • 模型:用上下文足够长的模型,注意上下文限制;如果有可能的话用带上下文缓存的提供商
    • 比较推荐的模型
      • Gemini 2.0 Flash Lite:输入 ¥0.55/1m, 输出 ¥2.19/1m, 上下文 1m
      • qwen-turbo:输入 ¥0.3/1m, 输出 ¥0.6/1m out, 上下文 1m
    • 如果最后得到的 token 数少,再考虑 deepseek-v3(官方上下文 64k,部分提供商 128k)等模型
  • 筛选:根据自己的需要提前删掉不需要的文件
    • 二进制文件已经预先被筛掉了,不会出现在文件树里
    • test / vendor / docker 相关的通常可以做排除
    • 如果只看后端,则可以把前端的全排除掉
  • 提示词:建议加在输入的最后,避免模型丢失焦点
    • 首次阅读:简要说明代码做了什么, 以及是如何做到的。列出核心函数所在的位置。
    • 理解流程:用 mermaid sequence diagram 说明 XXX 的完整流程
    • 分析结构:这个项目主要有哪些组成部分?用 mermaid flowchart 展示

开发

  • 大部分都是 LLM 写的(Windsurf + Claude),人主要是测试和提出修改意见。
  • 目前并没有用任何前端框架,都是裸 DOM 操作。(主要原因是没找到一个比较好的不需要构建过程的前端框架使用方式)。
  • 一开始的时候只是一个简单的单页面(所有代码都在 index.html)里的小应用,但随着复杂度上升和功能增加不得不做重构,拆分多个文件(不然的话编辑操作会因为影响行数过多常常失败)。未来可能一开始就考虑多文件架构会比较合适。
  • 目前每次 LLM 改完之后,得靠人手动操作下来验证是否有功能被破坏,有点麻烦。要是能有更自动化的 E2E 测试(例如基于 Playwright)就好了。

可能的后续方向

  • 直接从 Github 加载:目前直连 Github 下载 zip 会有 CORS 问题,需要一个后端来代理下(但是就引入额外的运行成本了;我还是更喜欢纯前端而不依赖任何后端的工具)
  • 预设提示词:提供几个常用提示模板方便一键使用
  • 本地文件夹支持:直接加载本地项目文件夹
  • gitignore支持:自动忽略.gitignore中指定的文件

其他方案对比

  • Cursor / Windsurf:需要依赖编辑器自己去读取文件,慢而且可能会缺失文件
  • repofix:开发之后才关注到,前端缺少控制能力,用起来不太方便
  • files-to-prompt:纯命令行工具,对我这种临时性探索稍微有点繁琐(但是用在其他工作流里可能比较合适)

The drive to develop

2025-03-03 23:23:31

这是一期杂谈,没什么干货。

“The drive to develop” 是早年间喷气式大脑(aka JetBrains)的一句宣传语。虽然目前无论是工作还是自己的项目里我用的都是 VSCode,但是毕竟大学编程的一大半时间都是在 JetBrains 家的各种 IDE 中度过的(毕竟有学生免费),所以对它家印象还是比较深的。第一次见到这句宣传语应该是在某次 ICPC 的直播上(当时 JetBrains 是官方赞助商之一),感觉直击心灵。在学校里的时候虽然有各种课程,但也有一些有趣的课程项目,再加上自己也会写点自己的东西,所以的确是有这种编程的动力的。这样的动力在工作后甚至也延伸了一段时间,上班的时候写公司的代码,下班之后忙自己的项目。

可惜这样的动力并没有延伸太久。随着工作年限增长,工作上要处理的事务也越来越多,虽然也写代码,但是写代码的时间并不是工作时间的大头,因为还有无数的关联任务需要完成(设计文档、代码评审、发布、告警处理…)。即使是到了周末,也会因为周中的工作几乎耗尽了精力,没有动力坐回电脑前写点什么;残存的精力已经只够我在床上躺着刷手机了,各种个人项目也因此停滞了很久。

但各类 AI 代码编辑器(更准确地说,是 LLM-assisted programming?)的出现改变了这一切。虽然 ChatGPT 早在 22 年底就发布了,但是我自己亲自上手体验已经是 23 年初。当时还是刀耕火种的时代,需要自己从网页里复制代码到 IDE 里。后来 Github Copliot 有了对话模式,可以稍微做一些交互式的任务了,但是因为没法自动 apply changes,最多只能 insert,体验还是不太行。再后来试用了 Cursor,第一次体会到了什么叫做 “AI-Native” 的编辑体验,变更大部分时间都可以直接 Apply 进去,不用再自己一个个找了;但是用起来还是感觉有点迟滞,例如因为索引还不太好使,很多时候都得手动指定某一个特定的文件加入上下文才能有比较好的效果。最近我在使用的是 Windsurf,起初不是很习惯(生成过程中看不到进度,和 Cursor 不同),但后来才发现了其 agent 能力的真正作用,多文件修改、代码库索引,再结合自带的联网搜索,真正做到了指哪打哪,信手拈来。

之前之所以空闲时间不太想写代码,并不是因为没有想法,而是虽然有想法,但是具体怎么实现的思路依然很模糊,而且中途还有高度的不确定性和时间成本,例如有的时候配环境就因为一些莫名其妙的依赖调试了两三个小时,或者是因为某一个感觉上很直接但是就是写不对的函数 rage quit。但是有了 LLM 之后,我可以直接对着编辑器说:去,把这个功能给我做出来,然后看着 agent 吭呲吭呲尝试各种方法,自己调试问题。虽然偶尔还是要人介入,但是心态上会好很多,感觉自己仿佛什么都能做。在我最近的一个小项目里,我给一个 Rust 的库实现了一个 web playground link 。虽然我知道步骤是先写一个 wrapper,然后编译 wasm,再写点 js 去调,但是里面有很多细节我是不知道的(需要装什么工具,怎么编译)或者是不想手动做的(例如写胶水前端代码)。于是我把我的思路告诉 LLM,一个小时之后我就有了一个能用的 demo。(其中甚至还包括至少二十分钟在等 rust toolchain 和 wasmpack 编译)这样的效率是之前自己想都不敢想的。

但是我也注意到,AI 代码编辑器的出现,只是帮助让有想法的人走的更快更远;如果一开始就没有想法,即使有这样的工具也不会去用。以我自己为例,AI 音乐/图片/视频生成已经实用化很长时间了,也有各类免费的服务可供选用,但是我却提不太起去用它们创作些什么的兴趣,也没有什么想法。由此推知,可能也有其他人心中有不少想让 AI 画出来的画面,有 the drive to draw,但是面对空白的编辑器窗口却不知道应该发出什么指令,帮助自己做什么事情吧。

最后,无论目标为何,希望大家都能找到自己趁手的工具,完成自己想做的事。

游戏推荐 - A Short Hike

2025-02-04 21:07:26

我很少主动玩游戏。虽然有时会看其他人的游玩/剧情视频,但是自己实际上手玩的几乎没有。可能是小学的时候玩得足够多,长大之后反而对游戏失去了兴趣。我不想在竞技类游戏里比拼分数,也不想在策略类游戏里消耗脑力,更不想在吃操作的游戏里磨炼键位。日常生活已经够累了,何必再在游戏里自寻烦恼呢?但是 A Short Hike 让我体会到了不一样的感受。这是一个玩起来放松、甚至治愈的游戏。(用番剧类比的话,就像《摇曳露营》的第一季。)它让我如此惊喜,以至我甚至愿意专门写这篇短文来推荐。

为什么它能有这样的神奇效果呢?我没法指出一个确切的原因,但是以下这些优点可能都有帮助。

  • 主线直接、支线丰富:主线剧情很简单,正如其名,爬到山顶就完成了,一般 2h 内就可以完成。但主线之外,还有各种各样的支线任务(钓鱼、划船、赛跑…),即使完成了主线,到了 end game 阶段也不会感到空虚;现在我依然会时不时打开,让自己放空一会。
  • 世界小、密度大:虽然游戏内的世界并不算大(一座大岛 + 几座小岛),但是地图设计很精致,跑图探索的时候会感觉每个方向都有事做。另外地图是完全开放的,想去哪就去哪,完全自由,不像一些开放世界游戏,需要完成前置任务或者是满足前置条件,才能解锁接下来的区域。
  • 操作友好、上手简单:作为游戏苦手,操作不是我的强项,但游戏内实际上只需要方向键移动和两个动作键(Z爬墙/滑翔,X操作工具),即使是很少玩游戏的我也能轻易上手;难度的渐进(通过耐力条金羽毛控制)也很合理,正常流程基本不会遇到卡关的情况,甚至还能探索出很多非预期解法;滑翔的体验非常棒,每次都让人意犹未尽。
  • 交互机制精妙:这里的交互指的是游戏内玩家和游戏世界的交互,而不是指用输入设备操作游戏;游戏内有一些特殊的工具(例如水桶、铲子)可以在多种场景下使用,而且非常直观,你觉得应该能行的操作很多时候还真能行。(甚至还有隐藏成就)
  • 对话有趣、角色立体:游戏没有配音,只有文字对话,且语句都很简短;但因为对话写的好,无论是主角,还是游戏里的其他 NPC 的形象都很立体,并不会觉得是普通的可以被任意替换的角色;甚至还有剧情弧光,一开始以为的坏人后来发现也有苦衷。
  • 画风独特、色彩明亮:用文字描述起来有些困难,去 Steam 页面看看预览图就懂了;像素大小可以根据自己的需要调整。
  • 音乐宁静:不同地区有自己的 BGM,而且是动态分层的,随着剧情进展会有变化;OST 已经加入我的歌单。

虽然优点很多,但是依然有一些小小的缺憾:

  • 固定视角:游戏内方向键只能操作主角的移动,视角方向是游戏自动切换的;虽然大部分情况下都很合理,但是有的时候自动切换会非常突兀。(明明移动方向不变,但是因为视角切换了,所以方向键输入也需要切换)
  • 没有游戏内地图:虽然玩的多会形成心理地图,但是还是会时不时迷路。

总评:9/10 (神作)

  • 一个适合于所有人的游戏

相关链接:

读书记录《悟道领域驱动设计》

2025-01-05 23:38:12

  • ch2 应用架构
    • 贫血模型 vs 充血模型
      • 贫血模型:类只是数据容器,没有行为(例如 pojo)
      • 充血模型:类有属性(数据),也有方法(行为)
    • 项目结构
      • 接口层:对外暴露api/消息consumer
      • 应用服务层:协调领域模型完成业务逻辑
      • 基础设施层:写db/缓存/调外部服务
      • 领域层:领域内的具体逻辑
    • 查询的两种实现
      • 1 读数据后加载领域模型(聚合根),聚合根转换为查询结果 view
      • 2 直接用实际存储的数据模型,跳过领域对象加载,直接用把数据模型转换为查询结果 view
  • ch3 实体和值对象
    • 实体:有唯一标识的领域模型
      • e.g. 内容发布系统的文章
    • 值对象:没有唯一标识,需要的时候随时构造,值一样就认为是相同的对象
      • e.g. 内容发布系统的文章标题
      • DP:Domain Primitive 领域内的基本数据类型,值对象的一种
        • e.g. 金融系统中的金额(money)
  • ch4 聚合与聚合根
    • 聚合:一组对象组成的对象树
      • 单个实体也是聚合
      • 聚合是一致性的边界;聚合内强一致,跨聚合最终一致
        • 一个事务只更新单个聚合
    • 聚合根:这个对象树的入口
      • 单个实体也可以是聚合根
      • 外部对象只能引用聚合根(通常通过持有一个id)
    • 拆分聚合
      • 一个实体被多个聚合根引用,应该被提升为聚合根
      • 1:N 的集合属性(Role-Resource Binding),因为存在双向查找的需要(Role找Resource,Resource找Role),通常也可能被提升为聚合根
  • ch5 Factory, Repo, 领域服务
    • Factory:从无到有创建领域对象
      • 需要分配 id
      • 通常用 builder 模式
    • Repo:保存和加载聚合根,和实际的存储解耦
      • save:聚合根持久化到数据库
        • 注意事务控制,所有实际的写操作应该在一个事务内执行(例如文章写一个表,文章内容写另一个表)
      • load:从数据库查询数据对象,组装回聚合根
      • 推荐实现行级 repo,一个聚合根对应数据库中一行数据
        • 如果存在查多行的需求,如 queryList,应该用 CQRS 分离出去
    • 领域服务:包含领域中不适合放在实体/值对象的业务操作,应该是无状态的
      • 例子:导出数据为 excel
  • ch6 设计模式
    • 责任链:多个handler逐个执行
    • 策略:从多种算法实现中选择一个(根据业务类型匹配到策略)
    • 桥接
    • 规约:验证复杂规则
  • ch7 防腐层 ACL
    • 避免外部系统变更影响到当前系统(字段名变化、包名变化…)
    • 实现:适配器模式
      • 出入参数应该是本地值对象,或者基本数据类型
      • 外部异常/错误码应该转换为本地异常/错误码
      • 只返回实际需要的字段
      • 一般在应用服务/领域服务调用防腐层,不要在实体和值对象使用
  • ch8 领域事件
    • 幂等实现:数据库唯一索引 / 状态机
    • 事件建模:用领域语言(例如账户被激活);建模为值对象/贫血对象(不可变的)
    • 应用
      • 触发其他领域/聚合的行为
      • 记录状态变化
    • 事件消息体:实体id,事件id,事件类型,发生事件
      • 也可选包含具体数据,减少查询需要,例如用户变更手机号事件带上新的手机号
    • 生成事件
      • 应用层创建:推荐,直接在应用层生成,然后调用基础服务发布
      • 聚合根创建:聚合根内生成事件,存储在聚合根内一个临时位置,应用层调用方法从聚合根取得,然后调用基础服务进行发布
    • 发布事件
      • 问题:保存聚合根和发布领域事件应该是一个事务,不能一个失败一个成功;但是引入分布式事务会造成复杂度上升
      • 解决:用一个db里事件表,repo save的时候不仅写聚合根,还写事件表;然后用事件表变更触发mq
        • 轮询补偿:一个外部定时任务,检查事件表中状态=未发布的事件,读出来发mq,然后更新状态为已发布
        • 拖尾:用一个db拖尾组件监听db变更,自动发布到mq
    • 订阅事件:事件 consumer 作为一个新的接口层,调用应用层服务
      • 注意幂等
  • ch9 CQRS
    • 问题:查询的时候加载聚合根可能没必要(例如只读取一部分字段);但是修改的时候必须要加载完整聚合根
    • CQRS 应用层分为 查询 query 和 修改 command 两部分,分别用不同的模型处理
      • command 依然加载完整聚合根
      • query 直接用数据模型(db数据)进行查询,转换为需要的返回 view
    • 实现
      • 同数据源:比较简单,query 不用 repo.load 而是直接读db
      • 异数据源:复杂,可能导致不一致
        • 需求:主数据存储 mysql,用 es 做文本搜索
        • 实现:应用层发布领域事件,db日志拖尾
        • 查询可以是一个贫血模型,因为没有复杂逻辑且只读数据不修改
    • 缺点:复杂度高、可能导致数据一致性问题、增加学习成本
  • ch10 事件溯源
    • 想法:存储领域事件,读取时通过重放领域事件得到最新聚合状态
      • 优点:完整业务跟踪能力;可以回滚到任意时刻的聚合根
    • 实现1:最原始的实现,直接存领域事件,读时回放
      • 只有一个事件表
    • 实现2:事件多查起来慢,因此加快照,回放时用 上次快照 + 上次快照后的事件
      • 事件表 + 快照表,save的时候可能需要生成快照
    • 实现3:快照表用拉链表实现,存储所有事件+聚合根所有版本(含有效期)
      • 拉链表:含有开始时间和结束时间,表明这一行数据在此时间段内有效
  • ch11 一致性
    • 聚合内一致性
      • 事务应该在 repo 实现,不应该在应用层实现,否则会造成事务过大
      • 用乐观锁避免并发更新问题
      • repo save 失败,需要在应用层重试,来确保聚合数据是最新状态(即重新 load)
        • 因为会重新调用外部接口,依赖的外部接口应该幂等
        • 不适合频繁更新的热点数据(可能导致频繁重试)
        • 重试次数规划好,一般一次就够了,多次的话应该考虑其他架构
        • 重试次数/触发原因可配置,一般只有乐观锁失败再重试,其他错误不应该重试
      • 读写性能问题
        • 一般业务做不到那么大
        • 读多写少:读写分离、缓存、复杂查询维护单独的读数据源、分库分表
        • 写之前的确需要完整加载聚合,写慢点就忍吧
    • 跨聚合一致性:实际上是分布式事务
      • 二阶段提交:prepare, commit;commit 可能失败,此时rollback
        • 代价比较大
      • 本地消息表+发布领域事件:见 ch8
      • 最大努力通知:上游不断发起通知调用给下游,直到下游确认
        • 适用于对可靠性要求不高
        • 发起者需要提供查单接口,供接收者主动查询状态
        • 发起者需要实现重复通知,接收者自己保证幂等
        • 发通知间隔应该指数退避,且限制最大次数,避免无限发送
      • TCC:try, confirm, cancel;try成功了confirm必须成功
        • 注意点1 幂等:confirm/cancel幂等
        • 注意点2 空回滚:没有调用try就调用了cancel;收到cancel查资源状态,没try过应该直接返回
        • 注意点3 事务悬挂:先执行cancel再执行try;收到try查资源状态,被cancel过也直接返回
      • saga:正向perform,补偿compensate
        • 注意点1 隔离性:正向操作完成后,其他事务已经能观察到了,负向回滚之后可能影响到其他事务(用户看到订单消失)
          • 考虑引入一个中间状态例如 pending
        • 注意点2 幂等:perform/compensate 都应该幂等
        • 注意点3 空回滚:没有perform就compensate;补偿前用业务主键查是否有perform,没有直接返回,并记录已回滚
        • 注意点4 事务悬挂:先compensate再perform;perform前查询是否compensate过,有则报错
      • 选型
        • 不要求实时,只要求最终一致:本地消息表、最大努力通知
        • 要求实时:TCC
        • 长事务、涉及外部/遗留系统:saga
  • ch12 战略设计
    • 概念
      • 限界上下文:一个特定业务内的概念、规则、流程
        • 如电商系统中的订单、商品、营销、物流
      • 上下文映射:不同限界上下文之间的协作关系
      • 子域:关联性强的限界上下文形成的大的业务概念
        • 主播+直播 -> 视频直播子域
    • 划分限界上下文:按照业务边界
    • 上下文映射
      • 共享内核:存在共享的代码、领域模型、基础设施等
      • 客户 供应商:客户(下游)给供应商(上游)提要求,如加接口、加字段
      • 跟随者:上游不响应下游要求,下游得自己做
      • 各行其道:上下游完全不关联
    • 子域类型
      • 核心子域:业务系统最重要的部分,业务价值高
      • 支撑子域:起到支撑作用,但是没有成熟/通用方案,需要自己构建
      • 通用子域:有通用性,存在成熟/通用方案,可以通过采购/开源获得
  • ch13 领域建模
    • 事件风暴法:收集所有领域事件,归纳领域模型
    • 收集内容
      • 领域事件
      • 命令:触发领域事件
      • actor:命令的人为发起者
      • 策略:命令的规则发起者,满足某种条件自动触发;定时任务也算
      • 外部系统:也是命令的发起者
      • 聚合
      • 读模型:actor 发起命令前读的数据(例如审核员查看内容),需要这些数据辅助决策
      • 热点:待定问题
    • 建模流程
      • 列举领域事件
      • 按业务流程排序领域事件;无法连接的说明可能遗漏了
      • 补充命令
      • 补充发起者
      • 提取聚合:同一个聚合的领域事件归类到一起
      • 补充读模型
      • 划分限界上下文,标注映射关系;注意需要标记上下游
      • 划分子域
  • ch14 研发效能
    • maven 脚手架
    • 响应封装 graceful response
    • 对象转换 mapstruct
    • 静态分析
    • 低代码
    • 持续集成/持续交付/持续部署
  • ch15 测试驱动开发 tdd
    • 红绿循环:写测试、测试失败、写代码、测试通过、重构、测试通过
    • 贫血模式的 tdd
      • dao:生成的,一般不用测
      • service:对基础设施的调用应该 mock
      • controller:mockmvc 直接测试 http 请求,验证响应(如返回码)
    • ddd 中的 tdd
      • 实体:测全分支;不应该依赖启动容器和基础设施
      • 值对象:覆盖业务规则
      • factory
  • ch16 敏捷开发
    • scrum
    • 看板
  • ch17 架构可视化
    • c4 模型
      • 系统上下文图:只展示核心系统、支持元素(如外部依赖、用户)
      • 容器图:展示主要数据选型和个容器的职责分工
        • 容器:可独立运行/部署的单元(如后台单体、缓存、数据库)
      • 组件图:展示可执行容器内部分工,指导开发
      • 代码图:UML/ER图,不推荐画(变更频繁)
    • 其他图
      • 系统全景图:展示关联的所有系统
      • 动态图:展示元素在运行时如何协作,用箭头和编号表示顺序
      • 部署图:说明部署方案,含有实例数量、机房等
  • ch18 重构
    • 模式
      • 修缮者:实现新方法,保留老方法,用开关切换,无问题后删除老方法
      • 绞杀者:设计新系统,用一个门面承接流量,逐渐在新系统实现功能,并切老系统的流量到新系统,直到老系统完全无流量
      • 推翻重建:彻底放弃老系统
    • 流程
      • 启动
        • 必要性评估:考虑性能、可靠性、技术栈、业务支持、研发效率、运营效率
        • 环境因素:企业战略、组织架构稳定性、文化氛围、管理风格
        • 效益和风险分析
        • 可行性分析:天时(和企业战略一致)、地利(已具备环境条件)、人和(团队愿意支持思想一致)
        • 干系人识别:团队成员、管理者、职能部门负责人、最终用户
      • 规划
        • 确认重构范围
        • 工作任务分解:任务分给唯一的某个人完成,可以向其他组员寻求帮助,但是负责人只有一个
        • 工期估算和进度计划
        • 沟通计划:管理层、项目成员、外部团队
        • 人力资源计划:人力不足需要申请人力、提前培训
        • 质量管理计划:业务流程梳理用力、数据一致性对比
      • 执行
        • 组建团队
        • 梳理现有业务逻辑:读代码、读历史文档、头脑风暴
        • 整理用例并评审
        • 实施开发:DDD、敏捷、测试驱动、CICD
        • 数据迁移
        • 灰度切量
        • 老系统下线:老系统可能还有调用者
      • 监控
        • 进度
        • 质量
        • 待办项
      • 收尾
        • 沉淀过程资产:架构图、FAQ、接口文档
        • 推动新系统普及:通知调用方
    • 数据迁移的实现
      • 方案1:双写
        • 步骤1:老系统开始写入新数据源;新系统需要支持写老数据源
        • 步骤2:历史数据从老数据源全量迁移到新数据源;检查新老数据一致性
        • 步骤3:读验证,灰度部分读流量到新系统
        • 步骤4:写验证,灰度部分写流量到新系统(新系统依然写老数据源,且定期校验)
        • 步骤5:全部流量切换到新系统,新系统双写老数据源
        • 步骤6:新系统稳定后,关闭新系统写老数据源开关,老系统下线
      • 方案2:双向数据同步
        • 正向:老->新:传输老系统的所有数据
        • 反向:新->老:只传输新系统生成的数据(对来自老系统的数据有特殊标记)
        • 步骤:正向链路一直打开,全量数据从老系统迁移到新系统,且保证增量同步;写验证前打开反向链路,直到老系统下线
  • ch19 布道领域驱动设计
    • 编码指南

2024 年末杂谈

2025-01-01 15:57:31

其实本来昨晚就想写了,但是因为工作安排需要线上值班,所以今天才动笔。2024 终于平安结束了,希望 2025 年也能一切顺利吧。

回顾 2024

  • 工作
    • 继续干着,职级正常升了一级;不过后续再往上升估计就比较慢了,得慢慢熬
    • 做了一些更复杂的工作,还有几个大的跨部门项目
    • 前半年被迫在一个不熟悉的领域干了很久,好在后半年回到了原来的业务
    • 出了一个事故,好在影响不大;但是更提醒自己得谨慎再谨慎了
    • 总体而言没有太大变化,不具体展开了
  • 学习 & 阅读
    • 年中考过了软考高级系统架构师;然而除了拿了一个实体证书,目前还没有起到什么作用
    • 年底去考了 N2,不过实际上也没怎么学习,几乎是裸考了,应该是不可能过的
    • 今年读了 7 本书,但是主要都是技术类的,非技术类的读的不多;印象深刻的是 《软件设计的哲学》(link),把一些工作上隐约能感受到但是想不明白的点给理清楚了
  • 开源 & 写作 & 探索
    • 更新了 12 篇博客,入选了 2 次阮老师的周刊
    • 开源项目 mainonly 在 Github 上有 172 个 star,也成为了自己日常不可或缺的工具
    • 书签增加了 326 条;新增了基于 LLM 的自动摘要 workflow ,未来找起来会方便一些
    • 开了一个微信公众号,把部分内容同步了过去;甚至收到了一位读者的赞赏,虽然金额不大,但还是很开心
    • Advent Of Code 2024:49/50,24p2 放弃了;发现自己对这类项目容易上头,很容易把下班后的时间全放在解题上了,还是心态不太行
    • LLM 相关
      • 尝试完全把 LLM 融入工作流,写了一些自用的小工具
      • Cursor 开发起来还是很爽的,模板类代码都可以代劳了,有足够好的注释/变量名生成的自动补全也非常靠谱,但是遇到一些复杂的逻辑问题时还是会卡住(可能是我用的模型能力问题?)
      • 不同的模型用多了会感觉有不同的性格,GPT 就比较简洁直接,Claude 虽然想得多但是有的时候会把方案做的太复杂;deepseek 就比较平衡,而且最重要的是便宜大碗,用起来不心疼
  • 生活
    • 今年出去旅游了 2 次:4 月清明假期去上海见了高中和大学同学,8 月去惠州海边试了下躺平生活;然而总体还是没有太多出门的动力
    • 建立了工作日中午运动的习惯:一般就是在公司健身房,边踩椭圆机边补番,一次25min,约350kcal;运动完用 app 打下卡,还是挺有成就感的;看了下数据今年有 162 次运动记录,大概是工作日的 80%
    • 从家里搬出来住:10 月底搬到了一个离公司比较近(一站地铁,可以步行)的位置,虽然新增了房租的开销,但是因为几乎没有通勤问题,生活质量极大提升,总体还是很值得的
    • 更频繁的约饭:基本维持了一个月约一次饭的频率,不至于太频繁让人喘不过气,也不会太久以至于忘记之前聊了啥;对维持精神稳定很有帮助
    • 更多文字记录:工作上用 Obsidien 写任务笔记,基本没有再遗漏过细节了,效率极大提升;尝到甜头之后开始在生活里也记录更多东西,无论是每次约饭聊了啥,还是每周有什么有趣/有意思的事情。如果不记录,很快就忘记发生过什么了
  • 投资
    • 今年开了股票账户;9月底的大涨没敢投入太多,因此也没赚多少,但好在最后没亏;实际体验过之后发现这种快进快出的风格还是不太适合自己,更习惯于一次买入就长久放着
    • 总体年化收益率 2.3%,按收益率排行:公司股票 > A股 > 美元存款 > 人民币货币基金
    • 被动收入(低风险无需操作的投资收益)基本达成了目标,一年的收益足够在房租低的城市完全躺平一年,但是安全边际还是太低了,还得努力提高投资水平

展望 2025

  • 工作
    • 希望能不出事故,继续干下去
    • 探索其他的工作机会(私企 & 体制内),考虑后路
  • 技术
    • 用 LLM 学更多
    • 用 LLM 构建更多
  • 生活
    • 继续保持运动习惯
    • 每个季度读一本无关工作的书
    • 学习日语到 N2 水平
  • 投资
    • 进一步分散投资
    • 不亏钱