---
title: "用 AI 開發終究會翻車？"
date: 2026-05-27T00:00:00+08:00
publishDate: 2026-05-27T00:00:00+08:00
lastmod: 2026-05-25T20:42:59+08:00
tags: ["LLM","AI","經驗","Ruby"]
toc: true
permalink: "https://blog.aotoki.me/posts/2026/05/27/ai-development-hits-old-challenges/"
language: "zh-tw"
---



上週發表 [Kobako：讓 Agent 安全的操作 Rails](https://blog.aotoki.me/posts/2026/05/20/kobako-ruby-sandbox-for-ai/) 介紹 [Kobako](https://github.com/elct9620/kobako) 這個 Gem 的目標後，繼續透過 Claude Code 推進開發進度，然而很快地就遇到要大幅度修改的狀況，這難道就是使用 AI 開發的宿命嗎？

這是很值得討論的問題，也就是使用 AI 協助開發，到底是因為 AI 能力不足所以做不好，還是人類給的設計太差。

<!--more-->

## 不確定性{#uncertainty}

在 AI 時代之前，我們有許多運作軟體開發團隊的方式，像是敏捷開發（Agile）就是其中一種對不確定性處理的做法。

為什麼要處理不確定性，是因為在開發出實際可以運用的軟體之前，我們很難知道使用者是不是真正的喜歡這個軟體。同樣的，我們的實作跟設計在真正被運行、修改之前，也很難知道當下選擇的技術、架構是不是真的能持續運行。

也因此，一些軟體架構理論（如：[Clean Architecture](https://blog.aotoki.me/tags/Clean-Architecture/)）被提出來，讓我們可以在符合條件的狀況下，很容易的修改或者調整。

到了 AI 時代，我們還是遭遇相同的問題，但是開發速度快十倍，遇到問題也同樣快了十倍。短短一週的時間，我就馬上遇到最初的設計沒考慮到未來加功能會遭遇的狀況。

> Kobako 大概一週左右就到我覺得能釋出 0.1.0 版本的程度，可能不算快。但前面還有半個月的準備，像是做技術調查、概念驗證、原型實作等等，已經在傳統軟體開發的基礎跟經驗上，很好的做完該做的事情。

## 先入為主{#preconceptions}

上一篇介紹 Kobako 時有提到，從 [druby](https://github.com/ruby/drb) 得到靈感，採取 RPC 的方式進行溝通，但最終讓我遭遇到了一個難以處理的問題。

因為 WebAssembly 中的 WASI Preview 1（WebAssembly System Interface）並沒有正常的 I/O 機制可用，所以從 druby 或者 RPC 的概念，建立一個 I/O Pipe 來溝通本身是比較複雜的，在 Claude Code 蒐集的資料來評估，採用 Host Linker 機制比較簡單。

先設定一個 Import Function 讓 WebAssembly 中可以呼叫這個 C 方法，要傳遞回傳值則利用 Export Function 呼叫 WASM 分配一塊記憶體，然後把回傳值放到裡面。這樣一來一回，就實現了把 Rails 物件綁定（Binding）給 WebAssembly 環境中 mruby 使用的機制。

但這就遇到一個問題，如果按照正常的 RPC 連線大概會像這樣。

```ruby
# Client-side
conn = RPC::Client.connect("remote_addr")

# Server-side
server = RPC::Server.bind("address")
server.accept do |conn|
  req = conn.read
  # ...
  res = server.dispatch(req)
  # ...
  conn.write res
end
```

在 Host 端扮演的是 Server 角色，從「讀取」跟「寫入」都是 Server 拿到的這個連線，也就是一組 I/O Pipe 來負責處理。

但是在 Kobako 利用 WebAssembly 機制實現綁定的特性時，變成這樣的狀況。

```ruby
sandbox = Sandbox.new
sandbox.on_dispatch do |req|
  res = sandbox.server.dispatch(req)
  sandbox.reply res
end
```

實際上握有扮演 I/O Pipe 的是 Sandbox 本身（原設計是 `Kobako::Wasm::Instance`）但是 Sandbox 本身又擁有 `Kobako::RPC::Server` 這個實例，如果要做將請求轉發，就得用上面的方式來實現，但這樣設計成 `Sandbox#dispatch` 反而更合理，而 RPC Server 本質上只是一個 Registry 並不具備 Server 的性質。

除此之外，RPC Server 還不足以負擔所有的 Dispatch（分發）任務，因為還有 `Kobako::Handle` 這個機制，可以讓 Host 端的物件像指標一樣被傳入到 Guest 裡面使用，在這 Binding 的物件會回傳物件，無法被 msgpack 編碼時很好用。

到這裡就會發現，物件之間的關係基本上變得一團亂，職責很難切開，概念也不符合物件的命名，還很高機率耦合在一起，這很難在初期注意到，但又是必須面對的問題。

> 最後讓我撞到這個問題，是因為我想讓 Ruby 重要的語言特性 Block 機制可以被支援，雖然無法做到百分之百，但有限的支援就能覆蓋很多情境，但上述的問題就直接卡死這個機制被實現。

## 重新來過{#restart}

要解決這個問題，以前會很直覺的認為砍掉重練，畢竟已經知道新的限制在沒有包袱的前提下修改是很容易的。不過，AI 時代也有 AI 時代可以採取的策略，也就是重新分析。

我先讓 Claude Code 排除 SPEC.md 規格中的定義，從 Kobako 想設計一個 Ruby 語言沙盒（Sandbox）的角度重新分析，假設當下實作的樣子是這樣，我的目標這樣設定時，這些實作扮演怎樣的角色。

大概幾小時左右，我重新區分出了新的概念（模組）

- Codec - 解決 Ruby 和 mruby 差異，基本上不變繼續用 msgpack
- Runtime - 運行 WebAssembly 的環境
- Transport - Ruby 和 mruby 溝通的機制
- Catalog - 運行過程中提供的資源（如：Binding、Handle、Snippet 等）

最後統一由 Sandbox 物件進行調度，上述幾個物件的依賴關係幾乎是扁平，而且刻意維持單向（沒有循環依賴）來確保運作是保持在單純的狀態。

> 再重新設計的時候，我將 Sandbox 的介面鎖定在最後一次釋出的版本，盡可能限縮 Breaking Changes 的影響範圍，即使目前還沒釋出穩定版本。

這就還會遇到新的挑戰，要這樣修改會是「大幅度重構」在過去會有很多阻力讓整體變得非常漫長，然而透過 AI 工具的輔助，大約三天左右就完成基本的調整，也許這篇文章發佈的當下已經整理完畢釋出最新的版本也說不定。

回到最初的問題，與其說是 AI 能力不足，不如說 AI 把「設計」這個變數放大了——好的設計讓 AI 跑得更遠，壞的設計則更快讓專案翻車。

就結論來看，使用 AI 開發會更快的撞到過去軟體開發中本來就會遇到的挑戰，如果一直放任不管最終還是會發展成[大泥球（Big Mud Ball）](https://zh.wikipedia.org/zh-tw/%E5%A4%A7%E6%B3%A5%E7%90%83_(%E7%BC%96%E7%A8%8B))的狀態，傳統開發的理論依舊很有用，而且會比過去更快遇到需要用到的時間點。
