敏捷開發與 Scrum 分享投影片

Update(2012/7/16): 由於投影片部分內容引用自 ezScrum 課程教材,不適宜公開,非常抱歉。他們的課程非常棒,推薦大家報名 Scrum敏捷方法實作班

這是今天在公司內部講的 “敏捷開發與 Scrum” 投影片(大概花了一個小時講,加上另一個小時實作練習),不是什麼經驗分享演講,畢竟還沒有 Scrum 的實際導入經驗 XD 所以只是東抄西抄整理重點,見笑了。

認識敏捷開發的精神不是第一次,但 Scrum 流程倒是頭一回。上回參加 Teddy 的 Scrum 課程,其中感觸最深的就是 cross-functional team 概念,全部 team members 都要參與 Story 討論與估計,無論其技術或設計背景。而上週末惡補 Scrum 書體會最深的則是 self-organized team 的 empowerment 和 accountable (賦權與當責)概念,可惜我還講的不好就是了。總之,學了 Scrum 流程,再回頭看敏捷宣言又有不同的體悟。

期待之後有更多實務經驗可以跟大家分享囉。

JavaScript,我真是搞不懂你啊!@JSDC.TW 2012

這是我在 JSDC.TW 给的 Lightning talk: “JavaScript,我真是搞不懂你啊!” 的現場錄影。因為投影片其實只是內嵌影片,所以看影片就好了。

講這個題目的目的完全只是娛樂效果(非戰)。因為成為 LT 講者可以免報名進場,所以就厚著臉皮來表演 Wat 看看,聽起來是有得到一些笑聲 XD 如果想進一步知道 JavaScript 為什麼會這樣處理,可以參考 解析 JavaScript,我真是搞不懂你啊! @JSDC 2012 這一篇文章。

Functional Programming for Java Developers 讀書摘要

這是我之前念 Functional Programming for Java Developers 一書的摘要記錄。這本書很薄只有90頁,是一本蠻不錯的 Functional Programming 概念入門勸敗書。

近來 Functional Programming (函數式編程,以下簡稱FP) 的重要性提昇就是為了因應 Concurrency 的需求。CPU 朝向多核架構發展,OOP的程式開發方式物件充滿可變狀態,造成撰寫 Concurrency 程式時陷阱多多。

當然,要寫FP不代表一定要用FP語言,還是可以用現成的OO語言去寫。但就像用沒有OO支援的C去寫OO風格,用專門的FP語言還是比較方便。

為何要FP?

作者說雖然是因為 Concurrency 而學 FP,但是後來卻很享受這種 Paradigm Shift (典範轉移)。OO 被發明因為 GUI,後來人們發現可以用來應用在各種領域上。OO 和 FP 都是工具,各有優缺點,但是現在人們碰到所有問題都用 OO 去解,就像你手上有著搥子,在你眼中什麼都看起來像釘子

FP 不代表比 OO 優越,畢竟 OO 的好處已經被證實且廣泛應用。而是目前時代不同了,OO 的缺點在某些領域已經到了不可忽視的地步,有些挑戰性的問題用 FP 解更為適合,例如:

閱讀全文〈Functional Programming for Java Developers 讀書摘要〉

那些 Functional Programming 教我的事 投影片

這是今天在 OSDC.TW 2012 演講的投影片:

這個題目是近年來第一次脫離 Ruby 舒適圈的演講(抖),準備的時候就發現 Functional Programming 不但講不完(30分鐘的演講教不會寫FP啦),有些概念又很難,真要講到 “Pure” 的 Functional Programming 我連 Monad 都還搞不清楚…orz 所以本來準備的內容在前一天又狠狠砍掉 1/3,開頭改成用 Concurrency Program 來引人入勝,希望這樣的目的有達成 :)

HTTP Verbs: 談 POST, PUT 和 PATCH 的應用

關於 HTTP 基本知識,可以參考我的 網路概論3: HTTP

在初學REST的這幾年,我都認為這幾個 HTTP Verbs 就是對應 CRUD:

  • POST = 新增
  • GET = 讀取
  • PUT = 更新
  • DELETE = 刪除

後來在設計 API only 的 Web service 時,常常搞不清楚到底要用 PUT 還是 POST,才發現我被 Rails 的鷹架範例誤導了(被框架框住想法了?),所謂的 PUT 其實也可以用到新增,而且還有一個蠻新的 HTTP Verb 叫做 PATCH,像 Github APIRails 4 都開始採用。

PUT 比較正確的定義是 Replace (Create or Update),例如PUT /items/1的意思是替換/items/1,如果已經存在就替換,沒有就新增。PUT 必須包含items/1的所有屬性資料。

但是這個行為似乎不怎麼好用,如果只是為了更新items/1的其中一個屬性,就需要重傳所有items/1的屬性也太浪費頻寬了,所以後來又有新的 PATCH Method 標準,可以用來做部分更新(Partial Update)。

