聊到这里大家应该大概明白了,这里也有一个不可能三角,就是研发效率、体验、性能这三者,选择 Web 是多效率少性能,选原生是多性能体验少效率,选跨端刚好是在这三者取一个中间,做得好容易都占,做得不好,容易没有一边占到好处的尴尬局面。
那么在现在 AI 写代码不是大问题的情况下,该怎么选呢?很多时候还是看场景。如果是重内容的浏览、阅读这类轻应用,直接用 Web 技术最省心;但如果是需要深度调用系统底层能力,或者追求更低内存和更顺手交互的应用,我依然更推荐原生技术,可以第一时间享受到系统官方本身的众多调优。有了 AI 之后,一个人写两套原生的成本其实大幅下来了,效率那一边被补上了不少,调试和体验验证反而成了人最需要花心思的地方。
两边的关系其实非常简单,外观和交互我尽量完全一致,尽可能让大家意识里面认为的 Windows 软件不那么精致美观的感觉在这里也保持我认为好的水平,然后底层代码各写各的,关于视觉的一致性还原有了 AI 以后很简单,我把原本的 Mac 版写在 Swift 里的颜色、字号、间距、圆角这些设计 token,直接写了一个脚本生成为 Windows 里面 XAML 资源,文案也是从 Mac 的多语言文件转成 Windows 的格式,11 种语言一起转过来,甚至大家看到的菜单栏里面的那个 runner 动画也是用的同一个脚本同步来的。
虽说同步,可能大家会觉得很简单,其实这里需要花点精力防止 AI 直接给你跑偏了,每次同步其实我会记录对应的是 Mac 的哪一个提交,这样两边就不会走偏去,然后功能上会有一份 SYNC.md 当作对账表,Mac 每加一个功能、修一个安全问题,Windows 这边都会标清楚哪些是已经实现、哪些是刻意不同还是不适用,以及为什么这样判断。
通过这两个机制,我可以很好地让两边的底层完全各写各的,但是又能很好地保持功能对齐,完全不需要刻意去硬写一层兼容去磨平,反而会变复杂,这里能复用的反而是设计、文案以及踩坑后定下来的各种规则,反而这些是 AI 非常喜欢的「能力抽象层」,也就是说随着时代发展,慢慢所谓的代码抽象没有那么重要了,反而是规则抽象更重要。
另外 Windows 开发还有一块比 Mac 要麻烦不少,就是证书签名,没有签名的,一打开会出现「Windows 已保护你的电脑」,甚至 Windows 11 的智能应用控制会直接不让运行,国内不少安全软件也会把没签名的程序当成可疑报毒,这和 Mac 上没签名、没公证的软件提示「已损坏」差不多,平时 Mac 上大家还可以跑一条命令解掉,Windows 上想正规分发,这一步没有啥好办法,就是需要签名。
证书搞定后,就是开发环境的事情了,我的研发要求是代码和 AI 的上下文必须全留在 Mac 上,Mac 用习惯了,真的很难使用 Windows 去研发,这里 Windows 只负责编译和验证效果和各种测试,所以我在 Mac 上装了 Parallels Desktop,大概 3 百多块一年,之前也听不少小伙伴吐槽过这个软件,不过在我的 M5 Pro 上跑 Windows 11 还挺顺滑,让我感觉这个钱花得很值。
它最好用的一点是我的 Claude 能很顺手地操作它,仓库直接作为 Parallels 的共享文件夹挂进虚拟机,变成一个 X 盘,Mac 上改完代码,虚拟机里直接编译,不需要来回 copy 文件,只是两边的编译产物要分到不同的目录,对于编译、启动、看日志的工作都被我写成了脚本,通过 Parallels 自带的 prlctl exec 直接在虚拟机里执行,程序启动就崩的时候 Windows 只给一个 0xc000027b,Mole 会把真正的异常写进日志,脚本跑完顺手打印出来,Claude 直接就能读到。相当于写代码还是在 Mac 上的 Claude Code 里,调试和运行都在虚拟机里,脚本不能解决的地方,直接用 Codex 的 computer use 去点,只有少部分那种系统确定弹框点不了,我在这里就是这个职责,点点系统弹框确定,哈哈。
说到这里,可以和大伙分享一下 Mole Windows 的架构分层,一共四层,最底下的 Core 只依赖 .NET,不碰任何 Windows 接口,清理规则、路径安全检查、卸载残留的判断都放到这里,测试用的是一个按 Windows 路径规则模拟出来的内存文件系统,这样在 Mac 上也可以很好地跑;往上是 Platform 层,注册表、文件系统、Win32 调用这些真正的系统接口都放在这一层,打开 EnableWindowsTargeting 以后,Mac 上也能编译通过;再往上就是写过应用同学非常熟悉的界面状态的 ViewModel 层,这里和 Mac 版本会一一对应,这样也能够在 Mac 上测,只有最上面这一层的 WinUI 界面必须去 Windows 里编译。
这也是让我一个月时间可以写 8 万多行 Windows 代码、4000 多个测试,但是几乎大部分可以在 Mac 上完成的一个很方便的地方,也是架构设计的巧妙处,当然这里我只提供之前做架构时候的想法建议,大部分是 Opus 5.5 去帮我整的,很牛逼。
接着可以聊测试了,我反而认为在 AI 时代,工程师最大的价值,除了架构设计外就是这个测试验证了,所以为了保证产品是可以售卖的,肯定需要在真实环境测试验证,反复找体验问题去修复的,所以我整了一台 ThinkPad X1 Carbon,这个机器质感还不错的,我把它当成真机环境,设置永不休眠,可以让我和我的 agent 24 小时去验证测试回归,包括获取各种真实系统环境的参数验证,400 多款 Windows 软件的卸载、残留清理、注册表、启动项测试,大部分在虚拟机里跑,x64 安装包、真实的高分屏、电池、SmartScreen 这些虚拟机看得不准确的,就直接放到这台机器上验,所以这台机器让我感觉也非常值。
然后为了不想在两台机器拷来拷去,最后还是用了 UU 远程,直接在 ThinkPad 那边开一个远程终端,我这边在 Mac 上直接操作,然后还把 ssh 连接当作备用,大部分软件的安装、卸载和调试,都是通过命令行脚本去完成的,剩下需要看界面的,直接通过远程桌面让 computer use 去操作,非常方便,最终相当于我还是在 Mac 上做的所有的事情。
这个过程走下来,到最后做完让几个天使用户帮忙试用,反馈确实帮大家解决了不少痛点,就觉得折腾这么一遭挺值的。能让 Windows 用户也用上顺手、干净的好软件,心里其实挺开心的。
当时想用 Swift 手写实现这个能力,原本的就是用 JS 实现的,非常复杂差不多快要放弃了,洗澡的时候突然想到一个点子,其实在 Swift 里面我完全可以去调用一个 JavaScriptCore 的能力,它是可以执行 JS 的,那我是不是可以把杂乱的 md 当作一个字符串传到这个里面去运行,然后帮我输出非常清晰规范化的 md 字符串,我再解析渲染出来,立马去测试验证,真的可以,效果感觉非常不错,在当时没有 AI 的时代,假如我用 Swift 去实现这个格式化引擎,不知道要多久才搞得出。
其实很多时候,假如你对各种语言都了解一点,碰到难搞问题的时候可以帮助你很快想到一些 hack 的办法去解决,甚至你还会融会贯通,反过来调用,比如当时在渲染的 Web 里面,需要去调用下载的能力,这个时候还可以反向去调用 Rust 的成型的系统能力帮我去下载,性能就更高。