2013年3月30日 星期六

UP與Agile process的比較

以個人經驗提出兩者的差異
  • agile使用user story捕捉需求,UP使用use case(文件,不是diagram)
  • agile工程師往往一人身兼多職,從design architecture、designer、programmer
  • UP並沒有強調是否一人身兼多職,但是往往將腳色獨立出來
  • agile很強調Test,不是只有Unit Test還包含ATDD、BTDD
  • agile認為文件必須要很精簡,UP則是比較鉅細靡遺
  • agile很重視客戶合作,強調客戶需要配合軟體開發
  • agile將"關係"維護在人之間,UP則是將關係落實到文件內
現在process其實有些相同的方向是
  • Iterative方式
  • 重視User Feedback
  • 重視需求文件
  • Risk driven
  • 重視Architecture
  • 重視business goal

agile實施起來未必比UP容易,agile裡的programmer往往一人身兼多種腳色,因此agile的programmer往往能力需要比較強,這也是為何早期XP強調pair programming的方式,一方面可以有feedback,另外一方面可以帶起新手

如果今天團隊內有人離職,UP因為將所有內容都記錄在文件上,理論上只要詳讀文件就可以理解,但是agile則是將一些隱含的關係放在人之間,一個programmer離職,恐怕新手只有user story以及code/test code可以讀取,有沒有design...等等的文件,則是看當時狀況

agile將某些資訊放到人之間,有個好處,由於資訊是共享的所以可以減少誤解,同時人在資訊上的更新,某種程度是比文件來的有彈性以及快速

agile最核心的難度恐怕在於改變人,因為過去客戶往往不瞭解他們的需求,同時也不了解軟體開發的一些資訊,客戶認為軟體專案如同販賣機一樣,只要錢投下去,軟體就會跑出來,agile正嘗試改變這一點

最後補上一個軟體工程的V-mode,由於agile很重視test,同時如果理解的話,應該就知道CMMI的 V&V在要求啥了。也就是只要了解軟體工程,自然可以滿足CMMI的Level 2, 3(除了4個跟organization相關的GG之外)

Specification by Example 書摘 (1)

這是一本不錯的書籍,雖然作者整理了一套需求捕捉的方法,但是如果沒有agile process的背景,可能會有點難以理解

Chapter 1.
一個好的需求文件,應該具備底下的優點
  • 眾人一致同意的功能
  • 精確地描述
  • 已完成此功能為目的
  • 文件容易改變跟維護
為避免傳統工作上的繁雜,在寫需求文件的時候應該注意下列幾點
  • 避免太過詳細且浪費的需求描述
  • 文件必須易懂
  • 文件必須可以驗證
  • 文件要具備一致性以及低成本的特性
  • 文件必須符合process的iterations

作者推崇一種稱為Living Documentation System的工具,也就是維護需求捕捉文件的系統概念,不管是word files或者web page...任何其他方式,這是一個要具備以上特色的系統

Chapter 2.
整本書的的需求捕捉架構大體上就被他的圖一言以蔽之,後面都是章節是一些實用技巧



Chapter 3.
作者注重Living Documentation System,他建議必須可以執行ATDD以及BTDD,因為一致性在文件系統很重要,作者使用Testing來維護系統的一致性,做這建議從一開始,需求就必須可以執行(executable)
書中也提到一個普遍性的誤會就是agile process不需要文件!其實這是根本上的錯誤。其實另外一個更廣泛的錯誤就是,開發人員認為環境一直改變,不需要維護需求,這才是更嚴重的問題,所以這樣的開發人員會把agile process當藉口

2013年3月28日 星期四

新的語言特色!!??

java從java 5(or 6)?開始引入一連串的特色,讓我覺得這個語言愈來愈難掌握,愈來愈像是C++翻版:P
從annotation開始,annotation是類似comment,卻可以指使compiler去做一些處理,如果使用過hibernate的人就知道,可以連hibernate原本用來對應DB與java物件之間的xml檔案,全部寫入annotation上
跟著為了溶入generic programming,也就是在C++的template以及STL like的函數,引入了<?>這個符號,generic programming本人可是對他又愛又恨阿,他是非常的powerfull,但是又非常的難以maintain以及extend(對了,有時候還非常難以debug,visual c++ 6.0,光compiler吐出來的message,就根本不能看,更遑論runtime了),只敢使用STL或者Boost之類,其他人寫出的template lib,我可是十分警戒

