作者accessdenied (存取違規)
看板Soft_Job
標題Re: [討論] 前人的code 後人翻寫的機率高嗎?
時間Wed Sep 26 19:55:39 2018
單元測試有時候反而破壞程式碼的易讀性和維護性
因為要做到單元測試,就得斷開所有的相依性
而對抗相依性,作法就是引入 DI。但是 DI 就會增加代碼閱讀和維護的複雜度。
舉例來說,如果代碼內有時間上的相依性,例如用了 DateTime 物件取得現在時間做某些
判斷,原本可以很簡單的寫出易於閱讀和維護的邏輯:
If (DateTime.Now > 12:00:00) then return “PM” else return “AM”;
為了讓單元測試可以控制驗證條件,只能
Interface IDateProvider { virtual GetNow(); }
Class DateMock : IDateProvider { GetNow() { return 13:00 }
IDateProvider dateProvider = container.Build(....);
If (dateProvider.GetNow > 12:00:00) then return “PM” else return “AM”;
然後再搞個 config 想辦法讓程式吃到你寫的 DateMock 類別....
上面是sudo code 就不用討論語法細節
這就是單元測試的代價!程式真的會比較容易閱讀嗎?
單元測試要花在切除相依性的條件花費的成本時間遠高於撰寫production code
而且你的 production code 能不能賺錢還不知道咧?
到底需不需要單元測試和 clean code ? 先搞清楚寫的用途和目的,你寫的東西有沒有
真正的商業價值再說吧。
不要把 clean code 和 TDD 無限上綱了
工程師最容易自嗨就是這樣,還會自以爲「這是專業」?
乞丐的乞討專業比我們強也不會產生「價值」。
※ 引述《banqhsia (BEN)》之銘言:
: ※ 引述《peanut97 (丁守中)》之銘言:
: : 大家中秋節快樂,快收心了。
: : 想問一個假設性問題,大家在工作上,如果有一份專案的 code 是某位前人一手寫的
: : 後來新人加入,變成前人帶新人,此時繼續維護那份code。
: : 但再過一陣子,前人離職了,唯一的創始者走了。
: : 新人把舊 code 重構,或是砍掉重鍊的機率高嗎?
: 先跟主管、老闆提,確認有人支持你,不然你會被當成怪物
: 「為什麼要改?」
: 「系統好好的幹麻改?」
: 「改了有好處嗎?」
: 「會花多久時間?」
: 「時間剩不多,要動不動隨便你,不要影響到時程」
: : 我的想像是,如果一份code是出自於1個人之手
: : 我的想像是,如果一份code是出自於1個人之手
: : 那麼code就是他的世界觀、他的切入點
: : 那麼code就是他的世界觀、他的切入點
: : 後面的人看著他的世界觀,有時候不一定能全部接受
: : 而有人的地方就有政治
: : 當他還在的時候,當然就不會亂動。
: : 而當他走了的時候,後面的人,一看不爽,就可能改寫成自己看得爽的、
: : 好改的code。
: : 如果是一個團隊,那當然要好好討論為什麼要改
: : 哪些因素造成現在不好的情況,以及主管同不同意改等等的。
: : 只是我很好奇,1,2人的專案,改的機率高嗎?
: : 是不是,code只能是「現在還存在公司的人」能控制的才行。
: 我們公司的經驗,以前因為很多原因 (十幾年前的 code)
: 導致系統沒有測試、沒有嚴謹的 coding style、方法註解也很少
: copy paste 是基本,沒有遵照 SOLID
: 錯誤不是丟 null 就是 false (欸! 我的 Exception 呢?
: 每個 team 成員想怎麼寫就怎麼寫,不管後續的漣漪
: 反正東西交出來就好 多棒
: 然後中間當然成員就是來來去去的
: 我看這之中大概也是 有人進來 -> 看 code -> what the fuck? -> 離職
: 大概就是這種循環
: 因為自己痛過,知道 clean code、設計模式 的重要性
: 進公司沒多久就跟主管說這 code 不能搞,一定要重構
: 每個工程師一定看不爽前人的 code XD
: 但是不是說說而已,總是要提出改善的方法
: 理所當然地,你前面那些問題我都被問過
: 幸好我的主管與老闆也是支持我這件事情
: 但是我們討論的結果,現在的 code 也沒辦法全部翻掉,怎麼辦
: 在那個時候剛好要把原本的系統生出一套 API
: 原本的系統是 server-side template render,模板與資料、樣式高耦合
: 這個沒辦法改成 API
: 我們的作法是,把原本 DAO 抽出來,放進框架裡當成 library
: 然後加一層 Adaptor 讓新系統相容舊模組
: 舊的 DAO 邏輯怎樣就不去動他,要改一律在 Adapter 裡面改
: (就算 method rename 也是)
: 在這個新系統,以 SOLID 與設計模式為基楚
: controller 與邏輯之間,包裝成 service 呼叫 (一律把該寫的東西放在該去的地方)
: service 只准有抽象的敘述,實作的部分寫成介面去依賴,由子類別注入 service
: 使用 DI Container 自動注入依賴的類別,不准直接 new (方便替換測試)
: 提交功能分支以前,要一併提交單元測試與 functional test,否則不准進 develop
: 這些都是規定好寫在專案文件裡面的 (以前沒有的文件,現在開始留下記錄)
: 接著就是重頭戲的部分 --- 跟人有關的
: coding style 怎麼統一? 就算統一了還是會有漏網之魚
: 不小心看到 violation 還是會白眼翻到尾椎
: 所以這邊必須用自動化工具檢查,綁在 CI 流程裡
: 這樣已經可以省掉很多人工的部分
: 接著定期 code review,挑出變數不懂、「比較不易修改」的類別… 等等
: 這樣至少做到了幾件事情
: 1. 以前沒有的,現在開始留下記錄
: 2. 把 legacy code 加上 test
: 3. 把系統改成 API server 從視圖抽離後,再動前端 (refactor 分階段)
: 4. 統一 coding style
: 5. 符合 SOLID、易於修改的架構 (為了以後著想)
: 當然前面幾篇貼文,有些前輩也有提到
: 「有人的地方就有政治」
: 這句話應該劃 _底線_ 三顆星 ★★★
: 這之中太多阻礙了,電腦不會背叛你,人會 XDD
: 做了這麼多品質保證措施,一定會遇到成員反對
: 「一個變數而已,幹麻花這麼久時間討論?」
: 「我覺得 code review 花太多時間」
: 「我覺得一個東西,幹麻拆這麼多類別,放在一個不就好?」
: 深入探討提出這些問題的同事心態,不外乎幾個原因
: 1. 影響 issue solving 時間 --> 覺得產出變慢
: 2. 覺得寫單元測試很麻煩 --> 覺得程式會動就好
: 3. 覺得一個類別不用提煉為抽象+實作 --> 現在又沒這麼多需求 幹麻拆?
: 但他們忘記事實是,程式本來就是會修修改改的,而且也是會給別人改的
: 這讓我想到一句話
: 「
: 據統計,你現在挖的坑,有80%會自己來擦屁股
: -- 不知道是誰 」
: 即便跟他們說品質是什麼,clean code 的重要性,他們還是覺得『不需要』
: 所以大多數的問題,都是「人」造成的
: stop writing legacy code,從你我開始
: 但如果你身邊沒人支持,更進一步的說: 沒有得到老闆/主管支持
: 這公司也差不多 GG (因為不在意品質)
: 如果你是 team 的其中一個人,試著先從統一 coding style 開始
: 我在 Modern Web 2018 聽到有一場 keynote 是 Terry Yin 大神的「遺留代碼經濟學
」
: 裡面有提到,其實也不用這麼討厭 legacy code
: 因為有那些東西,我們工程師才得以被養活。
: 如果你發現有 code 裡面處處是坑,怎麼辦? 那就別再挖坑了啊!!
: 我們現在做的,只是把以前沒做的都補回來而已
: 但是,不要再欠下更多的技術債
: 債總有一天還是要還的,而且還會生利息
: 最後我要說一句
: 很多前輩們會建議,要重構前先寫測試
: 但是
: 一個萬能類別/萬能方法/高耦合,要寫單元測試,發現整個系統都要寫
: 有些 hard code 的 class 還不見得可以 mock
: 「人」的問題
: 無解。
--
※ 發信站: 批踢踢實業坊(ptt.cc), 來自: 220.135.20.48
※ 文章網址: https://www.ptt.cc/bbs/Soft_Job/M.1537962940.A.A36.html
→ Killercat: 這個很正常 要先界定黑箱範圍 這算FAQ了... 09/26 20:10
→ Killercat: 黑箱一開始一定非常大 唯一的要求就是testable output 09/26 20:11
→ Killercat: 慢慢穩定以後才會慢慢的在把黑箱切小 09/26 20:11
→ Killercat: 另外其實你舉的例子怪怪的 這input output都是testable 09/26 20:12
→ Killercat: Mock也沒那麼困難 我不知道為什麼要搞那麼複雜... 09/26 20:13
→ accessdenied: 原本Input不在自己可控的範圍內,早上和晚上測到的o 09/26 20:15
→ accessdenied: utput不同就不能說是testable, 因為單元測試應該要 09/26 20:15
→ accessdenied: 能隨時跑、隨時測、都一致。 09/26 20:15
→ Killercat: er... Mock DataTime.now()很困難嗎 o_oa? 09/26 20:16
→ Killercat: 我知道你想表達的點 但是GMock(C++) Mokito(Java)做到 09/26 20:17
→ Killercat: 這件事情都不困難啊... ? 09/26 20:17
→ landlord: 我不用 interface 也可以模擬 DateTime 耶。 09/26 20:17
→ accessdenied: Anyway, 只是個例子,不用糾結在DateTime 的mock困 09/26 20:18
→ accessdenied: 不困難,實務上絕對會遇到為了Mock而大費周章的案 09/26 20:18
→ accessdenied: 例 09/26 20:18
→ landlord: 你講的是學習單元測試必定會碰到的障礙,這肯定沒錯 09/26 20:18
→ landlord: 但碰到障礙是解決障礙,還是退回原路,就是選擇了 09/26 20:19
→ Killercat: 有是有啦,不過真的不常見,底層才比較會碰到 09/26 20:19
→ senjor: 有,Database的Accessor要MOCK的確蠻麻煩的 09/26 20:19
→ landlord: 任何東西無限上綱跟把它當萬靈丹,都是有問題的 09/26 20:20
→ senjor: 但是回頭來說,程式也是人寫的,既然都知道有特例,幹嘛不 09/26 20:20
→ landlord: 但怎麼拿捏尺度,怎麼往更深入和實務學習,就是專業差異 09/26 20:20
→ Killercat: 我是覺得 碰到mock困難 該做的應該是提出案例討論 而非 09/26 20:20
→ senjor: 針對特例做閃躲就好,何必因為特例否決全部 XDD 09/26 20:20
→ Killercat: 全盤否定掉TDD / UT的concept 09/26 20:20
→ landlord: 為了單元測試而多墊很多不必要的中間層,那是菜鳥錯誤 09/26 20:21
→ Killercat: 應該說 除非語言很奇耙 不然其實現有套件來講 應該很多 09/26 20:21
→ Killercat: 都不需要你墊這些中間層只為了能測... 09/26 20:22
→ Killercat: 或者中間夾了層打不爛的巨大黑箱 不然其實特例討論即可 09/26 20:22
→ landlord: 大部分測試案例難加,難維護,維護成本高,都是設計太爛 09/26 20:23
→ landlord: 而要解決的問題是「設計太爛」,而不是「不寫測試」 09/26 20:24
→ accessdenied: 我沒有要全盤否定喔。但對堅持一定要的人,我覺得是 09/26 20:24
→ accessdenied: 可以多想想的 09/26 20:24
→ Killercat: 我倒覺得思考能不能做到的可能性很重要 不過的確,總會 09/26 20:26
→ Killercat: 碰到一些窒礙難行的時候,我是覺得就討論看看 09/26 20:26
→ Killercat: 說真的以我的經驗 這種case不是很多 09/26 20:27
→ x000032001: 不知道貴公司一天下git blame的次數有沒有上千次 09/26 20:29
→ sharku: Mock技術決定一切 09/26 20:30
→ landlord: 如果模擬now寫成那樣子,我贊成你,我也希望他不要寫。 09/26 20:32
→ landlord: 只是在製造債,把簡單代碼搞得更複雜而已。 09/26 20:32
→ Killercat: 文雅點 我們都叫他annotate而不是blame XD 09/26 20:34
→ accessdenied: 喔!91大週六有課程喔?工程師該去的都去喔,不要再 09/26 20:49
→ accessdenied: 寫拿著那噁心的DI code了 09/26 20:49
→ Killercat: DI都是有原因有目的才DI 不是DI噁心 是亂DI才噁心... 09/26 20:50
→ accessdenied: 也是啦,刀子殺人不是刀子的錯,是用刀的人 09/26 20:52
噓 KanzakiHAria: 大師先推再說 09/26 22:15
→ viper9709: 原來是這樣 09/27 00:45
推 superpai: 你不會寫 pure function嗎? 09/27 09:08
噓 Ghamu: 好棒 09/28 00:34