作者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://webptt.com/cn.aspx?n=bbs/Soft_Job/M.1537962940.A.A36.html
1F:→ Killercat: 这个很正常 要先界定黑箱范围 这算FAQ了... 09/26 20:10
2F:→ Killercat: 黑箱一开始一定非常大 唯一的要求就是testable output 09/26 20:11
3F:→ Killercat: 慢慢稳定以後才会慢慢的在把黑箱切小 09/26 20:11
4F:→ Killercat: 另外其实你举的例子怪怪的 这input output都是testable 09/26 20:12
5F:→ Killercat: Mock也没那麽困难 我不知道为什麽要搞那麽复杂... 09/26 20:13
6F:→ accessdenied: 原本Input不在自己可控的范围内,早上和晚上测到的o 09/26 20:15
7F:→ accessdenied: utput不同就不能说是testable, 因为单元测试应该要 09/26 20:15
8F:→ accessdenied: 能随时跑、随时测、都一致。 09/26 20:15
9F:→ Killercat: er... Mock DataTime.now()很困难吗 o_oa? 09/26 20:16
10F:→ Killercat: 我知道你想表达的点 但是GMock(C++) Mokito(Java)做到 09/26 20:17
11F:→ Killercat: 这件事情都不困难啊... ? 09/26 20:17
12F:→ landlord: 我不用 interface 也可以模拟 DateTime 耶。 09/26 20:17
13F:→ accessdenied: Anyway, 只是个例子,不用纠结在DateTime 的mock困 09/26 20:18
14F:→ accessdenied: 不困难,实务上绝对会遇到为了Mock而大费周章的案 09/26 20:18
15F:→ accessdenied: 例 09/26 20:18
16F:→ landlord: 你讲的是学习单元测试必定会碰到的障碍,这肯定没错 09/26 20:18
17F:→ landlord: 但碰到障碍是解决障碍,还是退回原路,就是选择了 09/26 20:19
18F:→ Killercat: 有是有啦,不过真的不常见,底层才比较会碰到 09/26 20:19
19F:→ senjor: 有,Database的Accessor要MOCK的确蛮麻烦的 09/26 20:19
20F:→ landlord: 任何东西无限上纲跟把它当万灵丹,都是有问题的 09/26 20:20
21F:→ senjor: 但是回头来说,程式也是人写的,既然都知道有特例,干嘛不 09/26 20:20
22F:→ landlord: 但怎麽拿捏尺度,怎麽往更深入和实务学习,就是专业差异 09/26 20:20
23F:→ Killercat: 我是觉得 碰到mock困难 该做的应该是提出案例讨论 而非 09/26 20:20
24F:→ senjor: 针对特例做闪躲就好,何必因为特例否决全部 XDD 09/26 20:20
25F:→ Killercat: 全盘否定掉TDD / UT的concept 09/26 20:20
26F:→ landlord: 为了单元测试而多垫很多不必要的中间层,那是菜鸟错误 09/26 20:21
27F:→ Killercat: 应该说 除非语言很奇耙 不然其实现有套件来讲 应该很多 09/26 20:21
28F:→ Killercat: 都不需要你垫这些中间层只为了能测... 09/26 20:22
29F:→ Killercat: 或者中间夹了层打不烂的巨大黑箱 不然其实特例讨论即可 09/26 20:22
30F:→ landlord: 大部分测试案例难加,难维护,维护成本高,都是设计太烂 09/26 20:23
31F:→ landlord: 而要解决的问题是「设计太烂」,而不是「不写测试」 09/26 20:24
32F:→ accessdenied: 我没有要全盘否定喔。但对坚持一定要的人,我觉得是 09/26 20:24
33F:→ accessdenied: 可以多想想的 09/26 20:24
34F:→ Killercat: 我倒觉得思考能不能做到的可能性很重要 不过的确,总会 09/26 20:26
35F:→ Killercat: 碰到一些窒碍难行的时候,我是觉得就讨论看看 09/26 20:26
36F:→ Killercat: 说真的以我的经验 这种case不是很多 09/26 20:27
37F:→ x000032001: 不知道贵公司一天下git blame的次数有没有上千次 09/26 20:29
38F:→ sharku: Mock技术决定一切 09/26 20:30
39F:→ landlord: 如果模拟now写成那样子,我赞成你,我也希望他不要写。 09/26 20:32
40F:→ landlord: 只是在制造债,把简单代码搞得更复杂而已。 09/26 20:32
41F:→ Killercat: 文雅点 我们都叫他annotate而不是blame XD 09/26 20:34
42F:→ accessdenied: 喔!91大周六有课程喔?工程师该去的都去喔,不要再 09/26 20:49
43F:→ accessdenied: 写拿着那恶心的DI code了 09/26 20:49
44F:→ Killercat: DI都是有原因有目的才DI 不是DI恶心 是乱DI才恶心... 09/26 20:50
45F:→ accessdenied: 也是啦,刀子杀人不是刀子的错,是用刀的人 09/26 20:52
46F:嘘 KanzakiHAria: 大师先推再说 09/26 22:15
47F:→ viper9709: 原来是这样 09/27 00:45
48F:推 superpai: 你不会写 pure function吗? 09/27 09:08
49F:嘘 Ghamu: 好棒 09/28 00:34