後來因為其他需要,我開始使用python之類的語言,好處是十分快速的可以開發出一些prototype,來驗證自己的演算法,即使python的速度大概只能達到C++的1/10,不過如果我用C++開發大概要一個月,python我只要一個星期就搞定了XD 在驗證想法上,python有很大的優勢,只要把速度的問題交給機器去克服:P
在使用python之後,突然我想到了一個問題,為何PHP/Python這類不純粹是OO的語言,可以發展得如此之好,甚至於好過原本Java,不是一直在說OO可以幫忙處理、分析複雜的問題嗎??為何感覺PHP/Python愈發興盛?這意味著有某些是我們沒考慮到的!?這個問題似乎還沒有個終極的答案,我只有把它留在心裡

最近看了一下,java 8的新特色,OMG,開始支援一些PHP/Python的特色,比方說lambda的特色,讓java感覺又為了相容一些functional programming的特色以及generic的特色,"走回頭路",整個語法亂到了一個極致。在此同時,已也已經披露了OO的一些極限,比方說,在以前(java 6),你想弄個類似function pointer(C/C++),必須定義一個介面,走template method或者其他pattern的模式,光為了一個function pointer,必須多出一個interface再多出一個implementation,當然在OO上面有好處,但是在code上面可是多了不少阿

另外上面這些java的新特色,有個幾乎千篇一律的特色就是.....全部是在compiler的時候做手腳,也就是在.java轉換成.class的時候自動產生一些額外的code

回到核心的問題,是否一個語言在與時俱進的時候,必須要弄出一些"不倫不類"的特色,弄了一些code sugar,但是降低了原本的簡潔性?我倒是覺得java乾脆弄出個新的關鍵字,不要再把一些舊有的模式跟新的模式作一種奇怪的搭配,然後用compiler去處理這件事情。我只能說,我對這些新特色十分感冒orz

2013年3月21日 星期四

軟體設計與開發之野人獻曝(2)

我一直在想我有沒有資格寫一篇如何熟悉軟體開發過程的地圖,一直誠惶誠恐,覺得自已不夠資格,不過我換個角度來想,我把我自己的過程寫下來,或許對一些新踏入的人,可以有一些參考作用,就比較釋懷了

誠如會寫軟體未必需要懂得軟體工程,但是軟體工程可以幫助提綱挈領,統籌資料

個人認為的次序是 "軟體工程"=>"軟體流程",需求捕捉以及分析的工具,還需要"設計工具"(UML/Design pattern/Architecture Design),最後是"程式工具"(Refectoring),但是這裡缺乏了專案管理的工具,畢竟市面上太多,懂得流程後,尋找工具或者手動應該會輕鬆得多

軟體工程(統合概念的書籍)
軟體開發流程的書籍 (UP, RUP, agile, scrum)






設計工具 (UML, design pattern, architecture design)




需求捕捉與管理


程式(Code)工具


其他(CMMI)

個人經驗,我是從RUP/UP入手,其實學習過程中,剛好另外一套稱為XP (eXtrem Programming)的學說興起,我個人在心理上比較認同XP(或許是自己是個programmer吧:-> )。使用RUP/UP的過程中,真的開始覺得他非常heavy,每件事情都快要鉅細靡遺,但是我也了解到了一個軟體開發過程中應該要有的大多數的成果(artifacts)

跟著也是一般人都會有的疑問,就是需求捕捉,之前寫得"從user case裡面直接分析出名詞跟動詞接著進入到設計階段"<=這絕對是簡化到誇張的講法,因為一般需要從需求裡面分析出domain model跟著才進化到desgin model

