[好文分享]應用部署的六種策略

引用出處

正文開始

目前有各種各樣的技術來將新應用部署到生產環境,
所以權衡對系統和終端使用者的影響降至最少,選擇正確的方式是非常重要的。
本文將著重討論如下部署策略:

  • 重建部署:版本 A 下線後版本 B 上線
  • 滾動部署(滾動更新或者增量釋出):版本 B 緩慢更新並替代版本 A
  • 藍綠部署:版本 B 並行與版本 A 釋出,然後流量切換到版本 B
  • 金絲雀部署:版本 B 向一部分使用者釋出,然後完全放開
  • A/B 部署布:版本 B 只向特定條件的使用者釋出
  • 影子部署:版本 B 接受真實的流量請求,但是不產生響應

我們來看一下每個策略最適合哪種使用者使用場景。
爲了簡化,我們使用 Kubernetes ,並用 Minikube 進行例子演示。
每個策略的配置例子和詳細步驟都可以在這個 git 倉庫 上找到。

重建部署

重建策略是一個冗餘的方式,它包含下線版本 A,然後部署版本 B。
這個方式意味著服務的宕機時間依賴於應用下線和啟動耗時。
重建部署

優點:

  • 便於設定
  • 應用狀態完整更新

缺點:

  • 對使用者影響很大,預期的宕機時間取決於下線時間和應用啟動耗時

滾動部署

滾動部署策略是指通過逐個替換應用的所有例項,
來緩慢釋出應用的一個新版本。
通常過程如下:
在負載排程後有個版本 A 的應用例項池,
一個版本 B 的例項部署成功,可以響應請求時,
該例項被加入到池中。
然後版本 A 的一個例項從池中刪除並下線。
考慮到滾動部署依賴於系統,
可以調整如下引數來增加部署時間:

  • 並行數,最大批量執行數:同時釋出例項的數目
  • 最大峰值:考慮到當前例項數,例項可以加入的數目
  • 最大不可用數:在滾動更新過程中不可用的例項數

滾動部署

優點:

  • 便於設定
  • 版本在例項間緩慢釋出
  • 對於能夠處理資料重平衡的有狀態應用非常方便

缺點:

  • 釋出/回滾耗時
  • 支援多個 API 很困難
  • 無法控制流量

藍綠部署

藍綠部署策略與滾動部署不同,
版本 B(綠)同等數量的被並排部署在版本 A(藍)旁邊。
當新版本滿足上線條件的測試後,
流量在負載均衡層從版本 A 切換到版本 B。
藍綠部署

優點:

  • 實時釋出、回滾
  • 避免版本衝突問題,整個應用狀態統一一次切換

缺點:

  • 比較昂貴因為需要雙倍的資源
  • 在釋放版本到生產環境之前,整個平臺的主流程測試必須執行
  • 處理有狀態的應用很棘手

金絲雀部署

金絲雀部署是指逐漸將生產環境流量從版本 A 切換到版本 B。
通常流量是按比例分配的。
例如 90%的請求流向版本 A,10%的流向版本 B。
這個技術大多數用於缺少足夠測試,或者缺少可靠測試,
或者對新版本的穩定性缺乏信心的情況下。

金絲雀部署

優點:

  • 版本面向一部分使用者釋出
  • 方便錯誤評估和效能監控
  • 快速回滾

缺點:

  • 釋出緩慢

A/B 測試

A/B 測試是指在特定條件下將一部分使用者路由到新功能上。
它通常用於根據統計來制定商業決策,而不是部署策略。
然而,他們是相關的,可以在金絲雀部署方式上新增額外功能來實現,所以我們這裏簡要介紹一下。
這個技術廣泛用於測試特定功能的切換,併發布使用佔大部分的版本。
下面是可以用於在版本間分散流量的條件:

  • 瀏覽器 cookie
  • 查詢引數
  • 地理位置
  • 技術支援:瀏覽器版本、螢幕尺寸、作業系統等
  • 語言
    A/B測試

優點:

  • 多個版本並行執行
  • 完全控制流量分佈

缺點:

  • 需要智慧負載均衡
  • 對於給定的會話,很難定位問題,分散式跟蹤是必須的

影子部署

影子部署是指在版本 A 旁邊釋出版本 B,
將版本 A 進來的請求同時分發到版本 B,
同時對生產環境流量無影響。
這是測試新特徵在產品負載上表現的很好用的方式。
當滿足上線要求後,則觸發釋出新應用。
這個技術配置非常複雜,而且需要特殊條件,尤其是分出請求。
例如一個購物車平臺,如果你想影子測試支付服務,
你可能最終會是使用者為他們的訂單支付兩次。
這種情況下,可以通過建立一個模擬的服務來重複響應使用者的請求。
影子部署

優點:

  • 可以使用生產環境流量進行效能測試
  • 對使用者無影響
  • 直到應用的穩定性和效能滿足要求後才釋出

缺點:

  • 雙倍資源,成本昂貴
  • 不是真實使用者測試,可能出現誤導
  • 配置複雜
  • 某種情況下需要模擬服務

總結

