iT邦幫忙

2026 iThome 鐵人賽

DAY 2
0
AI Engineering

AI 寫 Code 之後,我們還需要 Software Architecture 嗎?系列 第 2

Day 02:案例——沒有架構邊界時,AI 用最短路徑寫出的程式碼長什麼樣

  • 分享至 

  • xImage
  •  

前言:這不是「AI 寫得爛」,是「AI 選了最短路徑」

「AI 生成的程式碼看起來很亂,是不是因為模型能力還不夠?」

如果你也這樣想過,先重新想一次:AI 寫程式碼的能力,跟它寫出來的程式碼會長成什麼樣子,其實是兩件不同的事。AI 在沒有約束的情況下,會選擇「當下能讓需求動起來」的最短路徑——不是因為它寫不出更好的結構,而是因為沒有任何規則告訴它「這裡不該放這個」。 今天用一個具體案例,看清楚這件事實際發生時長什麼樣子。

今日目標

  • 看一個「沒有架構邊界」時,AI 實際會寫出什麼樣的程式碼
  • 理解「AI 寫得亂」背後真正的原因:不是能力問題,是約束缺席
  • 認識「技術債」跟「架構缺席造成的債」之間的差異
  • 建立今天案例跟系列主題句之間的連結,為後面 28 天鋪路

案例:一個被要求「加個功能」的 Controller

假設有一個需求:「使用者送出訂單後,扣庫存、算折扣、寄通知信、寫一筆稽核紀錄」。把這個需求丟給一個沒有任何架構約束的 AI agent,只給它一句話的需求描述,它多半會寫出類似這樣的東西:

❌ 沒有架構邊界時,AI 寫出的版本:
class OrderController
{
    public function submit(Request $request): Response
    {
        $order = $this->db->query(
            "INSERT INTO orders ..."
        );

        $stock = $this->db->query(
            "SELECT quantity FROM inventory WHERE product_id = ?"
        );
        if ($stock < $request->quantity) {
            throw new Exception('庫存不足');
        }
        $this->db->query("UPDATE inventory SET ...");

        $discount = $request->amount > 1000 ? 0.1 : 0;
        $finalAmount = $request->amount * (1 - $discount);

        $mailer = new SmtpMailer('smtp.example.com');
        $mailer->send($request->email, '訂單確認', "...");

        file_put_contents('/var/log/audit.log', json_encode([...]) . "\n");

        return new Response(['order_id' => $order->id]);
    }
}
→ 資料庫存取、業務規則(折扣計算)、外部服務(寄信)、
  基礎設施(寫檔案 log)全部寫在同一個方法裡,
  沒有任何一行程式碼是「錯」的,但整段程式碼沒有任何邊界

這段程式碼能動、邏輯也不難懂。但如果你請另一個 AI agent(或者是三個月後的你自己)去修改「折扣規則」,它得先讀懂整個方法在幹嘛,才敢動手改一行——因為沒有任何線索告訴它「折扣計算」跟「資料庫存取」是兩件不相干的事,可以分開改而不互相影響。

為什麼 AI 會選擇這樣寫

這不是 AI「不知道」該怎麼分層——如果你直接問它「Controller、Service、Repository 分別該負責什麼」,它答得出來一套教科書等級的答案。 問題在於:知道分層原則,跟在寫這一段程式碼的當下主動套用分層,是兩件事。

當需求本身就是一句話(「送出訂單後做這些事」),而沒有任何外部訊號告訴 AI「這個專案期待資料庫存取要放在哪裡、業務規則要放在哪裡」時,AI 會依循一個很自然的邏輯:把完成這個需求所需要的每一步,依照它們被提到的順序,依序寫進同一個函式裡。 這是完成單一需求的最短路徑,也是「看起來能動」的最快方式。如果 AI 有能力預測「這段程式碼一個月後會不會被罵」,它多半會選擇分層;但它沒有這個回饋訊號,它只有「這個需求現在動了嗎」這一個立即可驗證的訊號。

架構邊界要解決的,正是這個回饋訊號的缺口——它把「以後會不會被罵」提前變成「現在能不能通過某條具體規則」,讓 AI 不需要預測未來,只需要遵守一條當下就能檢查的規則。

