2008年10月25日 星期六

R25 程式設計中得到的快樂

最近認真想了一下程式設計到底有什麼快樂?聽取一部分同事的想法是說,從無到有創造出自己想做的產出、或者是花費心力到最後克服一個難做的功能。的確,第一層的快樂是產出結果,做出自己想做的東西並看著別人樂意去使用。(只是在專案裡由於時間的壓迫與客戶的緊逼,痛苦早就多於快樂)

後來與同事們一起在專案裡奮鬥的同時,看著他們不管在SA或SD上都投入很多時間做重覆性很高的工作,後來便開始設計小工具將來處理重覆性的工作。當小工具確切地符合同事的需要,看著他們用很少時間就完成原本要很久的工作,這是第二層的快樂。(以前開發的小工具也是基於這樣的想法所創作的──包括彩券對獎程式 ^^)

現在則是著眼在所有人都能夠設計出其他人一看就懂的系統或元件,同時藉由工具的輔佐讓開發的時間不會超過太多,卻可以有效減少以後維護與改變的影響。努力地把想法轉換為文章,就是希望所有人(至少是我所屬開發團隊裡的所有人)都有同樣的設計思維,把觀念應用在各種不同的領域上。這是第三層的快樂。

2005/10在上SUN OO-226課程之後,我曾經問講師一個問題:如何讓那些將設計重心放在快速開發的人接受OOAD的觀念?講師想了一會兒後回答那是沒有辦法的。2008/04大陸某大公司的資深工程師也問我類似的問題,我們簡短討論之後認為設計是有很多面向的,有人只看到快速的好處而我們也沒法說服他們去兼顧其他的角度。

從開始寫部落格至今,我為了讓全部的想法與作法能夠接續,在所有原先無法連接起來的斷點花了很多時間思索其原因結果並決定作法,同時藉著專案的機會加以初步驗證。或許沒有看過太多理論的書籍不會有對某些理論先入為主的包袱,才能靜靜地感受事物本質的流向。如今面對同樣的問題,我會淡淡地回答一句:讓你創造出來的全部事物在它該在的位置上做它應做的事,這是我心中回歸的設計本質;如此才不會讓那些事物因為趨向於某一目的而造成難以整理的混亂。

我想,這個簡單的道理應該適用在人生裡的任何一個角落吧。

2008年10月24日 星期五

R24 程式設計想法的差異

在某些電影的逃命場景裡,主角敏捷地利用周遭的地形地物脫離敵人的追殺,我們在感嘆主角的反應過人之際,是否也同樣地注意到他對可利用的一切事先所下的苦功?要能快速地使用手邊的資源,勢必要先瞭解該物件的特性與用法,才能依照心中的想法組合為可達成目標的使用順序。

坊間有很多學會XX語言程式設計之類的書籍,對於初次接觸程式語言的新手,那的確可以快速學會該語言的特性與用法。不過那樣的書主要是以學會並使用XX語言為主,裡面並不會說明如何去條理化達成功能的思緒;也就是說它提供的是準備被我們使用的資源,怎麼去用端看設計者的心裡怎麼想。

心中對於完成功能所作的系統佈置就像是Model,以某種程式語言根據心中想法做出的產出就像是View;在想法正確的前提下依功能流程動作在各種語言下所找到的最佳化寫法,是程式寫作上應作的努力。在特定專業領域裡累積的分析設計經驗,也應該能夠在各種不同的平台與語言下落實,其原因是達成功能的動作都可以在各種平台與語言找到對應的程式寫法。

在程式設計的領域裡,精通某種程式語言到出神入化的境界並能對各種需求做出對的結果,充其量只是很會使用該程式語言,如何系統化地佈置達成功能的全部想法以滿足各種不同面向的需求才是成功的關鍵。在需求不變時達成功能並不困難,在需要各種不同的分析統計或是變更時作最小的改變同時得知影響的範圍,我認為這才是設計的目的。

另外,文件記錄的應該是屬於心中想法的Model,而不應該是程式語言的呼叫方法。程式語言的函式庫有文件詳盡地說明各個方法,但是達成功能的分解動作與流程是深入分析過的人才有的結論,這是沒有地方可以找到的。記錄下達成目標的流程動作並對應到為此而宣告的方法與使用到的資源,才是這個系統裡最重要的資產。

2008年10月23日 星期四

R23 設計在於管理(4)──便利統計分析

得知每一個使用關聯的影響後,應該要把系統所有元件的範圍與方法的影響都記錄下來,作為各種不同用途的統計分析之用。製作一個Model的責任,是在於將之塑造為從各個不同面向都能得到對應的統計分析結果。

統計交易Log的初期,客戶想要知道的是每個Client執行每一支交易的時間是多少?送出到傳回的時間是多少?是否有執行時間過長的交易?其實在Log裡所有可得到的資訊有很多,像是交易開始的日期時間、使用者代號、工作站代號、交易代號與交易執行時間、交易送出到傳回的時間等等,如果只根據現有需求設計了工作站代號、交易執行時間、交易送出到傳回的時間三者關聯的統計報表,那麼在遇到客戶又想知道各個Client某天曾經執行過的交易代號時,應該會當場傻眼吧。

在這份Log裡很明白地具有六個特性,雖然客戶一開始只想要三個,但是設計時僅“滿足客戶現有需求”而沒有包含全部特性,那麼在未來客戶需要更多的屬性時該怪客戶要的跟原來不一樣嗎?倘使已經得知物件全部有那麼多的特性,在設計裡本來就“應該”包容全部以應付未來可能的改變;有時會因為特性實在與現有需求多出太多,那麼在取捨的時候就應有覺悟可能會產生要用到捨棄部份的風險。

