[{"content":" 完整論文：seam_carving.pdf · 程式碼：YuXiangLo/NTUPDP2026\n與吳雅蓁、羅宇翔合作，台大平行程式設計，Spring 2026。\n起點 Seam carving 是一種 content-aware 的圖像縮放演算法。它不是裁切或縮放，而是移除圖片中「最不重要」的像素路徑——seam——同時保留視覺上重要的區域。效果出乎意料地好。\n演算法主要有兩個沉重的步驟：先計算 energy map（梯度大小，對整張圖掃一遍）；再執行動態規劃（DP）找出從上到下代價最小的連通路徑。移除那條 seam，重複。\n在 CPU 上，這對大圖來說很慢。單核心處理一張 8K 圖（7680×4320，約 3300 萬像素）每條 seam 可能要幾百毫秒。我們有 V100。Energy 計算是完美的平行問題。問題看起來很明顯：能快多少？\n我們原本預期找到一個新的優化方式，拿到一個漂亮的數字。最後我們證明了為什麼漂亮的數字在結構上不可能達到——而這才是更有趣的結果。\nDP 的問題 用 Nsight Compute 對 1428×968 的圖做 profiling，問題立刻清楚了：DP kernel 佔每條 seam 牆上時間的 ~93%。其他全是雜訊。\nDP 的遞推關係是：\n1 M[i][j] = e[i][j] + min(M[i-1][j-1], M[i-1][j], M[i-1][j+1]) 每一行依賴上一行。全部 H 行都是串行的。每行內部的 W 列可以平行——但在第 i-1 行完成之前無法開始第 i 行，沒有任何重新排序能消除這個依賴。這就是我們一直碰到的牆。\n優化路徑 我們在每個步驟以 Nsight 為導引，建立了五個逐步改良的 kernel。\nNaive 每行啟動一個 CUDA kernel。在 4K（H=2160）時，每條 seam 要啟動 2159 次 kernel，每次都有完整的 global memory 往返。小圖上 overhead 主導，大圖上 global memory 流量主導。\nFused 用 shared-memory ping-pong buffer 把所有 H 行折疊進單一 kernel。行之間只用一次 __syncthreads()，2159 次 kernel 啟動變成 1 次，cost array 在行與行之間不再碰 DRAM，住在 96 KB 的 shared memory 裡。這是正確的結構；問題是能跑多快。\nPrefetch 來自閱讀 Nsight 輸出，不是直覺。Fused 的主要 stall 是 long_scoreboard——每個 warp 等 ~200 cycles 等 global load 讀 e[i][·]。修法是 software pipelining：在計算第 i 行（只讀 shared memory cost）的同時，每個 thread 把 e[i+1][·] 的 global load 提前發出存進 register。等 __syncthreads() 結束，load 已經 resolve 了。運算和記憶體傳輸重疊。這給出了最大的單步提升：kernel 層面比 Fused 快 1.67×。\nBytePtr 注意到 back-pointer——值只有 {-1, 0, +1}——卻用 4-byte int 儲存。改成 signed char 讓 back-store 流量縮減 4×，在 4K（3840×2160）時 back-pointer array 從 32 MB 縮到 8 MB per seam pass。\nTiled 是終於讓另外 79 個閒置 SM 動起來的設計。做法：把 W 列分成 K=60 條 strip（每個 SM 一條）。每條 strip 兩側各有 T 個 halo 列。在 T 行的 tile 內，一個 block 完全在 shared memory 裡工作，只在 tile 邊界把 center strip 寫回 global memory。\n正確性靠 seam-drift bound：在 T 行內，seam 最多偏移 T 列。任何誤差都在 halo 內。中心是 bit-exact。\n結果：kernel 啟動次數從 H 降到 ⌈H/T⌉（8K、T=64 時是 68 次），60 個 SM 同時活躍。輸出與單 block 設計 bit-identical。\n數字 在 8K（7680×4320），Tiled 達到 5.73 ms/seam——是我們所有 exact 設計中最快的。\n方法 ctrl 1080p 2K 4K 6K 8K CPU 單核 5.00 21.6 37.5 83.6 212 325 CPU 最佳（32 執行緒） 1.14 3.59 5.63 9.88 25.6 35.4 Naive 1.37 3.10 3.77 5.92 10.3 13.9 Tiled（我們的） 0.314 0.917 1.25 2.16 4.16 5.73 對比 CPU 最佳 3.6× 3.9× 4.5× 4.6× 6.2× 6.2× 比單核 CPU 快 57×。比調優過的 32 執行緒 NUMA-aware OpenMP 快 6.2×。最後這個數字是天花板——為什麼是天花板才是有趣的部分。\n天花板是結構性的：三個理由 到這裡，合理的問題是：我們只是不夠努力嗎？我們花了相當多時間問自己同樣的問題。答案是否定的，而且可以用三種方式說明。\n1. DP 的關鍵路徑是不可化簡的。\n依賴圖的最長路徑長度是 H——每行一個節點。任何排程都至少需要 H 個串行的 min-step。GPU 可以在每行內部平行化（W 列），但無法減少 H 個串行步驟。行內最佳平行化就是 Tiled 已經做到的。\n2. Transpose 幫不上忙。\n直覺上的想法：把圖旋轉，換個方向跑 DP。但 transpose 是 storage 地址上的雙射——只是重新標記了儲存，依賴圖不變，關鍵路徑還是 H。如果 transpose 後再 carve，算的是水平 seam（深度 W 而非 H），那是正交任務，不是原任務的更快版本。我們用實驗確認：對寬圖來說，transposed 方向更慢，因為 W \u0026gt; H。\n3. 把 DP 整個移掉也沒有提速。\n這是最令人驚訝的結果。我們實作了 greedy non-cumulative selection——沒有 cost matrix、沒有串行依賴、完全平行。它在每個解析度都和 Tiled 差在 3% 以內。為什麼？因為每條 seam 的 pipeline，不是 DP，才是端到端的瓶頸。8K 下，5.73 ms 分成 ~4.3 ms DP 和 ~1.4 ms preprocessing。Preprocessing 的下界（energy 計算 + seam 移除）需要讀取每個像素的 energy、寫出輸出——在 8K 是 ~398 MB，對 V100 的 ~900 GB/s 頻寬來說，無論用哪種 selection 方法都要花 ~1.4 ms。Greedy 沒有 DP，但同樣要付這個頻寬下界的代價。它贏不了。\n唯一有效的槓桿 有一個出口：改變問題本身。\nApproximate batch removal 把一次 DP pass 攤銷到 K=60 條 seam。一次前向 pass 產生 60 條 strip-local seam 候選；單一 kernel 在一次 image-wide 讀寫中移除全部 60 條。每條 seam 的代價從 Θ(HW) 變成 Θ(HW/K)。\n結果：8K 下 0.103 ms/seam——比 exact single-seam pipeline 快 56×。 代價是 strip-local 而非 global 最優：找到的 seam 是各自 strip 內的最佳，不保證是全域最小值。\n這就是正式產品 pipeline 實際採用的 batch mode。Exact per-seam 的設法在工業上不是重要的情境；我們研究它是因為結構性限制在這裡最乾淨。\n我們真正學到的 進去的時候，我們期望用新技巧超越前人的 GPU 實作。我們確實建出了我們找得到最強的 exact pipeline——Tiled 在每個解析度都比 Kim et al. 的 exact single-block DP 更快，Nsight 引導的 latency hiding 讓他們放棄的方案重新有競爭力。但比調優 CPU 快 6.2× 並不是他們近似方法達到的 76×。\n更有價值的輸出是理解「為什麼」。DP 的關鍵路徑不可化簡。Transpose 沒幫助。移掉 DP 沒幫助。頻寬下界跟演算法無關。這些不是實作的失敗——這些是問題的性質。\n有時候，最有用的結果是對可達前沿的嚴謹刻畫，加上解釋為何更多工程力氣也推不動它的不可能性論證。我們花在「證明天花板是真實的」的時間，比花在「撞上天花板」的時間還多。我認為這是讀這篇 paper 的正確方式。\n唯一的大槓桿是攤銷。如果你願意用 strip-local 最優換 global 最優，batch mode 給你 56×。如果你需要 exact 結果，在 V100 上 6.2× 已接近可達的最大值——而這個值和頻寬下界之間的差距（~1.3×）告訴你所剩不多了。\n","permalink":"https://lien0214.github.io/zh-tw/posts/seam-carving-gpu/","summary":"\u003cblockquote\u003e\n\u003cp\u003e完整論文：\u003ca href=\"https://github.com/YuXiangLo/NTUPDP2026/blob/main/paper/seam_carving.pdf\"\u003eseam_carving.pdf\u003c/a\u003e · 程式碼：\u003ca href=\"https://github.com/YuXiangLo/NTUPDP2026\"\u003eYuXiangLo/NTUPDP2026\u003c/a\u003e\u003cbr\u003e\n與吳雅蓁、羅宇翔合作，台大平行程式設計，Spring 2026。\u003c/p\u003e\n\u003c/blockquote\u003e\n\u003ch2 id=\"起點\"\u003e起點\u003c/h2\u003e\n\u003cp\u003eSeam carving 是一種 content-aware 的圖像縮放演算法。它不是裁切或縮放，而是移除圖片中「最不重要」的像素路徑——seam——同時保留視覺上重要的區域。效果出乎意料地好。\u003c/p\u003e\n\u003cp\u003e演算法主要有兩個沉重的步驟：先計算 energy map（梯度大小，對整張圖掃一遍）；再執行動態規劃（DP）找出從上到下代價最小的連通路徑。移除那條 seam，重複。\u003c/p\u003e\n\u003cp\u003e在 CPU 上，這對大圖來說很慢。單核心處理一張 8K 圖（7680×4320，約 3300 萬像素）每條 seam 可能要幾百毫秒。我們有 V100。Energy 計算是完美的平行問題。問題看起來很明顯：能快多少？\u003c/p\u003e\n\u003cp\u003e我們原本預期找到一個新的優化方式，拿到一個漂亮的數字。最後我們證明了為什麼漂亮的數字在結構上不可能達到——而這才是更有趣的結果。\u003c/p\u003e\n\u003ch2 id=\"dp-的問題\"\u003eDP 的問題\u003c/h2\u003e\n\u003cp\u003e用 Nsight Compute 對 1428×968 的圖做 profiling，問題立刻清楚了：DP kernel 佔每條 seam 牆上時間的 ~93%。其他全是雜訊。\u003c/p\u003e\n\u003cp\u003eDP 的遞推關係是：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\" id=\"hl-0-1\"\u003e\u003ca class=\"lnlinks\" href=\"#hl-0-1\"\u003e1\u003c/a\u003e\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-fallback\" data-lang=\"fallback\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003eM[i][j] = e[i][j] + min(M[i-1][j-1], M[i-1][j], M[i-1][j+1])\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003e每一行依賴上一行。全部 H 行都是串行的。每行內部的 W 列可以平行——但在第 \u003ccode\u003ei-1\u003c/code\u003e 行完成之前無法開始第 \u003ccode\u003ei\u003c/code\u003e 行，沒有任何重新排序能消除這個依賴。這就是我們一直碰到的牆。\u003c/p\u003e\n\u003ch2 id=\"優化路徑\"\u003e優化路徑\u003c/h2\u003e\n\u003cp\u003e我們在每個步驟以 Nsight 為導引，建立了五個逐步改良的 kernel。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eNaive\u003c/strong\u003e 每行啟動一個 CUDA kernel。在 4K（H=2160）時，每條 seam 要啟動 2159 次 kernel，每次都有完整的 global memory 往返。小圖上 overhead 主導，大圖上 global memory 流量主導。\u003c/p\u003e","title":"我們試著讓 Seam Carving 在 GPU 上跑更快——然後我們證明了為什麼它不能"},{"content":"核心問題 當語言模型被微調後相信 X，但預訓練時學到的是 Y——哪個會贏？更重要的是，能否在不重新訓練的情況下控制這個行為？\n我們做了什麼 跨格式資料增強 Pipeline：針對 ConflictQA 和 ConflictBank，生成語義等效的問題變體，用以測試模型在換句話說和格式轉換後對知識衝突的處理能力。這對於打造不過擬合到表面形式的 Benchmark 至關重要。\nActivation Steering 實驗：從 LoRA 微調模型中提取行為向量，並應用於凍結的基礎模型。行為方向的可遷移性出乎意料地強健——從 LoRA 訓練模型提取的 Steering Vector 即使不更新任何權重，也能將基礎模型的輸出推向預期方向。\n關鍵發現 微調在殘差流中注入了方向性偏置，且這種偏置在某種程度上可以和任務知識分離。如果你可以不更新權重就控制行為，那你也有可能撤銷微調訊號。這對 Alignment 研究有直接意涵。\n","permalink":"https://lien0214.github.io/zh-tw/projects/mem-gen/","summary":"\u003ch2 id=\"核心問題\"\u003e核心問題\u003c/h2\u003e\n\u003cp\u003e當語言模型被微調後相信 X，但預訓練時學到的是 Y——哪個會贏？更重要的是，能否在不重新訓練的情況下控制這個行為？\u003c/p\u003e\n\u003ch2 id=\"我們做了什麼\"\u003e我們做了什麼\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003e跨格式資料增強 Pipeline\u003c/strong\u003e：針對 ConflictQA 和 ConflictBank，生成語義等效的問題變體，用以測試模型在換句話說和格式轉換後對知識衝突的處理能力。這對於打造不過擬合到表面形式的 Benchmark 至關重要。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eActivation Steering 實驗\u003c/strong\u003e：從 LoRA 微調模型中提取行為向量，並應用於凍結的基礎模型。行為方向的可遷移性出乎意料地強健——從 LoRA 訓練模型提取的 Steering Vector 即使不更新任何權重，也能將基礎模型的輸出推向預期方向。\u003c/p\u003e\n\u003ch2 id=\"關鍵發現\"\u003e關鍵發現\u003c/h2\u003e\n\u003cp\u003e微調在\u003cstrong\u003e殘差流中注入了方向性偏置\u003c/strong\u003e，且這種偏置在某種程度上可以和任務知識分離。如果你可以不更新權重就控制行為，那你也有可能\u003cem\u003e撤銷\u003c/em\u003e微調訊號。這對 Alignment 研究有直接意涵。\u003c/p\u003e","title":"ICC Mem/Gen"},{"content":"系統架構 基於 FinLab 即時金融資料的完整交易系統，涵蓋從策略開發到自動執行的完整 Pipeline。\n策略層：在日 OHLCV 資料上的動量和均值回歸訊號。台股是散戶主導的市場，微觀結構與美股有顯著差異——許多西方量化文獻的規律無法直接移植，因此我們研究台灣特有的訊號。\n執行層：透過 crontab 自動化。三個排程工作分別處理帳戶資料抓取、獨立的回測報告，以及透過元大 Fugle API 和永豐 Shioaji API 的下單執行。透過 config.yaml 支援多用戶和多券商。\nDashboard：Web UI（Dash + Bootstrap + Gunicorn）顯示交易資訊、帳戶餘額圖表和月報酬率視覺化。以 Docker Compose 部署。\n目前：正在評估以 ML 進行訊號組合。紙交易持續運行中。\n技術棧 FinLab · shioaji · ta-lib · pandas · Dash · Flask · Docker\nGitHub 9 顆星 · v3.1.0 發布於 2026 年 1 月\n","permalink":"https://lien0214.github.io/zh-tw/projects/stock-analysis/","summary":"\u003ch2 id=\"系統架構\"\u003e系統架構\u003c/h2\u003e\n\u003cp\u003e基於 FinLab 即時金融資料的完整交易系統，涵蓋從策略開發到自動執行的完整 Pipeline。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e策略層\u003c/strong\u003e：在日 OHLCV 資料上的動量和均值回歸訊號。台股是散戶主導的市場，微觀結構與美股有顯著差異——許多西方量化文獻的規律無法直接移植，因此我們研究台灣特有的訊號。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e執行層\u003c/strong\u003e：透過 crontab 自動化。三個排程工作分別處理帳戶資料抓取、獨立的回測報告，以及透過元大 Fugle API 和永豐 Shioaji API 的下單執行。透過 \u003ccode\u003econfig.yaml\u003c/code\u003e 支援多用戶和多券商。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eDashboard\u003c/strong\u003e：Web UI（Dash + Bootstrap + Gunicorn）顯示交易資訊、帳戶餘額圖表和月報酬率視覺化。以 Docker Compose 部署。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e目前\u003c/strong\u003e：正在評估以 ML 進行訊號組合。紙交易持續運行中。\u003c/p\u003e\n\u003ch2 id=\"技術棧\"\u003e技術棧\u003c/h2\u003e\n\u003cp\u003eFinLab · shioaji · ta-lib · pandas · Dash · Flask · Docker\u003c/p\u003e\n\u003cp\u003e\u003cem\u003eGitHub 9 顆星 · v3.1.0 發布於 2026 年 1 月\u003c/em\u003e\u003c/p\u003e","title":"量化交易演算法 — 台股"},{"content":"昨天是我在 ShopBack 的特別一天——Town Hall 日，六個月旅程的句點。帶著滿滿的感謝、回憶，和對接下來的期待。\n在後端核心體驗團隊擔任軟體工程師實習生的這半年，我有機會參與各種有影響力的專案——從客服和 Watchlist Service，到 Notification Service，最後是 Travel 專案。能夠在高流量的真實系統中貢獻，留下第一個工程師的實際足跡，是非常難得的經歷。\n剛開始的時候，我一邊摸索一邊學習，有時候會卡關，但一直都在進步，一直都很享受這個過程。即使只是觸及了大規模系統設計的皮毛，我很慶幸能成為這個充滿熱情、樂於合作的團隊的一部分。\n透過這些經歷，我也深化了解決問題的方式——尤其是善用 AI 工具，做到從分析、Spec Review，到實作和報告的端到端流程。\n感謝我的主管 George Lee，他的智慧和對「Work Smarter」的示範讓我獲益良多。也感謝 Shih-Yuan Chen 和林一豪在客服服務的帶領，Hao-Ping Shih 在 Notification Service 的支持，以及 Nick Chen 和 Tim Pei 在 Travel 專案的指導——還有所有在這段旅程中給予幫助和協作的人。\n在 ShopBack 工作既鼓舞人心又令人興奮——置身於一個快速成長的公司和鼓勵主人翁精神與好奇心的文化中。\n這一章結束了，但所有的學習、友誼和能量，都會帶著往下走。\n向前，繼續前行。\n","permalink":"https://lien0214.github.io/zh-tw/posts/leaving-shopback/","summary":"\u003cp\u003e昨天是我在 ShopBack 的特別一天——Town Hall 日，六個月旅程的句點。帶著滿滿的感謝、回憶，和對接下來的期待。\u003c/p\u003e\n\u003cp\u003e在後端核心體驗團隊擔任軟體工程師實習生的這半年，我有機會參與各種有影響力的專案——從客服和 Watchlist Service，到 Notification Service，最後是 Travel 專案。能夠在高流量的真實系統中貢獻，留下第一個工程師的實際足跡，是非常難得的經歷。\u003c/p\u003e\n\u003cp\u003e剛開始的時候，我一邊摸索一邊學習，有時候會卡關，但一直都在進步，一直都很享受這個過程。即使只是觸及了大規模系統設計的皮毛，我很慶幸能成為這個充滿熱情、樂於合作的團隊的一部分。\u003c/p\u003e\n\u003cp\u003e透過這些經歷，我也深化了解決問題的方式——尤其是善用 AI 工具，做到從分析、Spec Review，到實作和報告的端到端流程。\u003c/p\u003e\n\u003chr\u003e\n\u003cp\u003e感謝我的主管 \u003cstrong\u003eGeorge Lee\u003c/strong\u003e，他的智慧和對「Work Smarter」的示範讓我獲益良多。也感謝 \u003cstrong\u003eShih-Yuan Chen\u003c/strong\u003e 和\u003cstrong\u003e林一豪\u003c/strong\u003e在客服服務的帶領，\u003cstrong\u003eHao-Ping Shih\u003c/strong\u003e 在 Notification Service 的支持，以及 \u003cstrong\u003eNick Chen\u003c/strong\u003e 和 \u003cstrong\u003eTim Pei\u003c/strong\u003e 在 Travel 專案的指導——還有所有在這段旅程中給予幫助和協作的人。\u003c/p\u003e\n\u003cp\u003e在 ShopBack 工作既鼓舞人心又令人興奮——置身於一個快速成長的公司和鼓勵主人翁精神與好奇心的文化中。\u003c/p\u003e\n\u003cp\u003e這一章結束了，但所有的學習、友誼和能量，都會帶著往下走。\u003c/p\u003e\n\u003cp\u003e向前，繼續前行。\u003c/p\u003e","title":"在 ShopBack 的六個月：後端實習的尾聲"},{"content":"背景 Shopback 的後端跑在一組微服務上——每個服務各自管理一個領域，透過網路溝通，各自擴縮容。這個架構有清楚的好處，也有顯而易見的成本：閒置的 container 仍然佔用分配的資源，就算當下沒在處理任何流量，機器費照算。\n遷移到 monorepo 架構的動機之一就在這裡。TypeScript monorepo 讓多個服務可以共享同一個 process 和資源池，閒置的開銷縮小，機器費也跟著縮小。\n我的子任務很具體：兩個服務原本透過網路互相呼叫 API，遷移之後改為在 monorepo 裡直接呼叫函式。聽起來是個重構。實際做起來，變成我實習期間最困難的一件事。\n為什麼這件事很難 第一層困難是技術面。Shopback 的 TypeScript monorepo 結構主要是為 library 和共用套件設計的——這是 monorepo 最常見的使用情境。在這個結構裡跑真正的後端服務是另一回事。服務有自己的啟動生命週期、自己的 framework 相依、自己的 runtime 問題。Library 沒有這些。\n團隊裡已經有工程師實作了部分 monorepo，但針對的是 library 使用情境。他們的程式碼我看得到，但沒辦法直接套用——他們設計時的假設對 running services 不成立，尤其是不同後端 framework（我們跨服務用了不只一種）在 monorepo 的依賴解析機制下如何互動。\n還有版本相容性的問題。TypeScript monorepo 在 workspace 層級管理套件版本，而某些跨服務依賴有寫死的版本假設，合在一起跑就會出現衝突。這不是能用 grep 找到的 bug，而是跑起來才會炸、而且 stack trace 看不出所以然的那種問題。\nMentor 也沒有答案 第二層困難是：我的 mentor 也沒做過這件事。\n這不是批評，就是事實。他有自己的工作，能幫的地方他都幫了。但針對我在解的這個遷移問題，他跟我一樣是從零開始。\n所以我在一個技術問題上獨立工作，沒有團隊裡的前例，codebase 夠大、搞清楚相關部分要花真正的時間，而 framework 設定在內部也缺乏文件記錄。\n兩三個 Sprint，我進度緩慢。我能看出需要做什麼，但真的讓實作跑起來是另一回事。\n那條 Slack 訊息 最後我做了一件我一直猶豫要不要做的事：主動傳訊息給另一個團隊的資深工程師，他是我知道曾碰過 codebase 相鄰部分的人。\n我也不太清楚自己為什麼等了那麼久。大概是不想打擾一個從沒直接合作過的人，加上一個「我應該自己想辦法」的假設。這兩個理由都不是卡住的好理由。\n他回了。他說明了他在處理類似問題時用的方法。我看了他的程式碼，拼圖對上了，下一個 Sprint 裡完成了遷移。\n前後加起來三到四個 Sprint。實習的一大段時間。\n帶走的東西 技術上的收穫是真實的，但不是最重要的。TypeScript monorepo 跑服務而不只是共用套件時有哪些限制；framework 版本邊界為什麼重要——我都學到了，以後用得到。\n更持久的東西是關於卡住的時候怎麼工作。\n我把自己的資源範圍框住了。我只看身邊直接的同事、能找到的內部文件、讀得到的程式碼。這些都不夠。答案就在一條 Slack 訊息外面，對象是一個我從沒說過話的人。\n現在回頭看——2026 年中，快一年後——我看到自己讓那個問題在腦袋裡多待了不必要的時間，因為我對「求助」的定義框得太窄。那個資深工程師不是我的 mentor，也不是被指定來幫我的人。但他是有我需要的知識的人，而大多數人在你清楚地開口問的時候都願意分享。\n別把求助的範圍限制在名義上負責幫你的那些人。對的人可能在公司的另一個地方、另一個團隊，或者，有時候，是某個你主動找到的陌生人。\n那個限制在我腦子裡，不在組織架構圖上。\n","permalink":"https://lien0214.github.io/zh-tw/posts/shopback-monorepo/","summary":"\u003ch2 id=\"背景\"\u003e背景\u003c/h2\u003e\n\u003cp\u003eShopback 的後端跑在一組微服務上——每個服務各自管理一個領域，透過網路溝通，各自擴縮容。這個架構有清楚的好處，也有顯而易見的成本：閒置的 container 仍然佔用分配的資源，就算當下沒在處理任何流量，機器費照算。\u003c/p\u003e\n\u003cp\u003e遷移到 monorepo 架構的動機之一就在這裡。TypeScript monorepo 讓多個服務可以共享同一個 process 和資源池，閒置的開銷縮小，機器費也跟著縮小。\u003c/p\u003e\n\u003cp\u003e我的子任務很具體：兩個服務原本透過網路互相呼叫 API，遷移之後改為在 monorepo 裡直接呼叫函式。聽起來是個重構。實際做起來，變成我實習期間最困難的一件事。\u003c/p\u003e\n\u003ch2 id=\"為什麼這件事很難\"\u003e為什麼這件事很難\u003c/h2\u003e\n\u003cp\u003e第一層困難是技術面。Shopback 的 TypeScript monorepo 結構主要是為 library 和共用套件設計的——這是 monorepo 最常見的使用情境。在這個結構裡跑真正的後端服務是另一回事。服務有自己的啟動生命週期、自己的 framework 相依、自己的 runtime 問題。Library 沒有這些。\u003c/p\u003e\n\u003cp\u003e團隊裡已經有工程師實作了部分 monorepo，但針對的是 library 使用情境。他們的程式碼我看得到，但沒辦法直接套用——他們設計時的假設對 running services 不成立，尤其是不同後端 framework（我們跨服務用了不只一種）在 monorepo 的依賴解析機制下如何互動。\u003c/p\u003e\n\u003cp\u003e還有版本相容性的問題。TypeScript monorepo 在 workspace 層級管理套件版本，而某些跨服務依賴有寫死的版本假設，合在一起跑就會出現衝突。這不是能用 grep 找到的 bug，而是跑起來才會炸、而且 stack trace 看不出所以然的那種問題。\u003c/p\u003e\n\u003ch2 id=\"mentor-也沒有答案\"\u003eMentor 也沒有答案\u003c/h2\u003e\n\u003cp\u003e第二層困難是：我的 mentor 也沒做過這件事。\u003c/p\u003e\n\u003cp\u003e這不是批評，就是事實。他有自己的工作，能幫的地方他都幫了。但針對我在解的這個遷移問題，他跟我一樣是從零開始。\u003c/p\u003e\n\u003cp\u003e所以我在一個技術問題上獨立工作，沒有團隊裡的前例，codebase 夠大、搞清楚相關部分要花真正的時間，而 framework 設定在內部也缺乏文件記錄。\u003c/p\u003e\n\u003cp\u003e兩三個 Sprint，我進度緩慢。我能看出需要做什麼，但真的讓實作跑起來是另一回事。\u003c/p\u003e\n\u003ch2 id=\"那條-slack-訊息\"\u003e那條 Slack 訊息\u003c/h2\u003e\n\u003cp\u003e最後我做了一件我一直猶豫要不要做的事：主動傳訊息給另一個團隊的資深工程師，他是我知道曾碰過 codebase 相鄰部分的人。\u003c/p\u003e","title":"整個團隊都沒做過的那次遷移"},{"content":"在 Shopback 有一個功能，我當時幾乎沒多想。\n需求很單純：PM 有一個 app 內選擇面板的設計——一個讓用戶從列表中選取項目的小 UI 元件。設計稿上要在每個項目旁加上商家圖示。我的部分是跟前端團隊協作，確保後端整合正確接上。沒有什麼技術難度，純粹是協調工作：PM 有想法，設計有 spec，我幫忙把兩端串起來讓功能可以上線。\n就這樣。\n上線後，stakeholder 分享了數據。那個面板的進入率——看到頁面後實際點進選擇面板的用戶比例——從大約 10% 跳到 40% 以上，提升超過 30%。\n我記得看到這個數字的時候，停頓了一下。\n我原本隱隱持有的模型 在這之前，我對軟體工作的影響力有一個安靜的假設：影響力跟難度成正比。問題越難，就越重要。系統遷移、效能重構、不直觀的演算法——那才是「真正的工作」。UI 調整？清單上加幾個圖示？那幾乎算不上工程。\n但數據不同意。\n那個面板的改動影響了大量用戶的操作。它改變了人們打開 app 之後做的事。而我花了更多時間做的那些工作——跨服務 API 遷移、錯誤率降低——固然重要，但它們對用戶的可見影響是比較安靜的。基礎設施工作通常都是如此。\n圖示是半天的協調工作，換來 30% 的行為轉變。\n為什麼好設計是真正的工作 圖示真正做到的事，是給面板裡每個項目一個身份。加圖示之前，列表項目大概只是文字或通用條目。加了之後，每一行都有視覺錨點——一個在說「這是一個真實的東西、一個可辨識的東西、值得你注意的東西」的信號。用戶對這個信號做出了回應。\n這不是新觀念。但「知道設計很重要」和「看著設計在一個你指得出來的指標上發揮作用」，是兩件不同的事。\n我更新的東西是「工程貢獻」的範圍。讓後端整合正確接上，讓圖示在對的時間載入，又不破壞面板原本的行為——這是真實的貢獻。不性感，但它是讓 PM 願景可以上線的那個東西，而能上線的東西是推動數字的那個東西。\n關於驕傲這件事 有一件事我沒有完全預期到：看到數字的時候，我的感受是什麼。\n那種驕傲不是來自技術難度，而是來自效果。我碰過的東西改變了大量用戶打開 app 之後的行為。這和乾淨解決一個困難問題的滿足感不太一樣——沒那麼智識性，但更連接到真實的人。\n我覺得這是軟體業值得追求的那種驕傲形式。不是「我解決了一件很難的事」，而是「我做的東西現在已經是某些人移動世界方式的一部分了」。即使那個移動很小——多點了一個按鈕，看到了之前沒看到的東西。\n有影響力的工作不一定是房間裡最難的問題。有時候，它看起來就像一排圖示。\n","permalink":"https://lien0214.github.io/zh-tw/posts/shopback-impact/","summary":"\u003cp\u003e在 Shopback 有一個功能，我當時幾乎沒多想。\u003c/p\u003e\n\u003cp\u003e需求很單純：PM 有一個 app 內選擇面板的設計——一個讓用戶從列表中選取項目的小 UI 元件。設計稿上要在每個項目旁加上商家圖示。我的部分是跟前端團隊協作，確保後端整合正確接上。沒有什麼技術難度，純粹是協調工作：PM 有想法，設計有 spec，我幫忙把兩端串起來讓功能可以上線。\u003c/p\u003e\n\u003cp\u003e就這樣。\u003c/p\u003e\n\u003cp\u003e上線後，stakeholder 分享了數據。那個面板的進入率——看到頁面後實際點進選擇面板的用戶比例——從大約 10% 跳到 40% 以上，提升超過 30%。\u003c/p\u003e\n\u003cp\u003e我記得看到這個數字的時候，停頓了一下。\u003c/p\u003e\n\u003ch2 id=\"我原本隱隱持有的模型\"\u003e我原本隱隱持有的模型\u003c/h2\u003e\n\u003cp\u003e在這之前，我對軟體工作的影響力有一個安靜的假設：影響力跟難度成正比。問題越難，就越重要。系統遷移、效能重構、不直觀的演算法——那才是「真正的工作」。UI 調整？清單上加幾個圖示？那幾乎算不上工程。\u003c/p\u003e\n\u003cp\u003e但數據不同意。\u003c/p\u003e\n\u003cp\u003e那個面板的改動影響了大量用戶的操作。它改變了人們打開 app 之後做的事。而我花了更多時間做的那些工作——跨服務 API 遷移、錯誤率降低——固然重要，但它們對用戶的可見影響是比較安靜的。基礎設施工作通常都是如此。\u003c/p\u003e\n\u003cp\u003e圖示是半天的協調工作，換來 30% 的行為轉變。\u003c/p\u003e\n\u003ch2 id=\"為什麼好設計是真正的工作\"\u003e為什麼好設計是真正的工作\u003c/h2\u003e\n\u003cp\u003e圖示真正做到的事，是給面板裡每個項目一個\u003cstrong\u003e身份\u003c/strong\u003e。加圖示之前，列表項目大概只是文字或通用條目。加了之後，每一行都有視覺錨點——一個在說「這是一個真實的東西、一個可辨識的東西、值得你注意的東西」的信號。用戶對這個信號做出了回應。\u003c/p\u003e\n\u003cp\u003e這不是新觀念。但「知道設計很重要」和「看著設計在一個你指得出來的指標上發揮作用」，是兩件不同的事。\u003c/p\u003e\n\u003cp\u003e我更新的東西是「工程貢獻」的範圍。讓後端整合正確接上，讓圖示在對的時間載入，又不破壞面板原本的行為——這是真實的貢獻。不性感，但它是讓 PM 願景可以上線的那個東西，而能上線的東西是推動數字的那個東西。\u003c/p\u003e\n\u003ch2 id=\"關於驕傲這件事\"\u003e關於驕傲這件事\u003c/h2\u003e\n\u003cp\u003e有一件事我沒有完全預期到：看到數字的時候，我的感受是什麼。\u003c/p\u003e\n\u003cp\u003e那種驕傲不是來自技術難度，而是來自效果。我碰過的東西改變了大量用戶打開 app 之後的行為。這和乾淨解決一個困難問題的滿足感不太一樣——沒那麼智識性，但更連接到真實的人。\u003c/p\u003e\n\u003cp\u003e我覺得這是軟體業值得追求的那種驕傲形式。不是「我解決了一件很難的事」，而是「我做的東西現在已經是某些人移動世界方式的一部分了」。即使那個移動很小——多點了一個按鈕，看到了之前沒看到的東西。\u003c/p\u003e\n\u003cp\u003e有影響力的工作不一定是房間裡最難的問題。有時候，它看起來就像一排圖示。\u003c/p\u003e","title":"讓我重新理解「有影響力」的那個小功能"},{"content":"背景 2025 年 4 月。六個人，一個學期，目標：用 Unity 完成一款完整的 2D 平台遊戲。\n這款遊戲叫 Bubblo。你扮演一隻泡泡生物，在像素風格的奇幻樂園中探索，拯救被關在籠子裡的村民，同時對抗各種針型敵人——蜜蜂、跳躍蜘蛛、還有一隻非常想把你戳破的獨角獸。核心機制是泡泡物理：在關卡中彈跳漂浮，用你的柔軟特性同時解決移動和戰鬥問題。\n我是專案負責人。我的工作是把一堆模糊的想法轉化為真正可以交付的遊戲細節——然後讓另外五個人在三個月內朝同一個方向前進，而且沒有人中途放棄。\n我從 Cmoney 帶來了什麼 在這之前幾個月，我剛結束在 Cmoney 的後端實習，九個月的真實敏捷流程：衝刺規劃、每日站立、Sprint Review、回顧。我參加了夠多這些會議，對它們的用途有個淺層的理解。\n所以開始 Bubblo 時，我有一套框架。我們會跑兩週的 Sprint，有正式的任務看板，做 Retrospective。我知道這些詞彙。\n但我還不完全理解的是：這些詞彙背後有一套隱含的假設——關於人們如何安排時間、「可用性」代表什麼，以及一個團隊最主要的義務是什麼。\n在公司，每個人最主要的工作就是這個專案。Sprint 是容器。敏捷儀式有效，是因為它設計的前提是全職投入。\n在學校，這對任何人都不成立。\n為什麼儀式不管用 我們的團隊成員有實驗室工作、其他課程、課外活動、實習申請要處理。有人在 Sprint 中途趕一篇研究論文；有人有兼職工作。每個人都是一個完整的人，在 Bubblo 之外有完整的生活。\n要跑一場完整的 Sprint 規劃，意味著請人們拿出他們根本沒有的兩個小時。開回顧會議感覺很表演，因為真正的阻礙只是大家很忙、而且對此感到愧疚。每日站立變成焦慮的來源，而不是同步機制。\n僵硬的結構在製造摩擦，而不是消除摩擦。\n設計一個真正合適的工作流程 所以我把它拆解精簡。\n任務看板留下來——這是最有價值的東西。所有人都能非同步看到「目前有什麼任務、什麼在進行中、什麼被卡住了」。維護這個看板不需要任何儀式，成本幾乎是零。\n我用非同步確認取代了排程站立：每次工作時段開始時發一條短訊息，沒有格式要求，只是「今天在做 X，如果 Y 沒解決可能會卡住」。低壓力，大家真的會看。\n每週同步變成一場有單一議程的會議：現在誰被什麼卡住了，我能在接下來三十分鐘內幫你解決嗎？ 不是進度報告，不是狀態更新。只有阻礙。跑二十到三十分鐘，阻礙清完就結束。\n背後的原則是：尊重每個人的第一義務不是這個專案，但讓人們在有時間的時候容易貢獻。精簡，但不鬆散。\n沒有人想要的 OOP 重構（但我們還是做了） 大約六週後，我們遇到了問題。角色行為的程式碼已經有機生長成一團亂麻。多個人在碰同一塊，一個地方的改動會在另一個地方產生難以追蹤的 bug。\n我們重構了。Sprint 進行中。學生時程上。\n在任何遊戲程式碼開始之前，我已經在紙上把元件架構設計成圍繞 Observer、State Machine 和 Command 模式。重構是要在實作中真正落地這些模式：讓狀態轉換變得明確、讓事件系統成為跨元件溝通的唯一真相來源。\n這是正確的決定。重構之後，新增一種敵人類型或玩家狀態變成了一個有邊界的操作。Sprint 最後三分之一明顯比前三分之二不混亂。\n時機感覺不對，但繼續修補一個纏繞的 codebase 會更糟。有些技術債複利速度夠快，必須提早還清。\nAI 工具的時代背景 整個專案我們用 ChatGPT。那時候還沒有 IDE 代理工具——沒有整合的程式碼生成，頂多就是補全。主要用法是：描述一個問題，拿到一個草稿，再把草稿改成能用的東西。\n一年後回頭看，工具已經完全不一樣了。今天這樣的專案看起來會不同——迭代更快，樣板程式碼的時間更少，出現不同種類的錯誤。我好奇的是隨著工具改進，什麼事情會變得更難，而不只是更容易。可能是架構決策——那些沒有明確正確答案、需要團隊決定方向然後承擔後果的決策。\n我真正學到的事 遊戲交付了。600+ commits，五個可玩關卡，完整音效，60fps WebGL。我們為此驕傲。\n但我帶走的不是 Unity 知識，甚至不只是設計模式——而是對工作流程設計的校準感。\n敏捷不是公式。它是建立在對特定情境的假設之上的一套原則。當情境改變了——學生團隊和公司是非常不同的情境——原則仍然適用，但儀式必須從頭重新設計。問題永遠是：這個儀式究竟想達成什麼，有沒有更輕量的方式達成它？\n另一件事：在同儕團隊裡做專案管理，大部分是情緒工作。讓人不被卡住，不只是任務協調——還要確保沒有人覺得自己的貢獻不重要，或者「我說要做的」和「我實際做的」之間的落差會被追究。這種安全感讓人在有時間的時候能夠出現，而不是因為這個專案感覺像罪惡感的來源而刻意迴避。\n進來之前我沒有完全理解這件事。現在我懂了。\n","permalink":"https://lien0214.github.io/zh-tw/posts/bubblo-devlog/","summary":"\u003ch2 id=\"背景\"\u003e背景\u003c/h2\u003e\n\u003cp\u003e2025 年 4 月。六個人，一個學期，目標：用 Unity 完成一款完整的 2D 平台遊戲。\u003c/p\u003e\n\u003cp\u003e這款遊戲叫 Bubblo。你扮演一隻泡泡生物，在像素風格的奇幻樂園中探索，拯救被關在籠子裡的村民，同時對抗各種針型敵人——蜜蜂、跳躍蜘蛛、還有一隻非常想把你戳破的獨角獸。核心機制是泡泡物理：在關卡中彈跳漂浮，用你的柔軟特性同時解決移動和戰鬥問題。\u003c/p\u003e\n\u003cp\u003e我是專案負責人。我的工作是把一堆模糊的想法轉化為真正可以交付的遊戲細節——然後讓另外五個人在三個月內朝同一個方向前進，而且沒有人中途放棄。\u003c/p\u003e\n\u003ch2 id=\"我從-cmoney-帶來了什麼\"\u003e我從 Cmoney 帶來了什麼\u003c/h2\u003e\n\u003cp\u003e在這之前幾個月，我剛結束在 Cmoney 的後端實習，九個月的真實敏捷流程：衝刺規劃、每日站立、Sprint Review、回顧。我參加了夠多這些會議，對它們的用途有個淺層的理解。\u003c/p\u003e\n\u003cp\u003e所以開始 Bubblo 時，我有一套框架。我們會跑兩週的 Sprint，有正式的任務看板，做 Retrospective。我知道這些詞彙。\u003c/p\u003e\n\u003cp\u003e但我還不完全理解的是：這些詞彙背後有一套隱含的假設——關於人們如何安排時間、「可用性」代表什麼，以及一個團隊最主要的義務是什麼。\u003c/p\u003e\n\u003cp\u003e在公司，每個人最主要的工作就是這個專案。Sprint 是容器。敏捷儀式有效，是因為它設計的前提是全職投入。\u003c/p\u003e\n\u003cp\u003e在學校，這對任何人都不成立。\u003c/p\u003e\n\u003ch2 id=\"為什麼儀式不管用\"\u003e為什麼儀式不管用\u003c/h2\u003e\n\u003cp\u003e我們的團隊成員有實驗室工作、其他課程、課外活動、實習申請要處理。有人在 Sprint 中途趕一篇研究論文；有人有兼職工作。每個人都是一個完整的人，在 Bubblo 之外有完整的生活。\u003c/p\u003e\n\u003cp\u003e要跑一場完整的 Sprint 規劃，意味著請人們拿出他們根本沒有的兩個小時。開回顧會議感覺很表演，因為真正的阻礙只是大家很忙、而且對此感到愧疚。每日站立變成焦慮的來源，而不是同步機制。\u003c/p\u003e\n\u003cp\u003e僵硬的結構在製造摩擦，而不是消除摩擦。\u003c/p\u003e\n\u003ch2 id=\"設計一個真正合適的工作流程\"\u003e設計一個真正合適的工作流程\u003c/h2\u003e\n\u003cp\u003e所以我把它拆解精簡。\u003c/p\u003e\n\u003cp\u003e任務看板留下來——這是最有價值的東西。所有人都能非同步看到「目前有什麼任務、什麼在進行中、什麼被卡住了」。維護這個看板不需要任何儀式，成本幾乎是零。\u003c/p\u003e\n\u003cp\u003e我用非同步確認取代了排程站立：每次工作時段開始時發一條短訊息，沒有格式要求，只是「今天在做 X，如果 Y 沒解決可能會卡住」。低壓力，大家真的會看。\u003c/p\u003e\n\u003cp\u003e每週同步變成一場有單一議程的會議：\u003cstrong\u003e現在誰被什麼卡住了，我能在接下來三十分鐘內幫你解決嗎？\u003c/strong\u003e 不是進度報告，不是狀態更新。只有阻礙。跑二十到三十分鐘，阻礙清完就結束。\u003c/p\u003e\n\u003cp\u003e背後的原則是：尊重每個人的第一義務不是這個專案，但讓人們在有時間的時候容易貢獻。精簡，但不鬆散。\u003c/p\u003e\n\u003ch2 id=\"沒有人想要的-oop-重構但我們還是做了\"\u003e沒有人想要的 OOP 重構（但我們還是做了）\u003c/h2\u003e\n\u003cp\u003e大約六週後，我們遇到了問題。角色行為的程式碼已經有機生長成一團亂麻。多個人在碰同一塊，一個地方的改動會在另一個地方產生難以追蹤的 bug。\u003c/p\u003e\n\u003cp\u003e我們重構了。Sprint 進行中。學生時程上。\u003c/p\u003e\n\u003cp\u003e在任何遊戲程式碼開始之前，我已經在紙上把元件架構設計成圍繞 Observer、State Machine 和 Command 模式。重構是要在實作中真正落地這些模式：讓狀態轉換變得明確、讓事件系統成為跨元件溝通的唯一真相來源。\u003c/p\u003e\n\u003cp\u003e這是正確的決定。重構之後，新增一種敵人類型或玩家狀態變成了一個有邊界的操作。Sprint 最後三分之一明顯比前三分之二不混亂。\u003c/p\u003e\n\u003cp\u003e時機感覺不對，但繼續修補一個纏繞的 codebase 會更糟。有些技術債複利速度夠快，必須提早還清。\u003c/p\u003e\n\u003ch2 id=\"ai-工具的時代背景\"\u003eAI 工具的時代背景\u003c/h2\u003e\n\u003cp\u003e整個專案我們用 ChatGPT。那時候還沒有 IDE 代理工具——沒有整合的程式碼生成，頂多就是補全。主要用法是：描述一個問題，拿到一個草稿，再把草稿改成能用的東西。\u003c/p\u003e","title":"一學期帶一個遊戲專案：敏捷沒有教你的事"},{"content":"第一份的結束 4 月 20 日。在 Cmoney 擔任後端開發工程師實習生九個月後，最後一天。\n第一份實習很奇特。你不知道自己不知道什麼，所以你無法分辨哪些是正常的，哪些是這個地方特有的。現在回頭看，我覺得自己在最關鍵的地方運氣很好：我落腳在一個有真實程式碼、真實規模，以及一個把教導這件事認真看待的導師的地方。\nCmoney 是什麼樣的地方 Cmoney 是台灣最大的金融資訊平台——700 萬 App 下載量，股市資料、投資組合追蹤和散戶投資人廣泛使用的金融分析工具的核心基礎設施。這裡的後端不是新創公司的乾淨程式碼。它是十年的生產歷史、累積的決策和繼承下來的限制。在這裡工作，你會感受到那份重量。\n我在核心後端積極進行微服務重構期間加入。目標是把業務邏輯從單體架構中抽出來，放進有版本管理的服務合約。我的工作是為旗艦產品的九個後端 API 實作這些重構。\n用艱難的方式學 OOP 我從課本學過物件導向程式設計。Cmoney 教了我 OOP 實際上能帶來什麼。\n我繼承的程式碼沒有清楚的關注點分離。業務邏輯直接在 Controller 裡。資料存取散落各處。狀態在層次之間洩漏。測試全綠——程式碼無法維護。\n重構它意味著在改善之前先理解它為什麼這樣寫。而要理解這件事，我首先需要搞懂 ASP.NET Core、C# 的型別系統，以及本該存在但不在的那些模式。\nTom——我的導師——在這件事上展現了我後來才完全體會的耐心。他沒有只告訴我要改什麼。他解釋為什麼原本的設計會造成問題，原本的模式應該是什麼，以及如何在不影響另一端 700 萬用戶的情況下逐步遷移過去。這是一種和懂得那些模式不同的能力。它是知道這些模式在什麼時候重要。\n到了第三個月，我已經從問「這裡我該怎麼做」，變成問「這段程式碼假設了什麼，這個假設現在還成立嗎」。這個轉變才是真正的收穫。\n規模下的金融 API 這個領域有它自己的挑戰。金融資料在開盤時不是一般流量。數百萬用戶同時刷新投資組合、執行選股器、查看即時股價。只有在那個規模和那個時間窗口才會出現的並發邊緣案例。你很快就學到「測試時可以跑」和「真實負載下可以跑」是兩種不同的主張。\n我深入熟悉了 C# 的高並發模式——async/await、連線池、時效性資料的快取策略——因為替代方案是在交易日早上九點發生事故。\n最後一個「真正手寫程式碼」的年代 我在 2025 年 4 月離開 Cmoney。兩三個月後，產業開始快速移動。\nAgentic Programming——AI Agent 從頭到尾處理從 Spec 到程式碼到測試的完整實作——從 Demo 變成了真實工程師使用的真實工作流程。「初階開發工作」的樣貌開始即時改變。\n我還不確定這對早期職涯工程師意味著什麼。但我知道，那九個月手動繼承破損的生產程式碼、在早上九點追著 Bug 穿越三個服務層、和導師爭論這個抽象層到底值不值得那個間接性——這類學習不會被 Agent 抽象掉。理解程式碼在做什麼，以及設計是否合理，仍然是難的那個部分。\n也許改變的是，機械性的部分變快了。判斷力這個部分沒有。\n感謝 感謝 Tom，用應有的方式教我 ASP.NET、C# 和 OOP——透過有真實後果的真實程式碼。感謝在每一步都解釋「為什麼」的耐心。\n感謝整個團隊，以及在我到來之前流過 Cmoney 歷史的大量工作，讓我能貢獻其中一小部分。\n這一章結束了。繼續前行。\n","permalink":"https://lien0214.github.io/zh-tw/posts/leaving-cmoney/","summary":"\u003ch2 id=\"第一份的結束\"\u003e第一份的結束\u003c/h2\u003e\n\u003cp\u003e4 月 20 日。在 Cmoney 擔任後端開發工程師實習生九個月後，最後一天。\u003c/p\u003e\n\u003cp\u003e第一份實習很奇特。你不知道自己不知道什麼，所以你無法分辨哪些是正常的，哪些是這個地方特有的。現在回頭看，我覺得自己在最關鍵的地方運氣很好：我落腳在一個有\u003cem\u003e真實\u003c/em\u003e程式碼、\u003cem\u003e真實\u003c/em\u003e規模，以及一個把教導這件事認真看待的導師的地方。\u003c/p\u003e\n\u003chr\u003e\n\u003ch2 id=\"cmoney-是什麼樣的地方\"\u003eCmoney 是什麼樣的地方\u003c/h2\u003e\n\u003cp\u003eCmoney 是台灣最大的金融資訊平台——700 萬 App 下載量，股市資料、投資組合追蹤和散戶投資人廣泛使用的金融分析工具的核心基礎設施。這裡的後端不是新創公司的乾淨程式碼。它是十年的生產歷史、累積的決策和繼承下來的限制。在這裡工作，你會感受到那份重量。\u003c/p\u003e\n\u003cp\u003e我在核心後端積極進行微服務重構期間加入。目標是把業務邏輯從單體架構中抽出來，放進有版本管理的服務合約。我的工作是為旗艦產品的九個後端 API 實作這些重構。\u003c/p\u003e\n\u003chr\u003e\n\u003ch2 id=\"用艱難的方式學-oop\"\u003e用艱難的方式學 OOP\u003c/h2\u003e\n\u003cp\u003e我從課本學過物件導向程式設計。Cmoney 教了我 OOP 實際上能\u003cem\u003e帶來什麼\u003c/em\u003e。\u003c/p\u003e\n\u003cp\u003e我繼承的程式碼沒有清楚的關注點分離。業務邏輯直接在 Controller 裡。資料存取散落各處。狀態在層次之間洩漏。測試全綠——程式碼無法維護。\u003c/p\u003e\n\u003cp\u003e重構它意味著在改善之前先理解\u003cem\u003e它為什麼這樣寫\u003c/em\u003e。而要理解這件事，我首先需要搞懂 ASP.NET Core、C# 的型別系統，以及本該存在但不在的那些模式。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eTom\u003c/strong\u003e——我的導師——在這件事上展現了我後來才完全體會的耐心。他沒有只告訴我要改什麼。他解釋\u003cem\u003e為什麼\u003c/em\u003e原本的設計會造成問題，原本的模式應該是什麼，以及如何在不影響另一端 700 萬用戶的情況下逐步遷移過去。這是一種和懂得那些模式不同的能力。它是知道這些模式\u003cem\u003e在什麼時候重要\u003c/em\u003e。\u003c/p\u003e\n\u003cp\u003e到了第三個月，我已經從問「這裡我該怎麼做」，變成問「這段程式碼假設了什麼，這個假設現在還成立嗎」。這個轉變才是真正的收穫。\u003c/p\u003e\n\u003chr\u003e\n\u003ch2 id=\"規模下的金融-api\"\u003e規模下的金融 API\u003c/h2\u003e\n\u003cp\u003e這個領域有它自己的挑戰。金融資料在開盤時不是一般流量。數百萬用戶同時刷新投資組合、執行選股器、查看即時股價。只有在那個規模和那個時間窗口才會出現的並發邊緣案例。你很快就學到「測試時可以跑」和「真實負載下可以跑」是兩種不同的主張。\u003c/p\u003e\n\u003cp\u003e我深入熟悉了 C# 的高並發模式——async/await、連線池、時效性資料的快取策略——因為替代方案是在交易日早上九點發生事故。\u003c/p\u003e\n\u003chr\u003e\n\u003ch2 id=\"最後一個真正手寫程式碼的年代\"\u003e最後一個「真正手寫程式碼」的年代\u003c/h2\u003e\n\u003cp\u003e我在 2025 年 4 月離開 Cmoney。兩三個月後，產業開始快速移動。\u003c/p\u003e\n\u003cp\u003eAgentic Programming——AI Agent 從頭到尾處理從 Spec 到程式碼到測試的完整實作——從 Demo 變成了真實工程師使用的真實工作流程。「初階開發工作」的樣貌開始即時改變。\u003c/p\u003e\n\u003cp\u003e我還不確定這對早期職涯工程師意味著什麼。但我知道，那九個月手動繼承破損的生產程式碼、在早上九點追著 Bug 穿越三個服務層、和導師爭論這個抽象層到底值不值得那個間接性——這類學習不會被 Agent 抽象掉。理解程式碼在\u003cem\u003e做什麼\u003c/em\u003e，以及設計是否\u003cem\u003e合理\u003c/em\u003e，仍然是難的那個部分。\u003c/p\u003e\n\u003cp\u003e也許改變的是，機械性的部分變快了。判斷力這個部分沒有。\u003c/p\u003e\n\u003chr\u003e\n\u003ch2 id=\"感謝\"\u003e感謝\u003c/h2\u003e\n\u003cp\u003e感謝 \u003cstrong\u003eTom\u003c/strong\u003e，用應有的方式教我 ASP.NET、C# 和 OOP——透過有真實後果的真實程式碼。感謝在每一步都解釋「為什麼」的耐心。\u003c/p\u003e\n\u003cp\u003e感謝整個團隊，以及在我到來之前流過 Cmoney 歷史的大量工作，讓我能貢獻其中一小部分。\u003c/p\u003e","title":"我在 Cmoney 的第一份實習——也可能是最後一個「真正手寫程式碼」的年代"},{"content":"\n遊戲簡介 靈感來自馬力歐和卡比的 2D 像素平台遊戲。你扮演 Bubblo——一隻泡泡生物，在奇幻樂園中探索，拯救被關在籠子裡的村民，同時對抗各種針型敵人：蜜蜂、跳躍蜘蛛，還有一隻非常想把你戳破的獨角獸。\n核心機制是泡泡物理。你可以彈跳、漂浮，並利用泡泡特性進行移動和戰鬥。五個關卡，穩定 60fps，完整遊戲循環含生命值與計分系統。\n我的角色 我是專案負責人——把模糊的構想轉化為可執行的遊戲細節，讓六個人在三個月內朝同一個方向前進。\n在任何遊戲程式碼開始之前，我先設計好元件架構。明確採用 Observer、State Machine 和 Command 模式——不是因為看起來厲害，而是因為六個人共用同一個 codebase，隱含耦合會直接殺死開發速度。衝刺中途針對角色行為系統做了一次完整重構，讓模式落地更乾淨。\n工作流程是另一個挑戰。我帶著從 Cmoney 實習學到的敏捷概念進來，但完整的 Scrum 儀式在學生團隊行不通——大家還有實驗室、其他課、社團要顧。最後我把它精簡成更輕量的形式：共用任務看板、短暫非同步確認，以及每週一次只為解除阻礙的同步會議。\n全程使用 ChatGPT，那時候還沒有 IDE 代理工具。\n最終交付 600+ commits。五個有獨特敵人設計的可玩關卡。完整音效。勝負狀態。打磨過的標題畫面。WebGL Demo 穩定跑在 60fps。\n在瀏覽器遊玩 →\n","permalink":"https://lien0214.github.io/zh-tw/projects/bubblo/","summary":"\u003cp\u003e\u003cimg alt=\"Bubblo 標題畫面\" loading=\"lazy\" src=\"/images/projects/bubblo-cover.png\"\u003e\u003c/p\u003e\n\u003ch2 id=\"遊戲簡介\"\u003e遊戲簡介\u003c/h2\u003e\n\u003cp\u003e靈感來自馬力歐和卡比的 2D 像素平台遊戲。你扮演 Bubblo——一隻泡泡生物，在奇幻樂園中探索，拯救被關在籠子裡的村民，同時對抗各種針型敵人：蜜蜂、跳躍蜘蛛，還有一隻非常想把你戳破的獨角獸。\u003c/p\u003e\n\u003cp\u003e核心機制是泡泡物理。你可以彈跳、漂浮，並利用泡泡特性進行移動和戰鬥。五個關卡，穩定 60fps，完整遊戲循環含生命值與計分系統。\u003c/p\u003e\n\u003ch2 id=\"我的角色\"\u003e我的角色\u003c/h2\u003e\n\u003cp\u003e我是專案負責人——把模糊的構想轉化為可執行的遊戲細節，讓六個人在三個月內朝同一個方向前進。\u003c/p\u003e\n\u003cp\u003e在任何遊戲程式碼開始之前，我先設計好元件架構。明確採用 Observer、State Machine 和 Command 模式——不是因為看起來厲害，而是因為六個人共用同一個 codebase，隱含耦合會直接殺死開發速度。衝刺中途針對角色行為系統做了一次完整重構，讓模式落地更乾淨。\u003c/p\u003e\n\u003cp\u003e工作流程是另一個挑戰。我帶著從 Cmoney 實習學到的敏捷概念進來，但完整的 Scrum 儀式在學生團隊行不通——大家還有實驗室、其他課、社團要顧。最後我把它精簡成更輕量的形式：共用任務看板、短暫非同步確認，以及每週一次只為解除阻礙的同步會議。\u003c/p\u003e\n\u003cp\u003e全程使用 ChatGPT，那時候還沒有 IDE 代理工具。\u003c/p\u003e\n\u003ch2 id=\"最終交付\"\u003e最終交付\u003c/h2\u003e\n\u003cp\u003e600+ commits。五個有獨特敵人設計的可玩關卡。完整音效。勝負狀態。打磨過的標題畫面。WebGL Demo 穩定跑在 60fps。\u003c/p\u003e\n\u003cp\u003e\u003ca href=\"/zh-tw/game/\"\u003e在瀏覽器遊玩 →\u003c/a\u003e\u003c/p\u003e","title":"Bubblo — 2D 平台遊戲"},{"content":"任務定義 Emoji 序列與繁體中文的雙向翻譯——不是字典查找，而是理解 Emoji 在台灣網路文化中如何承載意義的情境理解。\n資料集 從頭打造：透過群眾外包收集 emoji 對應中文的映射，再進行規則增強。核心挑戰：emoji 意義高度依賴情境，因平台、年齡層和台灣特有的網路用語而異。用三種不同的演算法/GPT 生成資料比例（20/80、50/50、80/20）評估資料組成的影響。\n模型與結果 使用 LoRA 搭配 RLHF 風格的偏好優化對 Taiwan-LLaMA 和 Google mT5 進行微調。\nmT5 表現優於 LLaMA，原因是其針對繁體中文的明確多語言預訓練。\n最有趣的發現：BLEU 分數高的模型在「聽起來自然」的人工評估上表現明顯更差——評估指標偏離目標的典型案例。我們加入 METEOR 評估和多輪人工標注來抓出這個落差。\n","permalink":"https://lien0214.github.io/zh-tw/projects/e-motchi/","summary":"\u003ch2 id=\"任務定義\"\u003e任務定義\u003c/h2\u003e\n\u003cp\u003eEmoji 序列與繁體中文的雙向翻譯——不是字典查找，而是理解 Emoji 在台灣網路文化中如何承載意義的情境理解。\u003c/p\u003e\n\u003ch2 id=\"資料集\"\u003e資料集\u003c/h2\u003e\n\u003cp\u003e從頭打造：透過群眾外包收集 emoji 對應中文的映射，再進行規則增強。核心挑戰：emoji 意義高度依賴情境，因平台、年齡層和台灣特有的網路用語而異。用三種不同的演算法/GPT 生成資料比例（20/80、50/50、80/20）評估資料組成的影響。\u003c/p\u003e\n\u003ch2 id=\"模型與結果\"\u003e模型與結果\u003c/h2\u003e\n\u003cdiv style=\"display:flex; gap:20px; align-items:center; margin-bottom:18px; flex-wrap:wrap;\"\u003e\n  \u003cimg src=\"/images/projects/taiwan-llama-v1.0.jpg\" alt=\"Taiwan-LLaMA\" style=\"height:110px; width:auto; border-radius:8px;\"\u003e\n  \u003cimg src=\"/images/projects/lora-peft.jpg\" alt=\"LoRA 架構\" style=\"height:110px; width:auto; border-radius:8px; background:#f5f0e8;\"\u003e\n\u003c/div\u003e\n\u003cp\u003e使用 LoRA 搭配 RLHF 風格的偏好優化對 \u003cstrong\u003eTaiwan-LLaMA\u003c/strong\u003e 和 \u003cstrong\u003eGoogle mT5\u003c/strong\u003e 進行微調。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003emT5 表現優於 LLaMA\u003c/strong\u003e，原因是其針對繁體中文的明確多語言預訓練。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e最有趣的發現\u003c/strong\u003e：BLEU 分數高的模型在「聽起來自然」的人工評估上表現明顯更差——評估指標偏離目標的典型案例。我們加入 METEOR 評估和多輪人工標注來抓出這個落差。\u003c/p\u003e","title":"E-MoTchi — Emoji ↔ 中文 NLP"},{"content":"關於 Bubblo Bubblo 是一款用 Unity 在一學期內由 6 人團隊完成的完整 2D 平台遊戲 Demo。我同時擔任技術架構設計者和專案管理者。\n我實際做的事：\n在任何遊戲程式碼開始之前就設計好元件架構——Observer、State Machine 和 Command 模式讓 6 個人不會互相踩到對方 每週站立會議和維護任務看板；PM 工作的大部分時間是在幫隊友解除阻礙 Demo 穩定跑在 60fps，5 個可玩關卡，完整音效，以及完整遊戲循環（生命值、計分、勝負狀態） 操作說明： 方向鍵或 WASD 移動，Space 跳躍。點擊全螢幕按鈕獲得最佳體驗。\n遊戲可能需要 30–60 秒載入，WebGL 版本約 80MB。\n","permalink":"https://lien0214.github.io/zh-tw/game/","summary":"\u003ch2 id=\"關於-bubblo\"\u003e關於 Bubblo\u003c/h2\u003e\n\u003cp\u003eBubblo 是一款用 Unity 在一學期內由 6 人團隊完成的完整 2D 平台遊戲 Demo。我同時擔任技術架構設計者和專案管理者。\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003e我實際做的事：\u003c/strong\u003e\u003c/p\u003e\n\u003cul\u003e\n\u003cli\u003e在任何遊戲程式碼開始之前就設計好元件架構——Observer、State Machine 和 Command 模式讓 6 個人不會互相踩到對方\u003c/li\u003e\n\u003cli\u003e每週站立會議和維護任務看板；PM 工作的大部分時間是在幫隊友解除阻礙\u003c/li\u003e\n\u003cli\u003eDemo 穩定跑在 60fps，5 個可玩關卡，完整音效，以及完整遊戲循環（生命值、計分、勝負狀態）\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e\u003cstrong\u003e操作說明：\u003c/strong\u003e 方向鍵或 WASD 移動，Space 跳躍。點擊全螢幕按鈕獲得最佳體驗。\u003c/p\u003e\n\u003cblockquote\u003e\n\u003cp\u003e\u003cem\u003e遊戲可能需要 30–60 秒載入，WebGL 版本約 80MB。\u003c/em\u003e\u003c/p\u003e\n\u003c/blockquote\u003e","title":"Bubblo — 2D 平台遊戲"},{"content":" 檢視 CV 下載 PDF 學歷 資訊工程系碩士 國立台灣大學 · 2025–2027 資訊工程系學士 國立台灣大學 · 2021–2025 · 已畢業 研究室： MIRLab（多媒體資訊檢索實驗室）· MSLab（機器智慧與智能代理系統實驗室）\n工作經歷 軟體工程師實習 · Shopback 2025 年 5 月 – 11 月 · 台北 · 混合辦公\nTypeScript Kubernetes Monorepo SQL CI/CD\n在 Monorepo 遷移過程中，將 10+ 個 API 呼叫改為直接函式呼叫，每次延遲從 ~100ms 降至 10ms 以下 開發的功能每月處理 1M+ API 請求，使用者興趣提升 15–25% 解決 5+ 個後端問題，讓相關 API 錯誤率降低 10–15% 後端開發工程師實習 · Cmoney 2024 年 7 月 – 2025 年 4 月 · 新北市 · 混合辦公\nC# ASP.NET OOP 微服務 MongoDB 高併發\n針對旗艦產品（7M+ 下載量）的 9 個後端 API 實作微服務重構 研究助理（大學部）· 中研院 Citi Lab 2023 年 9 月 – 2024 年 6 月 · 南港 · 遠端\nPython 資安 合成資料 Linux Audit Log\n建立處理 200M+ Linux audit log 的資料系統，支援 GAN 資安偵測研究 助教 · 台大資工系 2023 年 2 月 – 6 月 · 台北 · 實體\n資料結構與演算法\n專案 ICC Mem/Gen · MSLab 2026 年 3 月 – 5 月 · 4 人團隊 · 4 個月衝刺\n知識衝突 SFT PEFT LoRA Activation Steering\n為 ConflictQA/ConflictBank 開發跨格式資料增強 Pipeline 透過 Activation Steering 驗證微調後的行為向量可以控制未微調的基礎模型 量化交易專案 · MIRLab 2026 年 1 月 – 5 月 · 3 人團隊 · 持續進行中\nQuant 台股 日策略\nBubblo · 2D 平台遊戲 2025 年 4 月 – 6 月 · 6 人團隊 · 專案負責人 · 600+ Commits\nUnity C# OOP 設計模式\n在任何遊戲程式碼開始前就設計好元件架構，明確採用 Observer、State Machine 和 Command 模式 主導每週站立會議，維護三個月衝刺的任務看板 E-MoTchi · NLP 2024 年 11 月 – 12 月 · 5 人團隊 · 40 天衝刺\nNLP Taiwan-LLaMA Google mT5 LoRA RLHF\n使用 LoRA 搭配 RLHF 風格的偏好優化，微調多語言 LLM 進行 Emoji ↔ 繁體中文翻譯 技術能力 最熟悉： TypeScript · Python · C#\n後端： ASP.NET Core · Node.js · Kubernetes · Docker · MongoDB · PostgreSQL\nML/AI： PyTorch · HuggingFace Transformers · PEFT/LoRA · Activation Steering\n軟技能： 敏捷工作流 · 專案管理 · 研究能力\n語言： 中文（母語）· 英文（工作能力）\n","permalink":"https://lien0214.github.io/zh-tw/resume/","summary":"resume","title":"履歷"},{"content":" 歡迎聯絡，無論是研究討論、合作機會或單純打個招呼。 Email ted20030214@gmail.com LinkedIn linkedin.com/in/yi-wei-lien GitHub github.com/lien0214 電話 (+886) 902-323-591 傳送訊息 稱謂 — 先生 女士 博士 教授 姓名 主旨 — 請選擇詢問類型 — 工作機會 研究合作 一般詢問 其他 公司 / 學校 （選填） 訊息 請先輸入訊息內容。 用 Google 登入並送出 ⏳ ✓ 訊息已送出，我會盡快回覆。 發生錯誤，請直接寄信至 ted20030214@gmail.com。 ","permalink":"https://lien0214.github.io/zh-tw/contact/","summary":"contact","title":"聯絡"},{"content":" 台灣大學資訊工程系碩士（2025–2027） 台灣大學資訊工程系學士（2021–2025，已畢業）\n我在台大碩士班就讀中，同時在兩個截然不同的 AI 研究室工作：\nMIRLab（多媒體資訊檢索實驗室）— 進行台股量化交易研究 MSLab（機器智慧與智能代理系統實驗室）— 論文研究聚焦於大型語言模型中的知識衝突與記憶問題 碩士前，我在 Shopback 和 Cmoney 擔任後端工程師實習約 18 個月。也在中研院 Citi 實驗室擔任研究助理，研究資安領域的合成資料生成。\n研究興趣 LLM 知識表示：微調如何在殘差流中創造方向性偏置，以及這些偏置能否在不更新權重的情況下被控制 LLM 知識衝突：當參數化知識（來自預訓練）與情境知識（來自微調或 Prompt）衝突時會發生什麼，以及如何優雅地解決 量化交易：針對不同微觀結構市場（如散戶主導的台股）的系統化策略 LLMOps / AI 基礎設施：建立真正衡量目標的評估 Pipeline，而非只看 BLEU 學歷 資訊工程系碩士 國立台灣大學，2025–2027 資訊工程系學士 國立台灣大學，2021–2025 研究室： MIRLab（多媒體資訊檢索實驗室）、MSLab（機器智慧與智能代理系統實驗室）\n經歷時間軸 期間 職位 公司/機構 2025 年 5 月 – 11 月 軟體工程師實習 Shopback 2024 年 7 月 – 2025 年 4 月 後端開發工程師實習 Cmoney 2023 年 9 月 – 2024 年 6 月 研究助理（大學部） 中研院，Citi Lab 2023 年 2 月 – 6 月 資料結構與演算法助教 台大資工系 技術能力 最熟悉： TypeScript、Python、C# 熟悉的後端技術： ASP.NET Core、Node.js、Kubernetes、Docker、MongoDB、PostgreSQL 常用 ML 工具： PyTorch、HuggingFace Transformers、PEFT/LoRA、Activation Steering 目前學習中： Quant RL 、 Generative AI Safety\n聯絡方式 ted20030214@gmail.com · GitHub · LinkedIn · (+886) 902-323-591\n","permalink":"https://lien0214.github.io/zh-tw/about/","summary":"about","title":"關於"}]