#1195 MJML 邮件格式

2026-07-27

MJML(Mailjet Markup Language)是一种专门用于编写响应式 HTML 邮件的标记语言。相当于是 HTML 在电子邮件场景下的更高层的封装,由编译器生成兼容各种邮箱客户端的 HTML。主要解决邮件 HTML 的痛点:

  • Gmail / Outlook / Apple Mail 支持差异巨大
  • 邮件需要大量 table 布局
  • CSS inline 化复杂
  • 移动端适配困难

注意:MJML 只负责 UI 不含模板功能。如果需要使用变量或者其他模板语法,可以另外选择任何一种语言进行渲染,比如 Node Handlebars,Golang 模板,Python Jinja2 等。

MJML 最初由法国邮件服务公司 Mailjet 在 2015 年开源。目前项目已经独立维护,属于开源生态:

  • 语法:MJML
  • 编译器:Node.js 实现
  • 输出:标准 HTML Email

GitHub Repository: mjmlio/mjmlio

MJML 文件格式

扩展名:.mjml

<mjml>
  <mj-head>
    <mj-title>Welcome</mj-title>
  </mj-head>

  <mj-body>

    <mj-section>
      <mj-column>

        <mj-text font-size="20px">
          Hello Woody
        </mj-text>

        <mj-button href="https://example.com">
          Click
        </mj-button>

      </mj-column>
    </mj-section>

  </mj-body>
</mjml>

通过一下方法可以直接预览效果:

  1. VSCode 拓展 [MJML Official]
  2. 官方 MJML Live Editor
  3. 自己实现 Preview 服务,AI 很容易生成一个

渲染 HTML

安装:

npm install -g mjml

转换:

mjml template.mjml -o template.html

生成的 HTML 会包含:

  • table 布局
  • inline style
  • Outlook VML兼容代码
  • media query

#1193 转载:布尔变量取名规则

2026-07-24

传说中,编程有两大难题,一个是缓存失效,另一个是变量起名。

其中,布尔变量尤其难起名。怎样才能贴切地表达,这个变量是表示"真"(true)和"伪"(false)的布尔值呢?

我最近读到一篇文章《布尔变量起名的艺术》,提出使用四个前缀就能给布尔变量正确起名,我觉得很有启发。

  1. is-:描述事物的状态,后面跟形容词,比如 isActive,isDeleted,isEmpty。
  2. has-:描述事物的所有权或包含关系,后面跟名词,比如 hasAccess,hasChildren,hasValidationErrors。
  3. can-:描述事物的能力或权限,比如 canEdit,canDelete,canRetry。
  4. should-:描述事物的意图或逻辑,比如 shouldRetry,shouldCacheResponse。

除了这四个前缀,起名还有另一条规则:永远不在布尔变量名中使用否定词。
比如,不使用 isDisabled,而要用 isEnabled = false。

#1192 一种基于经纬度的地理位置编码方案

2026-07-24

阮一峰的新闻周刊介绍了高德的位置口令,高德本月推出了一个新功能,可以位需要提供准确位置的用户生成一个 6 位编码,其他人直接搜索这个编码就能找到那个位置,而不需要知道这个位置的名称。
文章后面说:要是国家出台统一算法就好了,为每个地点生成一个固定不变的位置码,用处就大了。

我想,这倒没有必要,为所有位置提供编码的方案不就是经纬度么?
经纬度在日常表达方面确实会有一些障碍,读起来不大方便,而且我不确定有多少人知道经纬度,虽然这是初中就学了的。
我设计了一个 8 位编码方案(BASE32,只包含数字和大写英文字母):

经纬度 1 度大约偏差 111 公里,精确到小数点后 3 位小数,可以控制偏差在 100 米范围内,对于日常使用是可以接受的。

总共 360,000 x 180,000 = 648 亿个点。
二进制 36 位可以表达(687 亿),也就是 5 Bytes。
BASE32 8 位足够可以表达了(10995 亿)(7 位只有 343 亿,差一点)。