公司定義的專案目標是以“滿足客戶現有需求”為方針。在揭示這個方向的同時我曾詢問要“如何讓專案的產出更有彈性地符合可能的改變?”,很可惜地並沒有人可以明確地定義,甚至還有人認為沒有必要。我依然固執地朝這個方向努力……。

2008年10月22日 星期三

R22 做人的方法(12)──易地而處

從關聯的兩邊往另一個方向看過去,是完全不同的風景。在有使用關聯的兩個物件之間,一個會是使用者,另一個則是被使用者;使用者全盤瞭解操作的劇本與時機,被使用者只能被動地應要求而行動。

人與人的相處同樣是由某些事所形成的關聯所建立的。像是客戶向公司訂購某個系統,公司就找我們組成專案團隊進駐開發;客戶告知我們關於需求的想法,而我們努力回饋分析設計的結果並形成產出。有的時候卻難免會形成開發團隊常會怪客戶需求一直變更,客戶也怪作出來的系統不穩定或品質不好這樣的循環。

想像客戶對於一個交易畫面的需求原本是三個人工輸入的欄位,在測試的時候發現第三個欄位可以從某個地方取得,因而希望作個小的需求變更改為自動取得。有些開發者會覺得任何需求變更都會造成多做一些事,感覺困擾而拒絕這個要求。如果自動取得的動作需要改變太多或是重要的地方,那麼為了避免風險自然應該反對;但若是這個改變只是引用現在系統現用的API或作點包裝,著眼在未來所有使用者節省的時間,則是應該接受。

面對的變化需要付出多少額外的代價,同時可以獲取什麼樣的利益是一般人作抉擇時的判斷,然而大多數的人僅以自己的角度來評估。一件事的發生絕大多數都像關聯一樣是雙方面的,懂得這層道理的人知道要從雙方的觀點來抓取最適合的平衡點,以最多、最有效的結果來評斷。同時會去關心對方想法的人,通常也會獲得對方釋出更多的善意。

關聯就是人與人之間的相處,人際關係是否和諧得看當事人是否能夠從對方的角度來看待事物。

2008年10月21日 星期二

R21 設計在於管理(3)──快速追溯關聯

在設計上能夠界定使用關聯的範圍時,接下來要做的是定義每個關聯的影響深度,從這裡可以得知改變的影響。影響會分為上下兩層,對下的使用改變主要是看流程中增減了哪些動作,對上則是看有哪些動作呼叫了自己。其中往上的追溯最為重要,因為自己改變就有可能令上層使用自己的方式有所不同。

程式開發工具裡都會有追溯的功能,不管是使用上的追溯或是繼承上的追溯都有,只要按一下熱鍵就能將指定Class、Method或Data有關聯的地方全部都列示出來。許多人看準了這個功能的好用,因而完全不在設計上作追溯的管理僅在需要的時候才利用這個功能,同時號稱這樣找出來的是完全正確的結果,不像追溯表那樣可能有無法同步的風險。

這樣的想法在單一的關聯上是正確的,但是根據許多人的經驗:使用上找使用關聯時往上找三層就受不了,繼承關聯在五層後會開始混淆,如此等比級數的發散光是找一個就可能吃不消,更何況系統裡的總數不知道有多少。每次系統上出了較嚴重的問題時,客戶就會要求在找出問題的同時,必須報告改變部分所作的改變會影響哪些功能。我也曾經為了一個重大的改變花了整個下午去定位並分析並向上追溯,搞到頭昏腦脹之後也沒法肯定那些是否為完全正確的結果。

越大型的專案,對於使用上的關聯管理起來越為複雜。沒有對的方法與工具,管理起來都是吃力不討好;沒有正確的觀念作為前導,同時也會忽略使用關聯的重要性。等到臨時需要改變,追溯出來的結果沒有涵蓋到執行時發生錯誤的地方,自然會被視為沒有品質可言。

與每層都追溯使用方法的方式比起來,我所想用的分層追溯應是相對簡單的作法。以每個介面作為切割點,根據現有的實作記錄往下使用的介面方法(超理想作法是由程式碼轉出使用關聯),等到需要的時候可立即追溯到上一層的介面使用;如果我的想法證明可以落實的話,應該可以立即追溯到最上層的所有使用功能。

2008年10月20日 星期一

R20 做事的方法(13)──分合之間的變化

想像一下我們進入一間小吃店點了一個粽子,然後觀察前後的物件變化。老板會拿一個碟子作為容器,放入粽子後加上醬料並擺上一個叉子送到桌上,我們食用之後消耗掉粽子與醬料,碟子與叉子則被收進廚房堆在一起等待清洗。雖然只是平常不過的動作,卻蘊含了物件間分與合的變化。

在起始的情況下物件是根據類別存放的,碟子、叉子、粽子與醬料都各自放成一堆,在事件發生(客人需要粽子)時才各取一個聚合為一份餐點。食用後的那份碟子與叉子會被收集到廚房,那裡有整間店全部待洗的餐具,等到清洗後才又回到各自存放的初始狀態。

從存放區的角度看,所有種類的物件都各自在一個集合裡;從客人的角度看,眼前是所有物件各取一份的集合;從廚房的角度來看,是所有的碟子與叉子都會堆放在一個集合裡。物件永遠是同樣的東西,但是從不同的使用面來查看時,組合起來的結果卻是大不相同。

