Sublime Text 資源整理

自從 Textmate 2 官方開發陣亡之後,就改用 Sublime Text 2 來寫 code 了。整理一些找到的好資料跟 Plugins:

Plugins

Syntax 相關

Settings

編輯 Preferences > Settings > User 加上:


{
  # 把 tab 換兩個空白字元
  "translate_tabs_to_spaces": true,
  "tab_size": 2,
  # 高亮所在行
  "highlight_line": true,
  # 存檔時移除多餘的空白
  "trim_trailing_white_space_on_save": true
}

還有推薦什麼必裝的 Plugins 嗎?

修改 AWS RDS 資料庫設定,以 max_allowed_packet 為例

記錄一下,以後肯定還會再用到。

因為 MySQL 預設的 max_allowed_packet 設定是 1M,對有些應用來說可能太小了。如果是自己架 MySQL 改 /etc/mysql/my.cnf 就是了,不過租用 Amazon RDS 就沒有這種事了。又它的 web console 也沒有提供介面可以改設定(parameters)除了可以用 AWS 的 web console 介面來改,也可以透過 Amazon RDS Command Line Toolkit 呼叫 API 來改,步驟如下:

  1. 下載 Amazon RDS Command Line Toolkit

  2. 參考 credential-file-path.template,編輯 credentials.txt 把 AWSAccessKeyId 和 AWSSecretKey 填上去

  3. 設定環境變數,例如:
    export AWS_RDS_HOME=/Users/ihower/RDSCli-1.12.001
    export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Home
    export PATH=/Users/ihower/RDSCli-1.12.001/bin:$PATH
    export AWS_CREDENTIAL_FILE=$AWS_RDS_HOME/credentials.txt
    export EC2_REGION=ap-northeast-1

  4. chmod +x ${AWS_RDS_HOME}/bin/*

  5. rds-create-db-parameter-group <自定的parameters群組名稱> -d "群組描述" -f MySQL5.5 新增一個群組來放參數

  6. rds-modify-db-parameter-group <自定的parameters的群組名稱> -p "name=max_allowed_packet,value=2097152,method=immediate" 把參數放進你自定的群組裡 (這裡單位只能用bytes)

  7. rds-modify-db-instance <DB Instance Name> -g <自定的parameters群組名稱> 把群組綁到指定的 DB 上

  8. 這個設定似乎不需要 reboot 就生效了。要重開的話,在 web console 也可以 reboot。

完整的文件在 AWS Command Line Reference,例如


rds-describe-db-instances <DB Instance Name> 看這個 DB 套用了什麼 PARAMGRP 群組
rds-describe-db-parameters <自定的parameters的群組名稱> 看這個群組的 parameters

A brief introduction to SPDY – 邁向 HTTP/2.0 – 投影片

這是這次在 WebConf Taiwan 演講的投影片。從接到演講邀請就想說講什麼題目最切題,雖然自己比較熟的是 Rails,但是好像從前年開始就沒有在公開的場合講 Rails 了(咦?) anyway,這次一開始投的是「如何善用 REST 和 Hypermedia 設計 Web APIs」,後來看 SPDY 覺得這更實用且影響更大,更值得介紹給大家。加上我自己之前看 HTTP 連線管理 就覺得這議題蠻有趣的。REST 只是一種設計風格,而 HTTP 可是大家賴以為生的通訊協定啊,所以就毅然決然決定來改題目…

會後似乎獲得了不錯的回響,看來很多人都是第一次認識 SPDY 啊,題目真是挑對了 XD

Defensive Programming 防禦性程式設計

TL;DR 避免 Defensive Programming,愛用 Fail Fast 策略。

什麼是防禦性程式設計,在 Erlang’s Programming Rules and Conventions: Do not program “defensively” 裡這樣說明:

防禦性程式發生在程式設計師不相信系統的輸入(input)資料時。一般來說我們不需要測試輸入的資料來確保功能正常。系統中大部分程式碼應該可以假設輸入的資料是正確的,只有一小部分的程式需要真的檢查資料,這通常發生在資料第一次輸入的時候。只要資料在輸入進系統時被檢查過,那麼就應該可以假設它是正確的。

也就是說,當你不相信輸入時,你就得去檢查它。有經驗的程式設計師應該都非常熟悉這種作法,他的缺點在於這常是多餘的資源浪費,不但浪費效能,也增加程式碼維護的成本。好比說一個方法已經檢查過參數了,該方法又繼續把參數傳遞給別的方法時,可能又要再檢查一次。如果所有方法都要針對參數重複做嚴格的檢查,豈不是非常囉嗦。所以 Erlang’s Programming Rules 建議你在系統裡不要檢查,crash 與否的責任在於呼叫方,在於資料一開始輸入進系統的時候,而不是這個方法。

但是,總是有需要檢查的時候,特別是資料第一次輸入進系統的邊界情況,以應用程式來說,就是指使用者的輸入; 以 Library 來說,就是指 Public API。這時候有兩種策略來對付不乖的參數: 1. 修正或略過資料的錯誤(defensive, compensate) 2.丟出錯誤(offensive, fail fast)

讓我們來看看例子吧。以 Check Null 型的問題來說,有三種寫法:

# defensive programming
# 如果參數不符合條件,就略過或修正它
def resize_image(image)
    if image && image.is_a?(File) # 檢查 image 不是 nil,而且是 File
        # execute code
    else
        # ingore, nothing happened.
    end
end

# non-defensive programming
# 不檢查,錯了就讓他 crash 吧
def resize_image(image)
    # execute code
end

# offensive programming (fail fast)
# 檢查,有錯就馬上失敗讓你知道
def resize_image(image)     
  raise "The image is invalid" unless ( image && image.is_a?(File) )                
  # execute code        
end

Daniel Roop 在 Why Defensive Programming is Rubbish 認為 defensive 策略是一種 Hide the Problem Programming,我覺得講的很好。為什麼呼叫方會傳不對的值進來呢? 這是應該要去 trace 的問題,而不是把問題隱藏起來。長久以往下來,整個系統的不確定性就會越來越多,也就越來越難維護。

比較建議的作法是,如果是系統內可以相信呼叫方,那就不要檢查了。如果是邊界情況需要檢查,就做 offensive programming,也就是 “Fail Fast”,把條件限制當做一種契約,如果呼叫方(caller)不照規矩來,整個方法就會 fail 掉,這樣的好處是可以提早發現問題,並且在問題發生的第一時間就可以修好,讓整個系統朝向可以互相信任的方向成長。

再舉一個更惡名昭彰的 defensive programming 的例子:

# defensive programming
def run
    # execute code      
    rescue  # 救回所有例外
end

此例中,無論發生什麼錯誤,這個方法都會回傳 nil,非常安全不會 crash。但是即使 execute code 的地方打錯字也不會怎樣,徹底把問題隱藏起來了。

可是….

可是我們設計API的時候,特別是像 Ruby 這種動態語言,常常允許參數很有彈性耶? 例如

 # 參數 i 除了可以是數字之外,也可以是字串,例如 "123"
def process_integer(i)
    var = i.to_i # 轉數字
    # process var
end

# 參數 s 除了可以是字串之外,也可以是 Symbol 或數字等
def process_string(s)
    var = s.to_s # 轉字串
    # process var
end

# 參數 a 除了可以是陣列之外,也可以只傳一個元素或nil
def process_array(a)
    arr = Array(a) # 讓 arr 一定是陣列
    arr.each do |x|
        # process x
    end
end

好吧,這種算是”有意 (intentional)”的 API 設計,我們擴大了參數的彈性,而且呼叫方(caller)明確知道可以這樣呼叫(最好搭配API文件)。在 Confident Ruby 這本書裡,提到了很多 Ruby 適用的技巧。

Contract by Design

另一種解決檢查參數前置性條件的根本方法,叫做”契約式設計 Contract by Design”,也就是在語言層面去宣告前置條件。不過,大部分的程式語言都沒有內建,Ruby 是有一些第三方套件(利用Ruby的meta-programming特性來做),不過用的人就不多了。

結論

  1. 在系統邊界內,盡量相信輸入,不做檢查
  2. 如果需要檢查,採用有錯誤就中斷掉的方式(Fail-fast),避免 defensive programming 把問題藏起來

後話

另一個會讓整個系統充滿防禦性風格的情況,可能發生在團隊感情不好的環境。為什麼呢? 因為溝通不良,加上沒有適當的規範,所以寫code的時候只好處處防著別人,為了怕 crash 要麻煩別人(caller),只好額外費工加一大堆多餘的條件檢查和處理,把問題都埋起來好讓方法不會crash…

參考資料

註: 有些人認為 “fail fast” 也算是 defensive programming 的一招。我這裡採用對比的 offensive programming 說法。

Kent Beck 的四個簡單程式設計原則

什麼是好設計,打從開始 coding 以來就是一個不斷追求的大哉問,從
SOLID 看到 Design Patterns,每一個原則看起來好有道理,每一個設計模式都在等著你的套用。

但是最近幾年的工作讓我有不一樣的心得,而不再費心追求「完美」的設計。同一個問題,每個人都會想出不一樣的解法,都說自己的解比較好。於是這些設計原則,常就淪為爭論的工具,當我覺得你的解法做太多事情時,搬出YAGNI; 當我覺得你的解法太複雜時,搬出KISS; 當覺得一個類別太肥大的時候,就搬出 SRP,反正都可以找到支持的論點就對了。很多時候其實不管哪一種方案,都是可以接受的可行方案,於是後來我就不想去爭個你死我活、斤斤計較當下的細節設計了。好設計應該是演進(grow)出來的,我首先在乎的反而是有沒有留下可用的測試和清楚好讀的程式,這樣才可以留下(後人)改進的機會。

回到正題,Kent Beck 在 eXtreme Programming 中,對於什麼是 Simple Design,留下了四個 Xp Simplicity Rules,我個人蠻喜歡的,因為很簡單又基本:

  1. Pass All Tests 通過全部測試:有測試才能夠讓軟體不斷的演進(grow)下去,不然你不是被 regression bugs 淹死,就是只能不計成本把code砍掉重練。如何寫好測試也是個大哉問,xUnit Test Patterns: Refactoring Test Code一書是其中的bible,嫌厚的話,可以先看看Refactoring Test Code這一篇paper,都是在講重構你的測試程式。當然,事情總是過猶不及,老闆付錢給你是為了得到 code,不是 tests。測試只是幫助我們寫好code的工具,追求100%完整的好測試並不是目的。

  2. Reveals Intent (Self-Documenting Code) 程式能夠表達出意圖:其中最重要而且基本的,就是要有好的程式命名,無論是類別、方法、變數等等。專講 Readable 的書有一本可以推薦 The Art of Readable Code。我這裡還推薦一個我最喜歡的技巧 Composed Method (搞笑談軟工也有一篇介紹),這招也是 Kent Beck 大師提出的,將細節的實作抽取成一個小方法,讓整個方法裡的操作都是類似的粒度,小小一招,大有作用。

  3. No Duplication (DRY) 不重複:”Every piece of knowledge must have a single, unambiguous, authoritative representation within a system” 完整定義請複誦三次。有時候重複並不是這麼明顯,例如作用重複的程式,但命名不同,或是命名重複,但程式作用不一樣。這裡推薦 David Chelimsky 的影片 Maintaining Balance While Reducing Duplication (slides)和 Maintaining Balance while Reducing Duplication: Part II,可以增進對 DRY 的理解,DRY 不只是在講不要重複程式碼而已。

  4. Has no superfluous parts (Minimizes the number of classes and methods) 不多餘:這一條也就是 YAGNI,不要實作多餘的功能。我個人不怎麼喜愛這個原則。不是不贊同,不需要的功能花時間去寫當然是浪費大家時間,而是這個原則太容易例外了。有時候會寫下多餘的程式來增加功能或擴充性,那是因為你擁有的經驗、直覺跟當時的情境告訴你應該這麼做,這可以在不久的將來就派上用場,過於強調 YAGNI 反而一板一眼教條主義了。光單看”減少類別跟方法的數量”跟 SRP 和 Composed Method 就有所衝突了,後兩者都會增加類別和方法的數量。

重要性 1 > 2 > 3 > 4,也有版本講 1 > 3 > 2 > 4。

參考資料

機會難得之 Matz 在台見面會

因為昨天才敲定這個行程,所以宣傳慢了。目前還有名額,不限有報名 conference 朋友,機會難得。報名請前往 Registrano

來台參加 RubyConf Taiwan 2012 的 Matz (Ruby 程式語言發明人) 和 Koichi Sasada (YARV作者) 將和大家進行粉絲見面會。本活動自由報名,不限參與 RubyConf Taiwan 的聽眾,免費。

時間: 2012/12/6 18:30 ~ 21:30 (Matz 約 19:00 到場)

議程: 現場 QA,自由聊天

地點: 伯朗咖啡天母店 – 台北市天母東路68號5F (新光三越A棟5F) Google Map

◎公車: 啟智學校:285、646、645、606、616、685 ◎捷運:芝山站出口,轉搭新光三越接駁車

報名請前往 Registrano 系統