有架構邊界時,同一個需求會被寫成什麼樣子

同樣的需求,如果專案有清楚的分層邊界(Controller 只處理請求/回應、業務規則收斂進 Service、資料存取收斂進 Repository、外部服務收斂進各自的介面),AI 依循這些邊界寫出來的版本會長這樣:

✅ 有架構邊界時,AI 寫出的版本:
class OrderController
{
    public function __construct(
        private OrderService $orderService,
    ) {}

    public function submit(Request $request): Response
    {
        $order = $this->orderService->placeOrder(
            $request->productId,
            $request->quantity,
            $request->amount,
            $request->email,
        );
        return new Response(['order_id' => $order->id]);
    }
}

class OrderService
{
    public function __construct(
        private InventoryRepository $inventory,
        private OrderRepository $orders,
        private NotificationSender $notifier,
        private AuditLogger $auditLogger,
    ) {}

    public function placeOrder(
        int $productId, int $quantity, float $amount, string $email
    ): Order {
        $this->inventory->reserve($productId, $quantity);
        $finalAmount = $this->calculateDiscount($amount);
        $order = $this->orders->create($productId, $quantity, $finalAmount);
        $this->notifier->send($email, '訂單確認');
        $this->auditLogger->log('order.placed', $order->id);
        return $order;
    }

    private function calculateDiscount(float $amount): float
    {
        return $amount > 1000 ? $amount * 0.9 : $amount;
    }
}
→ 每一段職責都有自己的邊界;要改折扣規則,
  只需要進 OrderService::calculateDiscount(),
  完全不用碰資料庫存取或寄信的邏輯

這裡的重點不是「第二個版本比較長、比較『正規』」,而是第二個版本裡的每一段程式碼,都有一個明確的地方屬於它——而這件事不是 AI 自己想到的,是架構邊界(分層規則、介面約定)事先替它劃好的。同一個 AI、同樣的能力,寫出來的東西完全取決於有沒有邊界可以依循。

技術債,還是架構缺席造成的債?

第一個版本的問題,很容易被籠統地歸類成「技術債」——但這個歸類其實模糊了問題的根源。真正的技術債,是團隊在某個時間點刻意選擇了一條較快、較不完美的路徑,並且知道自己欠了什麼;而架構缺席造成的債,是根本沒有人(或沒有規則)在場,讓「該分層」這件事連被考慮的機會都沒有。 前者是一筆記在帳上的債,後者連帳本本身都不存在。

這個區分很重要,因為兩者的解法不同:技術債的解法是排優先序、找時間償還;架構缺席的解法是先把帳本立起來——也就是先建立起分層規則、依賴方向、介面邊界這些東西,讓下一次同樣的需求進來時,AI(或任何人)有東西可以依循,而不是每次都要重新從零判斷「這段邏輯該放哪裡」。

今日思考題

回想你上一次請 AI 幫你加一個功能:它把邏輯全部塞進同一個地方,還是各自歸位到清楚的層級?如果是前者,你的專案裡有沒有任何東西(文件、範例程式碼、既有的分層慣例)告訴它「該怎麼分」?

今日重點回顧

  • AI 在沒有架構約束時,會選擇「完成需求的最短路徑」——把所有邏輯依序寫進同一個地方,這不是能力不足,是沒有回饋訊號告訴它該分層
  • 架構邊界的作用,是把「以後會不會被罵」的長期訊號,變成「現在能不能通過某條規則」的即時訊號
  • 技術債是團隊刻意選擇、知道自己欠了什麼;架構缺席造成的債,是連「該分層」的判斷機會都不存在
  • 解法不是事後補救,是先把分層規則、依賴方向這些「帳本」立起來

明日預告

明天要往下深挖一層:架構到底在「約束」什麼?答案不是規範「程式碼該怎麼寫」,而是限制「一次改動能碰到哪裡」——這個定義上的轉換,會讓後面 27 天講的每一個具體工具都有一個共同的判準。


上一篇
Day 01:系列介紹:AI 寫 code 快,但架構的角色改變了什麼
系列文
AI 寫 Code 之後,我們還需要 Software Architecture 嗎?2
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言