此時如果用“該有的都有了,多的不要用就好”的想法來看:沒事在存放區多準備大盤子的物件、給客人時沒事多給一隻西餐刀、吃剩的粽子不先倒掉而放到廚房的洗碗槽,的確不會影響到真正要做的事。但是在準備時多放置一組大盤子與西餐刀,勢必需要耗費資源去處理;客戶沒事看到吃粽子還附西餐刀,感覺一定很怪;洗碗槽裡同時丟進了殘渣清洗起來勢必得多花一點功夫。如果店裡僱用了外國的幫傭,說不定會以為這些本來就是這樣呢。

想要Working Smart,秘訣在於快速定義出哪些物件是需要的,同時有哪些事是必須處理的,然後規劃出最快速且正確地執行所有動作並且準備好全部物件。省卻動作的時間卻留下多餘的物件必然在未來會付出代價,因為動作只是一瞬間的事但是物件卻永遠存在,而且遺留下來的物件會造成必須做更多的動作!

2008年10月19日 星期日

R19 設計在於管理(2)──應付改變能力

在設計時曾說過系統要能應付不同需要的話,必須可以自由地在不同的面向作乾淨的切割。這種結果也必須精確地先定義每一個可切割的層次,再精確地定義每一個元件與函式放在應該存放的那個小格子裡。

業務曾問我目前維護的系統是否可以獨立出某個特定模組出來販售,我很直接地回答不行。否定的原因主要有兩個:一是層次與元件定義的不明確、另一是元件使用資源的模糊。層次與元件沒有理想定義造成想取出一些元件時,使用關聯的混亂迫使一堆不必要的元件也必須被選用;使用資源沒有界定範圍會讓元件取出時,無法得知實際使用到的資料到底有什麼。

改變是無所不在的,曾有同事說過讓客戶需求定案而不輕易改變會是讓系統品質提升的好方法,我反問他不久前才說過的經驗:要是架構師在最底層的功能就選錯了實作的技術的話呢?客戶時常改變需求沒錯,但是在不違背設計精神的前提下是允許加減功能劇本的動作的。然而更重要的是要讓自己的設計作好應付各種變化的可能。

精確的定位與關聯的最小化是必要的努力,事物與元件一對一的對應也是不可忽視的重點。唯有在設計時致力於這兩者的實現,才有機會讓設計的元件像積木一般靈活地分解組合與替換,卻不會花費過多的時間與造成過多的影響。

2008年10月18日 星期六

R18 設計在於管理(1)──管理源自精確

設計的目的在於管理系統外的需求與系統內的資源,在任何需求有變更或是程式有問題時,確認系統會動到的地方後可以快速正確地定位出影響的範圍。要能達到完整的系統管理,必須具有精確的設計與關聯的記錄;如果缺乏這兩個要素,在無法侷限影響範圍的同時,side effect只要發生在預估範圍之外就是代表品質的不穩定。

在各種類型使用關聯上,用人者與被用者全部都被明確定義且詳盡收集的話,系統的管理將會變得容易很多;就像是把所有的資料放到關聯式資料庫存放一般,在各種不同方面的需要都可以快速地取得想要的結果。這些都仰賴專案的成員精確地記錄下點點滴滴,再使用適當的工具存放與搜尋才能達到的成果。

今天在一個團隊裡如果聽到“我改一下很快就好”、“那個沒弄又沒有關係”之類的話語,可以斷定未來產出的系統絕對是亳無品質可言的,因為快與忽略是造成資料與關聯漏失的主要原因。想像一下一個專案裡要做的全部事與物,假設一共有一萬件大大小小的事情要做,此時漏失了一個沒有被記下,那麼要花多少功夫才能在未來出大紕漏時回溯出是由於這邊少了一件事呢?

製作一個中大型的系統並不是簡單的事,即使只是一個元件也包含許多待做的動作,缺少幾個事物的記錄時或許還可以依靠相關的部分推理出事實;可是缺漏的一多任誰再也沒那種精神再去回溯出來。如果回首看待自己曾經做過的產出有那種似曾相識但是又很糢糊的感覺,那麼肯定是沒有作好記錄管理的結果。

2008年10月17日 星期五

R17 工程首重精確(4)──注重問題根本

雜亂無章的系統很容易在品質方面出現暇疵,不小心寫錯的地方如果也不小心沒有被測試出來,等到系統到客戶手中後才不小心地蹦出來,除了要勞民傷財地追溯影響並修正測試外,客戶對於系統的品質也會越來越不信任。

系統的測試沒有辦法使用80/20原則,保證80%重要且常用的功能是沒有用的,因為只要有任何一個功能在上線後出問題,客戶就會把廠商直接叫過去嚴重的甚至退版。系統的測試應該是精確且完整的,然而在隨性開發的專案裡根本沒法定義出應測試的範圍,僅能含糊地說作好測試系統自然不會出問題。

“完整”必須經由精確地計算得到,不是用推估的方式。鯀不知水性,在治水時硬用全面圍堵想求滴水不漏,結果大量建立的堤防反被大水沖毀;舜明白全面圍堵的困難也知道無法平息洪水,因而根據水性計算出在最少的地方施工而達成目標。瞭解系統內部設計的根本才容易從根本解決問題,而不是根據外部現象硬加程式在表面把錯的修成對的。從表面修補時注重的只是滿足結果的產出,每一次掩飾都是讓系統隱藏一顆不定時的炸彈。

有時會發生罕見又難解的問題,因為找不到真正的發生原因且客戶一時間沒法重現而暫時擱置。在專案裡有幾次這樣的現象,但是到最後一定會迸發出來;有位同事在遇到兩年前的舊問題在上線版本復發時就曾感嘆地說,該來的總是會來,躲得掉一時不代表躲得掉永遠。

問題都應找到根本再加以解決,暫時其他因素苟且放過後,終有一天會回饋到自己身上的。

2008年10月16日 星期四

R16 程式碼的MVC

