Agentic coding lets me move very fast. A feature gets built in days. A design gets implemented in a matter of hours. Changing a layout, updating a template, reworking a flow: all of it happens at a speed I couldn’t have imagined a year ago.代理式编程(Agentic coding)让我能够飞速前进。构建一个功能只需几天,实现一个设计只需几小时。修改布局、更新模板、重构流程:这一切发生的速度,是我一年前无法想象的。
There’s one part of the stack where none of that speed applies to me: the data.但在技术栈中,有一个部分完全无法享受到这种速度,那就是数据。
Everything else has an undo button#其他一切都有“撤销”按钮
Most of my work these days happens from a phone, talking to a remote coding environment . A lot of that work is iteration, and iteration is cheap when a change can be reverted.如今,我的大部分工作都是通过手机连接远程编码环境完成的。其中很大一部分工作是迭代,而当变更可以撤销时,迭代的成本就很低。
- A layout that doesn’t quite work once people use it? The next version is a deploy away.如果一个布局在用户使用后发现不太好用?下个版本部署一下就行了。
- A bug? Fixed, deployed, gone.有 Bug?修复、部署,搞定。
- Some translations that are wrong? Redeploy, fixed.翻译有误?重新部署,修复。
- A redesign that makes part of the UI less confusing, but harder to use? Redeploy, fixed.重新设计导致界面一部分变得不那么令人困惑,但更难用了?重新部署,修复。
- Started a feature and don’t like where it’s going? Cancel it. The branch just sits there, harmless.功能做了一半发现方向不对?取消掉。那个分支就放在那儿,无伤大雅。
None of that goes out untested. Every change still runs through the test suite, and I still read the diff before it merges. What changed is the cost of a wrong call: if a screen turns out to be confusing once real people use it, the fix is minutes away. That’s why I’m comfortable doing that kind of work from the couch.这些变更都不会在未经测试的情况下发布。每一次变更仍会通过测试套件,我在合并前也会阅读差异对比。改变的是决策失误的成本:如果某个界面在真实用户使用后显得令人困惑,几分钟内就能修复。这就是我能安心在沙发上进行这类工作的原因。
So I move quickly, iterate, pivot, change my mind halfway through, deploy and redeploy, undo things I didn’t even finish, … It’s the reason I take more time to think about features now: generating a new version is so cheap that trying three of them is the normal way to work.所以我行动迅速,不断迭代、转换方向、中途改变主意、部署再重新部署、撤销那些还没完成的工作……这就是我现在花更多时间思考功能的原因:生成新版本的成本极低,尝试三个版本已经成了我的常规工作方式。
Data doesn’t#数据则不然
Every time I have to touch the data layer, I stop.每当我必须触碰数据层时,我都会停下来。
I can’t do it from a phone. I need to be behind a desk, at a laptop, in thinking mode, without distractions. Because this is the layer where a mistake doesn’t get undone by the next deploy.我无法在手机上完成这项工作。我需要坐在桌前,用着笔记本电脑,进入思考模式,不受任何干扰。因为在这一层,错误无法通过下一次部署来撤销。
There are coding patterns that soften the blow, sure. Soft deletes, a migration with a proper down(), a backup from last night. But those undo a change to the code, not the loss itself. If a migration truncated a column, the down() puts the column back. Empty.当然,有一些编程模式可以减轻这种冲击,比如软删除、带有正确 down() 方法的迁移,或者前一天晚上的备份。但这些只是撤销了代码的变更,而不是数据丢失本身。如果一次迁移截断了某一列,down() 方法确实能把这一列恢复回来,但它是空的。
Event sourcing
gets the closest. You don’t store the current state, you store every change as an event (OrderPlaced, AddressChanged, …) and build your tables from those. Break a table with a bad migration? Throw it away and replay the events into a new one. Spatie’s event sourcing package
has an event-sourcing:replay command for that. That’s a real undo button for the data layer.事件溯源(Event sourcing)最接近解决方案。你不存储当前状态,而是将每一个变更存储为事件(如 OrderPlaced、AddressChanged 等),并据此构建你的数据表。如果一次糟糕的迁移破坏了表格?把它扔掉,重新运行事件来重建一个。Spatie 的事件溯源包就有一个 event-sourcing:replay 命令可以做到这一点。这才是数据层真正的“撤销”按钮。
But the one-way door is still there, it just moved to the events. The log is append-only, so a badly designed event stays in it forever. An event you never recorded can’t be replayed. And an append-only log of everything a user ever did is the last thing you want when that user asks you to delete their account.但“单向门”依然存在,只是转移到了事件上。日志是仅追加(append-only)的,所以一个设计糟糕的事件会永远留在里面。一个从未被记录的事件无法被重放。而且,当用户要求删除账户时,你最不希望看到的就是一份包含该用户所有行为的仅追加日志。
Data is data. If you don’t have it, you can’t reproduce it.数据就是数据。如果你没有它,就无法复现它。
Take Snapkin . When you tell it once that the white blob on your breakfast plate is skyr and not yogurt, it remembers that and uses it from then on. I can rebuild the screen that asked the question in an afternoon. I can’t rebuild the answer. Only the person who ate the skyr knows that, and they told the app exactly once.以 Snapkin 为例。当你第一次告诉它早餐盘子里的白色团块是 Skyr 而不是酸奶时,它会记住这一点,并在以后一直使用。我可以在一个下午内重构出那个询问问题的界面,但我无法重构那个答案。只有吃掉 Skyr 的那个人知道答案,而他们只告诉过应用一次。
One-way doors#单向门
Jeff Bezos has a name for this. In his 2015 letter to Amazon shareholders , he splits decisions into two kinds:杰夫·贝佐斯(Jeff Bezos)对此有一个定义。在 2015 年致亚马逊股东的信中,他将决策分为两类:
Some decisions are consequential and irreversible or nearly irreversible – one-way doors – and these decisions must be made methodically, carefully, slowly, with great deliberation and consultation. If you walk through and don’t like what you see on the other side, you can’t get back to where you were before. […] But most decisions aren’t like that – they are changeable, reversible – they’re two-way doors.有些决策意义重大且不可逆或几乎不可逆——即“单向门”——这些决策必须经过深思熟虑、小心谨慎、缓慢地进行,并进行充分的审议和咨询。一旦你走过去,如果不喜欢门后的景象,就无法回到原来的位置。[……] 但大多数决策并非如此——它们是可以改变、可以逆转的——它们是“双向门”。
Agentic coding turned almost everything I build into a two-way door. A layout, a feature, a translation: walk through, don’t like it, walk back.代理式编程将我构建的几乎所有东西都变成了“双向门”。布局、功能、翻译:走过去,不喜欢,走回来。
Bezos was warning about treating two-way doors with one-way care, because that makes a company slow. With agents, I worry about the opposite mistake: walking through a one-way door at two-way-door speed, because everything around it moves that fast.贝佐斯曾警告过不要以对待“单向门”的态度去对待“双向门”,因为那会使公司变得迟缓。而有了代理工具,我担心的是相反的错误:以对待“双向门”的速度去穿过“单向门”,因为周围的一切都移动得太快了。
And the data layer is where the one-way doors are:而数据层正是“单向门”所在之处:
- Migrations.迁移。
- What you store, and in which format.存储什么,以及以何种格式存储。
- Data structures.数据结构。
- Backups.备份。
- Recovery.恢复。
Every data migration, and every moment in the app where I have to decide whether to store something and if so, how, makes me take a break. Those decisions are where most of my time and thinking goes now. Not the UI, not the UX, not the features, not what’s next on the list. All of those can stay fluid. They change, and I adapt them to what users say or want.每一次数据迁移,以及应用中每一个需要我决定是否存储某项数据(以及如何存储)的时刻,都会让我停下来休息。这些决策占据了我现在大部分的时间和思考。不是 UI,不是 UX,不是功能,也不是列表上的下一项。所有这些都可以保持灵活。它们会变,我会根据用户的反馈或需求进行调整。
The data is sacred.数据是神圣的。
That doesn’t make a data decision permanent. I can change a column type, split a table or move a field somewhere else later. But every one of those changes touches rows that already exist, so it takes a lot more effort and thinking to make sure nothing gets lost and that every change to the data is intentional. A layout change is a new deploy. A data change is a plan: what happens to every existing row, how I check it worked, and how I get back if it didn’t.这并不意味着数据决策是永久的。我以后可以更改列类型、拆分表或将字段移动到其他地方。但每一次此类更改都会触及现有的行,因此需要付出更多的努力和思考,以确保没有数据丢失,并确保每一次对数据的更改都是有意为之。更改布局是一次新的部署,而更改数据则是一项计划:必须明确每一行现有数据会发生什么、如何验证其有效性,以及如果失败了该如何回滚。
A field I decide to store is a field I now have to protect, back up, and eventually delete again. A field I decide not to store is gone for good. I can’t go back and ask people what they did last Tuesday.我决定存储的一个字段,就是我必须保护、备份,并最终删除的字段。我决定不存储的字段,就永远消失了。我无法回头去问人们上周二做了什么。
Privacy and retention don’t get rushed either#隐私和保留策略也不应仓促行事
Then there’s privacy and data retention. How long do I keep something? Who can see it? What happens when somebody deletes their account, and does that also cover the backups? What does a backup even mean if it holds data I promised to throw away?还有隐私和数据保留问题。我应该保留某样东西多久?谁能看到它?当某人删除账户时会发生什么,这是否也涵盖了备份数据?如果备份中包含了那些我承诺要删除的数据,那么备份又意味着什么?
None of those have a quick answer, and none of them get easier because an agent can write the migration in 30 seconds. Writing the migration was never the slow part. Thinking through what it does to data that’s already there is.这些问题都没有快速的答案,也不会因为代理可以在 30 秒内写出迁移脚本而变得简单。编写迁移脚本从来不是慢的部分,思考它对现有数据的影响才是。
The data is the application#数据即应用
Take everything away from an app: the UI, the website, the mobile app, the servers, the infrastructure. You can build all of that again, and with the tools we have now, faster than ever.拿走应用的一切:UI、网站、移动应用、服务器、基础设施。你可以重新构建所有这些,而且凭借我们现在的工具,速度比以往任何时候都快。
You can’t do that with the data your users gave you. Lose it, and whatever you rebuild is an empty shell with a login screen.但你无法用用户提供给你的数据做到这一点。一旦丢失,无论你重建什么,都只是一个带有登录界面的空壳。
So I’ll happily iterate on a layout from my phone. The migration waits until I’m at my desk.所以我很乐意在手机上迭代布局。至于迁移,等我坐在桌前再说。