部署應用有很多種方法,實際採用哪種方式取決於需求和預算。
當釋出到開發或者模擬環境時,重建或者滾動部署是一個好選擇。
當釋出到生產環境時,滾動部署或者藍綠部署通常是一個好選擇,
但是新平臺的主流程測試是必須的。
藍綠部署和影子部署對預算有更高的要求,因為需要雙倍資源。
如果應用缺乏測試或者對軟體的功能和穩定性影響缺乏信心,
那麼可以使用金絲雀部署或者 AB 測試或者影子釋出。
如果業務需要根據地理位置、語言、作業系統或者瀏覽器特徵等這樣引數來給一些特定的使用者測試,那麼可以採用 AB 測試技術。
最後但並不是最不重要的,影子釋出很複雜,且需要額外工作來模擬響應分支流量請求,
當可變操作(郵件、銀行等)呼叫外部依賴時這是必須的,
這個技術在升級新資料庫是非常有用,使用影子流量來監控負載下的系統性能。
下表可以幫助你選擇正確的策略:
總結
取決於雲服務提供商和平臺,如下文件是理解部署的很好開始:

  • Amazon Web Services
  • Docker Swarm
  • Google Cloud
  • Kubernetes

希望這是有幫助的,如果有任何問題或者反饋,可以在下面評論

(正文結束)

補充表格翻譯


| 策略 | 服務不斷線 | 真實環境測試 | 預算成本 | 退版時間 | 使用者影響 | 複雜度 |
| ———- | ———- | ———— | ——– | ——– | ———- | —— | — |
| 重建部署 | ✖ | ✖ | ✖ | ★☆☆ | ★★★ | ★★★ | ☆☆☆ |
| 滾動部署 | ✔ | ✖ | ✖ | ★☆☆ | ★★★ | ★☆☆ | ★☆☆ |
| 藍綠部署 | ✔ | ✖ | ✖ | ★★★ | ☆☆☆ | ★★☆ | ★★☆ |
| 金絲雀部署 | ✔ | ✔ | ✖ | ★☆☆ | ★☆☆ | ★☆☆ | ★★☆ |
| A/B 部署 | ✔ | ✔ | ✔ | ★☆☆ | ★☆☆ | ★☆☆ | ★★★ |
| 影子部署 | ✔ | ✔ | ✖ | ★★★ | ☆☆☆ | ☆☆☆ | ★★★ |


非常實用的文章,可惜中譯的圖片並非 gif,原文的超聯結也掉失,
特別重新修正以上問題,留作記錄

(fin)

代碼審查與交付的戰爭ー標準、風格與原則

Coding Standard / Code Review / Pull Request & Delivery

故事背景

  1. 團隊的部署流程是 Github Flow 與 Git Flow 混用 , 給它起個名字叫 GG Flow 好了.
  2. GG Flow 的過程需要開發人員需要透過 Pull Request 將修改推送給產品
  3. 擁有權限 Merge Pull Request 的成員被叫作 Reviewer
  4. Reviewer 通常由較資深人員或部門主管擔任,所以通常有比較多的無用會議要開
  5. Reviewer 在 Merge 之前需要作 Code Review
  6. Reviewer 需要遵循 Coding Standard 作 Code Review

實務面臨的問題與副作用

Coding Standard 並不能考慮到所有狀況

  1. 所以 Reviewers 會定期針對不同的狀況開會討論 Coding Standard
    • Coding Standard 會不定期改變 , 但是透過 Reviewer 佈達的方式,讓第一線的 RD 其實難以知道其全貌.
    • Coding Standard 改變後不會全面的翻改程式,實務上是作到哪裡改到哪裡
    • 以上兩點導致 Source Code 裡面有很多符合不同時期的 Coding Standard 的 Code
    • 任一個時間點, 誰都無法保証完全符合最新的 Coding Standard
  2. 人性,開發者會COPY/PASTE 方法開發參考 Legacy Code 開發
    • Legacy Code 不符合新的 Coding Standard
    • Reviewer 也是人, 所以 Code Review 時也會疏漏,而 Merge 進去不符合新的 Coding Standard 的 Code
    • 所以 Source Code 裡面還是有很多不同時期的 Coding Standard 的 Code
  3. 回歸一開始的問題 Coding Standard 並不能考慮到所有狀況
    • 還沒有開會前, 不同的 Reviewer 會有不同的想法
    • 開會後,在執行 Code Review 時, 不同的 Reviewer 會有不同的作法
    • 當一個 PR 有多人 Reviewer 時, 會有不同的意見 PR 因此被延遲 Merge
    • 結果,交付會變慢.

反思,標準還是風格?

思考一下,開發程式碼的目標與價值是什麼 ?
寫出 Clearn Code ?
還是交付產品 ?
這樣子的 Source Code 真的是 Clearn Code 嗎?

自問自答

Q1. 我們該有標準嗎?

A1. 當然要有標準,不過標準之所以為標準,應該有以下幾個特點.

  • 它應該要很簡單, 像是 Class 與欄位的命名規則
  • 它應放諸四海皆準, 不應該輕易被修改
  • 它應該可以被自動化的檢測
    假設能作到這 3 點, 這件事應該可以被自動化工具處理掉 .

Q2. 實務上就是很複雜, 所以才需要討論制訂標準啊

A2.
在實務上遇到很複雜的情況, 大多需要依賴約定成俗方式規範.
這是一種風格原則 ;
簡單的分類方法,
如果無法透過自動化工具作檢測,
就不應該歸類為標準.

註:有機會再介紹自動化的檢測工具

Q3. 風格原則標準有何不同?