系統上的MVC設計被很廣泛地接受,將功能所需的資料、流程與顯示分開存放,在絕大多數的狀況下可以僅異動或更換其中一塊而保持另外兩塊不受影響。將資料與程式依特性放置在不同的元件中能夠有這樣的好處,那麼在元件的黑盒子裡如果運用這個想法會不會有同樣的好處呢?

這兩年來我維護了許多其他同事所寫的元件,那些程式全部都是依個人風格所寫,由於開發時我沒有參與,接手時也沒有足夠的說明與文件,因此我相當清楚維護的瓶頸在哪些地方。問過一位喜歡看設計書籍的同事,他說沒看過有人像我這樣推動程式碼內容依MVC擺放的論點,因此我也沒有足夠證據證明它的好處,只能說之前的團隊在換不同成員處理一個元件時真的極易上手。

曾經與公司一位經理說過,我的設計方式可以具有變動彈性、方便維護、容易除錯的特點,但由於要定義架構元件的明確層次與複雜化程式內部的擺放,會使得開發需要的人力再多出一倍左右。那位經理反問我,要怎麼去說服上面同意給兩倍的人力來做出功能完全相同的產出?重心如果放在“那個階段可以產出什麼”的話,我完全無法說服任何人。

別人程式的難懂在於達成功能的所有流程與動作是什麼?使用到的變數放在哪裡?還有不知道某個區塊的程式到底想達成什麼功能?看過一些文章教人如何去抽絲剝繭、循序漸進地去看懂其他高手的程式碼,曾經歷此道的我可以斷然地說那是徒勞無功的。想法轉換為程式的變化如果沒有任何說明,就像一個小型的謎題一樣;你能想像在一個上百萬行的專案程式碼裡,有多少大大小小的“謎題”存在嗎?

猜燈謎時不見得每個參與者都能猜出答案,有的人花很長時間絞盡腦汁還是想不出來。但是卻有一個簡單的方法可讓所有人立即明白答案與原因──那就是讓出謎的人直接將答案告訴每一個人。

2008年10月15日 星期三

R15 工程首重精確(3)──建立通用原則

在介面的定義中我會分為兩個大層:一層是系統分析(SA)定義的,一層是系統設計(SD)定義的。介面的方法是遵循的是完成功能的逐步分解動作所設定,在分析設計時應儘量保持使用的層次與意義的完整。

從介面的存在所切分的兩層來看是截然不同的世界:從使用介面的角度來看,只需要知道介面方法提供了什麼功能、要怎麼使用;從實作元件的角度來看,則僅要努力達成規定的結果而不用管上層如何使用。在一般的文章裡大多都教我們將介面下的實作元作視為黑盒子,不用花太多心思在裡面,但是在撰寫與維護那些黑盒子時總不能跟經手的人說那本來就是黑盒子吧?

如何讓大家快速知道其他人的設計理念是增進團隊產能的捷徑,在目前各自為政的設計之下,每個人做出來的元件都有不同的風格,現在連看懂都很困難更何況是維護。在無法通盤瞭解別人所寫元件的同時,只能依據自己的想法另寫新程式碼來異動而盡量不去動看不明白的地方。

專案裡需要一個記錄用的序號產生功能,設計者為了讓多台電腦同時取用都能正常,寫了100行左右無任何說明的程式碼,但是接手的人看不懂為何要這麼多行而另外使用簡潔的寫法。最後在多伺服器同時取時果然出現了同號問題。這該算是誰的錯誤呢?

物件的擺放如果可以規定擺放與存取的規則,設計的流程如果可以規定出寫作與放置的標準,那麼所有人在第一次拿到別人所做的元件時都能夠立即曉得怎麼開始。黑盒子裡的好壞左右了使用它的程式成敗,如果專案裡的元件都沒有人可以弄懂別人負責的部分,這怎麼可能有團體效率可言呢?

精確的資料存放與步驟流程,就如同大公司貫徹的SOP一樣是維持品質與效率的最大訣竅。

2008年10月14日 星期二

R14 工程首重精確(2)──依意義作設計

每一個元件、每一個方法、每一行程式都應該依需求或設計原有的意義安排,一方面是為了精確地做到應做的動作,另一方面則是為了讓所有看到程式碼的人立即看得出關聯。

某天同事提出一個設計問題:傳入一個一定是偶數的整數,要作出+1-2+3-4+5-6……(數量與傳入的整數相同)。我的作法是採用迴圈依需求逐一地照算式運算,不過同事說最佳的設計是將算式化成(+1-2)+(3-4)+(5-6)……最後成為-1 * (n / 2)的精簡計算。當然用後者的方式是最佳化的設計,但如果沒有任何說明時其他的人根本不知道原來的需求是什麼;如果想法經過轉換則應該註明變化的經過才不致於無人理解。

在系統的設計中我們時常需要取得User ID這個變數,但是在執行交易的時候可能用數種不同的方法都能取得(例如從最底層的Map直接取得,或是用包裝好的API取得),這時應該規定在不同的層次時使用固定的對應拿法。動作的設計也應該先釐清所屬層次定義對應的方法,同時在動作的組合時一樣放在應該放的地方。

try {
 Thread.sleep(1000);
 MyAPI();
} catch (Exception e) {
}
同事在寫這段程式的時候,我建議他把MyAPI()搬出到最後。雖然sleep()在我們的經驗裡從來沒有拋出過例外,感覺上寫在外面或是裡頭對於執行完全沒有影響,但是在意義上是應該寫在外面的。同事同意了我的說法而作了修改。

系統的建立就是這樣,當想法逐步變成產出的同時,一層一層地精確鋪設出該有、該做的輸出,這是良好設計的初步。

