[ Ruby on Rails ] 重構經驗談 (二)

原本打算一篇解決,不過因為篇幅太長,加上昨天之前在準備NoSQL Taiwan #6的分享,索性就拆成兩篇,一篇寫我的看法,一篇寫案例分享。

前一篇文章分享了我對Rails project前後端好維護的看法,以及要怎麼做才可以達到好維護的狀況,有些人可能會覺得太抽象,沒辦法體會為何我會有這樣的想法,這篇我會用這次面臨的狀況做一些前後呼應,讓大家有深刻的體驗!

先來談談這個case的狀況,原本這個案子是外包的外國朋友使用Rails 2.3獨立開發。因為是外包,加上公司當初正在快速成長,搶快是必然的,為了快,程式維護性差也是很正常的一件事,在進去公司之前我本來就已經有這樣的體悟,但認真研究了一下程式碼才發現,狀況實在比我想像中的差了太多!而公司為了使用一些Rails 3才支援的gem,希望可以在最短的時間升級、持續開發新功能、從Heroku migrate到Engine Yard,並且把前端切出來讓F2E可以好工作。

坊間流傳的Best Practice該做的部份我就不多說,我僅把我認為嚴重傷害維護性的部份列出來:

一個controller有過多action、一個action多用途(put、get、post)

把某功能幾十個action都集中在一個controller,router一個action一個action設定,又因為action過多,所以就很天真的就把不同method的動作拼成同一個action,甚還有一些action裡面裝了一堆天差地遠的功能。

action redirect (純redirect 或 條件式redirect)

因為某些功能會因為不同狀況而選擇不同的目的地,所以就用了action去做redirect的選擇器,還有某些action就單純就只有一行redirect_to,有點不知道在搞什麼鬼。加上action很多情況下也會沒做什麼事就redirect,所以就會導致一個很可怕的狀況:

Click A => B redirect_to C => C redirect_to D .......

做一個動作經過好幾個redirect才到達目的地,不只是對於performance來說是相當昂貴的,trace code的時候常常也會摸不著頭緒!

Constant取代Model

因為懶惰,把需要建立Model去做關聯的部份直接用Constant去實作,導致每改一個東西都需要重新deploy就算了,Constant的值改變還會造成程式上判斷的問題。

Controller、Model或Helper中直接用字串組view

在Controller、Model或Helper直接組View是很可怕的事,必須要用很多加號或雙引號去把字串組起來,程式裡面有不少東西都是用這種方式去實作,例如:分頁、預覽格式……,很容易就把Controller、Model和Helper玷污!

ajax丟json string到backend,由backend重新decode

這是最令我納悶的一點,為何要把json組成string,丟到backend再用backend decode做處理,搞得前端變後端、後端變前端?

model中直接組SQL

很基礎的指令為了做條件是判斷直接寫SQL去做處理,不但讓code變得又臭又長,還增加SQL Injection的風險!

大量使用RJS

如同我前文所說:「我認為前端和後端本來就是不同世界的東西,跨過平衡的界線只會讓問題萌芽。」

RJS可以算是一個非常好的案例,為了方便Rails developer,允許大家在controller寫一些看似很方便的Javascript動作。但實際上的情形卻不然,它讓Controller添增了非常多不應該出現的code,把整個Controller搞的很複雜、trace code非常不容易!加上RJS在Rails 3開始正式被遺棄,讓升級變成非常困難的一件事!

inline Javascript(CSS) + content_for Javascript(CSS)

前文提到:「降低前後端的相依性。」

這個產品使用不少的前端效果,這不是一件壞事,但如同上一點所說,網站用了不少RJS,加上inline Javascript(CSS)、不少藏在content_for的Javascript(CSS),還有封裝好的Javascript(CSS),更有不少Javascript重複執行。這意味著一件事,當你要修改的時候,你必須碰運氣的亂槍打鳥找到你要找的功能,找到以後,也許還會改東壞西,非常難維護。