A3. 如上所說,標準應該能被自動化,
風格應該是團隊的文化自然形成的產物,
具體的實作可以透過讓開發者彼此之間作代碼審核
或是結對編程培養出屬於團隊的風格,
風格要基於標準之上,但是不能違反原則;

以下的原則可以作為參考

  • 可以建置並通過測試
  • 可讀性
    • self documenting
    • 有用的註解
  • 公開方法要可以被測試
    • 小心使用靜態類別
    • 注意 new Instance 的時機
    • 重複的代碼應重構
  • 保持 SOLID

初期的可能會發生在「{」要不要換行之類的問題上揪結之類的蠢事,
如果可以自動化,就把它作成標準吧…
如果不行的話, 就別揪結了.

實務上可能遇到各種狀況,
把 Reviewer 的權限下放到各個開發者身上,
或是使用結對編程,
就讓團隊成員去討論與決定風格.

以標準為根基,原則為天,
踩穩腳步,不要超出天空,
就讓團隊自由發揮吧.

最後,持續交付會比每兩周花一個小時開會決定 Style 的細節好多了. 不是嗎?

其它團隊分享的具體作法

  1. 超過一定時間就讓成員擁 Merge 權限
  2. Release 權限仍集中控管
  3. 錯了再改就好(保持敏捷)
  4. 給 pair 作 code review 與 merge (避免一人思維陷井)
  5. 兩個人無法解決時找第三方
  6. release 功能 優先於 一致的 coding standard
  7. 品質由測試管控而非 reviewer
  8. 先有測試才有重構
  9. 可讀性 優於 枝微末節的 coding standard 實踐
  10. 善用自動化工具( sonarqube / stylecop )

(fin)

補充 社群觀點
  • coding style 一般不管的。
  • class name/variable name,一定要叫有意義的名字。
  • local scope variable,換多少行,indentation,這些是小事
  • 一個成熟的 developer,隨時會被上司命令這些遠古火星文明(legacy system)去做考古工作
  • coding style 這些事,就像 emacs 和 vim 之戰一樣,戰到 skynet 出來了也不會戰完的
  • 如果要開會去討論 coding style,最終很可能讓團隊口服心不服地去跟隨我的 Coding Style。
  • 在 Coding Style 這種低層次的小事上用光了團隊成員之間互相容忍的能量,而在更重要的大事上無法好好合作。
  • 有很多事是比 Coding Style 重要的。
    • Object Modeling 是否跟 business logic 一致?
    • 還是 Object 有這個 attribute 但是根本沒在用?
    • Code Change 是否有做好測試?
    • 系統架構是否合理
    • 有做好 High-Avalibility 嗎?
    • 有沒有 Race Condition?
  • 是其是,非其非。真正有道理的,你說了對方便自然會聽下去。
  • 「Senior」是代表自己在專業上懂得比別人多,而不是比別人身份高級。

一年後的我想要什麼?

你的目標是什麼?

自由

什麼是自由?

  1. 情感上的自由
  2. 時間上的自由
  3. 經濟上的自由

經濟上的自由是一切的基礎

  1. 更多的收入
    1. 被動的收入
    2. 更高的薪資
  2. 更有成就感的工作
    1. 被同事尊重
    2. 被上司認可
    3. 受人歡迎
  3. 更多的選擇
    1. 技術提昇
    2. 領域擴展
    3. 人脈
  4. 健康的身體
  5. 更多享受生活

一年後的我想要什麼?

一年後的我想要什麼?

  1. 更高的薪資
    1. 維持現狀 K (x)
    2. 跳糟 K * 1.3 (x)
    3. 爭取加薪 K * 1.1
    4. 獎金與分紅
  2. 更有成就感的工作(什麼是成就感)?
    1. 擁有可以引以為豪的產品
      1. 拆解單體為服務導向
      2. 架構升級

    2. 分享與教學
      1. Blog
      2. 內/外部sharing
      3. 單元測試導入

  3. 更多的選擇
    1. 技術轉移
      1. Web 技術 轉移 為 Service導向技術
      2. container 技術
      3. cloud 技術

    2. 技術提昇
      1. .Net
      2. Infra
      3. Domain know how

爭取加薪 K * 1.1

而這些目標需要什麼才能爭取到?

分為三個面向,技術提昇、技術擴展與自我實現,

技術提昇

首先目前我擁有的技術能力有

  1. .Net solution的 Web Developer能力
  2. 同時兼顧有 DB 與 F2E 基礎進階能力

這與我過去的選擇有關, 一直以來都在 Web 深入研究
未來的一年仍要朝這個方向發展.
但是會改由 Web 導向轉變成服務導向,
而 Web 開發只是我本身所能提供給公司的一個服務而已,
跟著 .Net 的腳步我想把前台(包含大馬但不限於)昇級上一個版本,
包含目前使用的舊版 libary 與 Framework,
如此一來可以使用到新版 .Net 的語法,
同時也可以解開一些導入測試與微服務時遇到的困境.

現有能力列表

  1. C# & .Net Framework Solution (inculde Linq & Entity Framework )
  2. javascript (jQuery & Angular etc..)
  3. Database with Sql (MsSQL solution)
  4. Source Controle (Git)

略懂

  1. nodejs (with expressjs) & php(codeigniter) & ruby (RoR)
  2. Jenkins

技術的擴展

我認為公司的單體架構已經面臨到不得不拆的狀況,
公司也有意朝這個方向走那是最好不過的了,
但對現存的.NET 開發者而言,我認為人人都要有危機意識
我看到的現象

  1. 頁面會被CMS取代
  2. 大部份的API可以被Lambda取代
  3. 主流程的部份在跨國的目標下會逐步變成微服務

基於以上幾點, 除了.Net 的 solution 外,
更多情況是要使用別的 solution 或是混用,
對此我的視野必須有所提昇,

  1. Linux Bash
  2. Container 技術
  3. Cloud (AWS/Azure/GCP)
  4. Node.js
    在未來的一年開發流程或是維運流程會有很大很快的變化
    要多聽多看多想多問,公司有很多人才要儘可能的跟他們學習.

自我營銷

最後是自我實現的部份,
自我營銷是我很弱的一部份,
不善交際,不喜歡人群
這點我從去年就開始調整,
多參與公司內部的分享,不要害怕說錯
假裝自已是對的,再虛心接受別人的指點
不需要導師,因為人人都是我的導師
開始寫Blog並且貼到社群網站給人鞭
這是我目前的 https://blog.marsen.me

今年會繼續朝這個方向衝刺.
多分享 多犯錯 然後接受反饋學習.
兩個部份是我可以練習分享的機會
一個是測試的導入,
我們的遺留代碼,有很多可以分享的部份,
二是讀書會,
借由讀書會可以練習分享,
同時學習別人怎麼分享,
並且看完一本書,一舉數得.

具體主管可以幫助我的部份

  1. 加薪(沒有比這個更務實的了)
  2. 明確指出我的錯誤或是作的好的部份

Do more do faster

(fin)

2017年的學習回顧與展望

測試

參加了兩個活動,分別是單元測試這樣玩就對了,與測試即學習;

單元測試這樣玩就對了

最大的收獲是突破了寫測試的心魔, 在那之前總覺得 TDD 只是口號, 或是烏托邦的開發理想.
雖然之前也有花大錢出外受訓, 或是公司內部的內訓.
手上也有一些前輩的測試代碼, 但是就是沒有「感覺」,

1
2
3
不過仍然感謝前幾年的自已有將資源花在測試上面,
累積了兩年的測試經驗,
融會貫通卻只是一瞬間的事

最主要是講者展現了實務上面的需求與改進,
過去寫的加法運算, 或是吊人遊戲,
雖然也是從無到,也是先寫測試,
但或許是太過強調 TDD 太過強調從無到有
總與實際開發經驗相違背,
講者在過程中一句 「不要管先寫測試還是程式」
反而更貼近真實,先寫程式,再想想怎麼測試?
為了測試, 再重構, 逐步分解的過程令我大開眼界.

過去曾與人討論過 TDD 的議題,
總會得到「實務與理論有差別」、「其背景與環境因素不能寫測試」
這類似是而非的回答,當下我也無法辯駁
現在想起來只能莞爾.

測試趨動開發不等於一定要先測試,
特別是習慣於先寫產品程式的人,
不仿先寫產品程式, 再寫測試而趨動重構
當寫習慣了,知道哪些耦合會帶來重構的代價時,
再寫一次就人性就會自動迴避這些不好的 Patten .

別管順序,但是記得寫測試.

測試即學習

梅老師的課也是很毀三觀的,
老實說我跟本不知道他在幹嘛,
塔羅牌拿出來的時候,
我真的以為是美江再現(那個時候Seafood還沒有業力引爆( ・ω・)( ・ω・)( ・ω・))
那堂課的目標族群應該是 QA,
不過這個時代不應自我設限,
複習一下梅老師的分享,

探索測試

  • 儘可能的發散
  • 摹仿別人看事情的角度
  • 有意識的學習(mindful learning)
    • 記錄
    • 總結
    • 歸納問題的核心
    • 給它起一個名字(沒有專有名詞的話)
    • 有意識的逃離第一印象
    • 小心不經意的盲區(inattentional blindness)
    • 數量 X 練習 X 思考 X 學習 = 提昇

對我來說,給它起一個名字真是超級有用的,
新認識的朋友就叫作「王大棰」, 新的技術就叫「起司包」, 新的概念就叫「黑盒子」等…
不要過於專研於名詞, 用自已能理解的名字去框那個新事物的範圍,
最後再把名字用大家通用的名字取代掉就好了.
看了很多的方法論,會發現其實觀念沒什麼改變,
只是新的名詞會一直冒出來,
唯物主義、馬基維利主義、不擇手段、實用主義
改善、KANBAN、敏捷、精實 balabala…

“没有时间”- 完美的借口

2017 年影響我最深的一篇 Blog,
2012 年的文章, 篇幅也不長,
觀念也很簡單, 作就對了, 作壞了就丟掉再作一次,
很多的書都要我們刻意練習,
但是我們的時間哪有那麼多呢?
長大了之後,才來 1 萬小時的修練是不是太晚了呢?

下半年公司開始推行測試,
基層的工程師們其實反彈的聲音一直都在,
雖然一直有教育訓練與培養種子,
不過大多淪為口號的狀態,
我也只能用這篇文章作為勉勵.

其實現在的版本控制系統已經非常便利,
要建立一個 Sandbox 的分支,
在裡面嘗試各種可能性是非常容易的,
作壞了丟掉分支就好了,
身為專業的工程人員,測試是必須的,失敗也是,
在沙箱內失敗,其實是摔不疼的,勇敢嘗試吧.

生產力

2016 年是時間記錄的一年,
那 2017 年就是把記錄的時間,
轉換成生產力的一年.
幾本影響比較多的書,不過或許我仍需要二讀至三讀以上

  • 最有生產力的一年 → 時間 X 精力 X 專注力 = 生產力
  • GTD → 下一步要作什麼?
  • 軟技能 → 自我營銷

新的一年重心會放在習慣上面,
因為讓習慣趨動行為,
比起刻意遵循某些方法要好得多,
「刻意」太浪費精神力了,
下一步會如何呢?
希望能翻轉自已

其它

順利的帶媽媽出國了,或許對一些人來說不算什麼成就吧,
新的一年有些機會,去日本、以色列、俄羅斯、馬來西亞,
雖然八字還沒有一撇, 但至少會去一個地方吧…
其他的地方就只能見機行事了.

新年快樂
(fin)

[踩雷筆記] Visual Studio 2017 MSTest Framework 異常修正

應該要知道的事

  • 這是踩雷筆記
  • 2017 的筆記可能會隨時間變得沒有參考價值
  • Visual Studio 2017 的問題,並不一定適用其他版本
  • 最後面會有不定時補充

情境

載入測試時發生例外狀況
原本使用 Visual Studio 2015 建立的測試專案,
升級到 Visual Studio 2017 後, 發生以下錯誤

1
2
3
4
5
[2017/12/11 上午 02:09:59 Error] 測試探索程式 'SpecRunTestDiscoverer'
載入測試時發生例外狀況。例外狀況: 無法載入檔案或組件
'Microsoft.VisualStudio.QualityTools.UnitTestFramework,
Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
或其相依性的其中之一。 系統找不到指定的檔案。

導致結果

原本的測試數量為1942,變成459,遺失了7成5的測試案例.

  1. 測試專案會找不到測試,或是測試數量不正確.
  2. 可以使用 Visual Studio 2015 重新執行探測索測試,即可排除問題.

VS2015 已移除或未安裝該怎麼辦?

透過 MsTest 直接加入
Microsoft.VisualStudio.QualityTools.UnitTestFramework
的參考已經是舊的方法了,

在 Visual Studio 2017 建議的解決方案如下:

  • 移除方案中所有對 Microsoft.VisualStudio.QualityTools.UnitTestFramework 的參考
  • 透過 Nuget 安裝 MSTest.TestAdapter
  • 透過 Nuget 安裝 MSTest.TestFramework
  • 關閉 Visual Studio 2017
  • 移除 %temp%\VisualStudioTestExplorerExtensions內所有檔案
  • 重啟 Visual Studio 2017 並建置以觸發探索測試
    透過 Nuget 安裝 MSTest.TestAdapter/MSTest.TestFramework
    重啟 Visual Studio 2017 並建置以觸發探索測試

參考

(fin)

補充

  • 2018/06/02 :
    Visual Studio 2017 15.7.* 的版本之後 ,
    %temp%\VisualStudioTestExplorerExtensions 消失了 ,
    不過正常情況建置後 , visual studio 探索測試仍然可以正確找到測試。

[實作筆記] 單元測試與重構記錄(二) 發問篇

Q1 Controller 要測試嗎?

Logics in controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
[Route("Member/Get/{Id}")]
public JsonResult GetMemberList(long Id, string cc = "f")
{
var cleanCache = false;
//// logics here
if (this.IsFromCompany() && cc == "t")
{
//// do something ...
}

try
{
var memberList = this.memberService.GetMemberList(Id, cleanCache);
//// logics here
if (memberList.Any())
{
//// do something ...
}
else
{
//// do something ...
}

return this.Json(result, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
//// logics here
//// do something ...
}
}

自問自答

我認為要,
但是對於 WebAPI 回傳的JsonResult或是ActionResult
需要轉形才能作驗証
可以考慮整合測試勝於單元測試,
Controller 的通常是面對 Client Side 的呼叫.

Q2 當 Controller 只有取資料的邏輯

No Logics in Controller

1
2
3
4
public ActionResult Index()
{
return this.Service.GetIndex();
}

Q3 當 Service 只有取資料的邏輯

No Logics in Service

1
2
3
4
public Member Get(long id)
{
return this.DataAccessor.GetMember(id);
}

Q3.自問自答

我認為不要,
要測試商業邏輯,不要在意覆蓋率

Q4. 當 Service 只有取 Catch 資料的邏輯

No Logics in Service , just call another service

1
2
3
4
5
6
7
8
9
10
11
public Member Get(long id)
{
var enableCache = true;
var result = this.CacheService.GetCacheData(
cacheKey,
() => {
return this.DataAccessor.GetMember(id);
},
enableCache
);
}

Q4.自問自答

同上,仍然不需要,
要測試商業邏輯,不要在意覆蓋率,
要注意的或許是CacheService.GetCacheData是不是有包測試 ?
一般來說,Cache 的功能很泛用,測試的報酬率很高

Q5. 承上,當邏輯存在 Func 參數之中?

Logics in Func

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public Member Get(long id)
{
var enableCache = true;
var result = this.CacheService.GetCacheData(
cacheKey,
() => {
//// logics here
if(id > 9487)
{
return this.MemberAccessor.GetMember(id);
}else
{
return this.MemberV2Accessor.GetMember(id);
}

},
enableCache
);
}

Q5.自問自答

暫時無解,
或許是這樣 Pattern 不適合測試,需要調整架構嗎?
為了測試多包成一個公開方法,反而失去匿名函數的彈性優點,
不在匿名函數內寫邏輯更不合理,待求解答

Q6.當邏輯在 DA 層或 ORM 的 Query 中要如何測試?

Logics in ORM

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
上略...
using (var transactionScope = new TransactionScope(TransactionScopeOption.Required, transactionOptions))
{
using (Entities context = Entities.CreateNew(isReadOnly: true))
{
//// logics here
var query = from a in context.Activies.Valids()
where a.Activies_StartDateTime <= startTime &&
a.Activies_EndDateTime >= now &&
a.Activies_ShopId == shopId &&
a.ActiviesCondition.Any(i => i.Activies_ValidFlag
&& TypeList.Contains(i.Activies_TypeDef))
select a;
}
}

Q6.自問自答

不適用單元測試,應該整合測試作包覆

Q7. 當邏輯在 MappingProfile 該如何測試?

Logics in MappingProfile

1
2
3
4
5
6
7
8
protected override void Configure()
{
Mapper.CreateMap<PageEntity, UserPageEntity>()
.ForMember(i => i.Id, s => s.MapFrom(i => i.User_Id))
.ForMember(i => i.Title, s => s.MapFrom(i => i.User_Name))
.ForMember(i => i.PageName, s => s.MapFrom(i => i.User_Name + i.User_LastName))
.ForMember(i => i.LightBox, s => s.MapFrom(i => i.User_Sex == "male" ? true : false));
}

Q7. 自問自答

要作測試,檢查欄位 Mapping 是否正確,
但實務上若重用性不高,寫 MappingProfile 不如直接在代碼內轉換.
可以少寫 MappingProfile 的測試.

待解答…

(fin)

[實作筆記] AWS EC2開機筆記

應該知道的事

  • 這個是教育訓練的筆記
  • 使用 web console 建立 ec2
  • 使用 cli 建立 ec2
  • 2017 的筆記可能會隨時間變得沒有參考價值
  • 關鍵參數都打馬賽克,沒有牽扯到 EBS/S3/VPC
  • 對你可能沒有幫助

Web Console

  1. Login AWS

  2. 進入 EC2
    進入EC2

  3. Launch Instance
    Launch Instance

  4. 選擇 AMI (Amazon Machine Image )
    選擇 AMI

  5. 選擇 Instance Type (有錢隨便選,沒錢選 t2.nano)
    還擇 Instance Type

  6. 設定 Instance Details
    設定 Instance Details

  7. 如果想在開機的時候自動安裝一些程式,可以在 Advanced Details 加語法
    windows AMI 請用 Powershell
    Advanced Details

  8. 加硬碟
    加硬碟

  9. 加 tag
    加Tag

  10. 設定 Configure Security Group
    Configure

  11. 預覽與啟動
    預覽與啟動

  12. 最後一步,選擇 key-pair
    Key-Pair

CLI command

1
aws ec2 run-instances --image-id ami-da9e2cbc --count 1 --instance-type t2.nano --subnet-id subnet-cxxxxxxx --user-data file://userdata.sh --tag-specifications "ResourceType=instance,Tags=[{Key=Environment,Value=AWS-Training},{Key=Name,Value=AWS-Training_MarkLin}]" --security-group-ids sg-XXXXXX --key-name marktest.japan.training

參考文章

(fin)

[實作筆記] 單元測試與重構記錄(一)

前情提要

有幸參與了一個跨國的專案,
為了快速上線,決定將整套原本在台灣程式碼搬移到跨國專案上,
上線後再依使用者的需求調整開發功能,
而在搬移的過程中,有需多模組並未開啟。

現況

遺留代碼 → 跨國遇到的問題,
主管要求用複制整個台灣的環境、流程與代碼到國外的環境之上,
但是要求 One Code Base:

  1. 台灣的情境不一定適合國外環境
  2. 修改功能與台灣邏輯抵觸時,台灣 PM 不一定允許修改
  3. 到處寫例外 if、switch 處理 台灣其它國 的問題
  4. 完整重建整套系統非常費功,沒有資源時會犧牲子系統,導致國外環境部份功能無法使用或是必須與台灣共用

用明朝的劍,斬清朝的官

實務需求

將本來跨國未開啟的折扣活動模組打開,
簡單的流程大致如下:
購物車 → 取得購物車資料 → 折扣活動 → 計算

實務上,整個流程作了許多事
整個流程作了許多事

應該說作了太多事.
作了太多事

程式碼有壞味道,卻不能修改(重構).
因為沒有單元測試保護.

單一的 Process,複雜度過高的方法(12)

CalculateShoppingCartPromotionDiscountV2Processor.Process()

複雜度過高的方法

目標與執行順序

  1. 由 PM 或 QA 補足整合測試情境到足夠
    • 由實務上的需求來認定
  2. 刪除台灣的測試
  3. 解析 CalculateShoppingCartPromotionDiscountV2Processor
  4. 補上單元測試
    • Code Coverage(測試覆蓋率)
  5. 重構

最終的目標是重構

  • 心態:沒有時間,完美的借口
  • 重構前要先作整合測試
  • 現有的整合測試的缺陷
    1. 測試項目不符合馬來西亞現狀
    2. 測試項目未處理多語系
    3. 測試項目未處理小數點
    4. 測試項目難以閱讀
    5. 測試項目有重覆的覆蓋範圍
  • RD 與 PM 與 QA 合作

UAT 讓「人」讀得懂

原本的 UAT (RD)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
場景: case37:商品活動+全店活動(有排除);宅配,運費50元,滿350免運
.全店活動 / 有排除商品;滿額打折,多階(滿第1階),跨溫層
.折扣條件:滿199元,打95折 / 滿299元,打89折 / 滿399元,打84折
.商品活動;滿件折現,單階,跨溫層
.折扣條件:滿2件,折45元
假設 購物車中溫層"Freezer"商品為
| SalePageId | SaleProductSKUId | Price | Qty |
| 50 | 50 | 75 | 1 |
| 27 | 27 | 66 | 2 |
並且 購物車中溫層"Refrigerator"商品為
| SalePageId | SaleProductSKUId | Price | Qty |
| 26 | 26 | 55 | 2 |
並且 購物車中溫層"Normal"商品為
| SalePageId | SaleProductSKUId | Price | Qty |
| 25 | 25 | 2 | 2 |
並且 活動"1"範圍設定為
| TargetType | TargetIdList |
| Shop | 1 |
並且 活動目標排除商品頁為
| PromotionId | TargetExcludeSalePageList |
| 1 | 50 |
並且 現折活動"1"的折扣為
| Id | TypeDef | TotalPrice | DiscountTypeDef | DiscountRate |
| 1 | TotalPriceV2 | 199 | DiscountRate | 0.95 |
| 2 | TotalPriceV2 | 299 | DiscountRate | 0.89 |
| 3 | TotalPriceV2 | 399 | DiscountRate | 0.84 |
並且 活動"2"範圍設定為
| TargetType | TargetIdList |
| PromotionSalePage | 0 |
並且 活動目標商品頁為
| PromotionId | TargetSalePageList |
| 2 | 50,25,26,27 |
並且 現折活動"2"的折扣為
| Id | TypeDef | TotalQty | DiscountTypeDef | DiscountPrice |
| 4 | TotalQtyV2 | 2 | DiscountPrice | 45 |
當 計算活動折扣
那麼 購物車商品折扣後為
| SalePageId | SaleProductSKUId | PromotionDiscount |
| 50 | 50 | -12 |
| 27 | 27 | -25 |
| 26 | 26 | -19 |
| 25 | 25 | 0 |

「人」寫的 UAT

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
場景: 商品有兩檔活動,全店活動與商品活動;
.第一檔是全店活動 / 排除商品B;滿額打折,
.折扣條件:滿10元,打95折 / 滿20元,打89折 / 滿30元,打84折
.第二檔是指定商品;滿件折現,單階,指定商品A 、商品B
.折扣條件:滿2件,折3元

當 購物車中的商品為"商品A 與商品B"
| Title | SalePageId | SaleProductSKUId | Price | Qty |
| 商品A | 25 | 25 | 7.45 | 2 |
| 商品B | 26 | 26 | 4.45 | 2 |
並且 第"1"檔是全店活動 ,排除以下商品
| Title | SalePageId |
| 商品A | 26 |

而且 第"1"檔折扣條件是"滿額打折,滿10元,打95折 / 滿20元,打89折 / 滿30元,打84折",如下
| Id | TypeDef | TotalPrice | DiscountTypeDef | DiscountRate |
| 1 | TotalPriceV2 | 10 | DiscountRate | 0.95 |
| 2 | TotalPriceV2 | 20 | DiscountRate | 0.89 |
| 3 | TotalPriceV2 | 30 | DiscountRate | 0.84 |

並且 第"2"檔是指定商品,指定商品如下
| Title | SalePageId |
| 商品A | 25 |
| 商品B | 26 |
| 商品C | 27 |
| 商品D | 50 |

而且 第"2"檔折扣條件是"滿件折現,滿2件,折3元",如下
| Id | TypeDef | TotalQty | DiscountTypeDef | DiscountPrice |
| 4 | TotalQtyV2 | 2 | DiscountPrice | 3 |

當 計算活動折扣

那麼 購物車商品折扣金額及折扣後小計為
| Title | SalePageId | Price | Qty | PromotionDiscount | TotalPayment |
| 商品A | 25 | 7.45 | 2 | -2.55 | 12.35 |
| 商品B | 26 | 4.45 | 2 | -1.11 | 7.79 |

與 PM 及 QA 討論後 , 寫的 UAT 可閱讀性提高了
這裡用到了一些小技巧 , 讓 Cucumber 文件的可讀性更高
而不會有太多的重複方法 , 比如說把描述性的文字當作參數傳遞
實際上測試不會使用到這些變數 ,但是可以增加可讀性 .

Skip 台灣測試

因為已經有了跨國所需要的測試 ,
台灣的測試便可以退場了.
實際上也不符合現況, 如多語系、時差與小數點等問題

解析 CalculateShoppingCartPromotionDiscountV2Processor

解析

  1. 無折扣的情境
  2. 新舊相容的情境
  3. 排序
  4. 計算折扣金額
  5. 看見相依
    1. 程式碼中有 new 別的 class 的部份
    2. 程式碼中有使用靜態方法的部份

補上單元測試

最簡單的重構,就是將整個方法內的四個邏輯
拆成四塊個子方法,並為他們加上單元測試.
修改的過程,如果有紅燈就要修改成綠燈,
而整個成品要保證整合測試與單元測試都是綠燈.

此外,重構的過程中如果過到靜態方法,
或是 new 新物件, 都很有可能是種相依,
可以透過一些方法作解耦,
參考之前的文章單元測試這樣玩就對了

重構

最後一步就是大膽的重構了,
有了測試作保護,
可以作更大範圍的重構,
如下圖示,這裡揭露了在台灣原有的繼承結構,
而紅色的部份是在跨國用不到的類別.
類別

下一步,待續…

(fin)

20171023本周要聞/心得/學習擷錄

  1. Docker 宣布拥抱 Kubernetes

    1
    2
    3
    4
    Docker Swarm与整个Docker平台紧密集成,然而并非所有人都愿意选择Swarm。
    Hykes表示,默认的Swarm已经限制了Docker用户的完整体验,
    为此,Docker公司计划提供一个无缝平台,同时支持包含Swarm和Kubernetes集群的异构部署。
    Docker企业版(EE)将很快为Kuberenetes和Swarm提供全套Docker管理服务
  2. 免費字哪裡找?使用google font

  3. 使用 TypeScript 開發 nodejs 發生以下錯誤(使用 gulp 編譯成 js)

    1
    error TS2693: 'Promise' only refers to a type, but is being used as a value here.

    安裝 @types/es6-promise 以解決問題

    1
    npm i --save-dev  @types/es6-promise
  4. 使用 netstat -ano 指令在 windows 上查詢佔用的 port 與 PID

  5. 執行 npm ls moduleName 可以列出目前專案所以相依該模組的模組

  6. 執行 npm update 更新目前專案的模組

  7. package.json裡面的的節點dependencies放的是與專案相關的模組,
    devDependencies放的是與開發相關的模組。ex:gulp,
    請考慮你使用的模組,是為了開發?還是產品真的會用到。

  8. 想法

    • 懶人包應是協助跨越門檻與看見全貌的工具,
      雖然難以避免代入個人的價值觀,但是刻意為之是不好的。
    • 自我批判是一種建立反饋的最好方式,比起以公司\社群\世界最好的人要求自已,
      以最好的自已去要求自已理應該最恰當的,錄影、看鏡子之類的方式… 。
  9. [叫人意想不到的激勵科學 (TED 中英文字幕)] (https://www.youtube.com/watch?v=rFVhkIrVDzM)

    1. 自主性
    2. 掌握度
    3. 使命感

(fin)

[閱讀筆記] 異數

心得小結(2017.一讀)

作者認為成功的主要原因是優勢的累績與努力,
我們常說時間花在哪裡,成就就在哪裡,
但是許多傑出的例子(莫札特與比爾。蓋茲),
除了努力外,還有幸運的(?)際遇,
在很小的時候就開始邁向傑出,
這讓我想到福原愛;從小就接觸桌球又有母親(導師)親自調校,
但作者最終歸向時代與文化,兩個不可控的因素。
努力只能讓你成為比一般人好的人,
而加上際遇(時代與文化),才能成為傑出(ex:世界前 100 之類),
相比「我比別人更認真-刻意練習.讓自己發光」,
書比較強調導師與信念(熱情)的重要性,
異數比較偏向命定論。

並沒有很推薦,但是書中的馬太效應,
以及透過學習別的語言來改變文化帶來的
值得反思 .

筆記

ch1 好上加好的馬太效應

  1. 成功是優勢的累積
  2. 我們把成功與個人特質畫上等號,其他人因而失去出頭的機會。
    我們制定出來的規則反而壓抑成就,我們太早宣布某些人是失敗者。

ch2 一萬個小時的努力

  1. 他們的成功不完全是自已打造出來的,也是他們生長的時代造就的。

ch3 天才的迷思

ch4 天才的迷思.II

  • 才智和成就沒有絕對關係
  • 抽象智能與實用智能
    • 實用智能:社交常識與處事能力(後天習得,家庭),知道在什麼時候用什麼方式以及說什麼,以達到最大效果
  • 階級的文化優勢
    • 勞動階級和貧窮人家的孩子,保守不信任別人,厭惡權威,拙於交際

feedback:

  1. 千里馬常有,伯樂不常有。
  2. 我是勞動階級和貧窮人家的孩子
  3. 人脈的重要性。
  4. 如何與權威人士交際?

ch5 猶太律師的啟示

  1. 什麼都作,有生意上門就好了
  2. 機會其實是隱藏在逆境當中
  3. 你的父母是做什麼的,和你日後的成就大有關係
  4. 只要夠聰明就夠了

feedback:

  1. 七年級最慘的是哪一年次?
  2. 生逢其時 vs 生不逢時

ch 6 以血還血

ch 7 空中危機

  1. 文化的影響
  2. 以大韓航空空難為例-文化的包袱
  3. 用語言改變文化的包袱(ex:英文)

ch 8 稻米文化與數學能力

ch 9 知識力學校

後記 從牙買加到加拿大

  1. 亞洲數字發音較有規則且合乎邏輯
  2. 成功者的共通點就是努力

書單

  1. 意外的百萬富翁(Jobs 傳記)

其它

  1. 廖月娟的翻譯很不錯,感覺不出來是在讀外國人寫的書。
  2. 台灣財富排名 690 萬人是月光族 230 萬人有超 3 千萬資產

(fin)

Please enable JavaScript to view the LikeCoin. :P