2008年10月13日 星期一

R13 工程首重精確(1)──最佳化與範圍

我覺得精確才是工程的意義所在。為了達到某個目的而設計了一組最佳化的動作,精確且快速地達成應該要做的事;而在執行該組最佳化的動作時,所有可能使用到的資源也需精確地定義。

開發系統的時候都會使用Library,這些Jar File都有使用的時機點。在Client與Server架構下,有時沒有明確地定義而把許多檔案都一鼓腦兒地放在一個資料夾裡,說是整個資料夾都使用就保證系統一定可以動。但是客戶會感覺Client的資源過於龐大,多了很多不知道有沒有用的Jar File,不刪覺得需要瘦身但是又怕誤刪了一個造成系統出錯。(正確的作法應把Jar File放置在三個資料夾:Client、Server與C+S共用)

根據功能需求上的意義定義出應該做的對應動作方法應該也要是精確的。如果一個動作沒有清楚的定義,那表示該動作內的一組程式碼或更內部的API會露出在更上層,同時也意味著其他功能中要做那個動作時很可能沒有固定的使用方式。沒法精確地定義與定位使用的動作,就會造成從不同方向去看待時會有更大的機會發生錯誤。

定義出功能使用的程式與資源,也有利於未來想要抽出功能到其他地方使用時能夠精確地取出應用的區塊。倘使只為了完成系統的全部功能而設計,那麼往後根本無法定位出指定功能所必須的範圍。這也正是現在許多專案系統裡很普遍的結果。

2008年10月12日 星期日

R12 Log File的意義與應用

雖然說測試應用程式的好處在於可以便利地偵錯,但是上線的系統極可能不能作除錯動作甚至沒法快速更換除錯用程式的。在沒法看到程式內部狀況的時候,只能依靠程式的輸出來偵測內部的運作狀態;一般都會採用Log File作為這種記錄的機制。

病人與黑盒子元件我們只能根據先天已經存在的症狀來檢核,比較起來自行開發的系統在未來的除錯上有較多的優勢,因為我們可以自行決定哪些要記錄、哪些不要。雖然在考量要記錄什麼資訊時會基於執行數量的多寡與重要性等等因素加以取捨,但是必要的資訊還是應該記錄下來,以避免某些錯誤太難重現而錯先修正的先機。

Log最基礎的想法在於表達出程式曾經經過這裡,在必須的經過點留下記錄可以證明是否執行到應執行的程式;在重要的分歧判斷前留下Log則是為了記錄判斷條件在當時取用的資訊;執行時需要的資訊在初產出與取用前記錄下其內容。對於單一交易的記錄大致以這三個目的為主,執行時發現過多或不足再根據實際需要加以調整。(有時為了不同的需要,也會將應付不同目的的記錄分開放到不同的Log File裡)

如同醫生診斷病人時是以偵測當事人的病況加以治療而日後會提出某些疾病的統計分析,系統的Log除了對於單次交易的記錄之外,還應該考慮到同類型以及所有交易的統計所需。根據這樣的需要,我們必須定義交易起訖記錄以及內部重要步驟的起訖記錄,與其記錄的同時應該帶有的資訊(通常會考慮使用者、工作站、交易代號、開始時間、執行時間為最基本的記錄資訊);而所有記錄的結果應該另外撰寫讀取Log的程式加以分析並統計,才能快速且精確地計算出所需的報表。

先有使用者想處理的資訊存在,然後有因處理使用者資訊而存在的程式;在處理程式執行的同時會有靜態的記錄,再有處理靜態記錄的Log分析程式;對於Log分析程式產出的單一型式報表(例:使用者當日執行各交易次數統計),還會需要另一個報表統計程式產出各個處所或是整間公司的統計報表。資訊需要程式、程式產出資訊,資訊與程式就這麼循環地緊密相依著……。

2008年10月11日 星期六

R11 醫生的診療vs系統的除錯

醫術所謂的”望聞問切”四個字,現在看起來與系統發生問題時所要做的動作似乎相同。(暫時讓病人=系統)先看看系統表現出來的現象,聽取使用者的描述與想法,接著詢問其中還不夠明白的關鍵之處,最後從最可能出錯的地方對症下藥。

就我觀察的醫生診療過程差不多就是這樣,先觀察表面的症狀看符合哪些疾病的特徵,如有與數種疾病的表徵相同就再作更進一步的檢驗直到只剩唯一符合,再來就針對那種疾病施予對應的治療,過一段時間後再觀察病人是否已經好轉並決定接下來的處理。醫治的行為就在取得狀態與處理動作的循環中進行,直到病人痊癒為止;系統的除錯同樣也需要這樣的循環。

但是兩者還是有很大的差異。系統除錯時可以設定中斷點觀察內部程式的動作,可以立即修改程式再重新執行,必要時還可以隨便改動變數的值嘗試看看有什麼改變;診治病人時雖可以有內部的診視,但是有些也沒辦法太細致(懷疑某段血管硬化時總不能先切開來看看吧?),琢磨病情時也不能先下個可能的藥看看(萬一投錯藥掛點的話呢?)。

由於這樣的特性,我們可以在程式設計的領域看到不是相關科系的人同樣有所成就,但是醫生只有少量非相關科系的人能夠生存。另外,程式高手與名醫的相同點在於只要取得相對較少的資訊(甚至只需要對談)就可以判定問題的癥結,這是需要天份將大量的經驗在心中分析化與關聯化後才能達到的領域。

2008年10月10日 星期五

R10 與高階主管們的討論