百度地图开放平台上用坐标拾取器获取两个坐标用来测试,结果如下:

  • 华中科技大学的毛泽东像位置是 (114.420025,30.514723),精度控制到小数点后三位,换算过来就是 BRLMVBKC
  • 美国自由女神像位置是 (-74.044500, 40.689225),精度控制到小数点后三位,换算过来就是 ARYMQF7B

Base32 字母出现的频率较多,为了减少英文字母的使用,我改成十六进制的表达方式,只多了一位而已,可以接受。测试结果如下:

  • 华中科技大学的毛泽东像位置 (114.420025,30.514723) 换算过来就是 0C56CA8542
  • 美国自由女神像位置 (-74.044500, 40.689225) 换算过来就是 0470C817E1

Python 代码示例:

# woody geo codec

import base64

# encode = base64.b32encode
# decode = base64.b32decode
encode = base64.b16encode
decode = base64.b16decode

def wgc_encode(lat:float, lon:float) -> str:
    lat_num = int((lat + 180) * 1000)
    lon_num = int((lon + 90) * 1000)
    num = lat_num * 180000 + lon_num
    return encode(num.to_bytes(5, byteorder="big")).decode()

def wgc_decode(code:str) -> tuple[float, float]:
    num = int.from_bytes(decode(code))
    lat_num = num // 180000
    lon_num = num % 180000
    print(lat_num, lon_num)
    lat = (lat_num - 180000) / 1000
    lon = (lon_num - 90000) / 1000
    return lat, lon

for lat, lon in ((114.420025, 30.514723), (-74.044500, 40.689225)):
    print((lat, lon))
    code = wgc_encode(lat, lon)
    print(code)
    print(wgc_decode(code))

#1191 本地运行 LLM 的限制

2026-07-24

看了《你需要知道的 AI 内存知识》,我才知道:

  1. 本地运行大模型的核心资源主要有三个:内存容量、内存带宽和计算算力,三者共同决定模型规模和运行速度。
  2. 统一内存架构(Unified Memory) 可以让 CPU 和 GPU 共享大容量内存,从而突破传统独立显卡显存容量限制,更容易加载大模型。
  3. 模型名称中的 MoE(Mixture of Experts,混合专家模型) 表示模型只会激活部分参数进行计算,可以显著降低推理所需的内存和算力。
  4. 当前消费级设备,即使价格达到数万元,也仍然存在明显限制:要么模型规模受限,要么推理速度不足,实际更适合运行中小型模型或经过优化的 MoE 模型。

#1190 MD5 与 SHA256 的哈希性能对比

2026-07-24

JavaScript hashing speed comparison: MD5 versus SHA-256(Daniel Lemire, 2025-01-11)通过 JavaScript 基准测试对比了 MD5SHA-256 在 Node.js 23 和 Bun 运行时下的哈希性能,核心结论颠覆了“MD5 更快”的传统认知:

  • 测试背景与方法:使用 1GB 随机 Uint8Array 数据,在 ARM(Apple M2、Graviton 4)和 x86(Intel Ice Lake)系统上,分别对 md5Hashsha256Hash 进行基准测试,底层实际调用 OpenSSL 实现。
  • 关键性能数据:在现代 CPU 上 SHA-256 显著快于 MD5。例如在 Apple M2(Node.js)上,SHA-256 约 2.6 GB/s,MD5 仅约 0.6–0.7 GB/s;在 Intel Ice Lake 上 SHA-256 也达到 1.2 GB/s,优于或持平 MD5。
  • 原因分析:SHA-256 虽然在算法设计上更复杂,但现代处理器(ARMv8、x86 等)普遍带有 硬件密码学扩展指令(如 Intel SHA-NI),对其进行了加速;而老旧且已被破解的 MD5 并未获得类似优化。
  • 最终建议不应再使用 MD5。它既在安全性上已被彻底攻破(存在碰撞攻击),在实际速度上也失去了优势,开发者应默认选择 SHA-256 或更先进的算法(如 BLAKE3,若可用)。