需求分析設計,一個講求what to do,一個講求how to do,但是在what to do之前呢?如何讓人們懂得溝通?一個是住在火星的programmer,一個是住在水星的客戶(business man),一個只認得code,一個只會寫的一嘴好程式(最常見的需求,我要做的跟XXX網站一樣,XXX請自行帶入yahoo, amazon, 淘寶...),在上面幾本書有很好的解析,writing effective use cases比較偏重UP/RUP的模式,Sepcification by Example, User Stories Applied則是Agile

至於設計工具GoF的design pattern就不用說了,真的是很多OOD的工具,另外一本Pattern Oriented Software Architecture(簡稱POSA)則是講究Architecture,如果懂得Architecture以及Pattern中間模糊的差異(其實有時候蠻難切開的,比方說broker有時是architecture有時又是pattern),另外一本Patterns of Enterprise Application Architecture則包含了許多web based的pattern跟架構

其他常用的code工具refatoring則是如何改善code工具,除了改善,我覺得後面另外一個很重要的精神是,不要隨便拋棄code(約爾趣談軟體有提到類似的精神),但是refactoring有個天生的缺點,很繁瑣,如果沒有自動化工具,我建議把一些類似rename等等太低接的動作捨棄,專注界面的改善比較好

最後是CMMI,CMMI這東西抽象化程度不輸給軟體工程,完全只有給出what to do(工程/流程面相),沒有how to do(實際執行),不妨將軟體流程的成果對應到CMMI上面去,因為軟體流程幾乎可以cover CMMI的Level 2跟Level 3,但是很不幸Level 3有四個SG (special goal)是屬於組織流程的,軟體工程無能為力,說實話Level 4跟Level 5我也很難領悟

2013年3月16日 星期六

軟體設計與開發之野人獻曝(1)

發現很多人對於軟體工程嗤之以鼻,或者覺得這是一個象牙塔發展出來的東西,現實專案很少依照這樣的學理發展,我個人倒是認為,軟體工程是提供一個對照,軟體也好、硬體也罷,一定有個生產流程,但是過程中應該產生出來的東西是否被"省略"了

比方說,規格書被省略是最常看到的,軟體好像就從空氣中跑出來了,事實上不是這樣,需求或許未被寫出來,可是他一定留在某些人的腦袋當中

另外一個就是,軟體工程有時太過高度抽象畫,很難落實,或許流程(process)會比較貼近實際,但是流程往往又需要很高的門檻,比方說要熟悉UML(UML只是描述語言,並不是完全依賴於某種流程,反過來倒是很多流程依賴UML)

流程(process)會開始解釋,如何從需求捕捉到系統分析,也就是從what to do到how to do,從抽象的描述到軟體設計開始,如果沒有實際經歷,往往也是落到跟軟體工程一樣的結論,這是一個高階的開發流程,並沒有辦法使用

流程(process)為何比軟體工程更貼近實際呢?因為流程往往綁定特定模式,比方OO,但是軟體工程則是更抽象,喜歡用lisp這種非OO開發也可以,要用OO流程開發也可以,換句話說,軟體工程工過程中省略了某些部分,使得讀者很難連貫跟理解

以RUP為例子,流程(process)會解釋如何從use case的描述之中抽出名詞跟動詞,如何組合名詞跟動詞成為class,在設計的時候會進一步將class抽象成其他class,常見的手法就是套用design pattern。但是如果是The object primer書的agile模式,則是從user story轉換為use case diagram然後慢慢地轉換為class,步驟、方法上跟精神上是有所不同的

因為時間的關係,先簡單寫到這裡,可以看到,要懂得一種process至少知道軟體開發過程,要懂得UML,要懂得design pattern,還要知道某些開發者的角色扮該執行怎樣的工作,所以不是一個簡單的過程,流程(process)也是一種方法論,懂得之後未必需要完全依照執行,但是可以加深取捨的決策。又如我最一開始說的,即使沒有任何需求、設計文獻就落實到了code,但是這些東西是保留在程式設計師或者客戶腦袋中,不落實將會一些效應的存在(比方引進新人員,要花很多工夫在訓練新人融入專案)

軟體工程、軟體開發流程是否需要懂??我說: Everything is about the trade-off.

2013年2月20日 星期三

unix network programming: chapter 15 nonblocking IO

