
在軟體開發時經常可以聽到業主說:「這個需求很簡單,大概兩天就能好」?而且有了 AI 工具就更常聽到,還會被 PUA:「 這麼小的忙,你都不肯幫一下嗎?」
以下是摘要取網路上的想像故事,故事在對比「隨性開發」跟「導入 SDD 與 TDD 之後」的差異。這個開發案例中出現的致命問題、業界推薦的解決方案、引入 User Story、SDD 與 TDD 的防禦性開發思維,以及在 AI 時代下我們該如何優化開發流程,避免落入「無限外審與燒 Token」的窘境。
在資工系的「軟體工程」課堂上,四個充滿自信的大三學生接下了看似最基礎的專題——做一個「簽到系統」。最初的需求只有簡短的三行:老師建立課程、學生登入、學生按下簽到。預估工期:兩個星期。
然而,十三週過去了,系統從三張資料表暴增到二十七張資料表、六十三支 API、三十八個前端頁面,程式碼突破兩萬行。
這個故事真實地映照出所有軟體工程師、專案經理(PM)與團隊在真實世界中交手的血淚史。從需求蔓延(Scope Creep)、身分驗證的無底洞、邊界條件(Edge Cases),到併發衝突與滲透測試,每一個階段都是軟體工程的核心課題。
一、 系統開發常見問題與推薦解決辦法
1. 領域模型與資料庫設計:身分與關係的混亂
- 遇到的問題: 團隊初期將使用者簡單分為學生與老師,隨後陸續發現助教、一科多助教、學生兼任助教、老師變學生等複雜情況,導致資料庫 schema 頻繁重構。
- 推薦解決辦法:
- 採用 Domain-Driven Design(DDD,領域驅動設計): 在動手寫 code 前,先進行領域建模(Domain Modeling)。劃分「身分(User Identity)」與「角色(Role/Permission)」。
- 解耦關係: 使用
CourseMember或UserRoleMapping等中介表來處理多對多關係(Many-to-Many),將「人」與「人在特定課程中的權限」完全分離。
什麼是「領域模型」與「資料庫模型」的差別
領域模型不等於資料庫模型,兩者雖然都在描述資料,但方向完全不同。
領域模型專注於業務邏輯,用來理清現實世界裡的人、事、物彼此有什麼關係,完全不管資料庫要怎麼存。
資料庫模型則是實體關聯模型(Entity – Relationship Model),專注於技術實作,規定資料要在磁碟裡建幾張表、欄位型態是什麼,以及外來鍵要怎麼連。
簡單來說,領域模型是想怎麼解決業務問題,而資料庫模型是想怎麼把資料塞進電腦。
2. 輪子重造:身分驗證(Authentication)的無底洞
- 遇到的問題: 為了處理一個「登入」,團隊延伸出了雜湊、忘記密碼、Email 驗證、Token 過期、Session 管理、防爆破攻擊等一整套複雜機制,嚴重偏離核心業務。
- 推薦解決辦法:
- 不要自己建立 Identity System: 業界最佳實踐是直接對接 OAuth 2.0 / OIDC 或第三方認證服務(如 Azure AD / Entra ID、Google Workspace、Firebase Auth、Supabase Auth 等)。
- 校園或企業整合: 直接串接學校現有的 SSO(單一簽入)或校園帳號,省去維護帳密與安全性檢查的龐大負擔。
3. 邊界案例與物理限制:過度滿足不可能的需求
- 遇到的問題: 為防止代簽,團隊陸續引入 GPS、IP 檢查、動態 QR Code、Device Fingerprinting(裝置指紋),卻發現 GPS 在室內定位失準、校園 IP 變動、學生拍照傳群組等邊界問題。
- 推薦解決辦法:
- 明確約束條件(Constraints): 軟體無法解決所有的道德或物理限制。在設計階段就必須設定邊界,而不是試圖用程式技術解決人性的代簽問題。
- 適度採用替代或併行方案: 如同網路討論所言,「紙本簽到線上替代頁面」重點在於省紙與效率。若防弊成本高於系統價值,應評估合理的風控門檻(例如:動態 QR Code 搭配隨機抽查即可,無需做到網路銀行等級的裝置綁定)。
4. 併發與資料一致性(Concurrency & Race Condition)
- 遇到的問題: 全班五十人同時按簽到時,資料庫出現重複紀錄。開發者因為系統報錯,竟選擇移除 Unique Constraint(唯一性約束)。
- 推薦解決辦法:
- 嚴格的資料庫約束: 絕不能為了避開報錯而移除 Unique Index。
- 鎖機制與原子操作(Transactions & Locking): 採用資料庫 Transaction,或在 Redis / DB 層實作分散式鎖(Distributed Lock)與悲觀/樂觀鎖,確保高並發下的資料寫入一致性。
5. 權限驗證的安全漏洞:把按鈕藏起來不叫權限控制
- 遇到的問題: 前端隱藏了管理員按鈕,但後端 Endpoint 完全沒有校驗權限,導致普通學生透過 API 即可刪除課程。
- 推薦解決辦法:
- 零信任(Zero Trust)與後端權限檢驗: 遵循 RBAC(Role-Based Access Control)或 ABAC(Attribute-Based Access Control),在每一個 API Endpoint 的 Middleware 或 Decorator 驗證 Request 的 JWT Token 與角色權限。
6. 環境不一致:在他電腦上可以跑(Works on My Machine)
- 遇到的問題: 本機測試正常,部署到伺服器卻出現白畫面;引入 Docker 容器化後,因為沒有排查環境變數與依賴,反而把錯誤一起打包。
- 推薦解決辦法:
- 基礎設施即程式碼(IaC)與 CI/CD: 使用標準化的 Dockerfile 與 docker-compose,並搭配 CI/CD pipeline 進行自動化建置與測試。
- 環境變數隔離: 嚴格區分
.env.development與.env.production,確保部署環境與本機環境高度一致。
二、 工程心法拯救盲點:User Story、SDD 與 TDD
故事中學生們經歷的最大挫折,在於「走一步算一步」的隨機應變(Ad-hoc)。如果在專案第一天就嚴格導入 User Story、SDD(規格驅動開發) 與 TDD(測試驅動開發),整套系統的命運將截然不同。
1. User Story 與 Acceptance Criteria:第一天就劃定邊界
故事初期的三行需求只是一張沒有約束的「功能清單(Feature List)」。真正的 User Story 必須包含 Who(誰)、What(做什麼)、Why(價值是什麼),並搭配明確的 Acceptance Criteria(驗收標準)。
- 定義 User Story:
「作為一名學生,我想要在課堂進行中按下簽到按鈕,以便順利記錄我的出席率。」 - 定義 Acceptance Criteria(驗收標準):
- 學生必須在課程開啟後的 10 分鐘內點擊。
- 點擊時僅需通過基本的身份驗證,不需要進行地理或裝置綁定(第一階段 MVP 範疇)。
- 重複點擊不會產生多筆紀錄,系統應回傳「已完成簽到」。
透過 Acceptance Criteria,團隊可以在第一週就明確釐清什麼是 In Scope(範圍內)、什麼是 Out of Scope(範圍外),徹底絕育後續無限蔓延的需求。
2. SDD(Specification-Driven Development):寫程式前先建立契約
SDD(規格驅動開發) 強調「規格即契約」。在開工寫程式之前,團隊必須先產出嚴謹且可檢驗的文件,包括 API 規格(如 OpenAPI/Swagger)、資料庫 ERD 結構、權限對照矩陣與邊界例外處理規範。
- 避免設計空窗: 當
POST /api/v1/attendance的 Request/Response 規格與 Error Code 在開發前就已確定,前端與後端便能並行開發,不會到第五週才發現重複簽到時後端不知該回傳什麼。 - 阻止盲目擴張: 任何新需求的加入(如請假系統、通知推播),都必須先修改並審核 Specification。這能逼迫團隊思考新增規格的成本,而不是口頭上一句「加個功能很簡單」就隨意動工。
3. TDD(Test-Driven Development):從「組合崩潰」到「一開始就對」
故事第十週最經典的慘劇是:單元測試各自正常,合在一起全面崩潰。這正是典型的「先寫程式、後補測試」所產生的排斥反應。
TDD 的核心紅綠燈循環(Red-Green-Refactor):
- Red(紅燈): 根據 Acceptance Criteria 寫出一個會失敗的測試(先定義預期行為)。
- Green(綠燈): 寫出剛剛好能讓測試通過的最少程式碼。
- Refactor(重構): 優化程式碼結構,同時確保測試持續保持綠燈。
如果團隊一開始就寫好測試:「全班 50 人在同一秒併發點擊簽到,資料庫最終必須且只能寫入 50 筆不重複紀錄」,那麼併發衝突與 Unique Constraint 的問題,在第一天跑測試時就會立刻被逼出來,而不會拖到第十二週上線驗收時才引爆。
三、 在 AI 時代,軟體工程該如何優化?
隨著 ChatGPT、Claude、GitHub Copilot 等 AI 工具的普及,許多人認為寫程式的門檻降低了。然而,AI 時代的軟體工程,重點不再是「如何寫出 Code」,而是「如何精準定義需求與架構」。
如果這個團隊在第一週就使用 AI 輔助開發,他們會面臨什麼情況?
1. 避免「無限外審與燒 Token」:Prompt 與 規格精準化
在 AI 時代,如果需求沒有在一開始說清楚,AI 生成 Code 的速度越快,架構崩壞的速度也越快。
- PRD 與 Prompt 結合: 在請 AI 生成程式碼前,先利用 AI 進行「需求訪談模擬」。給予 AI 角色扮演設定(例如:「請扮演資深系統架構師,針對我們的簽到系統需求提出潛在的邊界條件與邊界漏洞」)。
- 明確寫出限制條件: 透過 Prompt 告訴 AI 框架限制(例如:「使用 Supabase Auth 處理登入」、「採用 RBAC 架構」、「不要自建身分驗證」),減少 AI 盲目生成低效或高風險代碼的機會。
2. 善用 AI 進行領域建模、SDD 規格與 Schema 設計
AI 非常擅長處理標準化的架構設計與規格生成。
- ** Schema 與 ERD 生成:** 在設計資料庫初期,可以將業務情境描述給 AI,讓 AI 協助生成完整包含 Edge Cases 的 Entity-Relationship Diagram(ERD)或 SQL DDL。
- API 規格先行: 讓 AI 先根據 User Story 產生 OpenAPI (Swagger) 規格檔,確認前端與後端資料結構一致後,再由 AI 生成對應的 Controller 與 Service 骨架。
3. AI 驅動的 TDD:先讓 AI 寫測試,再讓 AI 寫程式
在 AI 時代,TDD 是防止 AI 「亂寫程式(Hallucination Code)」的最強護欄。
- 將 User Story 與 SDD 輸入給 AI,要求 AI 先寫出完整的單元測試與整合測試案例(Test Cases)。
- 審核測試案例是否符合業務邏輯(如:併發寫入、過期 Token 阻斷、權限越權檢查)。
- 讓 AI 撰寫業務邏輯程式碼,並以「通過所有測試」為唯一的驗收標準。
- 這種方式能確保 AI 生成的程式碼完全符合預期,不會隨著需求疊加而走山。
結語:最成熟的技術判斷,是不寫不必要的程式
故事的結尾非常耐人尋味:老師因為開錯舊網址而以為系統壞掉,冠宇沒有選擇去寫網址導向、快取清除或自動更新,而是說了一句:
「老師,下次請用書籤。」
這不是逃避,而是軟體工程師最寶貴的素養——判斷問題的邊界與解決成本。
世界上其實沒有簡單的系統,只有還沒開始問問題的需求。軟體工程的本質,是在複雜度、開發成本、使用者體驗與安全性之間取得平衡。
在 AI 工具極度發達的今天,寫程式的技術門檻正在降低,但「系統設計」、「業務邏輯梳理」、「User Story 邊界劃定」與「TDD 測試驗證」的能力比以往任何時刻都更加重要。懂得在關鍵時刻說「不」,懂得利用既有的成熟輪子,才是軟體工程師真正的核心價值。
資工系大三有一門「軟體工程(Software Engineering)」,期末規定四人一組做專題。
老師在第一堂課特別強調:「題目不用偉大,我不希望看到什麼人工智慧城市、區塊鏈醫療平台、量子運算社群網站。你們只要做一個小而完整、真的可以用的系統。」
坐在第三排的冠宇聽完,轉頭跟組員說:「那我們做簽到系統好了。」
另外三個人一致同意。
因為大家都知道,簽到系統很簡單。
至少那天下午,他們是這麼認為的。
最初的需求只有三行:
老師建立課程。
學生登入。
學生按下簽到。
冠宇看著白板說:「這大概兩個禮拜。」
負責前端的育誠說:「介面一天。」
負責後端的俊豪說:「API兩天。」
負責資料庫的宗翰說:「資料表三張就夠。」
四個人互相看了一眼。
那是一種資工系學生特有的自信。
通常出現在專案開始之前,以及事情還沒真正發生之前。
第一週,他們建立了三張資料表:User、Course、Attendance。
老師看完問:「學生跟老師都是User嗎?」
冠宇回答:「對,用role區分。」
老師點頭:「那助教呢?」
四個人沉默五秒。
宗翰打開資料庫。
第四張表出現了。
TA。
老師又問:「一門課可以有兩個助教嗎?」
宗翰沉默十秒。
TA表消失了。
CourseMember表出現了。
老師再問:「那有些助教同時也是其他課的學生呢?」
宗翰盯著螢幕說:「沒問題,這就是多對多關係(Many-to-Many Relationship)。」
老師說:「很好。」
宗翰鬆了一口氣。
老師接著問:「那老師如果下學期變成另一門課的學生呢?」
宗翰把筆電闔上。
「老師,我覺得我們應該先討論人的本質。」
第二週,他們開始做登入。
原本的設計非常單純:帳號、密碼。
俊豪說:「密碼不能明碼儲存,要做雜湊(Hashing)。」
冠宇說:「當然。」
育誠問:「忘記密碼呢?」
三個人安靜下來。
俊豪說:「寄Email。」
宗翰問:「Email怎麼驗證?」
俊豪說:「驗證信。」
冠宇問:「驗證連結多久失效?」
俊豪說:「三十分鐘。」
育誠問:「使用者同時按五次忘記密碼,前四個連結還能不能用?」
俊豪沒有回答。
他只是把原本寫著「登入功能」的工作項目,改成了「身分驗證模組(Authentication Module)」。
過了兩天,那個模組下面已經有登入、登出、重設密碼、Email驗證、Token更新、Session管理、錯誤登入次數限制、裝置登入紀錄。
冠宇看了一眼說:「我們是不是偏離簽到系統了?」
俊豪說:「沒有,使用者要先登入才能簽到。」
這句話邏輯上完全正確。
也因此沒有人能阻止事情繼續惡化。
第三週,簽到按鈕終於完成。
育誠很開心地把畫面秀給大家看。
正中央一個藍色按鈕,上面寫著「簽到」。
冠宇按了一下。
成功。
大家鼓掌。
那是整個專案最後一次純粹的快樂。
隔天老師來看進度。
老師問:「學生如果人在家裡,可以簽到嗎?」
冠宇說:「目前可以。」
老師問:「那這樣簽到有什麼意義?」
四個人又沉默了。
育誠提議:「用GPS。」
俊豪說:「瀏覽器可以取得定位。」
宗翰說:「那就判斷距離教室一百公尺內才能簽。」
冠宇覺得有道理。
於是他們加入地理定位(Geolocation)。
兩天後第一次測試。
四個人全部坐在同一間教室。
冠宇的手機顯示距離教室17公尺。
育誠顯示42公尺。
俊豪顯示126公尺。
宗翰顯示他人在隔壁行政大樓。
宗翰抬頭看大家。
「我肉體在這裡,但資料不同意。」
他們查了一整晚,終於理解GPS在室內不是神諭。
老師隔天問:「那怎麼辦?」
冠宇說:「改用Wi-Fi。」
「怎麼判斷?」
「檢查IP。」
老師說:「學生開VPN呢?」
冠宇第一次產生了一個念頭。
也許點名簿其實是人類文明很成熟的技術。
但他沒有說出口。
資工系有一種奇怪的職業尊嚴:當紙本方法明顯比較簡單時,絕對不能太早承認。
於是他們開始研究校園無線網路。
研究兩天後,題目從「簽到系統」變成了「基於網路環境與地理資訊之多因子課堂出席驗證平台」。
老師看著題目問:「你們是不是一開始只想做一個按鈕?」
冠宇說:「需求演化(Requirement Evolution)。」
老師點點頭。
「很好,你們已經學會軟體工程最重要的一件事。」
四個人期待地看著老師。
老師說:「替失控取一個專業名稱。」
第四週,真正的問題出現了。
有人提出:「如果學生把帳號密碼給同學,請同學代簽呢?」
冠宇說:「那做QR Code。」
每堂課老師產生一組QR Code,學生掃描後簽到。
大家覺得很合理。
直到育誠說:「拍照傳到班群就好了。」
QR Code開始每三十秒更新一次。
俊豪說:「那用動態Token。」
宗翰說:「要不要綁裝置?」
冠宇說:「怎麼綁?」
「Device Fingerprinting。」
大家安靜了一下。
育誠問:「我們現在是在做簽到,還是在做網路銀行?」
沒有人回答。
因為俊豪已經開始查Device Fingerprinting的文件。
第五週,他們遇到併發(Concurrency)問題。
老師測試時讓全班五十人同時按簽到。
資料庫瞬間出現十二筆重複紀錄。
宗翰看著結果,臉色非常平靜。
那不是因為他不緊張。
而是一個人在真正理解事情有多糟之後,反而會進入某種哲學狀態。
他說:「理論上有Unique Constraint。」
俊豪問:「那為什麼還有重複?」
宗翰說:「因為昨天測試的時候我先拿掉了。」
「為什麼?」
「它一直報錯。」
俊豪說:「它報錯就是因為有重複資料啊。」
宗翰點頭。
「對。」
「所以你怎麼處理?」
「我把會報錯的東西拿掉。」
冠宇坐在旁邊,突然理解了很多現實世界的資訊系統為什麼會變成今天這個樣子。
第六週,老師要求加上請假功能。
冠宇說:「沒問題,小功能。」
其他三個人同時看著他。
經過前五週,他們已經知道「小功能」是一種極度不吉利的詞。
果然,請假功能一開始只有:日期、原因。
然後有人問:「公假跟事假要不要分?」
要。
「病假要不要附件?」
要。
「附件格式?」
PDF、JPG、PNG。
「大小限制?」
10MB。
「請假誰審核?」
老師。
「老師不在呢?」
助教。
「助教可以審自己的假嗎?」
不行。
「學生撤回後可以重新送嗎?」
可以。
「老師已經核准後還能撤回嗎?」
不知道。
「跨兩堂課呢?」
……
冠宇看著需求文件,突然說:「我發現我們不是在做簽到系統。」
育誠問:「那我們在做什麼?」
冠宇說:「學校。」
第七週,他們加入通知系統(Notification System)。
學生簽到成功要通知。
簽到失敗要通知。請假送出要通知。請假核准要通知。請假駁回要通知。
老師忘記開放簽到也最好通知。助教最好也收到。
然後問題來了:「通知用什麼?」
站內通知。
Email。
之後有人提議加推播通知(Push Notification)。
冠宇立即說:「不要。」
這是他第一次在專案裡成功阻止一項需求。
全組對他的領導能力重新產生敬意。
三天後老師問:「手機沒有開網頁,也能收到提醒嗎?」
冠宇低頭說:「我們會研究Push Notification。」
第八週,前端開始出問題。
育誠做了一個漂亮的簽到頁面。
桌面版很好看。
手機一開,按鈕跑到畫面外。
他修了響應式網頁設計(Responsive Web Design)。
平板又壞掉。
修完平板,Safari壞掉。
Safari修完,深色模式文字消失。
深色模式修完,有人把瀏覽器字體放大到200%。
整個介面像地震。
育誠盯著CSS三個小時,最後說:「人類為什麼需要這麼多尺寸?」
俊豪說:「因為世界不是固定寬度。」
育誠回答:「那世界設計得不好。」
第九週,資安問題來了。
老師找了一個學長幫忙做簡單的滲透測試(Penetration Testing)。
學長坐下不到十分鐘就問:「你們API有做權限驗證嗎?」
俊豪說:「有啊,前端沒有顯示老師功能。」
學長抬起頭。
「我問的是後端。」
全組沉默。
學長直接送出一個HTTP Request。
學生帳號成功刪掉整門課。
那一天,四個人第一次真正理解一件事:把按鈕藏起來,不叫權限控制。
宗翰問:「所以要做Role-Based Access Control?」
學長說:「對。」
俊豪說:「每個Endpoint都要檢查?」
學長說:「對。」
俊豪看著目前的四十七個Endpoint。
「我突然覺得被駭也是一種使用者體驗。」
第十週,他們開始寫測試。
老師要求單元測試(Unit Test)、整合測試(Integration Test)。
俊豪寫了第一個測試。
通過。
第二個。
通過。
第三個。
失敗。
他修改程式。
第三個通過。
第一個失敗。
他再修改。
三個都通過。
整合測試全部失敗。
育誠問:「為什麼?」
俊豪說:「因為每個零件單獨都正常。」
「那合在一起呢?」
「就不正常。」
冠宇說:「這句話很像我們這一組。」
沒有人笑。
因為大家知道這已經不是笑話。
第十一週,專案終於部署到伺服器。
本機測試完全正常。
部署後首頁白畫面。
俊豪說:「不可能,我電腦可以。」
老師剛好走過來,聽見這句話。
老師停下腳步。
「你們知道軟體工程裡最沒有資訊量的一句話是什麼嗎?」
大家搖頭。
老師說:「『在我電腦上可以跑(Works on My Machine)。』」
俊豪想反駁。
但他的確只有自己的電腦可以跑。
於是他們開始研究容器化(Containerization)。
冠宇問:「Docker會不會太複雜?」
俊豪說:「不會,它就是把環境包起來。」
六小時後,四個人圍著一個起不來的Container。
育誠問:「現在呢?」
俊豪說:「現在我們把問題也一起包起來了。」
第十二週,系統上線測試。
老師登入。
學生登入。
QR Code正常。
定位正常。
簽到正常。
請假正常。
通知正常。
所有人感動得差點站起來鼓掌。
老師看著畫面,點了幾下。
然後問:「如果老師開錯課程的簽到,可以取消嗎?」
四個人臉上的笑容同時消失。
冠宇說:「目前不行。」
老師說:「那加一下,應該很簡單吧?」
這句話像一枚手榴彈掉進會議室。
沒有人動。
老師疑惑地問:「怎麼了?」
冠宇很慎重地說:「老師,經過十二週,我們組內已經禁止使用『簡單』這個詞。」
老師笑了。
「那我換個說法,只要增加取消簽到。」
宗翰問:「取消之後原本簽到紀錄要刪除嗎?」
老師想了一下。
「最好保留。」
俊豪問:「那狀態改成Cancelled?」
「可以。」
育誠問:「學生畫面要顯示什麼?」
「顯示這次簽到已取消。」
冠宇問:「如果老師取消之後重新開放?」
老師停頓一下。
「那應該算新的吧。」
宗翰問:「新的Session?」
「對。」
俊豪問:「那舊Session的學生如果已經請假呢?」
老師沉默。
育誠問:「通知要重新發嗎?」
老師沉默更久。
冠宇慢慢把筆電轉向老師。
「老師。」
「嗯?」
「歡迎加入我們。」
第十三週,期末驗收。
四個人穿得比平常正式。
簡報第一頁寫著:「智慧課堂出席暨身分驗證管理平台」。
老師看到標題笑了一下。
「你們一開始不是說要做簽到系統?」
冠宇說:「是。」
「現在有多少資料表?」
宗翰回答:「二十七張。」
「API?」
俊豪說:「六十三支。」
「前端頁面?」
育誠說:「三十八個。」
老師問:「程式碼多少?」
俊豪說:「不算套件,大概兩萬多行。」
老師靠在椅背上。
「所以你們花十三週、四個人、兩萬多行程式,最後完成了什麼?」
冠宇操作系統。
建立課程。
開啟簽到。
QR Code產生。
學生掃描。
GPS檢查。
Token驗證。
權限驗證。
資料寫入。
通知送出。
畫面跳出綠色提示。
「簽到成功。」
教室裡非常安靜。
老師看著那四個字。
又看看四個學生。
「所以最後,還是讓學生按一下簽到?」
冠宇回答:「對。」
老師問:「那跟你們第一週做的有什麼不同?」
四個人想了很久。
最後宗翰說:「第一週的時候,我們不知道自己不知道什麼。」
老師笑了。
「現在呢?」
宗翰說:「現在我們知道不知道的東西還很多。」
老師點頭。
「很好,這才是這門課真正的成果。」
驗收結束後,四個人走出教室。
育誠問:「下學期專題要不要繼續做這個?」
冠宇立刻說:「不要。」
俊豪說:「我想做簡單一點的。」
宗翰問:「例如?」
俊豪想了一下。
「待辦事項系統(To-Do List)。」
三個人停下腳步。
冠宇很謹慎地問:「使用者可以共用待辦事項嗎?」
「應該可以。」
「可以指定負責人嗎?」
「可以。」
「截止日呢?」
「有。」
「重複任務?」
「可以加。」
「附件?」
「好像需要。」
「通知?」
「也要。」
「權限?」
俊豪開始不說話。
育誠補了一句:「如果兩個人同時修改同一個待辦事項呢?」
宗翰接著問:「刪掉的任務要不要可以復原?」
冠宇問:「子任務呢?」
俊豪站在走廊中央,沉默了足足十秒。
最後他說:「不然我們做計算機。」
其他三人同時看向他。
沒有人立刻答應。
因為經過一學期的軟體工程訓練,他們已經具備了真正的專業素養。
遇到任何看起來很簡單的需求,都會先懷疑。
過了一會兒,宗翰才小心翼翼地問:「哪一種計算機?」
俊豪說:「就加減乘除。」
冠宇問:「小數呢?」
「要。」
「負數?」
「要。」
「除以零?」
「要處理。」
「括號?」
「……」
「運算優先順序?」
「……」
「歷史紀錄?」
「……」
「浮點數精度(Floating-Point Precision)?」
俊豪抬起頭,看著走廊盡頭。
「算了。」
「怎樣?」
「我們還是做簽到系統好了。」
三個人點點頭。
因為在資工系待久了,你最後會理解一件事:世界上其實沒有簡單的系統。只有還沒開始問問題的需求。
而所謂的軟體工程,就是一群人先花三個月,把所有問題問出來,再花三個月發現原來前面還漏問了一半。
至於那套簽到系統,後來真的被系上拿去試用了。
第一天上午九點,冠宇收到老師的訊息:「系統是不是壞了?我按開始簽到沒有反應。」
四個人立刻上線查Log。
資料庫正常。
API正常。
伺服器正常。
網路正常。
權限正常。
前端正常。
他們查了二十分鐘,什麼問題都找不到。
最後冠宇跑去教室。
老師坐在講桌前,把筆電轉給他看。
冠宇只看了一眼就知道原因。
老師開的是上學期留下來的測試網站。
冠宇沉默了一會兒,幫老師換成正式網址。
系統立刻正常。
老師有點不好意思地問:「這種情況有辦法避免嗎?」
冠宇站在講桌旁。
那一瞬間,他腦中閃過網址重新導向、舊版停用頁面、環境標示、版本檢查、自動更新、DNS設定、部署流程、快取清除,以及至少八種新的例外處理。
他想起十三週前,那個相信「簽到系統兩個禮拜就能做好」的自己。
然後他做出了這學期最成熟的一次技術判斷。
他說:「老師,下次請用書籤。」
老師點點頭。
「好。」
問題解決。
沒有新增資料表。
沒有新增API。
沒有新增套件。
甚至沒有寫一行程式。
冠宇走出教室時,第一次真正感受到自己學會了軟體工程。
不是因為他終於知道怎麼解決所有問題。
而是他終於知道:不是所有問題,都值得寫程式解決。