这是采用的 JavaScript 做的基准测试,但是结论说明的是通用硬件现象:现代 CPU(ARM/x86)有 SHA-256 硬件指令加速,而 MD5 无优化,任何语言调底层实现都如此。
因此,在现代处理器上,SHA256 性能大幅优于 MD5 的结论在所有语言上都是通用的。

#1189 百度云 ICP 备案记录

2026-07-16
  • 原备案信息(阿里云 · 2019):备案名称「码厩Markjour」,备案号 鄂ICP备15002462号
  • 迁移原因:不再使用虚拟主机,业务迁至百度云低成本主机,需在百度云重新提交接入/备案,备案名称变更为「码厩开发者笔记」。

初审被拒,原因:

  1. 应急联系电话校验:应急手机号不能与负责人手机号码相同,需提供另一位真实可联系人的号码(当年没有这个要求)。
  2. 前台菜单合规审核:初审要求网站不得出现“留言”和“给我写信”菜单,这算页面交互入口(推测当年备案时尚未增加这两个菜单)。
    • 这两个功能从没用过,暂时屏蔽前端入口以通过核验,后面再看如何处理。

初审通过之后,备案进入 “管局审核中-短信校验中” 状态。
几分钟之后就会收到工信部短信验证码,需要进入指定链接输入验证码、手机号、身份证后六位进行核验。
短信核验之后过一会儿,备案进入 “管局审核中-人工审核中” 状态。

#1188 阅读代码之前执行的 git 命令

2026-07-11

AI 总结了 The Git Commands I Run Before Reading Any Code,可以作为参考。
PS:其中部分命令的有效性是建立在提交规范执行良好的基础之上。我过去经手的项目全部没有提交规范。😂

Five git log commands that diagnose a new codebase before you open a single file: code churn hotspots, bus factor, bug clusters, and crisis patterns.

1. 变更热点 列出去年改动最频繁的 20 个文件,榜首常是团队"无人敢碰"的那个。高变更本身不糟,但若叠加"无人愿拥有",便是代码库拖累的最强信号——每次修改都是补丁叠补丁,小改动的影响范围不可预测。微软 2005 年研究证实,变更率指标比复杂度更能预测缺陷。

git log --format=format: --name-only --since="1 year ago" | sort | uniq -c | sort -nr | head -20

2. 总线因子 按提交数排名贡献者。一人占 60%+ 即为风险;若其已离职半年便是危机。长尾同样关键:30 位贡献者中仅 3 位近一年活跃,说明建造者已非维护者。注意 squash-merge 会压缩作者信息,下结论前需确认合并策略。

git shortlog -sn --no-merges

3. Bug 集群 形态同变更热点,但过滤含 bug 关键词的提交。同时出现在两张列表的文件风险最高:反复出错、反复修补却从未根治。此法依赖提交信息规范。

git log -i -E --grep="fix|bug|broken" --name-only --format='' | sort | uniq -c | sort -nr | head -20

4. 提交节奏 按月统计提交数。稳定节奏健康;单月减半常因有人离职;6–12 个月下滑曲线揭示团队失速;周期性尖峰后沉寂则是批量发布模式。这是团队数据,而非代码数据。

git log --format='%ad' --date=format:'%Y-%m' | sort | uniq -c

5. 回滚与热修频率 一年中偶发回滚正常;若每几周一次,说明团队不信任部署流程,背后是测试不可靠、缺预发环境或回滚困难。零结果同样值得玩味。

git log --oneline --since="1 year ago" | grep -iE 'revert|hotfix|emergency|rollback'

#1187 Ubuntu 上钉钉不能启动的问题

2026-07-10

从官网下载的钉钉 deb 文件,安装之后竟然无法启动。

