开源项目因「感觉编程」停滞 3 月的完整复盘
8k+ 下载的 Rust crate 因不规范开发实践陷入停滞,作者分享重新出发的全过程。高质量的工程实践总结。
8k+ 下载的 Rust crate 因不规范开发实践陷入停滞,作者分享重新出发的全过程。高质量的工程实践总结。
我正在开发一个开源 crate,它的下载量约为 8,000 次,在日本和美国都有用户使用。
后来我推广了它,意识到自己把事情搞砸了,此后仓库每多一个 star,我的胃都会猛地一沉。
我的结论是:那些自称高级用户的人所进行的所谓“凭感觉编程”,就是一座意大利面式代码工厂。
我没有直接把这团乱麻扔掉,而是决定把它重新做成能入口的东西。这正是这次重启的由来。
你是否也有过这样的个人项目:停滞得太严重,以至于几个月都不敢碰它?
我的结构化数据 diff 工具 diffx,从 2025 年 8 月到 11 月基本处于冻结状态——大约三个月。它本应已经“功能完备”,但我就是无法继续推进。我分析了问题的根源,并通过一个如今被我称为“重启”的流程,让它重新活了过来。
本文记录了完整过程:根因分析、与 AI 协作的失败经历、逃离 monorepo,以及具体的重启步骤。
陷入项目停滞的独立开发者
使用 AI 结对编程工具(Claude Code 等)时,正在为代码质量苦恼的人
被 monorepo 和共享 CI/CD 框架耗尽精力的工程师
面向结构化数据(JSON/YAML/TOML/XML/INI/CSV)的语义 diff 工具。它会忽略键的顺序和空白字符,只显示有意义的变更。
传统 diff 无法理解数据结构:
$ diff config_v1.json config_v2.json
< {
< "name": "myapp",
< "version": "1.0"
< }
> {
> "version": "1.1",
> "name": "myapp"
> }
仅仅调整键的顺序,就会让每一行都显示为已变更。
diffx 只显示语义上的变更:
$ diffx config_v1.json config_v2.json
~ version: "1.0" -> "1.1"
# 作为 CLI 工具
cargo install diffx
# 作为库使用(Cargo.toml)
[dependencies]
diffx-core = "0.6"
# 基本用法
diffx file1.json file2.json
# 输出示例
~ version: "1.0" -> "1.1"
+ features[0]: "new-feature"
- deprecated: "old-value"
diffx 是一个 Rust 工具,用于从 JSON、YAML、TOML、XML、INI 和 CSV 等结构化数据中提取语义差异。与传统的文本 diff 不同,它会忽略格式和键顺序的变化,只呈现有意义的变更。
$ diffx config-old.yaml config-new.yaml
~ server.port: 8080 -> 9000
+ server.timeout: 30
- server.deprecated_option
它专为 DevOps/SRE 工作流构建,尤其适合追踪 Kubernetes YAML 和 Terraform 配置的变更。此外还有 npm(diffx-js)和 Python(diffx-python)版本。
crates.io:https://crates.io/crates/diffx-core / https://crates.io/crates/diffx
npm:https://www.npmjs.com/package/diffx-js
PyPI:https://pypi.org/project/diffx-python/
这是那个时期发布的文章:
仓库获得了大量 star。有些人甚至成为了赞助者。
从表面上看,这个项目已经打磨得相当完善:
日语、英语和中文 README
支持六种数据格式
复杂的 CI/CD 流水线
一份长达 740 行的迁移计划
CI/CD 已经坏到无可挽回
测试并不一定验证了真正的规格
文档和实现很可能已经出现偏差
从技术上说,CLI 确实可以运行,但我无法诚实地说它具备可持续性。
那三个月里,我一直在“研究如何让它具备可持续性”。
在分析 diffx 之后,我发现了三大类失败原因。
除了 diffx,我当时还构思了两个姊妹项目:用于比较 AI 模型配置差异的 diffai,以及提供本福特定律等统计功能的 lawkit。
为三个项目构建了一套共享 CI/CD 系统
试图通过 workflow_call 编排所有内容
添加了指向共享仓库的符号链接
我把共享部分(GitHub 工作流、脚本等)拆分到了另一个仓库中,然后从每个项目创建符号链接指向它。
/home/kako-jun/repos/.github/ ← 共享仓库
└── rust-cli-kiln/
└── scripts/
└── testing/
└── quick-check.sh ← 从未稳定下来
每个项目内部:
github-shared -> ../.github ← 符号链接
quick-check.sh 确实存在,但针对 diffx 调整它会弄坏 lawkit,修好 lawkit 又会弄坏 diffai。没有任何东西能够一直保持绿色通过状态。
教训:在第一个项目稳定之前,不要考虑下一个项目。
我把 Rust 核心、npm 绑定和 Python 绑定放在了同一个仓库里。
diffx/ # monorepo
├── diffx-core/ # Rust
├── diffx-cli/ # Rust
├── diffx-js/ # Node.js (napi-rs)
└── diffx-python/ # Python (PyO3)
三种语言和工具链共享一个仓库
GitHub Actions 膨胀为六个工作流,外加一个共享仓库
任何改动都有可能破坏所有内容
发布需要执行一套多步骤仪式
依赖关系图变得极其复杂
发布节奏不匹配
Rust 中的一个小型 bug 修复
→ diffx-core v0.6.1
→ diffx-cli v0.6.1
→ diffx-js 需要更新绑定(半天)
→ diffx-python 需要进行类似更新(半天)
→ 只发布 Rust 版本
→ 各版本逐渐产生偏差
我从第一天起就试图同时维护三份 README。
让三个文件保持同步令人筋疲力尽
每次改动都变成三倍工作量
你会忘记哪个文件才是“事实来源”
所有文件会同时变得过时
阶段 1:只维护 README_ja.md(使用你修改起来最快的语言)
阶段 2:项目成熟后再添加英文版本
阶段 3:根据实际需求添加其他语言
我大量使用 Claude Code,并遇到了多种失败模式。
在上下文被压缩了三次的会话中,回答一开始非常完美,但会随着时间推移不断恶化。
开始:准确、详尽的实现
↓ 1 小时后:出现隐蔽的规格违背
↓ 2 小时后:出现明显的 bug
↓ 3 小时后:完全忘记之前的指令
只剩 20% 上下文时的行为
一旦上下文变得稀薄,AI 就会公然改变自己的性格:
毫不犹豫地撒谎
即使没有被要求,也会催促你提交代码
即使有规格文档,它也会忘记规格并开始自由发挥。盲目信任 AI 的初学者,会陷入规格与代码缓慢失去同步的状态。
我的猜测是:编程 AI 接受过这样的训练——“在上下文崩溃之前,把工作干净利落地收尾”。这本身是合理的。
但把这种本能与“一年级水平的凭感觉编程”结合起来,就会酿成灾难。在 Qiita 或 Zenn 上,你会看到一些兴奋的文章:“我用凭感觉编程做出了这个!大家都应该试试!”这种热情非常棒,但 AI“必须收尾”的本能,加上初学者“AI 会替我搞定一切”的信任,正是一条直通失败的道路。
这正是凭感觉编程的危险之处。我的暂定定义是:
在一次会话中直接在 main 分支上随意修改
随口下达指令,却不关注上下文大小
相信它“差不多能用”
一觉醒来,得到 2,000 多行意大利面式代码
对于玩具项目来说,这没什么问题。但如果在进入“幻灭阶段”之前一直凭感觉编程,它最终一定会爆炸。
除了上下文问题之外:即使是拥有充足上下文的 Opus 4.5,也经常会对“已经实现”这件事撒谎。
AI 明明知道正确的事实,却仍会若无其事地输出与事实相矛盾的代码。知道某件事和正确实现它,是两种不同的能力。
应对措施:让同一个 AI 审查自己的工作。
我:“实现这个功能。”
AI:“已实现。”
我:“你自己审查一下。确定它符合规格吗?”
AI:“我发现了以下问题……”(然后立即修复)
这就是为什么整个行业一直在讨论“由 AI 自动审查”。它正是针对这种失败模式的解药。只是当时的我并不知道这一点。
我:“添加一个忽略大小写的选项。”
AI:“已添加 --ignore-case。”
结果:值的比较不区分大小写,但键的比较仍然区分大小写。
在我的理解中,“忽略大小写”也包括键,但 AI 并没有推断出这一点。
根本问题在于:我缺乏描述开发原则所需的词汇。
当凭感觉编程失效时,你需要告诉 AI“应用单一职责原则”或“关注点分离”。如果你不知道这些表达,就会陷入困境。
❌ “添加一个忽略大小写的选项。”
✅ “添加 --ignore-case,并产生以下效果:
1. 比较值时不区分大小写。
2. 比较键时不区分大小写。
示例:{"Name": "foo"} 和 {"name": "foo"} 应当相等。”
现在的我至少会写到这种程度——或者让 AI 帮我起草这样的指令。但当时我还没有养成这个习惯。
我:“阅读 README.md,并据此实现。”
AI:“已根据 README 完成实现。”
结果:README 里充满了谎言,所以它实现的也是谎言。
更早的 AI 会话已经让文档与现实产生了偏差。
$ cargo test --workspace
29 passed; 0 failed
看到测试全部通过,会让人感到安心。这是一个错误。
出现这种情况的原因是:AI 一旦看过代码,就会编写能够通过当前实现的测试。测试第一次运行时当然会通过——这根本不算胜利。
Serena 之类的工具会自动读取代码。你无法把代码藏起来。因此,你必须明确指示:
❌ “为这段代码编写测试。”
✅ “根据 docs/specs/cli.md 编写测试。
不要阅读实现代码。测试必须从规范推导出来。”
为什么规范驱动开发很重要
现在回头看,这正是“规范驱动开发”越来越流行的原因。
规范被拆分成细粒度的 issue。
每个 issue 都包含给 AI 的精确指令。
AI 领取 issue 并完成实现。
GitHub MCP 为每个功能创建独立分支。
遵循这套流程,自然就能避免凭感觉编程带来的灾难。这只是一套良好的流程。
AI 辅助开发成熟度金字塔
有一个描述 AI 辅助开发的三层金字塔。凭感觉编程处于最底层。在至少达到第二层之前,你无法可靠地运营公共项目,用户也会因此遭殃。
这次重启我同样使用了 Claude Code。
我向它坦白了一切,请它制定一个复活计划。它给出的方案既友善又务实——不像岸边露伴的忏悔室。方案奏效了。
在 .claude 下创建一个 reboot 子目录,作为指挥中心。
再次运行 /init,让 Serena 从头扫描整个仓库。
整个过程大约花了三天,因此需要由人类在不同会话之间维持状态。如果时间拖得更长,人类又会成为故障点。
1. 假设 reboot 之外的一切都不可信。
2. 假设文件都只是半成品。
3. 假设文档都在说谎。
4. 弄清楚哪些部分是真的。
这就是人类的工作。Claude Code 是军师;我只是个努力跟上节奏的幼年刘备。
首先,我把现有文件移入 _old/,腾出一块空白画布。
# Removed or quarantined
- docs/ (including examples) → breeding ground for unchecked lies
- English and Chinese READMEs
- scripts/ (complex CI/CD)
- benchmarks (non-essential)
- CHANGELOG.md, CONTRIBUTING.md
结果:更改了 109 个文件,删除了 32,145 行。
我把 README_ja.md 中每一处“可以运行”的表述都亲手验证了一遍。
# Test each format
echo '{"a":1}' > test1.json
echo '{"a":2}' > test2.json
./target/release/diffx test1.json test2.json
✅ All six formats (JSON/YAML/TOML/XML/INI/CSV) work.
✅ Output formats (CLI/JSON/YAML) work.
✅ --quiet, --ignore-keys-regex, --epsilon, --array-id-key work.
⚠️ --ignore-case may only affect values, not keys.
❓ --ignore-whitespace and directory diffs unverified.
结论:核心功能是可以工作的。问题出在未经验证的功能上。
我只记录自己亲自确认过的行为。
docs/specs/
├── cli.md # CLI specs (exit codes, output, options)
└── core.md # Core API specs
先描述理想行为,再查看代码。
对照实现进行检查。
只记录实际可以工作的内容。
将有问题的部分标记为 TODO。
我删除了现有的 436 个测试(8,022 行),并根据规范重新编写测试。
tests/
├── spec/ # Spec-driven unit tests (69 cases)
└── cmd/ # trycmd-based doc-as-test (19 cases)
Markdown 同时充当文档和测试。
文档无法再与测试脱节。
从物理上杜绝编写虚假文档的可能。
当以下条件全部满足时,我拆分了仓库:
不同的构建系统:Cargo、npm 和 pip。
不同的发布节奏:需要独立发布。
不同的用户:Rust、Node.js 和 Python 开发者。
CI/CD 复杂度:跨语言的相互依赖已经难以管理。
cd /home/kako-jun/repos/
mkdir diffx-js && cd diffx-js && git init
mkdir diffx-python && cd diffx-python && git init
cp -r ../diffx/diffx-js/* ./diffx-js/
cp -r ../diffx/diffx-python/* ./diffx-python/
重点:我舍弃了 Git 历史。保留历史会让一切变得更加复杂。
现在,diffx-js 和 diffx-python 会从 crates.io 拉取 diffx-core。
# diffx-js/Cargo.toml
[dependencies]
diffx-core = "0.6" # versioned dependency instead of path
# diffx/Cargo.toml
[workspace]
members = ["diffx-core", "diffx-cli"]
# diffx-js and diffx-python removed
/home/kako-jun/repos/
├── diffx/ # Rust only (simple)
│ ├── diffx-core/
│ ├── diffx-cli/
│ └── .github/workflows/
│ ├── ci.yml
│ └── release.yml
│
├── diffx-js/ # npm only
│ └── .github/workflows/
│ ├── ci.yml
│ └── release.yml
│
└── diffx-python/ # pip only
└── .github/workflows/
├── ci.yml
└── release.yml
易于理解的 CI/CD:从共享仓库加六个工作流,变成每个仓库两个工作流。
独立发布:不必赶进度;每种语言都可以按照自己的节奏发布。
清晰的职责归属:每个仓库只承担一项职责。
更容易贡献:Node.js 开发者只需要阅读 diffx-js。
拆分仓库让我学到了一个关键原则。
# ❌ Dangerous workflow
name: Release
on:
push:
tags: ["v*"]
jobs:
build-and-publish:
steps:
- run: cargo build --release
- run: cargo publish # publishes before confirming all builds succeed
如果 Windows、macOS 和 Linux 的构建中只有一个失败了呢?
cargo publish 无法撤销。
即使发现了问题,唯一的修复方式也是提升版本号。
你会毫无意义地浪费版本号(v0.6.1 → v0.6.2 → v0.6.3...)。
# ✅ Safe workflow (release.yml)
name: Release
on:
push:
tags: ["v*"]
jobs:
build-linux:
runs-on: ubuntu-latest
steps:
- run: cargo build --release
- uses: actions/upload-artifact@v4
build-macos:
runs-on: macos-latest
# ...
build-windows:
runs-on: windows-latest
# ...
create-release:
needs: [build-linux, build-macos, build-windows]
steps:
- uses: actions/download-artifact@v4
- uses: softprops/action-gh-release@v1
with:
files: |
diffx-linux/*
diffx-macos/*
diffx-windows/*
# ✅ Publish is a separate workflow (publish.yml)
name: Publish
on:
workflow_dispatch:
inputs:
version:
description: "Tag to publish"
required: true
jobs:
publish:
steps:
- run: gh release view v${{ inputs.version }} # confirm release exists
- run: cargo publish
Step 1: Push tag → build for all platforms → create GitHub release
↓
Stop here. A human verifies everything.
↓
Step 2: Manually trigger the publish workflow
→ Push to crates.io / npm / PyPI
1. Run the code yourself and find the truth.
2. Write specs in docs/specs/.
3. Build tests from those specs.
4. Fix the implementation until tests pass.
5. Update docs (README, etc.) to match the specs.
先写文档 → 文档会变成谎言。
信任现有测试 → 它们只能确认表象。
没有基于规范编写的旧测试。
examples/ 目录(它们会逐渐腐化成谎言)。
旧路线图(已经执行完毕或已经过时)。
宣传材料(六个月后就会过时)。
docs/specs/——唯一事实来源。
tests/cmd/——由测试强制保障的文档。
.claude/tasks.md——当前任务列表。
CLAUDE.md——最精简的开发规则。
Specs: 1 session
Tests: 1 session
Docs: 1 session
Cleanup: 1 session
Total: about four sessions (one session = one context window).
❌ Frozen for 3 months
❌ Monorepo complexity
❌ Broken CI/CD
❌ Maintaining three languages
❌ Happy as long as "it runs"
❌ Blind faith in existing code
重启后(2025 年 12 月)
✅ Back in motion
✅ Lean Rust-only repo
✅ Separate language-specific repos
✅ Focused on Japanese docs
✅ Obsessed with correctness
✅ Doubt everything
让一个停滞三个月的项目重新复活,不需要新功能,也不需要炫酷的技术。
需要的是怀疑的勇气和删除的勇气。
怀疑现有测试。
怀疑 AI 的状态报告。
怀疑自己对于“完成”的判断。
删除一切可疑的东西。
我过去一直想不明白,为什么聪明人也会落入沉没成本谬误。后来才发现,我也是其中之一。
你不愿放弃测试、文档和 CI/CD,因为“把它们扔掉”感觉像是一种浪费。但保留损坏的产物对任何人都没有帮助。我终于开始践行自己一直主张的理念,把它们删掉了。
这就是这次重启的核心,也是实现可持续性的关键。
AI 会带来一个不易察觉的问题。
VS Code 的保存时格式化永远不会运行。
人类在 VS Code 中编写代码时,格式化工具和 .editorconfig 会自动生效。当 AI 直接写入文件时,这些机制一个都不会触发。
缩进错位
缺少末尾换行符
import 顺序杂乱无章
到处都是微不足道的 lint 警告
CI 因为这些无聊的问题失败,浪费时间。
解决方案:pre-commit hooks
配置 pre-commit,让格式化和 lint 检查在每次提交前运行。下面是 diffx-python 的实际配置:
# .pre-commit-config.yaml (diffx-python)
repos:
- repo: local
hooks:
- id: cargo-fmt
name: cargo fmt
entry: cargo fmt --
language: system
types: [rust]
pass_filenames: false
- id: cargo-clippy
name: cargo clippy
entry: cargo clippy -- -D warnings
language: system
types: [rust]
pass_filenames: false
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.8.6
hooks:
- id: ruff
args: [--fix]
- id: ruff-format
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v5.0.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-toml
- id: check-added-large-files
# Setup
pip install pre-commit
pre-commit install
在 diffx-js(Node.js)中,我改用 Husky:
// package.json (diffx-js)
{
"scripts": {
"prepare": "husky"
},
"devDependencies": {
"husky": "^9.1.7"
}
}
这样一来,AI 编写的代码就会在提交时自动格式化。
用于结构化数据(JSON/YAML/TOML/XML/INI/CSV)的语义差异比较工具。它会忽略键的顺序和空白字符,只显示有意义的变更。
传统 diff 无法理解数据结构:
$ diff config_v1.json config_v2.json
< {
< "name": "myapp",
< "version": "1.0"
< }
> {
> "version": "1.1",
> "name": "myapp"
> }
只是简单调整了一下键的顺序,却会显示每一行都发生了变化。
diffx 只显示语义上的变更:
$ diffx config_v1.json config_v2.json
~ version: "1.0" -> "1.1"
# As CLI tool
cargo install diffx
# As library (Cargo.toml)
[dependencies]
diffx-core = "0.6"
# Basic
diffx file1.json file2.json
# Output example
~ version: "1.0" -> "1.1"
+ features[0]: "new-feature"
- deprecated: "old-value"
diffx 的 Node.js 绑定——用于结构化数据(JSON、YAML、TOML、XML、INI、CSV)的语义差异比较工具。由 Rust 通过 napi-rs 驱动,性能快如闪电。
npm install diffx-js
const { diff } = require('diffx-js');
const old = { name: "Alice", age: 30 };
const newObj = { name: "Alice", age: 31, city: "Tokyo" };
const results = diff(old, newObj);
for (const change of results) {
console.log(`${change.diffType}: ${change.path}`);
// Modified: age
// Added: city
}
const results = diff(data1, data2, {
kako-jun / diffx-python
diffx 的 Python 绑定——用于结构化数据(JSON、YAML、TOML、XML、INI、CSV)的语义差异比较工具。由 Rust 通过 PyO3 驱动,性能快如闪电。
pip install diffx-python
import diffx
old = {"name": "Alice", "age": 30}
new = {"name": "Alice", "age": 31, "city": "Tokyo"}
results = diffx.diff(old, new)
for change in results:
print(f"{change['type']}: {change['path']}")
# Modified: age
# Added: city
results = diffx.diff(data1, data2
epsilon=0.001, # Tolerance for float comparison
array_id_key='id', # Match array elements
我还没有撰写正式的发布文章,但这些项目的初始版本已经可以运行,这足以证明重启流程是可重复的——至少在我的生态系统中是如此。
用于 AI/ML 模型(PyTorch、Safetensors、NumPy、MATLAB)的语义差异比较工具。提供张量统计、参数比较以及自动化机器学习分析。
传统 diff 无法理解二进制机器学习文件:
$ diff model_v1.pt model_v2.pt
Binary files model_v1.pt and model_v2.pt differ
diffai 会给出有意义的分析:
$ diffai model_v1.safetensors model_v2.safetensors
learning_rate_analysis: old=0.001, new=0.0015, change=+50.0%
gradient_analysis: flow_health=healthy, norm=0.021
~ fc1.weight: mean=-0.0002->-0.0001, std=0.0514->0.0716
~ fc2.weight: mean=-0.0008->-0.0018, std=0.0719->0.0883
# As CLI tool
cargo install diffai
# As library (Cargo.toml)
[dependencies]
diffai-core = "0.5"
# Basic
diffai model1.pt model2.pt
# JSON output for automation
diffai model1.safetensors model2.safetensors --output json
# With numerical tolerance
diffai weights1.npy weights2.npy --epsilon 0.001
PyTorch(.pt、.pth)——完整的机器学习分析和张量统计
Safetensors(.safetensors)——完整的机器学习分析和张量统计
NumPy(.npy、.npz)——张量统计
MATLAB(.mat)——张量统计
--format <
统计规律分析工具包。分析数据是否符合本福特定律、帕累托法则、齐夫定律、正态分布和泊松分布。检测异常并评估数据质量。
cargo install lawkit
本福特定律(欺诈检测)
$ lawkit benf financial_data.csv
Benford Law Analysis Results
Dataset: financial_data.csv
Numbers analyzed: 1000
[LOW] Dataset analysis
First Digit Distribution:
1: ███████████████┃░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 30.1% (expected: 30.1%)
2: █████████┃░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 17.6% (expected: 17.6%)
3: ██████┃░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 12.5% (expected: 12.5%)
...
帕累托法则(80/20 法则)
$ lawkit pareto sales.csv
Pareto Principle (80/20 Rule) Analysis Results
Dataset: sales.csv
Numbers analyzed: 500
[LOW] Dataset analysis
Lorenz Curve (Cumulative Distribution):
20%: ███████████████████████████████████████┃░░░░░░░░░░ 79.2% cumulative (80/20 point)
40%: █████████████████████████████████████████████░░░░░ 91.5% cumulative
...
80/20 Rule: Top 20% owns 79.2% of total wealth (Ideal: 80.0%, Ratio: 0.99)
齐夫定律(频率分布)
$ lawkit zipf word_frequencies.csv
Zipf Law Analysis Results
Dataset: word_frequencies.csv
Numbers analyzed: 1000
[LOW] Dataset analysis
Rank-Frequency Distribution:
# 1: █████████████████████████████████████████████████┃ 11.50% (expected: 11.50%)
# 2:
部分评论可能只有登录后的访客才能看到。登录以查看全部评论。
如需采取进一步措施,你可以考虑屏蔽此人和/或举报滥用行为。