這一章特別有趣,因為之前node.js對於nonblocking IO有點消化不良,慢慢地體會到,nonblocking IO的思維之後,覺得這樣的設計對於performance真很有幫助,不過控制也是比較困難,這篇文章不會提到書中如何實作,想了解的網友可以直接翻閱書籍

最讓人直接聯想到的nonblocking IO思維是connect()這個function,如果說只有一個連線,個人認為block反而容易理解,nonblocking沒太大用途。但是想像一個應用,比方設計一個由地理位置分析周遭環境的application,你需要透過超過五十頁的網頁內容來整合,這時候connect()會block就變成了一個效能的瓶頸,因為每個網頁都要等前一個網頁的3-way handshake完成才能繼續工作,如果改用nonblocking,幾乎可以在短時間內就發出五十個http請求,這樣就快得多了

但是nonblocking其實也有些副作用
  • nonblocking非常耗用CPU效能
  • nonblocking的程式碼相對難以掌握,可以參考書中,由blocking/select IO(40 lines)膨脹成nonblocking IO的135 lines,可見一斑,
  • 書中沒有提到的流程控制相對困難,可參考node.js的flow control部分,就知道連流程控制也是一大挑戰
另外一個作者書中提到的效能比較也是一個很好的指標
  • block and wait version : 354(s)
  • select and blocking : 12.3 (s)
  • nonblocking IO: 6.9 (s)
  • fork(): 8.7 (s)
  • thread : 8.5 (s)
作者比較推薦簡易的版本fork()或者thread,因為nonblocking在程式碼控制方面比較困難

回過頭來說,其實上面選用除了第一個之外,各有各的不同考量,我給出的建議如下
nonblocking IO: performance
fork(): different task or paralle task
thread: similar task or share data

如果要同時發出大量的連線需求,使用nonblocking是比較好的選擇,如果資源充裕(memory很多/cpu也很強)使用fork()也比較好,因為thread遇到block IO整個process就被block住,其他thread也失去了活動機會,根本沒有幫助

如果說需要share data,基本上挑選thread會比較好,因為thread在share data表現上比IPC容易一點,雖然也有同步資料的問題,但是同步資料在fork()也是無法迴避的。相同的動作,如http server讀取檔案,使用thread就是不錯的選擇

fork()比thread的缺點就是要配置大量的記憶體空間,還有share data要透過IPC,好處是不會被block住。
此外process在kernel上分配CPU time的機會也比thread好,比方說目前server有一個process,programmer寫一個抓取遠端資料分析的程式,為了增進效能,將其一分為二,一個使用fork(),一個使用thread,那麼一個是三個process,一個是兩個process,如果均勻分配,使用fork()方式的將會佔據67%左右的CPU,另外一個使用thread只有33%。當然這是一種武斷的算法,要看使用的CPU/IO乃至於網路的情況而定

所以到底是該用nonblocking IO或者blocking IO?該用thread or fork()?其實都有不同的考量,由於richard stevens已經去世了,新版(3ed)的不知道有沒有討論到asynchronous IO??這或許再看看,在2ed richard stevens只是輕輕帶過這種IO的存在,事實上,網路上已經有開始有使用asynchrounous IO的文章了,應該是值得期待的

2013年2月18日 星期一

unix network programming書摘 -- Part 1

只有記錄我認為容易忽略的地方,文章內不會有TCP/IP的詳細過程,可以參考richard stevens的另外一本大作TCP/IP Illustrate,另外手邊有的unix network programming一書是2ed

Chapter 1
伺服器端
 1: #include "unp.h"
 2: #include <time.h>
 3: 
 4: int
 5: main(int argc, char **argv)
 6: {
 7:  int     listenfd, connfd;
 8:  struct sockaddr_in servaddr;
 9:  char    buff[MAXLINE];
10:  time_t    ticks;
11: 
12:  listenfd = Socket(AF_INET, SOCK_STREAM, 0);
13: 
14:  bzero(&servaddr, sizeof(servaddr));
15:  servaddr.sin_family      = AF_INET;
16:  servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
17:  servaddr.sin_port        = htons(13); /* daytime server */
18: 
19:  Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
20: 
21:  Listen(listenfd, LISTENQ);
22: 
23:  for ( ; ; ) {
24:   connfd = Accept(listenfd, (SA *) NULL, NULL);
25: 
26:         ticks = time(NULL);
27:         snprintf(buff, sizeof(buff), "%.24s\r\n", ctime(&ticks));
28:         Write(connfd, buff, strlen(buff));
29: 
30:   Close(connfd);
31:  }
32: }

