Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Refactoring lesson : from GPA 1.4 to GPA 3.0

Yi-Ting Cheng
September 24, 2016

Refactoring lesson : from GPA 1.4 to GPA 3.0

RubyConf China 2016 - Xdite

Yi-Ting Cheng

September 24, 2016
Tweet

More Decks by Yi-Ting Cheng

Other Decks in Technology

Transcript

  1. Refactoring lesson : from
    GPA 1.4 to GPA 3.0

    View Slide

  2. About me
    • xdite
    • 微信号 xxddite
    • 公眾号 Xdite

    View Slide

  3. 全栈营教头
    • Ruby on Rails
    • Agile
    • Growth Hack
    • 认知⼼心理学 + 技术教学
    https://ruby-china.org/topics/31080
    無編程經驗的新⼿手,如何在四周開發實戰等級產品

    View Slide

  4. 为什么会来这次⼤大会
    • 刚好我在北京
    • 筹备组就问我要不要来
    • 顺便投⼀一个题⺫⽬目
    • 去年我都没有在燃烧我的激情 ( a.k.a 写 code )
    • 想了⼀一下还是投了⼀一个题⺫⽬目

    View Slide

  5. Refactoring lesson : from
    GPA 1.4 to GPA 3.0
    酷,程序员听到「翻修」就充满激情

    View Slide

  6. 1.4 -> 3.0
    codeclimate.com

    View Slide

  7. 3.0

    View Slide

  8. 1.4

    View Slide

  9. 背景故事

    View Slide

  10. 2014 年 7 ⽉月
    • 矽⾕谷 O2O 送餐公司
    • 种⼦子轮 拿了 250 万美⾦金
    • A 轮拿了 1100 万美⾦金
    • 来台湾征才
    • 8 ⼈人公司加⼊入了 SpoonRocket
    • 公司⼤大举招⼈人

    View Slide

  11. • Angular - 客⼾戶 Web 端
    • Android
    • iOS
    • Rails - Admin
    • jQuery - Dispathcher (⼈人⼯工配餐台)

    View Slide

  12. • 2 ⼈人技术团队,⼀一个⽉月之内扩编到 20 ⼈人
    • ⼤大家就⼀一直踩到隔壁的脚

    View Slide

  13. • 虽然技术团队 20 ⼈人
    • 每天可以拉上超过 20 ⽀支 pull-request
    • 但是每天只能 deploy 2 ⽀支 pull-request
    • 凌晨 3:00 (台湾时间)开店,SA 只能睡公司....

    View Slide

  14. • 经常性 Rollback
    • 20 ⼈人还是只有 2 ⼈人战⼒力
    • 每天开店⽼老是炸
    • ⽆无法做促销,呼叫⼀一只菜单 API ~= 700ms
    • 开启 Admin Controller 超过 60 秒

    View Slide

  15. • 刚加⼊入没多久就后悔
    • Grape API
    • 每⼀一只 Grape API 约 1000-2000 ⾏行
    • 毫⽆无逻辑的 EndPoint
    • 重复逻辑到处复制贴上
    • Admin 后台的单⼀一 index action 也⾼高达数千⾏行

    View Slide

  16. • CTO 与第⼀一个 RD。凭借蛮⼒力硬是上线
    • 永远保持能卖东⻄西就好
    • 我⻅见识到的是 GPA 1.0 的代码也能募到这么多钱....
    • 所以代码是否干净与能不能赚钱没有关系

    View Slide

  17. ⼈人⼈人都想 Refactor

    View Slide

  18. 但⼈人⼈人都不想 Refactor

    View Slide

  19. • ⾝身为 RD Lead
    • ⼤大便只能由我先吃
    • 毕竟这是我的⼯工作..

    View Slide

  20. 結果
    • API 代码 95 % 以上覆盖
    • 上了 CI
    • 全代码覆盖率 60%
    • GPA 3.0

    View Slide

  21. 挑战 1 :从哪⾥里开始?

    View Slide

  22. • 快 100 只 API
    • 50 只以上 Controller
    • 快 70 个 model
    • 10000 多⾏行代码

    View Slide

  23. 答案:先补⽂文档

    View Slide

  24. WIKI 上必须要有主要 API 的⽂文档
    • GET / POST
    • 输⼊入参数 / 输出结果
    • 回传值
    • (真实资料)

    View Slide

  25. • 取得全局感
    • ⼤大致上有多少 API 我们得⾯面对
    • 估计⼯工作量
    • 估计「重构」所损失的战⽃斗⼒力 ( 必须公司可承受)

    View Slide

  26. ⽼老板说他只能承受 2 周没有⽣生产⼒力...

    View Slide

  27. ⽼老板说他只能承受 2 周没有⽣生产⼒力...
    当然事实上是 1 天 都不能没有

    View Slide

  28. 挑战 2 :先写哪边的 Test ?

    View Slide

  29. 答案:先写 API Test

    View Slide

  30. • 把每⼀一⽀支 APP 都开⼀一⽀支 Redmine Ticket

    View Slide

  31. 实作步骤
    • 半强迫每个 RD,⼀一个礼拜必须写 10 ⽀支测试
    • 没时间也⾄至少做到
    • API 200
    • 验证格式正确
    • ⼿手头掰不出测试资料就使⽤用「真实资料」

    View Slide

  32. • 牵涉到 3rd Party 的 API 不写 Test
    • 呼叫演算法的 API 不写 Test 






    • 因为代码太渣了,⽆无法强⾏行 mock…

    View Slide

  33. • ⼀一个礼拜补完所有 API Test …(⾄至少有 200 与格式)
    • ⾄至少 RD 要是拉 pull request,没有绿灯就可以叫他滚
    回去了
    • 快速滤掉第⼀一层低级错误
    • rollback 率⼤大幅下降

    View Slide

  34. 挑战 3 :先重构哪部分的代码 ?

    View Slide

  35. 从 API 分离下⼿手
    • Before : ⼀一⽀支 API 档案 > 1000 ⾏行代码
    • After :⼀一⽀支 APP 的「档案」必须⼩小于 160 ⾏行

    View Slide

  36. 从 API 分离下⼿手
    • 原先 API ⼤大约有 10 个 Endpoint
    • 降到 3 个 End Point

    View Slide

  37. 开始有办法清楚的看懂代码

    View Slide

  38. 闪坑
    • 不会再被 if / else 坑到
    • 不会再也不知道⾃自⼰己再改那个 end point 的代码
    • 有机会把「相近」的 Resource 放在同⼀一个档案
    • 因为有 200 Test 可以放⼼心的搬家

    View Slide

  39. 抽出业务逻辑
    • 把业务逻辑搬到 ServiceObject
    • API 只负责呼叫 Service Object
    • API 的 Test 只负责验证 200

    View Slide

  40. View Slide

  41. View Slide

  42. • 添加 error!
    • 将业务逻辑细细拆分
    • 终于可以仔细拆分不同
    状况,不需到处 mock
    • 甚⾄至可以开始 stub
    ServiceObject

    View Slide

  43. 开始处理⽆无法 Mock 的 API
    • 要求「演算法」也抽出成 ServiceObject
    • 不测试演算法
    • 但 API 必须补「格式」Test

    View Slide

  44. ⼩小结

    View Slide

  45. • Step 1: 先估计「⼯工作量」
    • Step 2: 装上红绿灯,⾄至少保证永远有基础绿灯。
    • Step 3: 切细变成可以「重构」的区块
    • Step 4: 盒⼦子打包法,补⿊黑盒⼦子测试

    View Slide

  46. 挑战 4 :补「重构」哪部分的代码 ?

    View Slide

  47. CodeClimate Refactor 法
    • 找出 F 等级代码。
    • 专攻重复区域,打包成 method,补 unit test。
    • ⼜又补了⾄至少 > 50 个 Test
    • 重复的代码 = 重要的业务逻辑(到处都⽤用到)

    View Slide

  48. • ServiceObject (商业流程)
    • Calculator ( 计费可以随时抽换不同公式 )
    • Validator ( ⼀一个 Order ⾄至少要经过 10 道验证,把验证做到可叠
    加 )
    • Serializer ( 原先是 Hash,改⽤用 Serializer 好管理 )
    • Cells ( 让 View 可重复使⽤用 )
    • StrategyClass ( 可策略抽换应付「营销活动」与「价格模型」 )
    • Worker ( 把效能瓶颈移到背景去执⾏行 )
    • Concern ( 复合使⽤用把常⻅见⼯工具类 method )

    View Slide

  49. ServiceObject
    • 查价 / 库存
    • 下单
    • 退单
    • 跟踪订单
    • 计算运送所需时间
    • 不同版本 API,使⽤用不同 ServiceObject

    View Slide

  50. View Slide

  51. Calculator
    • 不同地区不同税率
    • 冷的不收税、热的收税
    • 司机⼩小费
    • 礼物卡的税务逻辑

    View Slide

  52. Strategy
    • 地区营销活动
    • API 上可插拔
    • 活动结束直接关掉 Strategy

    View Slide

  53. Worker
    • 付款呼叫 Stripe
    • 伪卡
    • 霸⺩王餐⾏行为

    View Slide

  54. SOA
    • 演算法
    • Dispatcher (⾼高 JavaScript 耦合)

    View Slide

  55. ⼩小结

    View Slide

  56. • 每个业务逻辑的接⼝口必须干净,容易插拔
    • 每整理⼀一个业务模块,就补 Test
    • 修改 / 新增功能,必须补 Test

    View Slide

  57. 花了 1 个⽉月

    View Slide

  58. • 2 pull request => 5 => 10
    • 终于可以上 CI server
    • 不是绿灯,pull request 不会有⼈人理

    View Slide

  59. 讲完 API ,来讲 Controller

    View Slide

  60. • ⼀一个 Controller ⼏几千⾏行代码



    • ⽼老板把逻辑都写在 JavaScript ⾥里⾯面
    • 因为 Dispatcher 台是 JavaScript 写的
    • 只好其他业务逻辑也⽤用同样的⽅方式

    View Slide

  61. 第⼀一步
    • 把 Dispatcher 「以外」的部分搬去 Rails CRUD
    • 不太需要写 Test
    • 烂了请美国同事 hotfix,也不影响消费者下单
    • 拆了 ~15 ⽀支 Controller

    View Slide

  62. 第⼆二步
    • 把 Dispatcher 当作是 Client
    • 不再是 Rails 与 JavaScript 混合产⽣生 View
    • 显⽰示逻辑都使⽤用 JavaScript
    • Rails 作为 API 提供资料
    • 补 API Test

    View Slide

  63. • Frontend 组搞了很久
    • 巨坑....
    • 那时候剛有 React, …
    • 学到以后复杂逻辑绝对別⽤用 jQuery 先搞。

    View Slide

  64. 经过这⼀一轮
    • 1 个⽉月就从 1.4 => 2.95
    • 代码覆盖率到 40% +
    • 花了另外的 5 个⽉月 2.95 => 3.06
    • 花了另外的 5 个⽉月代码覆盖率到 60%
    • ⾜足够好

    View Slide

  65. 挑战 5 :Release 太快带来的问题

    View Slide

  66. • 每天 Release 15 ⽀支 pull request
    • db migration 或是改资料,还是个坑,需要⼈人为介⼊入
    • 运营团队抱怨虽然没有 500,但是业务逻辑⼀一直在变
    • 应该要出 Release ⽇日报

    View Slide

  67. zendesk/samson

    View Slide

  68. ⼀一键 deploy

    View Slide

  69. View Slide

  70. Deploy 架构
    • samson 修改版 for heroku deployment
    • heroku pipeline

    View Slide

  71. • ⾃自动汇集 pull request 成 deploy ⽇日报
    • 必须叙述作⽤用
    • 若有 migration 必须在 #Risk 环节注记
    • Deploy ⾃自动执⾏行

    View Slide

  72. 成果
    • GPA 3.06
    • 60 % test coverage
    • 15 pull request/day
    • API has ~95% test coverage.
    • major API response time < 80 ms
    • We shipped 6-8 major features every week.

    View Slide

  73. Take away
    • 先搞清楚有多少测试要写。 (否则会遭到公司否决)
    • 先写 Integration Test
    • Integration test -> Service Object test -> Unit test
    • 尽量提供 developer 「动机」去写
    • 测试资料
    • 可复制的测试代码

    View Slide

  74. Take away
    • 做 Refactor 前⼀一定要先上 Test
    • 前期⺫⽬目标是「有动机去做」
    • 中期⺫⽬目标是「接⼝口干净」
    • 远期⺫⽬目标是「⾃自动部署」

    View Slide

  75. 不过

    View Slide

  76. • 虽然「补测试」这么不爽
    • 之后创业我还是不会先写测试
    • 因为我理解了 GPA 1.0 还是可以拿到 1100 万美⾦金
    • 商业的重点是「测试」你的 「idea」,⽽而不是「代码」
    • 赚了钱你要雇⼏几个⼤大⽜牛来帮你重构都⾏行

    View Slide

  77. Q & A

    View Slide