-> % ll ~/Downloads/com.alibabainc.dingtalk_8.1.0.6021101_amd64.deb
-rw-rw-r-- 1 catroll catroll 387M 2026-07-10 21:39:31 /home/catroll/Downloads/com.alibabainc.dingtalk_8.1.0.6021101_amd64.deb

-> % apt install ~/Downloads/com.alibabainc.dingtalk_8.1.0.6021101_amd64.deb

一边查看日志(journalctl --user -f),一边点击钉钉图标,可以看到以下错误日志:

7月 10 21:49:27 eqr com.alibabainc.dingtalk.desktop[361857]: ubuntu
7月 10 21:49:27 eqr com.alibabainc.dingtalk.desktop[361857]: ubuntu branch
7月 10 21:49:27 eqr com.alibabainc.dingtalk.desktop[361857]: preload_libs=./libgbm.so ./plugins/dtwebview/libcef.so
7月 10 21:49:27 eqr com.alibabainc.dingtalk.desktop[361873]: ERROR: ld.so: object './libgbm.so' from LD_PRELOAD cannot be preloaded (cannot open shared object file): ignored.
7月 10 21:49:27 eqr com.alibabainc.dingtalk.desktop[361873]: Run Main is_gpu=0 is_zygote=0 is_render=0 is_crashpad_handler=0 cmd : ./com.alibabainc.dingtalk
7月 10 21:49:27 eqr com.alibabainc.dingtalk.desktop[361873]: Load /opt/apps/com.alibabainc.dingtalk/files/8.1.0-Release.6021101//dingtalk_dll.so failed! Err=/opt/apps/com.alibabainc.dingtalk/files/8.1.0-Release.6021101//dingtalk_dll.so: cannot enable executable stack as shared object requires: Invalid argument
7月 10 21:49:27 eqr systemd[4607]: Started app-gnome-com.alibabainc.dingtalk-361857.scope - Application launched by gnome-shell.

经过 AI 的一番分析,结论是:

钉钉 deb 包依赖的 ELF 动态链接配置与当前系统环境不匹配。patchelf 修正了二进制的解释器、RPATH 或依赖库路径,使程序能正确加载运行时库之后就可以启动了。

sudo apt install -f patchelf
sudo patchelf --clear-execstack /opt/apps/com.alibabainc.dingtalk/files/8.1.0-Release.6021101/dingtalk_dll.so

#1186 冰淇凌的制作

2026-07-09

冰淇淋制作流程

1. 混合(Blending):打好地基
乳脂(奶油/牛奶)、糖、乳化剂、奶粉混合加热。这一步决定了冰淇淋的“底子”好不好,乳脂越高,口感越顺滑。

2. 均质(Homogenization):打碎脂肪
通过高压把牛奶中的大脂肪球打成微米级的小球。这一步是为了防止脂肪上浮,让口感变得细腻、均匀,没有粗糙感。

3. 老化(Aging):让蛋白质休息
将混合物迅速降温至4℃左右静置4小时以上。目的是让蛋白质充分吸水膨胀,这样后面打入空气时才不容易塌陷,质地更稳定。

4. 凝冻(Freezing):魔法发生的地方
这是最关键的一步!在-6℃至-10℃的凝冻机里疯狂搅拌。

  • 混入空气:让体积膨胀(膨化率),变得蓬松。
  • 形成冰晶:快速搅拌防止生成大冰晶,只留下微小的冰晶,这就是“细腻”的来源。

5. 硬化(Hardening):定型
如果是硬冰淇淋(如哈根达斯),会放入-30℃的冷库急冻,固定形状。

为什么不同品牌口感不同?

  • 哈根达斯(硬冰淇淋)膨化率低(空气少),乳脂极高,所以口感像吃冷冻的奶油,非常厚重。
  • KFC/麦当劳(软冰淇淋):凝冻后直接出机,膨化率高,空气多,所以口感轻盈像云朵,但也化得快。
  • 蜜雪冰城:为了控本,非乳脂固形物(如奶粉、糖浆)多,乳脂少,冰晶相对较大,所以感觉没那么“润”。