在六月中旬,公司安排了一場我對高階主管們的報告。原本雖是要檢驗近兩年來我的工作成果與未來工作展望的,但我捨棄了制式的報告而選擇提出近來看到的專案問題(列在前幾篇)與自己想努力的方向(在前一章節)製作成投影片。一個小時的說明下來,高階主管們頗能認同我看到的現象。

最後邀請了一位某大公司的資深人員聽取我的說法並提供意見,他說了一堆不過我聽到的重點只有兩個:一個是我使用的某些專有名詞與其意義不符,另一個則是他在台灣看過不下十個方法論至今還沒有一個成功的。

我必須承認我所使用的名詞不正確,因為我的經驗都來自於工作上的體驗而非書本的知識;我明白了一個道理後只是試著用我認為適當的方式來描述,在Java World裡的幾篇回覆也有人提出類似的論點。不過宇宙的偉大並不是因為它叫宇宙,豬的懶惰並不是因為牠叫做豬,而是它或牠本來就存在;經驗中對的道理並不會因為叫錯名字而變成不對的,那是一直存在的,除非旁人只因名稱不對就不屑一顧。

令我反感的是第二個意見。幾乎當場就反問:如果經歷過那麼多方法論都沒成功,怎不直接對主管們說乾脆不要做了?不過幸好我還懂得一點做人而沒說。不過後來就再也沒看過那個人了。

時光匆匆地過去四個月,從那次會議之後我就轉為支援解決國內專案的問題而時常忙到半夜才回家,大陸的合作開發也暫時停止。到現在為止,還沒有看到任何的轉變……。

2008年10月9日 星期四

R09 做人的方法(11)──適合做黑心產品的想法

這幾年時常看到大陸許多黑心產品的新聞。這些黑心產品都具有相同的特色:從外表上看起來與正常的產品無異,但是內部所使用的都是劣質的替代品。同事們聊天談到這個話題,我搭腔道:其實製作黑心產品的人很適合來做專案,因為只要讓系統能通過客戶的檢驗即可,等到客戶使用另外的方式來測試時,才會發現裡面是亂做的。

沒錯,讓系統在表面上可以通過使用者的測試,正是驗收的關鍵;也由於客戶通過驗收與否事關專案的成敗,使得非常多的人僅把這個目標作為開發系統終極方向。功能的劇本內有些程式有誤造成表現出來的不對,就加程式把不對的部分補做成對的;有些該關起來的視窗因某些判斷不對而保持開著,就加程式判斷若開著就硬把它關掉。沒有找出分歧出錯誤的岔路卻硬加多餘的程式將表面補成對的,如同將眼前漏水的洞硬補起來後,終將發現水還會從其它的地方漏出來。

在龐大的系統裡要找出真正的錯誤點的確很難,也難怪很多人都求快速地在外面補正錯誤,不過卻也因為這種想法造成系統疊床架屋又難以理解。(在寫Log分析程式時我也曾偷懶直接刪除一些轉出後有少欄位的記錄,而沒有去找出為什麼會有漏掉欄位的資訊)以同樣的思維應用在生活的事物上,就會出現許許多多表面只應付檢驗,骨子裡卻失去本意的替代性作法。

凡事如果只看自己的需要,卻沒去注意萬物來去的流向與意義,終將只能做出半調子的成果來。

註:客戶將幾個專案的廠商人員集中在一個樓層,我一直發現垃圾筒裡有很多可以回收的紙製容器與塑膠容器,前幾天終於受不了貼張紙條提醒那些在清洗後是可以回收的,這幾天觀察是有些改善,但是還是有很多直接丟進垃圾筒的可回收容器。如果人的思考方式已經定型下來,要改變實在是太難了!

2008年10月8日 星期三

R08 支援專案所見(5)──遇例外就時間失控

在專案裡除了開發功能與除錯外,最耗費時間的莫過於臨時有狀況發生時要去追查相關資訊作進一步分析了。曾經有的經驗是:系統上線後大致都正常,但是在某段時間裡交易執行的時間是正常時的五倍到十倍。

客戶非常在意頻寬的使用與執行的效率,因此要求我們清查出問題那幾天裡每個交易執行與連線所花的時間;Log File一如平時所見,整個系統有四個伺服器,每個伺服器一天有50 MB以上的資料,而且還要加上數個Client的Log File。記錄的查看很容易,只要找出範圍再逐一過濾;但是客戶要的是全部資料耶!全部看完那禮拜就不用做別的事了。

我當然沒有去看內容,而寫程式判別Log File內特殊的關鍵字取出我所要的資訊存到CSV類型的檔案裡,再藉由排序的功能找出最耗時間交易的全部資訊再作分析。這個處理程式我寫了約三個工作天,但好處是未來任何時候的記錄檔拿回來後,只需要十分鐘就可以取得所有交易的內容作排序與解讀。

時間的失控主要是因為原先放置的東西沒有管理。如果今天的Log File內容不是有順序的邏輯,那麼不可能寫出轉換的工具程式;如果寫工具程式時沒有考慮Log File內交易記錄的全部屬性,那麼在日後客戶另外要求要看指定Client的所有執行交易代號統計時,還得另外再花時間撰寫。

例外,是臨時需要的資訊是原先的設計沒有規畫進去的,臨時為了解決那些例外的需要得花非常多的時間去處理。降低例外的影響有兩個原則:擴大一開始的設計範圍儘可能容納更多考慮得到的例外;另外就是保持設計的彈性,在例外發生時可以快速地將之包含進來卻沒有太大的風險。

2008年10月7日 星期二

R07 支援專案所見(4)──疊床架屋解決問題

原來的功能模組已經完成得差不多時,如果遇到一些與原來流程不盡相同的改變,要是因之前沒有良好設計而擔心融入新的考量時會有解不完的問題,那麼通常都會找出相異的地方後使用if else指令另繞出一條不同的路來放新的流程動作。這便是疊床架屋型的設計。