主要流程填寫sockaddr_in結構,socket()=>bind()=>listen()=>accept()=>read()/write()=>close()

客戶端

 1: #include "unp.h"
 2: 
 3: int
 4: main(int argc, char **argv)
 5: {
 6:  int     sockfd, n;
 7:  char    recvline[MAXLINE + 1];
 8:  struct sockaddr_in servaddr;
 9: 
10:  if (argc != 2)
11:   err_quit("usage: a.out <IPaddress>");
12: 
13:  if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
14:   err_sys("socket error");
15: 
16:  bzero(&servaddr, sizeof(servaddr));
17:  servaddr.sin_family = AF_INET;
18:  servaddr.sin_port   = htons(13); /* daytime server */
19:  if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)
20:   err_quit("inet_pton error for %s", argv[1]);
21: 
22:  if (connect(sockfd, (SA *) &servaddr, sizeof(servaddr)) < 0)
23:   err_sys("connect error");
24: 
25:  while ( (n = read(sockfd, recvline, MAXLINE)) > 0) {
26:   recvline[n] = 0; /* null terminate */
27:   if (fputs(recvline, stdout) == EOF)
28:    err_sys("fputs error");
29:  }
30:  if (n < 0)
31:   err_sys("read error");
32: 
33:  exit(0);
34: }
流程為填寫sockaddr_in結構,socket()=>connect()=>read()/write()=>close()

Chapter 2
重要的為TCP以及UDP的特色,以及richard stevens書中那張state diagram,中間牽涉到如何透3-way handshake建立連線,以及何時會斷線,為何TIME_WAIT狀態要等2MSL


  • 個人認為重要的觀念是TCP/UDP為全雙工,也就是系統如何處理這樣的概念,從這裏面衍生出了不少問題
  • TCP使用socket pair的概念來辨識連線,也就是Server IP+Server Port+Client IP+Client Port,因為UDP沒有連線概念的約束(也可以自行實作,但是TCP天生幫programmer處理這些)
  • 同時開始考慮,一台機器可能不只一個網路介面,或者說不只一張網路卡,一個service(如FTP)可以同時在所有卡啟動,只要指定socket結構內為INADDR_ANY即可,如果要綁定數張,就只能一個一個來
  • 另外書中提到kernel緩衝區的可以用SO_SNDBUF/SO_RCVBUF(setsockopt())來調整,TCP因為有window size的關係,照理不應該有buffer用盡的問題,但是UDP卻會,又加上UDP是unreliable,buffer一旦滿了就只會drop,連通知都不會有

Chapter 3
  • 因為unix/linux設計socket不只可以用在ethernet還可以用在domain socket上面(一種IPC),所以網路上用的是sockaddr_in結構但是在connect()/bind()/accpet()...參數用的卻是sockaddr結構(一種所謂generic socket結構),所以在呼叫這些api往往形態要轉型
  • bind()、connect()、sendto()是將資料由user space傳送到kernel space
  • accept()、recvfrom、getsockname()、getpeername()則是由kernel space回傳資料到user space
  • byte order的問題,網路上使用的是big-edian,但是intel cpu是little-edian,所以有這問題,通常有四個函數來處理htons()、htonl()、ntohs()、ntohl(),h表示host、n表示network、s表示16bits、l表示32bits
  • 新的由IP字串取得address的function為intet_pton()以及反向函數inet_ntop()其中p表示presentation
  • 作者時做了好一些輔助函數,很有參考價值