Table View

這應該不用多說,實在太可怕了!Table裡面有Table,Table裡面再加Table,光Table View配合前面兩點其實就足以毀滅整個project的維護性……

檔案無結構化

前篇文章大概說過這個往佔大概有40~50個Controller和Model,打開指定的目錄就一次列出所有檔案是一件很可怕的事!

看完以上列出的幾點,不知道大家腦袋是否有浮現出令人傻眼的畫面?

為了滿足公司開出來的需求「在最短的時間升級、持續開發新功能、從Heroku migrate到Engine Yard,並且把前端切出來讓F2E可以好工作」,我先徹底的做了以上的分析,了解問題點所在,接著排定優先權:

1. 第一階段重構 (用最短的時間解決upgrade的問題)

  • 檢查gem的相依性,並完全upgrade成支援Rails 3的Gem
  • 徹底移除RJS,先用吐js.erb的方式快速轉移RJS的功能
  • 把所有該加=的地方都補上
  • 把controller用namescope包裝並修改所有routes
  • deploy to EngineYard

2. 第二階段重構 (著重於backend)

  • 拆除redirect action,使用helper去取代
  • 拆除多action的controller,用nested resources
  • 拆除多功能的action
  • 檔案結構化

3. 第三階段重構 (著重於front-end)

  • 刪除所有Table View
  • 拆除所有inline Javascript(CSS)和content_for Javascript(CSS),用前一篇的方法去處理
  • 把原本用js.erb的部份改用json + handlebar
  • 整理剩餘部分

這三個階段說起來簡單,但斷斷續續因為功能新增的需求也做了幾個月,雖然沒有到我理想中的完美,但至少已經把它變成一般Rails developer和F2E改的動的code!

在這整個過程的關鍵,我覺得最重要的是分析問題的能力,把問題詳細列出,向主管要求足夠的時間。接著不求一次到位,先了解短期目標,按部就班一個階段一個階段的進行,用收斂的方式先將大問題化成小問題,小問題在變成沒問題。問題解決後,就是必須嚴格要求自己和Team member按照既有的架構去養成習慣。否則,不用多久又會回到之前難維護的狀況了!

這次的重構經驗談到此告一個段落,如果有問題歡迎發問,希望這兩篇文章對於有閱讀我部落格的朋友們有些幫助! :)

本篇發表於 未分類。將永久鏈結加入書籤。

[ Ruby on Rails ] 重構經驗談 (二) 有 3 則回應

  1. 通告: [ Ruby on Rails ] 重構經驗談 | hellolucky's blog

  2. halida 說道:

    请问 Constant取代Model 这个问题是什么? 是指class XXX; ABC = ‘abc’; end这种吗?
    常量设置之后就不能变更, 我的解法是采用方法。 请问这块有什么讨论的文档可以借鉴的?

  3. hellolucky 說道:

    http://ruby-china.org/topics/6302 有舉例,把他轉過來好了

    Constant代替Model這個部分,因為代碼牽扯到公司機密,所以我隨便舉個不相干的例子供你參考。

    假設一個User會有一種Role,每種Role會有各自的屬性,且可能被更改。

    正常來講我們會開一個User model 和一個Role model 去做一對多的連結,如果要修改Role的屬性就從後台修改。

    而我指的Constant代替Model就是指開一個User model,把所有Role寫成constant放在User model裡面。

    ex:

    class User < ActiveRecord :: Base
    Role = {
    :normal => { :attribute1 => xxx , attribute2 => xxxx },
    :manager => { :attribute1 => xxx , attribute2 => xxxx },
    :admin => { : attribute1 => xxx , attribute2 => xxxx }
    }
    end
    讓應該用model管理的東西跑到代碼裡。

發表迴響

您的電子郵件位址並不會被公開。 必要欄位標記為 *

*

您可以使用這些 HTML 標籤與屬性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>