這種設計法很快而且風險很小,只要條件找對就可以成功。但是一塊一塊固化的程式以及一個一個的if夾雜在一塊,要維護與變更比較多層內的程式可就恐怖了,因為一旦拉扯到重心所在的物件時,恐怕整個系統倒得比什麼都快。還有即使是if else的條件判斷也應該規畫好放置的地方以及共用的考量,才不會因信手拈來而雜亂無章。

很可惜只要專案時程一壓迫,變動都會被只求快速地完成而無暇兼顧佈置,這也正是許多系統令人痛苦的地方。有時在增加新功能元件的時候,主管都會要求要好好地依理想設計,但是元件本身可以,想要把元件處理的部分“好好地”融合到原來的系統裡卻太困難了。

這時我會舉一個例子給主管:有兩個罐子各標示著要放紅豆與綠豆,但是原先的人放的時候不嚴謹,兩邊都各混到一些錯誤的豆子,後來的人再嚴格地把紅豆與綠豆分類放好,這對那兩個罐子而言還是混亂的,並沒有多大用處啊!最後整個系統還是要再度重整的,因為從一開始就沒有做好應有的設計,後面再怎麼做好效果都是有限甚至是做白工。

2008年10月6日 星期一

R06 支援專案所見(3)──流程與動作的綑綁

專案裡看到的程式幾乎都是這種類型居多,在撰寫局部功能的時候在一個方法裡放置了所有要做的動作;當然包括了分析上的行為與細微的API全集中在一起。這是程式寫作時直覺的思考方式,把功能要做的事依其順序寫下來。

這樣設計對於實作一個功能是正確的,反正到最後跑出來的過程與結果是正確的,但是對於變動來說卻是很困難的。首先得先定位出分析意義上的動作步驟範圍,思考上的一個步驟可能由多行程式碼所達成,而那些程式碼是緊密而不可分的;其次,順序變動時還要去思考其他同樣呼叫此方法的功能,是否會因此而造成錯誤的狀況;再者,在其它方法裡若發現同樣目的的程式碼時,是否要抽取出來也難抉擇,因為是否抽得乾淨與是否涵蓋所有使用的功能都不易確認。

進一步的設計是把依序寫下的動作分門別類地佈置與封裝以形成方便重用的元件,可惜的是現在經手的程式有很多從底層就已經開始混淆,這在專案上想覆寫時也會遇到明明只要改一點小地方,卻非得整個方法內容剪貼出來修改而完全棄置原來的程式。長久下來就造成專案產出程式裡的盤根錯節與過多的重覆程式碼,無論是誰都難以再度釐清。

直覺的設計寫法在極小型的專案裡還可以應付,因為需要記憶的部分不多;但是在中大型的專案裡思考內容很多而且由多人開發,那光憑少數人的記憶是完全無法應付管理與追溯的要求的。

2008年10月5日 星期日

R05 做事的方法(12)──切換Task會浪費資源

現在的電腦不只可以多執行緒,還可以多CPU,一次可以讓許多工作同時執行,我們可以同時排進大量的執行緒後在預期的時間結束工作。有時主管也會要求我們做類似的動作,就是同時要處理兩件以上的事情,而且要同時有進度且在預定的時間內完成。

在同時處理多件事時,意味著我們必須把第一件工作執行到某個程度時將之凍結,然後把腦中的資料清空後再將第二件工作之前凍結的狀態還原到腦袋裡;不可否認地有很多人可以快速的切換,但是切換時必定會耗費一些時間而且要承擔可能漏掉什麼的風險。

人腦到底是單執行緒的構造,專心一意才有可能把事情做得更好。曾有一篇文章說,上班時間內超過四分之一的時間是在接與手邊工作無關緊要的電話,以及重組被電話打斷的思緒;工作之間的切換也會造成類似的影響。要完成一項工作至少需要應有的時間,在中間插入其他工作只是在表面上都有進度而已,但是根本不會加快任何一項工作的時間。

另一個就是在看別人程式的時候。把自己的設計想法放開,去看懂別人的設計想法也是很花費腦中資源的,因為一切內容的放置都得重組才能瞭解;別人的Class、Method、Attribute,以至於元件的架構佈署、功能放置、命名規則都要摸索一番。之前公司開發底層架構時有另外找了一些新的人員加入,後來在查看問題時大家都說某人的程式的寫作風格不同而看不懂,因而造成難以修正以改寫的後遺症。這正是我推動一致寫作風格想避免的狀況。

2008年10月4日 星期六

R04 支援專案所見(2)──不是我寫的不知道

當系統發現問題或需要修改時,如果那時原來的開發人員不在難免會另外找人先瞭解問題,並希望他嘗試去解決。但是一開始總是會先聽到“那不是我寫的,不知道耶”這句話。在急迫時我也會被指定去解決比較困難的幾個問題,在剛進入專案幫忙時真的是什麼都不知道,那句話也不知在腦中盤旋幾次了,不過當然不能說出來,只能按部就班地抽絲剝繭下去找出問題的發生點。

一開始會先去問發生狀況的部分是要達成什麼功能,也就是先問出大致的需求內容;接著會問如何操作才會發生異常的狀況以便重現問題;然後確認正常狀況應該是什麼,以比較對與錯之間的差異是什麼。資訊收集後再來就是用除錯模式驗證程式碼,但是在此之前還要知道流程與程式的對應在哪裡,先弄懂程式上的架構才能追進程式的流程逐步檢查。

