Ken Chen

Go

如何優雅包裝錯誤:聊聊 Go 的 error

錯誤處理是 Golang 最常被討論的一個點。這有幾個因素,首先,這跟它「錯誤是值」的設計理念有關,開發者需要在業務流程中穿插錯誤處理,違反關注點分離的原則,當然會引發爭議。另外,在 1.13 前,Golang 標準 errors 庫的表現力有限,當需求較為複雜時,需要開發者自行發明錯誤處理輔助函式。這讓人不禁好奇,Golang 的錯誤處理設計原則是什麼?有沒有比較好的實踐?或者說,我們能不能找到一種方式,優雅地處理錯誤? 錯誤與異常 先來看看不同人的觀點,Robert Martin 在討論到錯誤處理時,是如此建議的 使用異常替代返回錯誤碼,錯誤處理代碼就能從主路徑代碼中分離出來,得到簡化 他給出的例子是 try { deletePage(page); registry.deleteReference(page.name); configKeys.deleteKey(page.name.

By Ken Chen

Go

讓錯誤成為資源:gRPC 的錯誤處理模型

錯誤處理是所有 RPC 服務都要具備的設計,但是怎樣的錯誤處理模型,算是好的模型呢?從字面上來看,錯誤處理可以分解成「錯誤」跟「處理」,如果用 RESTful 的觀點,將錯誤當成是 Resource,一個好的模型應該要能匹配不同場景的 Resource,並根據場景需求來處理這些 Resource。 錯誤模型 在 RESTful 中,通常會用 HTTP Status Code 當錯誤訊息的分類(Category),錯誤內容則放在 Payload。這樣的好處是,只要看到分類,就能先進行大方向的處理,如果需要特定資訊,再從 Payload 拿取。通常錯誤內容的格式會自行定義,以支付服務 Stripe 的 API 為例,定義的格式就有 * type (string) * code (string) * decline_

By Ken Chen
在 GitLab 顯示測試覆蓋率:以 Go 為例

Go

在 GitLab 顯示測試覆蓋率:以 Go 為例

對現代開發者來講,單元測試已經不是可選,而是必備了。單元測試能保護程式碼,讓錯誤提早現形,也能讓重構時更安心。通常我們在評估單元測試的執行狀況時,會用 coverage 當成其中一項指標。當然,coverage 還是會有一些使用的場合跟侷限,當談到專案落地,可能大家會想知道的是,coverage 該怎麼使用,才能幫助到專案? 開發、審查、回顧 我們先來看看什麼時候會需要知道 coverage?通常依照團隊的工作流程,將它分為三個階段:開發、審查、回顧。每個階段關注的場景會略有不同。 一個一個講。對開發中情形,開發者想知道的是剛寫完的邏輯是否能正常運行,有沒有對應的測試,覆蓋範圍是否已經足夠,如果還有條件分支沒覆蓋到的話,是哪裡?是不是每個錯誤都有處理了。這時最需要的是,codebase 要能 highlight 剛剛講的資訊,幫助開發者一眼掌握。 當開發完成,feature branch 被提交到原始碼管理系統,例如 GitLab,會需要一名

By Ken Chen

Go

用 Fx 來替 Go 依賴注入吧

相信平常開發時,即使沒真的用到,也會聽別人提起「依賴注入」的概念。我們都知道依賴注入的目的是解耦模組間的依賴,但具體來說,依賴注入應該要怎麼進行呢?Go 對於依賴注入有什麼比較好的實踐呢?這篇就來談談 Go 相關的依賴注入話題。 常見的實踐方式 講到依賴注入,從 OOP 的觀點來看,可以回到 Martin Fowler 的 SOLID 原則,其中的 Dependency Inversion Principle 落實到編程中,就是依賴注入。James Grenning 曾經簡單扼要說明 DIP 原則的出發點 Martin tells us that high-level modules shouldn’t depend on low-level modules. 這裡,高層級的模組指的是商業邏輯的實現。依賴反轉原則之所以重要,

By Ken Chen

Go

初探 Go 的單元測試:兼談 Stub 跟 Mock

測試是程式的防護網,能確保程式符合設計,而當開發者需要對程式進行重構,以增進品質時,測試也可以確保程式不會出現改 A 壞 B 的情況。從商業角度來看,測試能降低維護與改善程式的成本,進而提高軟體開發的競爭力。 既然測試這麼好,那為什麼常看到軟體專案中沒有測試?在我的經驗中,主要原因有兩個:首先是軟體開發初期,架構還不是很穩定,API 隨時有可能改變,在 API 不穩時,如果就開始寫大量測試,會造成後面很大的維護成本,試著想想,API 的改變,可能就牽涉到測試流程跟測試資料的改變,而之前的 Corner Case 很可能都變成沒有價值的投資,在這情況下寫測試沒有意義。 再來,程式中可能會引用到第三方套件,例如 ORM 或 HTTP 之類的外部依賴,如果要實際測試,就會需要建構測試環境,而這些也會有建置與維護成本,像是網路斷掉,可能就會在程式邏輯沒動到的狀況下,讓 HTTP 的測試失效,這些維護成本會讓寫測試的投資報酬率看起來不太划算。

By Ken Chen

Go

自動生成重複代碼:使用 Go 的 Template

開發軟體時,常常會發現有些函式或方法很類似,例如對 Callback Function 來說,開發者都需要註冊回調函式,並在適當的時機,將資料交給回調函式處理,我們可以將這兩個動作,稱為 OnAction 跟 EmitAction。儘管繼承或組合能讓程式碼重複使用類似的組件,幫助開發者節省時間,但對於較複雜的情況,像是不同類(Class)的函式名稱也要不同時,仍需要仰賴開發者自行編寫。 試著想想,如果開發者僅僅寫設定檔(Config File),就有程式能根據設定檔來自動產生程式碼,不是很美好的事嗎?這是有的,在實務上,這類用於產生程式的程式被稱為 Code Generator,例如前面介紹過的 genny。Golang 有內建 generate 這個命令行工具,能幫助開發者將 Code Generator 跟編譯更密切結合在一起。 本文會用 Callback 的 Generator 當例子,講解如何開發並使用一套 Code Generator。

By Ken Chen
如何驗證使用者身分:使用 JWT

Node.js

如何驗證使用者身分:使用 JWT

驗證與授權是開發網路應用時一定會遇到的問題,前者指的是確認使用者身分,讓 Server 明白請求者是真正的使用者,而不是其他人假冒;後者指的是該使用者有權限進行操作。JWT 處理的主要是前一個問題。 先來看看 JWT 出來前的做法。在傳統技術中,使用者會在登入時輸入帳號密碼,Server 由資料庫驗證無誤後,創建一組 Session ID,放入回應的 Cookie ,Client 後續請求都會在 Cookie 帶上 Session ID,方便 Server 檢驗。 由於只看 Session ID 無法說明使用者身分正確,還需要看該 Session ID 是否有儲存在 Server,而 Server 的數據通常儲存在資料庫,一來一往之間,就會造成 Server 端額外的開銷。JWT 只需要在 Server 儲存一組

By Ken Chen
Conan:建置並管理 C/C++ 的產出物

C/C++

Conan:建置並管理 C/C++ 的產出物

以前我們討論過如何在 C/C++ 的專案中,使用 Conan 管理第三方套件。當開發應用程式時,只要用 Conan 就能引入現成的函式庫;但如果角色轉換,開發的不是應用程式,而是函式庫,為方便他人引用,就需要將函式庫打包成 Conan Package,並上傳到 Server。在常見的開發情境中,開發者既需要引用別人的函式庫,也期待自己的函式庫能讓別人引用。 本文會講解如何編寫 Conan 的 Recipe,打包 C/C++ 的函式庫,並在實際的應用程式中引用。 需要 Clone 程式碼的,可以到這裡。 Create Conan Project 既然要打包程式,就需要先建立起專案 mkdir conan-recipe && cd conan-recipe 依照官方說明,可以用 conan

By Ken Chen
用網頁製作你的簡報:reveal.js

JavaScript

用網頁製作你的簡報:reveal.js

試著想像一個情境,你是一名工程師,需要跟他人分享你的創意,你希望雙方認知建立在相同的基礎上,當說到「狗」時,聽眾明白你指的是拉不拉多,而不是吉娃娃。投影片是個簡單有效的同步工具,但 Windows 的 PowerPoint 有幾個惱人的問題: * 僅具備簡單畫面編輯工具 * 版型固定 * 沒有 Quote * 沒有 Code Section * 沒有 Syntax Highlight 投影片應該要像攝影機,講者使用它來專注說故事。而 PowerPoint 設計的目標,是讓不具備程式能力的人,也能將畫面放上電腦。它有基礎的視覺設計能力,但並不足以應付進階應用。 reveal.js 是以 HTML 為基礎的簡報框架,藉由 CSS 跟 HTML 分離,講者可以更專注在內容,將設計交由第三方庫來處理。由於本質上是由 HTML、CSS 跟

By Ken Chen

Go

Go 的泛型替代方案:型別斷言與代碼生成

用函數式編程(Functional Programming)的風格寫程式時,會常常重複使用一些通用函式,舉個例子,假設有個 array,開發者需要從該 array 中篩選符合條件的元素,重新組成新的 array。合理的情況是,如果有個 filter 函式,只需要設定條件,剩下的事情都能交給語言處理。 Golang 的 append 有點這味道,類似 JavaScript 的 push,能新增 slice 中的元素,但 Golang 畢竟不是徹底的函數式語言,內建函式庫中沒有 filter、map、reduce 等等 array 常用的函式。如果想要自行實現,語言的強型別系統會要求開發者在使用前告知對象型別,這等於是對不同的型別都要實現幾乎相同的函式,可想而知不切實際。 同樣是強型別語言,C++ 或 C# 在面對這問題時,是靠著泛型(

By Ken Chen
雙向互動的即時訊息:Websocket 入門

Node.js

雙向互動的即時訊息:Websocket 入門

在 HTTP 設計之初,網路應用主要是交換文件,因此當提交訊息或更新訊息時,需要刷新整個頁面,這也導致大量 HTML 被重複傳輸,浪費使用頻寬。後來 AJAX 被提出,讓 HTTP 可以只取得想要的伺服端訊息,同時在沒有重新導向的情況下更新頁面,讓 HTTP 更符合現代網路應用情境。 但是對需要互動的應用,像是聊天室、遊戲、即時狀態監控等等來說,如果使用 HTTP 傳遞訊息,則需要客戶端頻繁向伺服端輪詢(Polling),有點像客戶端三不五時跟伺服端問說:「你有沒有新資料需要更新的啊?」可想而知會造成客戶端跟伺服端很大的負擔。比較理想的情況是,應該存在一個事件驅動模型,當伺服端有事件發生時,它會主動通知訂閱的客戶端,客戶端再進行更新,而這就是 WebSocket 這套通訊協定誕生的原因。 Websocket 沒有限定語言,但為了簡化操作,後端可以用 node.js,好跟前端的 JavaScript 共用一套函式庫。本文中會使用

By Ken Chen

Go

資料庫版本遷移:以 Go 為例

接續前面,繼續來討論資料庫議題吧。 在商務初期,追求的是驗證市場,這時資料庫往往只有相對簡單的版本,各種欄位也還不是很齊全。隨著商業模式逐漸成熟,資料庫會需要負擔更多的營運功能,也會需要在原有表格中加入新欄位。資料庫的版本管理問題就出現了。 我們通常稱呼資料庫版本遷移為 Migration,在 Laravel 或 RoR 中都有整合好的 Migration 工具,而 Golang 目前仍需要仰賴自己動手,golang-migrate 是現在比較成熟的專案。本文會講解如何使用 golang-migrate 的 CLI 跟函式庫,來建立資料庫 Migration。 Introduction 既然說版本遷移是 Migration,為什麼不直接稱呼 Version 就好,它跟 Version 有什麼不同?兩者的區別可以看看下圖 簡單來說,Version 指的是資料庫的狀態,而 Migration 指的是狀態到狀態之間的改變。因為後端程式會使用到資料庫,如果用到資料庫沒有的欄位,就會出現問題。

By Ken Chen