用幾個 Ruby code 來舉例吧:

POST 新增:

# POST /items
def create
  @item = Item.new
  @item.attributes = { :name => params[:name],
                      :image => params[:image] }
  @item.save
end

PUT 替換(新增或完整更新),此例中如果image參數沒有傳,會被更新成空:

# PUT /items/{id}
def replace
   @item = Item.find_by_id(params[:id])

  unless @item  # if @item.nil?
      @item = Item.new
      @item.id = params[:id]
  end

  @item.attributes = { :name => params[:name],
                       :image => params[:image] }
  @item.save
end

PATCH 部分更新,此例中如果image參數沒有傳,就不會被更新:

# PATCH /items/{id}
def patch
   @item = Item.find(params[:id])
  @item.attributes = params.slice(:name, :image)
  @item.save
end

DELETE 刪除,此例中無論如何 items/1 最後都不存在了

# DELETE /items/{id}
def destroy
   @item = Item.find_by_id(params[:id])
  @item.destroy if @item
end

有時候拘泥於”語意”這件事情不容易想清楚設計 REST API 時要用哪一個 HTTP 方法,因為有時候不一定是CRUD的形式。我認為 9.1 Safe and Idempotent Methods 定義中的 “Idempotent” 特性蠻實用的。idempotent 的意思是如果相同的操作再執行第二遍第三遍,結果還是跟第一遍的結果一樣 (也就是說不管執行幾次,結果都跟只有執行一次一樣)。根據 HTTP 的規格,GET, HEAD, PUT 和 DELETE 是 idempotent,相同的 Request 再執行一次,結果還是一樣。只有 POST 和 PATCH 不是 idempotent,POST 再執行一遍,會再新增一筆資料,PATCH 則是有不能保證 idempotent 的可能性(徵求例子)。POST 和 PATCH 都不是 idempotent 的操作,這也是為什麼 Github API 裡用 POST 當做 PATCH 的相容取代方案。

另一個 HTTP Methods 特性是”Safe”,這比較簡單,只有 GET 和 HEAD 是 Safe 操作。Safe 特性會影響是否可以快取(POST/PUT/PATCH/DELETE 一定都不可以快取)。而 Idempotent 特性則是會影響可否 Retry (重試,反正結果一樣)。

Safe? Idempotent?
GET Y Y
POST N N
PATCH N N
PUT N Y
DELETE N Y

透過 Idempotent 的特性,有時候可以幫助你判斷該用哪一個 HTTP Methods。回到前面講 PUT 好像不太好用,例如以瀏覽器為主的 HTML 應用表單,要麻是 POST 新增資料,要麻就是用 PATCH 部分更新已經存在的資料(你不會希望用 PUT 修改個人資料的時候,每次都要重傳照片吧),因此比較少用到 PUT。這也是為什麼 Rails 4 把表單修改的 PUT 改成 PATCH Method,透過 Rails 鷹架產生出來的 update,其實符合的是 PATCH 行為而不是 PUT。

不過還是有一些我認為蠻適合用PUT的情境,例如訂閱東西該用POST還是PUT呢?

POST /subscriptions
# 還是
PUT /subscriptions/{something_id}

訂閱東西只有兩個狀態,”已訂閱”或”沒有訂閱”,這個訂閱的操作再重送幾次,還是”已訂閱”,所以我認為蠻符合 PUT 的 idempotent 特性。而對應的取消訂閱 API 想當然就是

DELETE /subscriptions/{something_id}

另外一個我覺得有趣又實用的 PUT 例子是,設計 API 给可以離線 offline 使用的行動裝置(例如iPhone)。支援 offline 產生的資料,通常會使用 UUID 來產生 ID,這樣就不需要透過中央伺服器管控 ID,方便裝置之間的同步。這樣的情境下,新增資料的 REST API 其實可以提供兩種:

POST /items # 參數帶 uuid=4937973d-e349-460a-a6ad-38625125b24a。如果不帶uuid則由server來產生uuid
# 和
PUT /items/4937973d-e349-460a-a6ad-38625125b24a

對行動裝置的 client 來說,用POST的問題在於離線環境的不穩定,有可能POST之後沒有收到回傳,因此行動裝置不確定有沒有同步成功,這時候要再重試(retry),但是用 POST 就爆炸了,因為 server 會再新建一筆 uuid 重複的資料。但是用 PUT 就沒有問題了,PUT 是 Idempotent 的操作,可以重送沒有關係 (可以再搭配 Conditional PUT 的機制,用 ETag 和 Last-Modified Headers 確保沒有覆蓋衝突)

如果是沒有 offline 需求的 client,例如第三方應用,那麼就可以用 POST /items 這個 API,交由 server 來產生 uuid。

其他參考資料