即使公司的專案已經先行提供通用的主要架構,但是在可以客製化撰寫程式的地方,每個人寫的程式碼都有自己的風格,物件的擺放也依自己的想法;公司目前沒有提供標準的設計方法論可遵循,以至於產出的過程與結果都依個人喜好而有所不同。由此可見,要追查一個陌生的問題難度著實不低。

更困難的是,客戶重視解決問題的同時要記錄系統對應修改了哪些程式,有哪些功能流程走過修改的地方而且可能造什麼影響;另外也會問是否有其他情況會在其他模組裡造成類似的錯誤。在時程急迫且修改者對修改部分不甚瞭解時,如果只改看起來是問題的地方就會時常漏東漏西的;一旦修改問題時發生其他side effect的機率偏高時,客戶對系統品質的信任自然會越來越低。

2008年10月3日 星期五

R03 支援專案所見(1)──不瞭解架構與函式

公司對於主力的客戶領域設計了重用的產品,好幾個專案共用同樣的設計架構(Framework),架構裡放置了許多以前專案經驗的元件。原本期望讓以前作過的東西在未來可以不用再花精神製作,但是因為開發的時程不足造成分層的佈置不夠完善與說明文件不夠詳盡,使得新人的學習曲線變得十分漫長。

依我所見,系統分析定義的流程動作不夠完整、系統設計沒有放置應有的對應函式、產品提供的函式說明不夠清楚,使得每一段負責的人都沒有辦法快速且正確地堆疊出達成需求的完整計畫。雖然說對於產品的瞭解是根據各人自己的學習,但是沒有層次條理又沒有足夠說明的產出,要只依靠追蹤程式碼以及口耳相傳來傳承想法的話實在是事倍功半的。

在支援專案的期間,有許多成員問過我很多關於產品的問題:大到一個元件的完整生命週期內容,小到一個屬性的意義,在在都能發現他們的所知都只侷限在他們曾經做過的功能上。架構與底層提供什麼?可以怎麼使用、怎麼操作?在專案上要怎麼應用、怎麼延伸功能?這些內容描述的不清楚,對成員來說都如同霧裡看花般難懂。

就像現代年輕人對於中文一樣,認識字的形、音、義不多,詞的意義與使用情境也瞭解很少,對於文字基本單位的認知過少,在組織文句的時候可以使用的基本字詞相對地就很少,在這種情況下怎麼能期待有流暢且意境深遠的作文產生呢?這也正是專案裡普遍所見的問題。

2008年10月2日 星期四

R02 實作型vs理論派

與同事聊天的時候,會將往不同方向堅持的兩個類型稱為實作型與理論派。

偏重實作者一切都以滿足需求規格書為主,客戶對功能與效能的要求都是開發的目的,但是除此之外其他的部分似乎都可有可無。對客戶而言,只要系統的輸入與輸出行為模式正確,這個系統就是堪用的;至於裡面如何設計與佈署,需求變更後的衝擊有多少就沒有人知道。

“設計應該保持彈性”、”系統應該進行完整的測試”,為了確保開發出來的系統品質與彈性,大師們提出了許多有用的論點。但是每一種堅持都需要額外花時間(而且是不少的時間)來達成,對於必須承擔時間壓力的專案來說,能夠勉強作出客戶能夠驗收的系統就已經普天同慶了,又怎有多餘的精力讓系統內部更美好?

看書不多的我只能算是實作型的一員,但是多年來的經驗令我明白設計的彈性與品質也是必須同時做好。然而在面對實作型的人提出要求品質的論點時,會被說是不看實際執行困難的理論派;面對理論派的人提出方便行事的快速作法時,又會被說成是講求速效走捷徑的實作型。處於夾在中間的尷尬境界,除非能夠提出一種令兩邊都能同時驗證通過的方式,否則到最後一定會被視為只會喊好聽口號的人──即使要說服心裡已經有固定想法的人很艱難,那也是必然會走的經過而已。

“做了、做完、做對、做好”是做事的四個評估階段,然而除了自己做好之外,讓其他人在未來“好做”,才是我放在心中的最後目標。

2008年10月1日 星期三

R01 知難行易vs知易行難

世界上有非常多的事物,只需要投入時間去摸索很快就能有小成就;像是釣魚、做菜、繪畫……與程式設計等等,知道如何開始後很快就可以有初步的能力。然而開始是容易的,若想要將自己的能力在該領域提升到較具水準的規模,卻必須要有足夠的專業知識才可以,否則做出來的成果很可能會七零八落。

由於入門容易,知難行易就是鼓勵大家放手去做,只要懂一點基本的道理就可以做出粗具規模的東西;實際領會困難,知易行難則是表達要邁入高手的境界,聽理論的內容感覺很容易但是卻難以達到盡善盡美的程度。大部分的學習曲線在前期都可以快速成長,到了後期卻得累積許多知識與經驗後才能稍微前進一小步。

公司的幾個專案陸續遇到了一些狀況造成氣氛不佳,主管幾位同在資訊業的朋友也抱怨做得很辛苦。在程式設計的領域裡有很多大師在開發軟體的各個方面都揭示過非常符合理想的概念,可是如果業界有理想標準的話,為何那些專案都無法因套用而受惠?使得大多專案成員都做到死去活來?而且同專案的成員都沒法看懂其他人所寫的程式呢?

實作是比較容易的,因為它可以經由測試加以驗證,任何人都可以很快地看到結果是否正確,而且修改與反應也大多是立即可以再度測試出結果的。但是設計並無法快速驗證,通常會在更改錯誤或是需求變更時才會發現整個改變過程裡發生窒礙難行之處才會恍然大誤,但是到那時通常都已經來不及再調整回來。設計的問題在內部結構開始定型後,就已經埋下了日後難以修改的地雷。