Chapter 4
重點之一,流程圖,書中逐一解說各個function用途


  • socketaddr_in要轉型的原因如前一章,另外要注意,因為可以合乎各種family,所以在connect()/bind()/accept()除了轉型之外,還需要傳入結構大小的指標
  • 這裡衍生出一個重要的觀念,TCP server一定要bind()嗎?答案是不一定,可以由系統指派,之後在用getsockname()取回sockaddr得知,而client的sockaddr資訊則可以用getsockpeername()取回
  • fork()解決一小部分 socket為了全雙工所面臨的問題,因為程式不能因為系統呼叫而block住


chapter 5
  • 書中提到了系統的問題,也就是signal會干擾正常流程,以及如何避免child process變成zombie的問題,使用waitpid()來解決,wait()因為kernel不queue signal可能還是會產生zombie
  • SIGPIPE有可能是因為網路上遇到RST訊號,這屬於網路問題,但是unix反應到process上變成signal,必須妥善處理才能避免連線的問題

chapter 6
  • 解說了blocking/nonblocking IO跟IO multiplexing、還有signal IO、asynchronous IO
  • select()函數示範了IO multiplexing,這個函數可以簡單的處理網路的IO以及一般的輸入,但是中間呼叫其他system call還是有可能會block整個process,所以要小心
  • select() -- read ready
    • 正常狀況,資料高於低水位,可以透過setsockopt()的SO_RCVLOWAT調整低水位
    • 對方已經關閉連線,會回傳EOF
    • 在完成connected的queue中有可用的socket
    • 有sock error存在,read()回傳-1,利用getsockopt()取得SO_ERROR資料
  • select() -- write ready
    • TCP在socket已經連線的時候可以寫入,或者UDP/TCP寫入資料多於低水位的時候,使用setsockopt()的SO_SNDLOWAT調整
    • 連線端已經關閉寫入,會產生SIGPIPE
    • 網路有sock error存在,write()回傳是-1,利用getsockopt()取得SO_ERROR資料
  • shutdown()函數以及setsockopt()的SO_LINGER可以調整關閉socket時候的動作,必須考慮到網路的封包。
  • close()只是關閉write/read fd並沒有關閉網路連線(或者TCP沒有送出FIN)
Chapter 7
介紹常見的socket options,分成幾個層次,一般socket options、IPv4 options、IPv6 options、ICMP options、TCP options。一般socket options指的是大多與protocol獨立無關,由kernel透過與protocol無關的code設定。
我摘要了幾個前面常常碰見的options,有些options大多集中在書本前面,且偏向一般socket options

  • SO_BROADCAST : 廣播
  • SO_ERROR : 網路出錯
  • SO_KEEPALIVE : 兩個小時透過封包確定TCP連線還存活著
  • SO_LINGER : FIN訊號的處理
  • SO_RCVBUF/SO_SNDBUF : 設定TCP/UDP的buffer,TCP不用特意調整,他有window size機制
  • SO_RCVLOWAT/SO_SNDLOWAT : 資料的低水位控制,有關效率以及select()
  • SO_REUSEPORT : 
  • SO_RESUEADDR : 只要IP不同,特別不用等待2MSL就可以重新bind()
  • TCP_KEEPALIVE : 要開啟這個選項就要同時開啟SO_KEEPALIVE
  • TCP_NODELAY : nagle演算法實作


Chapter 8
UDP介紹,如同TCP一樣,有張流程圖,不過不是一定的

  • 不是一定的理由是,用戶端可以呼叫bind()跟connect(),當然兩者也可以都忽略
  • 書中探討到了UDP失去了TCP特色的問題,比方UDP無法得知連線IP/Name,UDP沒有流量控制,UDP沒有重傳機制
  • UDP因為不做3-way handshake,所以無法確定client/server是透過哪個IP或者介面卡傳輸,兩邊都可以從自己主機上的任一IP或者介面卡回應另一方
  • UDP可以透過IP比對,或者DNS比對(多個IP對應到單一domain name),來驗證資料來源
  • UDP可以透過前一點機制與Timeout設定來確定封包有沒有送達
  • UDP可以透過SO_RCVBUF改善流量問題
  • UDP的好處之一,可以透過一個client同時對多個server提出要求,但是會引發一個問題是ICMP的訊息將是非同步產生
  • 其他可以參考我之前寫的socket FAQ