<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>同人的生活派對 &#187; 開發流程</title>
	<atom:link href="http://www.lifeparty.idv.tw/blog/archives/category/%e8%bb%9f%e9%ab%94%e9%96%8b%e7%99%bc/%e8%bb%9f%e9%ab%94%e5%b7%a5%e7%a8%8b/feed" rel="self" type="application/rss+xml" />
	<link>http://www.lifeparty.idv.tw/blog</link>
	<description>君子學以聚之,問以辨之,寬以居之,仁以行之</description>
	<lastBuildDate>Fri, 05 Mar 2010 04:18:37 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>掌握設計演進的節奏</title>
		<link>http://www.lifeparty.idv.tw/blog/archives/2784</link>
		<comments>http://www.lifeparty.idv.tw/blog/archives/2784#comments</comments>
		<pubDate>Tue, 19 Jan 2010 10:07:32 +0000</pubDate>
		<dc:creator>jim yeh</dc:creator>
				<category><![CDATA[品質文化]]></category>
		<category><![CDATA[問題解決]]></category>
		<category><![CDATA[寫作]]></category>
		<category><![CDATA[專案管理]]></category>
		<category><![CDATA[思考]]></category>
		<category><![CDATA[生活感觸]]></category>
		<category><![CDATA[職場]]></category>
		<category><![CDATA[設計原則]]></category>
		<category><![CDATA[軟體審查]]></category>
		<category><![CDATA[開發流程]]></category>
		<category><![CDATA[領導]]></category>

		<guid isPermaLink="false">http://www.lifeparty.idv.tw/blog/?p=2784</guid>
		<description><![CDATA[在軟體開發的過程中，有沒有方法可以避免我們浪費心力在無謂的堅持上，然後用比較簡單而又有效率的方式來完成我們的工作呢？經過與同事上面的對話，同人想到運用到我在分享會中所提到的觀念與實務，可以很輕易地掌握設計演進的節奏。藉由此篇文章分享出來，也算當做同人在 1/9 敏捷開發分享會後的一個註腳吧。]]></description>
			<content:encoded><![CDATA[<p>在 <a href="http://cb.esast.com/cb/wiki/9584">1/9 在新竹舉辦的敏捷開發方法分享會</a>，當同人分享到 XP <a href="http://en.wikipedia.org/wiki/Refactoring">Refactoring</a> 實務的經驗時，台下有一位聽者剛好也是我目前的同事提出一個問題：該由誰來決定何時應該重構的問題。同人當時回應重構多半發生在軟體架構的設計上，一般開發應用程式的程式員通常比較不太會有機會重構。在專案每天早會上，團隊各個成員會報告他們目前進行的工作狀態，當同人發現他們遇到架構面上的問題，我便會著手進行架構的重構以避免系統發生疊床架屋的現象。</p>
<p>同事好奇重構的決定是否有客觀的標準，同人表示這部分多半還是個人主觀的經驗居多。在同事後來開車載我回台北的路上，我們再次談到決定重構的時機。同事覺得重構的時機似乎不是一件容易掌握的事，同人進一步地解釋，當時我們在應用程式的開發沒有太多重構的機會最主要的原因，是因為在架構上力求簡潔而單純的設計概念，使得應用程式的開發已經變得很簡單，實在不太需要運用重構用來增加設計的彈性。</p>
<p>趁這個機會，同人向同事強調架構的彈性不應該以需求不得改變為前提，而是要能夠因應「有限度」的變化而發展而不斷地調整及演進。也就是好的架構並非從恒久不變的核心來出發，而是要先去識別出問題的輪廓才找得到適用的核心。同人經常在軟體開發的實務中看到，人們花費了太多的心力來堅持不變的核心，到最後才會發現原來問題是出在自己對問題假設錯誤。</p>
<p>那麼，在軟體開發的過程中，有沒有方法可以避免我們浪費心力在無謂的堅持上，然後用比較簡單而又有效率的方式來完成我們的工作呢？經過與同事上面的對話，同人想到運用到我在分享會中所提到的觀念與實務，可以很輕易地掌握設計演進的節奏。藉由此篇文章分享出來，也算當做同人在 1/9 敏捷開發分享會後的一個註腳吧。</p>
<h4>設計演進的基準</h4>
<p>軟體開發與其它的產品開發有一個很大的不同，在於軟體通常很難在一開始就定義出明確的需求規格，取而代之的是軟體的發展方向，是用來解決利害關係人在真實世界所面臨的問題。這也是使用 IPO 傳統目標導向來開發軟體經常遇到的困難，當需求的改變不可能不經常發生的時候，軟體開發在品質文化上就不應該採用「照章行事」的模式，而是應該建立具有回饋機制的開發系統來把穩軟體的開發方向。</p>
<p>可以「把穩方向」的軟體開發系統應該具有什麼樣的回饋機制呢？根據<a href="http://www.anobii.com/books/013ad41f7a862e80dd/">溫伯格在他的軟體管理學的觀點</a>，管理者賴以把穩方向的回饋機制，必須是可以直接及穩定的觀察專案目前的狀況、並且比較專案目標與現況的差異、然後以後續如何減少差異為目標改變或調整計劃、最後再根據計劃採取行動來改善專案狀況。其中有關於直接而穩定的觀察，直接代表肉眼可直接觀察的專案結果，穩定代表不同觀察者每次觀察的結果都相同。</p>
<p>相信從以上的觀點，我們可以清楚看到「把穩方向」的品質文化與敏捷開發調整式規劃的關連。規劃的目的不是為了得到一個巨細彌遺的計劃讓我們照表操課，而是指引一個達到目標的概略方向，然後因應專案實際現狀來調整計劃，來使我們不致迷航。基於這樣的觀念，運用 <a href="http://en.wikipedia.org/wiki/Test-driven_development">TDD（測試驅動開發）</a>剛好可以提供對專案進行直接及穩定的觀察。</p>
<p>TDD 改變我們對解決問題的假設，不假設用什麼方法來解決問題，而是假設問題情境來思考各種可能的方法，並發展出最經濟的解決方案。假設方法如何解決問題並不是不好，只是這樣很容易讓開發者把他所熟悉的方法當成黃金錘，但最後所開發出來的軟體卻不見得符合使用者實際的需要，而且通常要花費很長的時間才會發現以上的落差，因此不會有足夠時間和資源來符合使用者的需求。</p>
<p>如果能儘早驗證開發的成果是否符合實際的需要，開發者就可以在早期得到使用者的回饋，進而調整努力方向來改善開發成果。傳統的開發方法沒有辦法做到早期回饋，是因為使用者要等到軟體開發出來才能接觸到系統，而且通常他們缺乏軟體開發的專業知識，所以在這之前他們是很難給予開發者有效的回饋。TDD 的開發思維則是促使開發者從思考軟體的使用情境出發，不要太早接觸繁複而細節的設計或實作，而是因應實際的需要而定義出界面規格，然後依據這些規格來決定該如何驗證問題能夠被解決。</p>
<p>因此，TDD 可以直接而穩定的在早期觀察開發狀態，提供設計演進的基準。這樣的基準可以讓開發者在開發過程中，直接面對目標而開發系統而不致迷航。也就是因應現實問題情境的需要，開發者未必有足夠的時間與心力來把設計做到盡善盡美，而是嘗試定義出最主要的功能需求，先採用最簡單的方式來滿足它們，然後再視使用者回饋的實際需要增添或修正功能，必要的時候甚至可以進行重構來維持設計簡潔與完整。換言之，TDD 是用來使開發者面對目標，讓開發範圍不要無謂擴張的一項有力工具。</p>
<h4>延緩設計的決策</h4>
<p>然而，當開發者採用了 TDD 的開發模式之後，是否正意味著我們儘快將使用者需要的功能實作出來，並不需要進行太多的設計工作，是否代表設計對使用 <a href="http://en.wikipedia.org/wiki/Extreme_Programming">XP 實務</a>的開發者來說是不重要的呢。但依照同人自身的經驗來看，對於使用 XP 實務的開發者而言，設計並非不重要，而是留下為實際的問題改進設計的彈性。</p>
<p>TDD 並非不做設計，而是把做更周詳的設計的時間延後到可以得到更佳設計決策的時候。或者更根本地來說，TDD 本身就是一種設計手法；而不是因為它以寫測試案例開始，而就把它當做開發的測試過程，<a href="http://www.lifeparty.idv.tw/blog/archives/2669">這樣的誤解反而違反 TDD 的基本精神</a>。</p>
<p>就設計的觀念來看，設計概念的完整性會直接影響設計的良窳。因此開發者應該盡全力來找出解決問題最重要的概念，同時隱藏或略除不必要的實作細節，來使設計更容易了解與實作。這也就是設計關鍵在於抽象化的道理，但問題是在對問題認識未盡全面以及成本或時程的限制，開發者通常沒辦法在第一時間找出解決問題最適當的核心概念。</p>
<p>因此，TDD 在還沒寫實際的程式之前，先撰寫測試案例。其所關注的問題並不是有效率的測試，而是務實的設計。先以測試案例的方法來識別出系統的大致輪廓，目的是以解決問題為前提，把問題的範圍限制在開發者可以全局掌控的情況下發展解決方案。而不是為了解決方案的堅持而使問題發散，最後反而使問題失焦而終致失控局面的發生。</p>
<p>如此，縱使軟體開發的變化是難以預測的，但只要每一次的變化都可以將廣大的可能性，限制在某一部份，那麼開發者就可以在系統的穩定與彈性之間維持良好的動態平衡；既不會讓需求的變化造成設計的崩壞，也不會因為技術的限制而造成設計的僵化。</p>
<p>穩定的設計可以在環境改變的情況下，不致使系統失去控制，彈性則是可以適應需求的變化而改進系統的設計。期待設計在一開始一次到位，這通常是不切實際的期待，還不如面對現實，先用簡單的方式滿足需求，然後隨著對問題的更深入理解，自然而然地演進出可以適應變化的設計。</p>
<p>這樣的觀念是把軟體開發的焦點放在系統邊界，然後隨著環境變化而逐步演進核心的設計，與傳統機械觀點的隱喻所不同的是，軟體開發不是努力去製造一些東西，而是運用生物演化觀點的隱喻：軟體開發是為了改變一些事情而努力。TDD 與 Refactoring 的搭配，正是促成軟體開發演化出複雜適應性以適應變化的實務方法。它們可用來避免軟體設計求道之過，所謂「<a href="http://zh.wikisource.org/wiki/%E6%97%A5%E5%96%BB">道可致而不可求</a>」不去強求而自然得到，才是真正的致呀。</p>
<h4>提早整合的行動</h4>
<p>其實軟體開發專案要把穩方向是很困難的，溫伯格認為主要的原因多半是管理者介入無效的管理作為；沒辦法掌握好「動作要小，行動要快」的原則，結果更增加專案的複雜度與風險，使得問題更加難以處理。</p>
<p>因此，如果使用 TDD 與 Refactoring 這兩項實務，可以讓我們具體地用測試案例來直接而穩定地觀察開發成果，運用簡單的設計來解決問題，又可以在必要的時候施重構來改善設計，以增加適應變化的彈性。那這樣還有沒有可能沒辦法掌握好設計演進的節奏呢？同人認為唯一的可能就是開發者沒有提早整合的行動，例如沒有在早期接受回饋以調整測試案例與重構，使得行動太慢，動作太大。</p>
<p>為什麼開發者會沒有提早整合呢？理論上，當開發者開發的程式完成 TDD 的測試程式之後，照理要進行清理程式碼的動作。比如說進行重構來去消除程式碼的重覆性或使程式碼看起來更簡潔而易懂。這樣可以讓程式碼在每一次的修正之後，都還是最乾淨的情況下，所以是不大可能會增加程式碼複雜度而造成未來難以維護的問題。</p>
<p>然而，在考量專案實際的問題下，開發者不見得每一次都會有足夠的時間來整理他的程式碼，使之形成良好的程式寫作風格與結構。而且管理者也很難知道開發者到有沒有力行這樣的原則，除非能夠對每一支程式來進行 <a href="http://en.wikipedia.org/wiki/Software_peer_review">Peer Review</a>，否則只有「完成功能之後必須清理程式碼」的說法，而沒有為提昇程式碼品質而在每支程式交付時，提供改善回饋的具體措施，對專案其實並沒有太大的助益。</p>
<p>可能有人會認為<a href="http://en.wikipedia.org/wiki/Pair_programming">搭檔編程</a>的實務，就是讓每一次的程式開發都提供回饋。基本上同人並不反對這樣的說法，但要這樣做必須注意開發團隊的文化是否能夠支持這樣的做法。</p>
<p>同人以前曾有專案成員調動到使用搭檔編程的專案團隊，雖然看起來搭檔編程似乎讓她能力有所成長，但我也發現到她也出現適應不良的現象而心生抗拒，而當同人與他當時的主管當時的關心，也很難讓他願意分享自己的心情。由此看來，似乎還是有人習慣自己一個人寫程式，而不喜歡由兩個人共同開發一支程式，而且開發資源的分配者也不見得能夠支持這樣的觀念。</p>
<p>當然，要針對每一支程式做到 Peer Review，在實務上還是有很大的困難的。同人認為問題並不是 review code 要花費多少時間，而是專案需不需要針對程式碼進行儘早的回饋，以在最適當的時機採取最適當的行動？這其實是取決於專案願意花費多大的代價來提昇軟體的品質，不然當品質不合乎需求時，專案所節省下來的預防與檢驗成本將會為造成更大的失敗成本。</p>
<p>要落實提早整合的行動，專案團隊每天早上的 <a href="http://en.wikipedia.org/wiki/Stand-up_meeting">Daily stand-up</a> 會議與採用 <a href="http://en.wikipedia.org/wiki/Daily_build">Daily build</a> 持續整合的機制，是讓開發團隊的反應能力可以「與時俱進」的回饋活動。就像同人在分享會所分享的實務，在 Daily stand-up 會議得到開發實際碰到的問題，其實是幫我們識別關鍵設計概念的機會。由於過去問題尚未出現或對它們還不太明瞭，所以我們不需要或沒辦法進行設計的深入考量。而在 Daily stand-up 會議中，我們可以及時得到用來演進設計的資訊，並討論如何在後續的計劃中調整設計。</p>
<p>至於 Daily build，每天都會整合出可以實際運作的軟體。在 build 軟體的過程中，會自動執行 TDD 的自動化測試，以確保系統功能都是正常的。如果在 build 的過程中發生問題，馬上會立即通知相關的開發者立即解決問題。這樣可以在每天都讓每個開發小組都能密切地保持緊密的溝通與整合，發現問題立即處理而不需要等問題擴大之後才動大刀。</p>
<p>Daily stand-up 會議與 Daily build 的活動，讓每天都持續溝通與持續整合，每次都讓專案都朝向目標前進一小步，更重要的是，讓成員與時俱進地掌握設計演進的節奏！</p>
]]></content:encoded>
			<wfw:commentRss>http://www.lifeparty.idv.tw/blog/archives/2784/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>以整理房間來隱喻軟體開發</title>
		<link>http://www.lifeparty.idv.tw/blog/archives/2764</link>
		<comments>http://www.lifeparty.idv.tw/blog/archives/2764#comments</comments>
		<pubDate>Fri, 08 Jan 2010 11:19:29 +0000</pubDate>
		<dc:creator>jim yeh</dc:creator>
				<category><![CDATA[問題解決]]></category>
		<category><![CDATA[專案管理]]></category>
		<category><![CDATA[思考]]></category>
		<category><![CDATA[生活感觸]]></category>
		<category><![CDATA[開發流程]]></category>

		<guid isPermaLink="false">http://www.lifeparty.idv.tw/blog/?p=2764</guid>
		<description><![CDATA[有沒有比較生活化的例子可以用來隱喻測試驅動開發和重構呢？同人覺得用最近我們搬家整理房間的經驗，正好可以隱喻這些實務的開發方法。]]></description>
			<content:encoded><![CDATA[<p>1/9 同人要<a href="http://cb.esast.com/cb/wiki/9584">在新竹分享敏捷開發方法的經驗與心得</a>。很湊巧地，在這個禮拜我經歷了有關測試驅動開發與重構實務方法的熱烈討論，也先後寫了三篇文章來表達我的觀點。不過，前面寫的那三篇文章比較傾向用原理的角度來看測試驅動開發與重構，對於沒有接觸過測試驅動開發與重構實務的讀者而言，可能比較不容易體會。那麼有沒有比較生活化的例子可以用來隱喻測試驅動開發和重構呢？同人覺得用最近我們搬家整理房間的經驗，正好可以隱喻這些實務的開發方法。</p>
<p>同人最近才剛搬完家，一些還沒整理的東西都封裝在一箱一箱的紙箱中，我們也只能將它們暫時堆放在固定的角落，等到我們有空再慢慢整理。不過，老婆在育嬰假即將結束之前，剛好女兒的三歲生日也快到了，她希望幫女兒辦一個生日派對。邀集常和女兒在一起玩的小朋友以及他們的媽媽來我們家玩。</p>
<p>有了之前<a href="http://www.wretch.cc/blog/meichu1015/22222346">大家搓湯圓</a>的經驗，辦<a href="http://http://www.wretch.cc/blog/meichu1015/22267272">生日派對</a>應該不成問題，只是家裡有很多東西都還沒整理好，客廳也亂糟糟的，要如何邀請朋友來我們家玩呢？</p>
<p>所以要邀請朋友來我們家幫女兒辦生日派對，第一要務就是要把家裡整理乾淨，至少客廳要整理到看起來像個樣子。在沒有足夠時間整理之下，我們應該把客廳已經拆封紙箱內的物品，可以歸位的就將它歸位，還沒辦法歸位的物品則必須找地方將它們暫放，而尚未拆封的紙箱則必須在書房找位置整齊堆放，以免阻礙平常生活所需的動線與空間。</p>
<p>不過，也不能只把沒有拆封的箱子全部「藏起來」，客人來家裡參加生日派對，有關吃的、喝的、玩的相關物品，我們都必須將它們找出來，才能滿足為女兒舉辦生日派對的需要。</p>
<p>所以基於為女兒舉辦生日派對的需要，我們把派對需要用到的東西找出來，把與舉辦派對無關的東西收起來，等有那一天需要用到的時候再拿出來用。屆時，原來在派對需要用到的東西可能又會變得不符合實際需要的東西，我們會將它們收起來或是清理掉。不過，慢慢地，我們會清楚在我們搬過來的這個地方，我們會愈來愈清楚我們真正需要的物品是什麼，然後封箱的物品會愈來愈少，讓生活空間變大，可以容納更多生活對空間的需求。</p>
<p>以軟體開發的觀點來看，測試驅動開發的測試案例就好像整理房間的功能性需求一樣，為了符合舉辦生日派對物品及空間的需求。當決定了這個準則以後，我們就會知道我們實際上需要什麼東西，不需要的東西該放到什麼地方。於是我們可以花最少的努力來達成我們的目標，就好像測試驅動開發用最簡單的實作方式完成功能。至於重構是，當發現需求改變時，我們會再次調整空間的使用並整理需要的物品，以容納實際使用的需要。</p>
<p>其實，如果時間足夠，整理房間最完美的方式是把東西拆封定位，放到該放的位置上，並且清理掉不需要的東西。但現實的問題是，我們沒有足夠的時間，以及我們並不知道那些東西是我們真正需要的。有時候，今天我們認為需要的東西，明天可能根本就用不到，但如果丟掉它們，改天需要用的時候卻會遺憾我們已經丟掉了。</p>
<p>所以，比較敏捷的做法是整理現在需要用到的東西，以回應我們最重要的需求。一開始的需求不複雜，所以不需要花很大的心力來整理，等到對房間空間利用的需要更清楚的時候，再來更進一步的整理房間擺設。</p>
<p>整理房間是如此簡單明瞭地表現敏捷軟體開發的開發邏輯，相信寫到這裡，大家可以更清楚為什麼測試驅動開發並不一定要馬上重構程式碼了吧？尤其是徹底重構，整理房間的例子很清楚呈現：如果可以徹底重構，那我們也可以在第一時間把房間整理好，那也沒有重構這回事了。但從過去搬家整理房間的經驗看到，這根本就做不到呀！</p>
<p><strong>延伸閱讀：</strong>（其它與測試驅動開發與重構的文章）<br />
<a href="http://www.lifeparty.idv.tw/blog/archives/2669">測試驅動開發的精神</a><br />
<a href="http://www.lifeparty.idv.tw/blog/archives/2716">測試驅動開發要徹底重構？</a><br />
<a href="http://www.lifeparty.idv.tw/blog/archives/2737">測試驅動開發的步驟</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.lifeparty.idv.tw/blog/archives/2764/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>測試驅動開發的步驟</title>
		<link>http://www.lifeparty.idv.tw/blog/archives/2737</link>
		<comments>http://www.lifeparty.idv.tw/blog/archives/2737#comments</comments>
		<pubDate>Thu, 07 Jan 2010 06:46:19 +0000</pubDate>
		<dc:creator>jim yeh</dc:creator>
				<category><![CDATA[問題解決]]></category>
		<category><![CDATA[專案管理]]></category>
		<category><![CDATA[思考]]></category>
		<category><![CDATA[溝通]]></category>
		<category><![CDATA[生活感觸]]></category>
		<category><![CDATA[編程技巧]]></category>
		<category><![CDATA[職場]]></category>
		<category><![CDATA[衝突]]></category>
		<category><![CDATA[設計原則]]></category>
		<category><![CDATA[開發流程]]></category>
		<category><![CDATA[閱讀]]></category>

		<guid isPermaLink="false">http://www.lifeparty.idv.tw/blog/?p=2737</guid>
		<description><![CDATA[敏捷開發並不是教條式的照本宣科，開發者要懂得變通最重要的是用心思考，而非把必要的思考都看成精神層面的問題，這並非適用於敏捷開發的心智模式。以下是同人在 Facebook 的 Scrum community in Taiwan 的回應，但文辭有略為做過一番修飾，可以用來澄清我對測試驅動開發步驟的看法。]]></description>
			<content:encoded><![CDATA[<p>昨天寫的〈<a href="http://www.lifeparty.idv.tw/blog/archives/2716">測試驅動開發要徹底重構</a>〉，曾經提到 Steven 說到「如果像閣下文中說：『系統不斷演進及需求不斷改變之下，也可能會使架構或設計愈來愈複雜而變得難以維護』，我則認為是在進行 TDD 時候沒有徹底去重構系統。」，同人認為這段話有根本上的邏輯的繆誤，於是回應：</p>
<blockquote><p>假如上面的話是成立的，那麼代表只要徹底重構系統就不會造成「系統不斷演進及需求不斷改變之下，也可能會使架構或設計愈來愈複雜而變得難以維護」嗎？如果是這樣，那 XP 根本就不需要 Refactoring 這個實務來改善程式的結構，因為你都徹底重構程式，程式結構變差的情況是根本不可能發生。</p>
<p>而且依照本人 20 年來軟體開發的經驗，我還不沒有看到有程式一開始就寫得很好，到後來可以不用改變架構而符合新需求的。倒是隨著對問題的更清楚，或是程式需求的變化，讓程式必須重構的現象時常履見不鮮！</p>
<p>所以如果自以為自己博覽群書，很懂得TDD的實務。也不要忘了用心思考，以免自己對TDD的最佳實務的認知，不小心犯了根本上的邏輯繆誤？</p></blockquote>
<p>結果，後來同人在 <a href="http://www.facebook.com/group.php?gid=179345672472">Facebook 的 Scrum community in Taiwan</a> 看到 Steven 做了以下的回應：</p>
<blockquote><p>先說件簡單易明的事情，其實我很不喜歡閣下把 TDD 放到 『精神』 層次，卻忘記了基本步驟。</p>
<p>之前的討論根本連事實層次都被忽略，根本談不上是什麼多少年的經驗或者如何用心思考，連 TDD 的最基本步驟也忘記，TDD 的基本步，不是閣下做多少年工作就可以把人家的定義去改變的，我也不明白為何有 20 年工作經驗就可以把 『Refactoring』 說成 『並不必然是 TDD 的必要的步驟』，這不是邏輯問題，更不是有過什麼開發經驗然後用心思考就可以改變的事情，這是就算對軟體開發的認知不夠閣下那麼 『全面』 的都能看出的謬誤，如果閣下這樣就認為是因為說不過閣下就建議多看書本，本人深表遺憾。</p>
<p>我是來討論問題的，我沒有興趣去傷害閣下感情，好好閱讀書本，只是反映 TDD 三個步驟是什麼根本不存在爭議，更沒有 『好好閱讀什麼書或文章才能跟我討論』 的意思，我還未自大得要別人看過多少書才可以討論問題，亦正如討論問題我也不用跟別人說我有多少年工作經驗一樣，而且本人是衷心認為多讀書是有益的（不管是閣下還是什麼人），多讀書亦不是為上來辯論的，不過閣下如果感到有所不悅的，我就先行道歉，還是希望冷靜一點討論問題。</p>
<p>而簡單的例子也是思辦的過程的一部份，一方面是簡單易明地討論問題，另一方面是如果連簡單的例子也說不通，又怎麼能去談更複雜的問題呢？</p>
<p>上面提到：』那XP根本就不需要 refactoring 這個實務來改善程式的結構，因為你都徹底重構程式，程式結構變差的情況是根本不可能發生。』</p>
<p>不如冷靜一點再讀讀這句子，』XP 不需要 Refactoring 是因為徹式地進行 Refactoring』，我就看不明白這是什麼邏輯，一邊說不需要，另一邊說徹底地進行。</p>
<p>這裡的問題是軟件是會改變的，可能是新增功能，也可能發現有其他問題，每次帶來的變更其實都需要進行重構的。所以說 XP 不需要 Refactoring 也不正確。</p>
<p>世上的確沒有一寫就好的代碼，而且世界是會變的，Refactoring 就是避免以後的更改越來越困難。把 『徹底地進行重構』 理解成 『XP 不需要重構這實踐』 完全沒有邏輯可言。</p>
<p>我也沒有反對不用改變程式架構就能滿足新需求，亦沒有否定 Refactoring 的重要性，只不過我還是建議新的功能以 TDD 方式進行開發，有測試、有代碼、然後進行重構的。</p>
<p>在足夠測試覆蓋下進行重構是可使系統在不斷演進及需求不斷改變之下，使架構或設計仍然處於可以維護的狀態，相反我指出的是，如果程式架構和設計越來越難維護，是重構的力度不足夠。</p>
<p>前面還提到：』但重構的目的為何？就重構的定義在不改變功能的情況下改善程式結構，以增加程式碼的彈性以利未來增加或改變功能。因此如同那第三步所言，為了去掉重覆性而重構。』</p>
<p>如果重構只是為了去掉重覆性，那 TDD 的第三步不如叫 『Remove Duplication』 好了，無可否認代碼重覆是很常見的問題，但把這裡的重構限制成消除代碼其實會局限系統的將來發展，而且到了 TDD 的第三步，系統是應該有足夠的測試去覆蓋系統，重構的力度沒理由只局限於新增功能和現有程式的重覆。</p>
<p>我就相信閣下是 Refactoring 的專家，也應該會知道一些 Refactoring 的模式是完全相反的，例如 『Pull Up Method』 和 『Push Down Method』，更是需要觀察當時的情況來作決定，而沒有一面倒那個才是好的模式，我實在不明白為何要把 TDD 的 Refactoring 局限到只做 『去掉重覆性』。</p>
<p>這是實務上會發生的事情，消除代碼重覆以外的重構還是會發生的，如果只是單單只是 『消除重覆』，這會是另一個我認為進行重構不夠徹底的事情。</p>
<p>上面已經不單單是用心思考，而是由理論到實踐都可以看得到的事情。</p>
<p>跟討論 Refactoring 和 TDD 觀點以外的聲音，就引用 Chet Henderickson 的說法，全部都是我錯好了，現在可以解決問題嗎？</p></blockquote>
<p>看了 Steven 的回應，同人當下的反應是不想浪費時間與心力與他周旋下去，但後來想到或許是因為 <a href="http://cb.esast.com/cb/wiki/9584">1/9 敏捷開發分享會</a>我要分享實施 XP 的經驗與心得，也許這是一個巧妙的<a href="http://en.wikipedia.org/wiki/Synchronicity">同步事件</a>。我可以趁這個機會，導正對 XP 或是 Agile 的一些錯誤觀念。</p>
<p>例如敏捷開發並不是教條式的照本宣科，開發者要懂得變通最重要的是用心思考，而非把必要的思考都看成精神層面的問題，這並非適用於敏捷開發的心智模式。以下是同人在 Facebook 的 Scrum community in Taiwan 的回應，但一些詞句有略為做過一番修飾，以清楚表達我對測試驅動開發步驟的看法。</p>
<p>呵呵，Steven Mak 的回應很有趣，把別人說成錯的不代表自己就是對的。這跟他先前面對觀點的差異就要人好好的看書的行為是如出一轍，但問題是要別人好好看書也不代表說這句話的人書看得比別人廣泛或是深入。更何況，看很多書是一回事，有沒有讀懂書中作者所傳達的意思又是另外一回事。從 Steven 喜歡用斷章取義的方式解讀我的觀點，以抽取他想要的意義來看，恐怕他看書的目的只是揀選他要的部分，沒有弄懂作者想要傳達的意思的可能性可能居多吧！</p>
<p>對了，我忘了 Steven 的中文可能不夠好，沒辦法弄懂我所表達的意思。如果是這樣，他其實大可以告訴我，我不會叫他去好好地學中文，或是嘲笑他那麼簡單的句子都看不懂，而是想辦法要怎麼表達才會讓他了解我的想法。省去他猜錯我的意思，而顯露出他並沒用心體會或是根本不想思考別人的觀點的窘態。</p>
<p>當然，以上的假設可能是不成立的，他的中文其實很好。但如果是這樣，他的邏輯思維能力真的要加強，因為思考為什麼是最基本的能力，不是什麼經神層次。</p>
<p>記得去年同人應邀到中山大學演講，有機會與鄭炳強教授在餐敍之中交流軟體開發的觀念。他特別強調軟體工程最重要的不是 know how，而是 know why。因為遇到不同需要而要採用適當的方法，唯有具備 know why 的能力才能做到。以 Steven 那麼重視看書來增加知識的態度來看，他應該也很重視軟體工程的學界權威的看法吧？可是如果鄭教授看到 Steven 把 know why 的思維定位成精神層次，我想也很可能會令他很搖頭吧！</p>
<p>回歸正題，Robert C. Martin 在《<a href="http://www.books.com.tw/exep/prod/booksfile.php?item=0010293039">敏捷軟體開發</a>》這本書（林昆穎，吳京子，2005）中提到測試驅動開發法，他說：</p>
<blockquote><p>所有的產出的程式碼都是為了讓失敗的單元測試通過而編寫的。首先，我們先寫一個單元測試案例，由於它所要測試的功能並不存在，所以它不會通過，然後我們再編寫程式碼，使得測試案例通過。</p>
<p>編寫測試案例與程式碼之間的反覆來回非常快速。只花費一會兒的功夫。測試案例與程式碼一起演進，而測試案例會比程式碼更早一點。</p>
<p>結果，一組非常完整的測試案例隨著程式碼一起增長，這些測試可讓程式員檢驗程式是否正常運作。如果有一組小搭檔做了些小修改，他們可執行這些測試案例，以確保修改並未對程式碼造成任何破壞。這會極有利於進行重整。</p></blockquote>
<p>咦，堂堂一位敏捷開發大師級人物，怎麼他也沒提到 TDD 要重構，只說可以有助於未來的重構，難道他也擅改了 TDD 的步驟了嗎？讓我們看另外一本書。點空間的朱子傑和陳盈學翻譯的《<a href="http://www.books.com.tw/exep/prod/booksfile.php?item=0010326978&amp;">The object primer 3/e 中文版－靈活模型驅動開發與UML2</a>》中有更具體說明TDD的步驟，而且還附了流程圖（有興趣自己買書來看）。</p>
<blockquote>
<ol>
<li>快速地加入一段測試，基本上只要能夠測試一段程式碼即可，此時你的測試結果會是失敗的。</li>
<li>執行你的測試，一般是全部的測試套件（test suite）。有時候為了速度上的考量，你可能只決定執行這一部分的測試。目的是確認新的測試結果確實是失敗的。</li>
<li>更新你的功能程式碼，使得測試可以通過。</li>
<li>再次執行你的程式。</li>
<li>如果測試還是失敗，再執行步驟3。</li>
<li>如果通過測試，便重新開始（此時若有重複的程式碼，你便需要重構你的設計）</li>
</ol>
</blockquote>
<p>上面的步驟有提到重構，但也提到重構的前提是如果有重覆的程式碼才要重構。那如果沒有重覆程式碼，那還需要重構嗎？還是如 Steven 所說，去除重覆的程式碼的重構還是不夠徹底的重構？</p>
<p>答案很顯而易見，TDD的步驟本來就沒有規定一定要重構。事實上，重構對有經驗的開發者就是像吃飯或喝水般如此直覺，在開發程式的過程中，諸如像改變函數或變數的名字讓它們變得有意義或容易了解，一段重覆被呼叫的程式碼把它們提取成函數或類別。他們經常無意識地去重構，但有意識地面對他們開發過程所遇到的問題，諸如程式碼的重覆或不易了解等問題，而不是漫無目的的為重構而重構。</p>
<p>是以在開發功能的程式碼，因為編程手法的熟練，重覆碼根本就不存在，難道還需要一個重構的 process 嗎？由此可知，Steven 的徹底重構之說，其實就是邏輯上所謂的以偏概全，用部分的事實擴大到全面性的觀點。但真相是 TDD 未必要進行重構，除非你需要它。所以這當然是邏輯的問題，其實從我指出他邏輯的矛盾之後，他的反應更顯出欠缺邏輯的思辨能力。</p>
<blockquote><p>不如冷靜一點再讀讀這句子，』XP 不需要 Refactoring 是因為徹式地進行 Refactoring』，我就看不明白這是什麼邏輯，一邊說不需要，另一邊說徹底地進行。</p></blockquote>
<p>Steven 上面的質疑乍看來似乎有道理，但仔細看看，原來他把時間的因素拿掉了，有<a href="http://en.wikipedia.org/wiki/Straw_Man">偷換觀念</a>的嫌疑。我原先的說法是說因為先前徹底地重構，所以未來就不需要重構了，被他偷換成一邊說不需要，另一邊說徹底進行。</p>
<p>照他原來的邏輯，如果先前徹底重構的程式碼到後來還需要再次重構，那原來的重構還能叫徹底重構嗎？我們常看到有些人會在問題發生的時候，會說因為別人寫的程式碼出問題，是因為沒有徹底的重構，但在明眼人的眼中，這種說法可以用台語說「出一支嘴」！要嘛有徹底重構先見之明的人，你就再一開始就告訴大家什麼叫做徹底的重構，保證以後不會出問題，省得以後出了問題再說重構不夠徹底，後見之明說重構不夠徹底，這種說法誰都會說但卻根本做不到！</p>
<p>更何況重構的方法有好幾種，決定該怎麼重構的時間還沒到，如何徹底重構？或許有人會想依據未來的預測來徹底重構，但這樣想的人大概忘了敏捷方法是不進行預測的，只有因應現實而改變。</p>
<p>蘇格拉底說：「事情的好壞不重要，重要的是你的想法和看法。」本來針對事情來討論是一件好事，目的是為了思考彼此之間的差異點而獲得成長。無奈 Steven 一開始為了捍衛他的觀點，就針對和他觀點不同觀點的人，拼命強調同人說 TDD 不必然需要 Refactoring 的觀念是錯的，叫我要多看書暗示我不夠用功，其實這樣的行為模式突顯他面對意見紛歧沒有反省思辨的能力。</p>
<p>至於同人提出邏輯思辨的想法就叫做精神層面的說法，同人只能說這其實表現出 Steven 自以為是的「自我感覺良好」，運用修辭技巧閃來閃去還是難逃嚴謹的邏輯批判。他要表演這些同人其實沒什麼意見，他可以繼續活在他的象牙塔之中。但對於同人而言，我的收穫只是多看到一個負面教材，這對我 1/9 敏捷開發分享會的聽眾和閱讀我文章的讀者來說可是一大貢獻呀。Steven 說他不小心傷害我的感情；呵呵，少來了，不禮貌與不尊重的行為代表他不夠理性，只是顯出他的無理而已。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.lifeparty.idv.tw/blog/archives/2737/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>測試驅動開發要徹底重構？</title>
		<link>http://www.lifeparty.idv.tw/blog/archives/2716</link>
		<comments>http://www.lifeparty.idv.tw/blog/archives/2716#comments</comments>
		<pubDate>Wed, 06 Jan 2010 05:29:16 +0000</pubDate>
		<dc:creator>jim yeh</dc:creator>
				<category><![CDATA[問題解決]]></category>
		<category><![CDATA[專案管理]]></category>
		<category><![CDATA[思考]]></category>
		<category><![CDATA[溝通]]></category>
		<category><![CDATA[生活感觸]]></category>
		<category><![CDATA[編程技巧]]></category>
		<category><![CDATA[開發流程]]></category>

		<guid isPermaLink="false">http://www.lifeparty.idv.tw/blog/?p=2716</guid>
		<description><![CDATA[對 David Ko 提出 Kent 認為 Red/green/refactor 是 TDD 的三字箴言的說法，同人倒是覺得有探討的必要。以下分享我在 Facebook 回應 David Ko 的觀點，這些觀點應該可以解釋為什麼測試開發不需要徹底重構；其實重構並不是問題，而是到底什麼叫做徹底？而且如果 TDD 可以徹底重構，那麼一開始就可以讓設計一次到位，那寫好的測試程式以後也用不著了，不正是多此一舉？]]></description>
			<content:encoded><![CDATA[<p>同人在〈<a href="http://www.lifeparty.idv.tw/blog/archives/2669">測試驅動開發的精神</a>〉中，提到「Refactoring 並不必然是 TDD 的必要的步驟」的觀點。<a href="http://www.wretch.cc/blog/kojenchieh">David Ko</a> 回應他猜測 Steven 是根據 Kent 在《Test-driven Development》一書的前言中提到的說法，來認為測試和 Refactoring 也是TDD很重要的一環。他說：</p>
<blockquote>
<blockquote><p>&#8230;here&#8217;s what we do: we drive development with automated test, a style of development called Test-Driven Development(TDD). In Test Driven Development:<br />
* Write new code only if an automated test has failed<br />
* Eliminate duplication<br />
&#8230;&#8230;</p>
<p>The two rules imply an oder to the tasks of programming.<br />
1. Red &#8211; Write a little test that doesn&#8217;t work &#8230;<br />
2. Green &#8211; Make the test work quickly&#8230;<br />
3. Refactor &#8211; Eliminate all of the duplication&#8230;.</p>
<p>Red/green/refactor &#8211; the TDD mantra</p></blockquote>
<p>在這段敘述中，Kent 認為 Red/green/refactor 是 TDD 的三字箴言，是 TDD 這個 practice 重要的工作。</p>
<p>所以 Steven 才會強調在做 TDD 時，』測試是第一步』，』Refactoring 本身是 TDD 三步之中其中必要的一步』</p></blockquote>
<p>假如正如 David Ko 所說，測試與 Refactoring 是 TDD 很重要的一環，而來強調 TDD 不應該忽略 Refactoring 的工作，這樣的觀點我可以接受，但同人隨後又看到 Steven 的說法，覺得他自以為是的論點讓我很遺憾。</p>
<blockquote><p>本人從來沒有指出 TDD 是測試 Practice，亦沒有認為測試是 TDD 的目的。</p>
<p>上面的 『功能』 指代碼的內容規格，寫測試時候就是思考新功能的行為，用測試用例的方式去表達出來。就正如：</p>
<p>AssertEquals(3, add(1,2))</p>
<p>那麼簡單，已經是對 add() 作出規範，投入兩個參數，輸出一個數字作結果，並規範了新 fn 的名字是 『add』。寫這句 Assert 時候就應該思考到 add 有兩個參數，投入 1 和 2 就會得 3 這個數字。</p>
<p>重構是 TDD 其中一步，不存在混淆點，再者，沒有測試覆蓋下的根本不能說成重構；另一方面，先寫代碼後補測試是很痛苦的事情。</p>
<p>如果像閣下文中說：「系統不斷演進及需求不斷改變之下，也可能會使架構或設計愈來愈複雜而變得難以維護」，我則認為是在進行 TDD 時候沒有徹底去重構系統。</p>
<p>我建議閣下好好閱讀 TDD 的書本，Refactoring 是 TDD 必需的步驟，由 Kent Beck 到 Robert Martin 以至其他不太有名的 TDD 書本作者，都指出 TDD 的第三步是重構，而不是 『Refactoring 並不必然是 TDD 的必要的步驟。』</p></blockquote>
<p>看到「我建議閣下好好閱讀 TDD 的書本」這種不尊重不同觀點的說法，同人根本就不想浪費時間來跟對方爭論。即使耐著性子仔細看他的論點，像那些「沒有測試覆蓋下的根本不能說成重構；另一方面，先寫代碼後補測試是很痛苦的事情。」、「如果像閣下文中說：『系統不斷演進及需求不斷改變之下，也可能會使架構或設計愈來愈複雜而變得難以維護』，我則認為是在進行 TDD 時候沒有徹底去重構系統。」等說法，更讓同人懷疑他對 TDD 的認知，其實是過於偏向理念化而不夠切合實際。</p>
<p>不過，對 David Ko 提出 Kent 認為 Red/green/refactor 是 TDD 的三字箴言的說法，同人倒是覺得有探討的必要。以下分享我在 <a href="http://www.facebook.com/group.php?gid=179345672472">Facebook 的 Scrum Community in Taiwan</a> 回應 David Ko 的觀點，這些觀點應該可以解釋為什麼測試開發不需要徹底重構；其實重構並不是問題，而是到底什麼叫做徹底？而且如果 TDD 可以徹底重構，那麼代表一開始就可以讓設計一次到位，那寫好的測試程式以後也用不著了，不正是多此一舉？<br />
&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;&#8212;<br />
柯兄，</p>
<p>我想在點空間，您應該曾看到我與某些只談理論忽略實際的顧問級大師論戰吧？即使當時我和他私下交情還不錯，但看到他用簡單的例子把軟體開發化約成不需要研究 how，或系統分析不需要懂領域知識的觀念，就不得不告訴他，他對軟體開發的認知是不夠全面的。這次對 TDD 實務的分歧點，我想也是差不多的情況。</p>
<p>只不過我不喜歡』我建議閣下好好閱讀 TDD 的書本』這樣的說法，在實務上我看過太多空談理論而不懂思辨問題的人，這種自以為是的論調讓我不想浪費時間回應他，但以下針對柯兄所提來說說我的看法。</p>
<p>單就字面來看，測試是第一步，重構是最後一步，看起來是沒錯，但問題是為什麼呢？測試先行我想應無疑義，我文章也談了很多，在此不再多說。但重構的目的為何？就重構的定義在不改變功能的情況下改善程式結構，以增加程式碼的彈性以利未來增加或改變功能。因此如同那第三步所言，為了去掉重覆性而重構。</p>
<p>但為什麼需要去掉重覆性呢？答案不外乎兩種，一種是日後重覆性造成程式維護的困難與增加錯誤機率，另一種是程式員擔心發生以上的現象或是因為個人對程式設計的信仰所致。</p>
<p>第一種狀況的重構是合理的，因為開發者是針對現實來解決問題；第二種狀況的重構很常見卻不見得是應該被鼔勵的做法，因為開發者是針對理想或信仰來行動，卻不見得符合實際的需要。但這兩種狀況都可以用重覆程式碼的壞味道的概念來合理化重構的行動，所以重構是正確的行動嗎？答案是視情況而定。</p>
<p>一些 TDD 的書籍或教本所舉得例子都很簡單，使得每一步驟都顯得理所當然。但真實的專案真的是這樣嗎？但個人當初在導入 XP 實務的時候，所遇到的困難，都沒辦法從那些書中得到答案，而是要親身體驗；書中教你的 what 其實需要你實施後的 how 及 why，才能更為全面把那些實務做到好。</p>
<p>最後，提一提測試涵蓋面的問題。在我還沒實施 TDD 的時候，當時我很肯定 TDD 的方法，但我想到的問題是測試程式要寫到什麼程度。但等到我開始實施 TDD 的時候，才發現測試程式的涵蓋面根本就不是問題，而是你需要 test driven 幫你做什麼。如果問題具體而明確，那那會有涵蓋面或不涵蓋面的問題？這時，才了解 Peter Ho 及盈學兄當年在點空間說過 TDD 不是測試而是設計的手法。還有那三句擺在我心中的經典名言：</p>
<blockquote><p>Without refactoring, there is no room for plugging in new functionality to adapt the coming force.</p>
<p>Without testing, we couldn’t know where the edge is.</p>
<p>Without integration, the spark of life will die out.</p></blockquote>
<p>一點淺見，供參考，但我絕不會叫別人好好閱讀什麼書或文章才能跟我討論&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.lifeparty.idv.tw/blog/archives/2716/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>測試驅動開發的精神</title>
		<link>http://www.lifeparty.idv.tw/blog/archives/2669</link>
		<comments>http://www.lifeparty.idv.tw/blog/archives/2669#comments</comments>
		<pubDate>Tue, 05 Jan 2010 10:46:00 +0000</pubDate>
		<dc:creator>jim yeh</dc:creator>
				<category><![CDATA[問題解決]]></category>
		<category><![CDATA[專案監控]]></category>
		<category><![CDATA[思考]]></category>
		<category><![CDATA[編程技巧]]></category>
		<category><![CDATA[職場]]></category>
		<category><![CDATA[設計原則]]></category>
		<category><![CDATA[開發流程]]></category>

		<guid isPermaLink="false">http://www.lifeparty.idv.tw/blog/?p=2669</guid>
		<description><![CDATA[測試驅動開發的精神，不應該用一般機械論的觀點來進行工作或任務的化約，而是基於複雜理論的重要觀念；維持穩定與變化的動態平衡，不在於掌握系統核心而在於邊緣，讓變動限定在人們可以掌握的範圍內，這或許才是測試驅動開發最關鍵的精神吧！]]></description>
			<content:encoded><![CDATA[<p>在 Facebook 的 <a href="http://www.facebook.com/group.php?gid=179345672472&amp;ref=share">Scrum Community in Taiwan </a>看到 <a href="http://www.wretch.cc/blog/kojenchieh">David Ko</a> 提到：</p>
<blockquote><p>聽到有人說 TDD 是個測試的 practice，跟 RD 不是那麼有關，我想這是誤會了。TDD是一種設計的活動，它並不是單純在做 verification，它是一種 spec 確認的活動。</p></blockquote>
<p>David Ko 還分享了一篇<a href="http://aydsoftware.blogspot.com/2009/03/tdd-synergy.html">探討 TDD 的文章</a>供大家參考。這讓同人想到我之前曾經<a href="http://www.lifeparty.idv.tw/blog/archives/266">對 InfoQ 的一篇有關軟體開發信仰問題的文章做過的評論</a>。</p>
<p>在那篇文章的評論當中，同人認為作者不應該拿 <a href="http://en.wikipedia.org/wiki/Test-driven_development">TDD</a> 與<a href="http://en.wikipedia.org/wiki/Acceptance_test">驗收測試</a>相提並論，因為 TDD 並不是測試的 practice，而是設計的活動。如果這兩種實務可以相提並論，似乎就像是爭論有了良好品質的設計就可以省略測試，或是增加測試的效率就可以取代設計的重要性。同人當時在我的評論中提到：</p>
<blockquote><p>測試的涵蓋面要夠廣，軟體開發所需的開發成本與時間就要增加，所以與其靠檢驗來控制品質，還不如把設計做好。所以，品質是設計出來的，而非檢驗出來的。</p>
<p>不過，雖然品質並不是檢驗出來的，但軟體要具備足以信賴的品質，卻不能省略測試。舉個例子來說，不管開發者如何確保他的開發所產出的品質，驗收測試是絕對不可能省略的，否則客戶是很難信任軟體是合乎他們需求的。良好的軟體設計可以減輕測試的負擔與壓力，但它絕對無法否定軟體測試的價值。</p></blockquote>
<p>因此，同人認為不該拿 TDD 來與各種形式的測試來做比較，但在 David Ko 提出這個主題後面的討論，我卻看到一個我不太認同的說法。<a href="http://www.facebook.com/profile.php?id=668168338">Steven Mak</a> 提到：</p>
<blockquote><p>世上總有些傻人愛做傻事，阻止不了他們的 <img src='http://www.lifeparty.idv.tw/blog/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />  不單是由測試去訂定功能規格，更不要忘了 Refactoring!</p></blockquote>
<p>同人以為由測試去訂定功能這個觀點不符合 TDD 的精神，因為正如同我之前一直強調的，TDD 並不是測試。至於 <a href="http://en.wikipedia.org/wiki/Refactoring">Refactoring</a> 是不改變功能的情況下使程式的結構可以更有彈性或是更簡單，它也無法用來訂定功能規格。但 Steven Mak 卻說：</p>
<blockquote><p>TDD 就是由測試去驅動開發功能，寫測試就是第一步，到底有什麼不符合 『TDD 精神』？還有，Refactoring 本身是 TDD 三步之中其中必要的一步，沒有訂定功能用的。但我相信把 TDD 以為是測試的人是忘掉了 Refactoring 這一步。</p></blockquote>
<p>看來 Steven Mak 以測試驅動開發的第一步就是寫測試程式為理由，以為測試並沒有不符合 TDD 的精神，只是認為人們忘卻去做 refactoring，才會以為 TDD 只是用測試來訂定功能規格。同人認為這樣的觀點混淆了測試驅動開發的目的不在測試驅動本身，而是主導開發過程中可以具體落實簡單設計。也就是說測試並非目的，而只是手段而已！</p>
<p>事實上功能不是由測試來訂定的，而是由使用者實際需求來決定。測試程式只是讓開發者找到更具體的方法，來具體驗證使用者的實際需求，這也就是 David Ko 所說確認規格的活動。因此，測試驅動開發的目的是讓我們開發的程式碼能夠符合實際的需要，用可驗證的程式來確定我們開發的程式是符合實際需要的，以避免 <a href="http://en.wikipedia.org/wiki/Overengineering">over-engineering</a> 或是 under-engineering，所以其目的是為了讓我們發現更符合實際需要的設計。</p>
<p>運用測試驅動開發，可以讓開發者的設計儘量簡單，因為開發的目標很具體地呈現在眼前。開發者已經事先對需求的確認花過一番心思考量，把現階段已經確認而且最重要的需求寫成測試程式，一旦開發的程式符合測試程式的要求，那就代表程式已然滿足實際的使用者需求，而使開發者不致浪費心力在不重要或是目前無法確定的需求上。</p>
<p>因此測試驅動開發可以讓開發者立即用最<a href="http://www.lifeparty.idv.tw/blog/archives/888">簡明而單純</a>的設計完成開發工作，好處是讓開發者知道自己在做什麼，而非浪費時間在做沒有意義或無關緊要的事。但任何一開始簡明而單純的設計，在系統不斷演進及需求不斷改變之下，也可能會使架構或設計愈來愈複雜而變得難以維護。這時候單靠 TDD 的簡明與單純也可能沒辦法容納這種複雜性，而是要運用 Refactoring 來增加設計的彈性；也就是在不改變系統功能的情況下，改善系統設計的結構，使它可以因應未來增加需要的功能。</p>
<p>因此 Refactoring 與 TDD 是兩個相互獨立的 practice，雖然 TDD 可以讓重構更為可行。但兩者不必要要混為一談，也就是 Refactoring 並不必然是 TDD 的必要的步驟。</p>
<p>在大部分的情況下，用 TDD 落實簡單的設計就已經夠用了，除非我們真的發現到我們需要更大的彈性才需要進行 Refactoring 的行動。敏捷開發不是強調方法論的教條，因此不管是 TDD 或是 Refactoring，都是為了真實世界的問題的需要，而非方法論的信仰，這也是同人之前評論 InfoQ 那篇文章，最主要想要批判的重點。</p>
<p>因此，雖然 TDD 這三個字母或是測試驅動開發這六個字當中，測試驅動就佔掉了這個名詞的 2/3，但切莫以為 TDD 就是以測試去訂定功能規格，這只是對 TDD 流於表相的認識，而看不清楚這個名詞最重要的兩個字「開發」的真相。</p>
<p>測試驅動開發的精神，不應該用一般機械論的觀點來進行工作或任務的化約，而是基於複雜理論的重要觀念；維持穩定與變化的動態平衡，不在於掌握系統核心而在於邊緣，讓變動限定在人們可以掌握的範圍內，這或許才是測試驅動開發最關鍵的精神吧！</p>
]]></content:encoded>
			<wfw:commentRss>http://www.lifeparty.idv.tw/blog/archives/2669/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>敏捷開發實戰經驗分享會後感</title>
		<link>http://www.lifeparty.idv.tw/blog/archives/2114</link>
		<comments>http://www.lifeparty.idv.tw/blog/archives/2114#comments</comments>
		<pubDate>Sun, 08 Nov 2009 00:02:52 +0000</pubDate>
		<dc:creator>jim yeh</dc:creator>
				<category><![CDATA[問題解決]]></category>
		<category><![CDATA[學習]]></category>
		<category><![CDATA[專案團隊]]></category>
		<category><![CDATA[專案規劃]]></category>
		<category><![CDATA[專案風險]]></category>
		<category><![CDATA[溝通]]></category>
		<category><![CDATA[生活感觸]]></category>
		<category><![CDATA[職場]]></category>
		<category><![CDATA[開發流程]]></category>

		<guid isPermaLink="false">http://www.lifeparty.idv.tw/blog/?p=2114</guid>
		<description><![CDATA[分享會在台北市電腦公會舉行，看到現場互動氣氛的熱絡，以及會後學員們給予不少正面的評價，感覺大家收穫都不少。其實包括我自己在分享會結束之後也產生了一些想法，倒是想藉由此文章分享我的分享會後心得。]]></description>
			<content:encoded><![CDATA[<p>上個月 24 日應 <a href="http://scmteamwork.blogspot.com/2009/10/agile-development.html" target="_blank">MaoYang 兄之邀</a>，分享我在敏捷開發的實戰經驗。這場分享會還找來了 <a href="http://www.wretch.cc/blog/kojenchieh" target="_blank">David Ko</a> 兄分享他在公司導入 <a href="http://en.wikipedia.org/wiki/Scrum_(development)" target="_blank">scrum</a> 開發管理方法的經驗，同人則負責分享我之前在專案中推行 <a href="http://en.wikipedia.org/wiki/Extreme_Programming" target="_blank">extreme programming</a> 工程實務的經驗。分享會在台北市電腦公會舉行，看到現場互動氣氛的熱絡，以及會後學員們給予不少正面的評價，感覺大家收穫都不少。其實包括我自己在分享會結束之後也產生了一些想法，倒是想藉由此文章分享我的分享會後心得。</p>
<p>同人很喜歡 David Ko 兄提到愛因斯坦為 <a href="http://www.brainyquote.com/quotes/quotes/a/alberteins133991.html" target="_blank">Insanity</a> 這個字所下的定義：「Doing the same thing over and over again and expecting different results」我認為這個定義很貼切地描寫許多人在軟體開發過程所展現的心態；過去做過行不通的做法，卻認為在今天可以行得通，結果讓人一直瘋狂或是不斷地精神錯亂。</p>
<p>但為什麼人們要盲目地做些行不通的事呢？其實以同人這麼多年軟體開發的經驗來看，他們不見得是意識不到這些做法行不通，而是可能因為害怕與恐懼，讓他們不敢嘗試新的方法來解決問題。</p>
<p>縱使無法解決問題的挫折是令人沮喪的，但如果要放棄過去習慣的做法來開發系統，他們更會茫然不知所措，擔心因此對現況失去掌控能力。於是明知過去的做法有問題，但更害怕失去它就會一無所有，於是只好將它緊緊地捉在手上，並期待這一次會有奇蹟出現，改寫過去失敗的命運。</p>
<p>不過如果人們理性一點，都會意識到要改變命運不應該期待奇蹟，而是需要「勇氣」讓我們改變心智模式，然後採用有效的方法來解決問題。在這方面，同人覺得比較幸運的是我常碰到好主管，能夠支持我想要把事情做好的想法。</p>
<p>記得過去的主管 Y.L. Liu 曾經告訴過我的話：「如果過去這樣做行不通，那今天就應該嘗試不一樣的做法」即使改變必然會遭遇到阻礙，然而當我們勇於面對阻礙而因應問題時，才能促使我們打破過去的習慣來進行有紀律地思考與行動，進而更有效地解決問題。</p>
<p>紀律，也就是 discipline 這個字。它的意義並不是做我們過去熟悉的事，而是熟悉了解問題是什麼，並加以解決問題的過程。如同我<a href="http://www.lifeparty.idv.tw/blog/archives/175" target="_blank">過去的文章</a>所強調的，軟體開發不只是工程，或是工藝，而是解決問題的過程。敏捷開發其實並非依賴制式的軟體開發流程或方法，而是基於重要價值觀與原則發展出來的實務，而最重要的價值觀就是為了思考如何「解決問題」，至於使用流程方法都只是手段而不是目的。</p>
<p>然而，根據同人的經驗，想要軟體開發過程運用以上的觀念，我認為最困難的是對專案目標的混淆。在這次的分享會之中，同人也發現有些 PMP 背景的朋友，他們很關心如何準確預估專案的範圍、時程與資源，因此希望了解敏捷開發如何來解決這樣的問題。但其實敏捷開發方法根本不需要做精確的預估，因為改變是無法預估的。所以它強調的是反應變化的能力，而不去為預期或抑制變化做太多的努力。</p>
<p>或許有人會把精確的專案預估，看成是專案成功的重要目標之一。但以同人這位 PMP 對專案管理的認知來看，精確預估並非專案的目標，而是達成降低專案風險目標的手段之一。或許用這種手段來蓋房子，或生產看得到、摸得著，可以明確度量的產品可以做得很好，但用它來開發軟體卻不見得可以行得通。</p>
<p>我們應該改變對軟體開發專案的傳統思維；假如軟體開發的本質，就是難以精確預估，那麼我們就不該將力氣浪費在預測上，而是應該用來進行對專案更有效益的事情上。但這不代表敏捷開發方式不做規劃，而是規劃的重點不在精確地預測未來，而是用來定義專案的基準線；利用每一次的反覆過程的回饋，用來改善或調整後續的計劃，以增進我們回應變化的能力。</p>
<p>因此，使用敏捷開發我們不需要具細彌遺地預測未來的改變，只需集中心力面對今天所發生的問題。換句話說，開發者也不需要一份不會變更的功能需求清單，而是了解專案目前所要解決的實際問題，進而<strong>運用</strong>(adopt)思考及創意、<strong>調適</strong>(adapt)方法然後再<strong>熟練</strong>(adept)所需要的技能來解決問題。</p>
<p>那麼，以上敏捷開發的思維是否打破專案管理的基本觀念？同人從不認為如此。依照 <a href="http://en.wikipedia.org/wiki/A_Guide_to_the_Project_Management_Body_of_Knowledge">PMBOK</a> 的專案管理知識領域與流程本來就支援管理改變的做法，問題只在於管理者是否掌握住變更管理的重要原則並熟練它們：</p>
<blockquote><p>首先、必須確認改變對專案有正面效益；</p>
<p>其次、必須確認影響變更的因素已發生；</p>
<p>最後、最重要的是管理變更。（PMI，2000）</p></blockquote>
<p>我們看到這些原則不但並不違背敏捷開發的思維，同時兩者是相通並且相輔相成的。的確，對於軟體專案而言，改變意味著增加軟體開發的風險，但害怕專案風險的心態，也代表你的團隊面對風險只能迴避它們以確保安全，但你所獲取的利潤也相對變得較低（<a title="More about Peopleware" href="http://www.anobii.com/books/Peopleware/9789867889645/01dc7d45cbe3e8fadc/" target="_blank">DeMarco &amp; Lister，2007</a>）。於是你必須辛苦地在市場上試圖降低軟體價格來與對手競爭，除非你能夠勇敢地面對挑戰，選擇快速回應變化才會創造機會，為專案產生更大的正面效益。</p>
<p>那麼對於環境或需求的變化，軟體開發團隊要如何快速回應呢？依據同人的經驗，大規模的事先設計（BDUF，Big Design Up-Front）通常無法立即迅速地反應變化，而且常常會造成過度的工程化（over engineering）的問題。而依賴開發組織導入某些流程、工具或方法論其實也並非成功的關鍵。我認為只有增進團隊溝通與合作，讓團隊能為面對問題而共同努力，才能夠快速地回應變化。</p>
<p>或許有人會認為詳盡的文件可以增進溝通，但實際上它的成效不高而且常常要人們花費很多的心力，除非你發現它真的有幫助，否則你應該只需要<a href="http://www.lifeparty.idv.tw/blog/archives/1113" target="_blank">寫必要的文件</a>。雖然工具或方法論可以增進開發的效率，但它們也會讓工作變得艱難。因為無法被替除的工作，它們是更具備知識密集的特性，需要更有才幹的人來完成任務（DeMarco &amp; Lister，2007）。所以在導入任何工具或方法論之前，你應該提醒自己，<strong>敏捷開發是以人為基礎，面對的是現實而非理想</strong>。</p>
<p>誠如 David Ko 在分享會中說得好，任何方法的導入如果最後變成政令宣導時，那就非常不妙了。同人則以為快速回應的關鍵不在於遵循方法論的做法，而在於面對專案的現實問題，讓團隊共同因應問題而改變。David Ko 提出了很多他的專案所導入的做法，同時分享他感受到團隊成員自動自發的喜悅，也讓同人希望能夠見賢思齊。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.lifeparty.idv.tw/blog/archives/2114/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>如何在系統失敗前發現錯誤</title>
		<link>http://www.lifeparty.idv.tw/blog/archives/2063</link>
		<comments>http://www.lifeparty.idv.tw/blog/archives/2063#comments</comments>
		<pubDate>Mon, 26 Oct 2009 05:40:37 +0000</pubDate>
		<dc:creator>jim yeh</dc:creator>
				<category><![CDATA[CNet/ZDNet]]></category>
		<category><![CDATA[利害關係人]]></category>
		<category><![CDATA[問題解決]]></category>
		<category><![CDATA[寫作]]></category>
		<category><![CDATA[專案團隊]]></category>
		<category><![CDATA[專案風險]]></category>
		<category><![CDATA[新聞]]></category>
		<category><![CDATA[組織]]></category>
		<category><![CDATA[職場]]></category>
		<category><![CDATA[軟體審查]]></category>
		<category><![CDATA[開發流程]]></category>
		<category><![CDATA[閱讀]]></category>

		<guid isPermaLink="false">http://www.lifeparty.idv.tw/blog/?p=2063</guid>
		<description><![CDATA[這篇文章是投稿 ZDNet Taiwan 的文章原稿，由 ZDNet Taiwan 以〈如何在系統異常前發現錯誤？〉、〈如何在系統異常前發現錯誤？（下）〉兩篇文章刊登。文章原稿未經 ZDNet Taiwan 編輯，內容可能與 ZDNet Taiwan 約略有所不同。
前一陣子有兩個與資訊系統失常有關，而且眾所矚目的新聞事件，也就是戴爾電腦網路購物系統與台北捷運內湖線的系統異常。相信很多人都認為這兩個系統會發生系統異常相當離譜，在系統上線之後才發現系統無法正常運作，造成系統使用者的困擾，同時也會讓人對系統可靠度與穩定度失去信心，而增加系統的失敗成本。
雖然平心而論，想要事前預料系統可能發生的問題，並加以預防或因應其實並不容易，因為開發系統，尤其是軟體開發常會碰到事先難以預料的問題。但如果能在錯誤造成危害之前，就能夠發現問題並採取適當的行動來解決它，應該就能減少系統的失敗成本。因此，看到戴爾與台北捷運內湖線的重大系統異常，讓筆者想探討如何在系統失敗前發現錯誤，以避免系統失敗的巨大損失。
設計不夠好？
戴爾是世界知名的電腦直銷公司，擁有 13 年的網路直銷經驗。對於這種有豐富網路直銷經驗的公司來說，系統連續發生產品標價錯誤的問題，實在是一件令人感到不可思議的事情。在戴爾發生第二次標價錯誤事件之後，筆者聽到有一位工程師出身的朋友指出，戴爾筆記型電腦的標價錯誤，是因為他們的系統設計不良。他依據新聞的報導，對比自己的網站開發經驗，認為可以確定這絕對是設計的問題。研判是促銷資料沒有正確關連產品資料，才會發生這種錯誤。
從戴爾回應外界連續標價錯誤事件的說法，第一次錯誤定位為人為作業疏失，第二次錯誤是因為系統異常。這麼看來朋友的說法似乎有些道理，但從系統開發流程的角度來看，卻讓筆者產生一個疑問。如果是因為設計有問題，應該是可以在系統正式運行前被測試出來，但為什麼要直到錯誤釀成災禍才被使用者發現？朋友表示要做到完整測試系統是很困難的，還不如把系統設計做好，這樣系統自然不會出錯。
在觀念上，我同意朋友的說法，因為好的設計的確可以減少系統發生錯誤的機會。但問題是朋友的想法在實務上卻有操作上的困難。因為設計夠好是很難被清楚定義，尤其是在專案時程及資源有限的情況下，想要設計出可以在各種情況下適用的系統是非常困難的。面對系統運作環境與需求變化無常的情況下，設計通常只是一種權衡與取捨之道；沒有可以解決所有問題的最佳設計，只有針對解決重要問題的最適當設計。
如果我們不能定義出具體明確的系統問題，所謂的較好的設計也只不過對未來可能變化的假設所做的設計，但實際上未來的變化可能會出乎我們的意料之外。當我們對系統的假設不再成立時，就會產生系統可能發生異常的風險。因此，戴爾出現系統異常的原因，問題的關鍵可能並不在設計的好壞，而是沒有掌握好問題的複雜度；今天系統碰上比過去更複雜的問題，是當初設計系統之時所沒有想到的情境。
造成錯誤的原因
從筆者過去系統開發的經驗顯示，過去長期運作正常的系統，經常會因為運作環境發生變化，而使系統在現今發生功能失常。我想戴爾的情況應該也是類似的狀況，否則如果是設計有問題，就很難解釋為什麼過去運作正常的系統，會在今天出問題。如同商業周刊的評論「戴爾烏龍 在於沒換腦袋」所提到的：
戴爾系統無法偵錯的關鍵——戴爾仍以經營企業顧客的思維在做消費者生意，否則怎會沒把消費者異常下單行為納入管理流程？
戴爾成立以來都是以企業市場為重，占營收比重超過八成。直到二○○七年才進入消費市場，這是很大的突破，因為經營企業市場，客戶數量少，強調服務與產品穩定度，但經營消費市場，客戶數倍增，就必須靈活彈性。
但此次事件讓我們看到，即使經歷兩年，戴爾網路系統的「腦袋」還沒轉過來，管理階層也是一般。
從戴爾大中華區中小企業處許肇元的說法，我們也可以了解戴爾系統異常的問題。網路上有一篇 jeremy 寫的專訪中提到許肇元對短短 10 多天連續出了兩次錯誤的解釋：
「因為我們成長的速度太快，而系統並沒有配合我們的成長。像是我們的訂購流程，每個零組件都可以客製化，訂一台筆電的流程換算下來就幾十個關卡，每個關卡都跟價錢有關，牽一髮而動全身。這次事件中，我們真的學到很多，也重新檢視了我們的系統。」
這更讓人相信，問題的關鍵並非單純的系統單一功能失常，而是戴爾忽略了商業模式改變會對系統產生影響，而沒有做好事先預防與事後可以及時因應的準備。
由此可知，造成戴爾系統發生錯誤看起來並非出在各部分功能的問題，而是系統整體整合出現問題而造成系統異常。那麼台北捷運內湖線的系統異常是不是也是相同的問題？從相關新聞報導我們發現，系統發生錯誤的原因也是因為系統沒有整合好，內湖線無法順利整合木柵線舊有系統。這大概從決策當局決定採用規格無法統一的中運量的系統，以及冒險採用無線通訊新技術時就已經註定了這樣的結果。再加上測試時間不足，自然會使品質問題更加雪上加霜而惡化。
造成系統失敗的條件
如果戴爾電腦和台北捷運內湖線的系統異常，種種跡象都顯示是整合出現問題，那麼我們不禁要問：為什麼它們的整合都會出現問題呢？從筆者系統開發的經驗來看，我相信是因為系統整合牽涉的問題太多或是太複雜，使得開發者難以掌握。再加上人們在尚未意識到系統的複雜度之前，常會認為自己有能力解決所有的問題，但實際上他們想要這樣做卻做不到。一言以敝之，系統失敗的根源其實是來自於人性的弱點，雖然這個真相往往被硬體、作業系統或平台的功能失常所掩蔽。
如同著名的軟體工程顧問溫伯格在《第一級評量》提到，造成軟體系統失敗的條件有八個 F，它是分別是弱點（Frailty）、愚蠢（Folly）、執迷不悟（Fatuousness）、好玩（Fun）、欺騙（Fraud）、狂熱（Fanaticism）、硬體功能失常（Failure）與運氣（Fate）。筆者發現這些造成失敗的條件，其實正是表現人性弱點的不同面向。
弱點
弱點是做想做的事卻做不到，它是軟體失敗的終極源頭。因為人不是完美的，他們做不到設計所要求的，不論那是一個程式設計，或是一個過程設計。溫伯格認為管理階層的責任是設計出一個程序以規範程式如何修改，承認自然界的事實，與確保程序本身被執行。而且他認為人們傾向在發生錯誤後懲罰嫌疑犯其實很不好，因為他會讓人隱藏錯誤、浪費時間在找嫌疑犯、以及分散注意力忽略管理階層的責任；建立並執行能及早找出失敗，並預防悲慘後果的程序。
愚蠢
愚蠢是做到想要做的事，但它卻是錯的事。愚蠢的基礎是無知，雖然它在當下沒有發生錯誤，卻會在以後造成錯誤。不過透過學習可以改善無知，進而將愚蠢矯正過來。溫伯格認為建立完整訓練師徒制、技術審查計劃、提供落實計劃的支援，是管理階層可以用來矯正愚蠢的職責。
執迷不悟
執迷不悟是指不肯學習，一直做出蠢事，一次又一次的做。此外，想要管理好一個愚蠢的人，卻不提供他根除愚蠢所需的訓練和經驗，這也算是一種執迷不悟的行為。溫伯格認為在軟體工程機構中，除了把執迷不悟的人送到其它行業去，否則沒有什麼防護措施可以抵擋執迷不悟的人。
好玩
好玩是程式設計師會寫一些奇怪的程式來為自己找樂子，溫伯格認為沒有人能夠預測別人認為好玩的事是什麼，因此好玩的心理是所有失敗的源頭中最危險的一個，因為它防不勝防。但管理者應該提供預防之道：一是開放透明的系統，另一個則是讓單單工作本身具有足夠的趣味。
欺騙
欺騙是用非法的方式從一個系統中獲取個人利益。溫伯格認為好玩是在失敗的源頭中，帶來的最小的損失。因為一個系統找樂子的方法有千百種，但值得一偷的東西卻沒多少。他認為軟體工程經理要好好閱讀以資訊系統詐騙為主題的文章，並採取一切可能的預防措施來防堵它。
狂熱
狂熱是試圖摧毀或瓦解一個系統，而原因不是為了個人利益，而是為了報復。溫伯格認為防範弱點而採取的行動中，多數也可以減少恐怖份子所造成的威脅與影響。
硬體功能失常
溫伯格提到硬體若不能造著當初設計的目的而執行工作，就會造成功能失常的現象，這類問題多半可以用軟體來克服。他認為當人們抱怨硬體造成他所寫的軟體出問題時，我們應該找出它表達的意思，以免遺漏這句話所帶來的重要資訊：

硬體沒什麼大不了的功能失常，但程式設計師需要找藉口來隱瞞一些事實。
 硬體功能失常問題都在一般的預期範圍內，可能程式設計師沒有採取正確的防護措施。例如將程式碼或測試腳本做備份。
硬體功能失常，但沒做妳硬體供應商關係的管理工作。
硬體功能失常是由人為錯誤所造成的，如使用者做出出乎意料的動作。

運氣
溫伯格指出運氣不好是多數表現不佳的經理愛用藉口，這不是事實。他建議當我們聽到一個經理老愛說運氣不好時，我們應該把運氣兩字換成經理，因為沒有不好的士兵，只有不好的軍官。
系統異常與人性弱點
從以上造成系統失敗的條件我們可以知道，系統發生異常的原因可能是系統的設計不夠好、硬體設備或作業系統出錯或是系統運作的環境太複雜了，但發生問題的真相卻都大部份是因為人性的弱點。因此，要在失敗前發現錯誤，進而採取行動防止系統失敗，重點管理好人性弱點，而非不承認它的存在，卻只在事後責備人們沒有盡到責任，但事實上最大的責任是管理階層沒有盡到管理的責任。
例如在台北捷運內湖線在 7/10 發生系統大當機的事件後，當外界質疑為什麼發生這麼嚴重的當機事件時，筆者注意到有一篇新聞報導提到市府官員有人表示「這個問題，80 % 是因為電腦中毒」言下之意系統異常多半是因為硬體的功能失常所致，而比較不可能是軟體的瑕疵或人為的錯誤。
溫伯格說過「對錯誤的直接觀察，本身並無意義，但是對『人們作何準備來面對錯誤的發生』的統合觀察就很有意義」那位市府官員的說辭，筆者相信只是為了隱瞞了一些事實，以免公布實情而讓損失更加擴大，然而這卻表現反應他們對面對系統錯誤發生的準備並不夠充分。
筆者再舉一位朋友的經驗為例，以前他們公司採用 .Net 開發平台開發新產品。由於他偏好 Java 的程式寫作慣例，加上當時微軟聲稱與 C# 整合不成問題，讓他很想用 J# 程式語言來開發系統。雖然他的同事擔心系統的整合會出現變數而反對，但由於他的堅持，管理階層還是照他的意思，讓他用 J# 開發他的程式，與其他同事以 C# 的程式來進行整合。
後來在整合時，他們發現碰到很多平台上及程式語言本身的問題。為了解決這些問題，他只好修改他的程式以處理這些問題，但也讓系統愈變愈複雜，結果使軟體問題層出不窮。但朋友仍然還是堅持要用他喜歡的方式開發系統，最後在管理階層無法忍受他的執迷不悟，並且在彼此無法達成共識的情況下，要求他離開了那家公司。
從這位朋友的故事中，我們看到他的弱點、愚蠢以及他和管理階層的執迷不悟。他的弱點是想實現他的設計理念並完成不同語言的整合，但後來卻發現這是個艱鉅的任務。在發現了專案時程及市場上的壓力並不允許他實現他的設計理想時，卻一再地堅持做自己想做的事而非應該做的事，這是愚蠢。而與管理階層之間一次又一次想要對方同意自己的觀點，卻又不去理性客觀地評估現實，而只是一廂情願地以為讓對方發現此路不通就會懸崖勒馬，這是他與管理階層的執迷不悟。
朋友的經歷並不是特例，在實際的系統開發專案中，筆者總是看到相同的故事正在持續上演。就像戴爾電腦、台北捷運內湖線發生系統異常的事件一樣，應該發揮效果的程序、流程與方法，在關鍵時刻竟然沒有發揮作用。筆者認為問題的關鍵是在於人性的弱點，我想只有在適當地管理好人性弱點之後，程序、流程與方法才能真正地落實，並且發揮出應有的效果吧。
管理的重要性
如果導致系統異常的關鍵是在於人性的弱點。那麼管理階層就應該負起管理人性弱點的責任，以避免專案因為人性弱點而造成系統異常的意外事件而慘遭失敗。從去年跨年夜發生的台灣大哥大行動電話用戶大當機的事件，又再一次地讓我們看到管理對避免系統異常而造成失敗的重要性。
去年跨年夜，台灣大哥大發生行動電話用戶大當機，經檢調查出是台灣諾基亞西門子公司離職工程師，涉嫌以女友名義登入台灣大資料庫並刪除資料造成大當機，檢方昨天將陳依妨害電腦使用罪嫌起訴。
筆者看到新聞提到那位工程師，否認是遭開除而挾怨報復，只說會這麼做是因為「好玩」。讓我想到溫伯格說的，好玩的心理是所有失敗源頭最危險的一個，因為沒有人可以預測到別人認為好玩的事是什麼。
當然，我想事件的真相應該不是因為那位工程師基於好玩的心理，而是被公司開除而心生報復。造成台灣大哥大系統當機的原因，固然是難以預料到的惡意破壞，但這並不代表這種系統失敗是無法防止的。筆者認為問題在管理上，因為管理階層忽視人性弱點，而沒有盡到管理者應盡的責任。
或許有人會認為筆者這樣說對管理者要求太多了，但如果系統開發團隊沒有紀律來把事情做好，這的確是管理者的問題。管理者設計或制定流程，目的是為了幫工程師把事做好，但如果流程不能落實，那是必然代表管理出現了問題，所以管理者必然難辭其咎。
好比說，為什麼離職員工可以用他離職前的帳號密碼來登入系統，然後做出一些危害系統的行為？又或者，為什麼會讓人興起想要破壞系統的動機，而身為負責系統成敗的高階管理者，為什麼會不去防範可能破壞系統的行為？
因此，即使可能是因為好玩，管理者也要思考如何降低人們為了找樂子而影響系統的動機。如前面所提到過的，讓員工的工作更有趣，同時讓流程更透明。此外，避免員工試圖摧毀或瓦解一個系統，不是為個人利益而是為了報復。管理者應加強防範弱點而採取的行動，因為它們多數也可以減少這種攻擊。
以上這些都是管理者的職責，以避免系統因為人為的疏忽而失敗。總而言之，預防系統失敗，管理最重要的工作就是認清「人的不完美」，才能知道如何管理人性，進而避免發生人為錯誤而造成意外，產生系統的重大損失。
]]></description>
			<content:encoded><![CDATA[<p>這篇文章是投稿 <a href="http://www.zdnet.com.tw/" target="_blank">ZDNet Taiwan</a> 的文章原稿，由 ZDNet Taiwan 以〈<a href="http://www.zdnet.com.tw/enterprise/column/softwaredev/0,2000087962,20141804,00.htm" target="_blank">如何在系統異常前發現錯誤？</a>〉、〈<a href="http://www.zdnet.com.tw/enterprise/column/softwaredev/0,2000087962,20142211,00.htm" target="_blank">如何在系統異常前發現錯誤？（下）</a>〉兩篇文章刊登。文章原稿未經 ZDNet Taiwan 編輯，內容可能與 ZDNet Taiwan 約略有所不同。</p>
<p>前一陣子有兩個與資訊系統失常有關，而且眾所矚目的新聞事件，也就是戴爾電腦網路購物系統與台北捷運內湖線的系統異常。相信很多人都認為這兩個系統會發生系統異常相當離譜，在系統上線之後才發現系統無法正常運作，造成系統使用者的困擾，同時也會讓人對系統可靠度與穩定度失去信心，而增加系統的失敗成本。</p>
<p>雖然平心而論，想要事前預料系統可能發生的問題，並加以預防或因應其實並不容易，因為開發系統，尤其是軟體開發常會碰到事先難以預料的問題。但如果能在錯誤造成危害之前，就能夠發現問題並採取適當的行動來解決它，應該就能減少系統的失敗成本。因此，看到戴爾與台北捷運內湖線的重大系統異常，讓筆者想探討如何在系統失敗前發現錯誤，以避免系統失敗的巨大損失。</p>
<h4>設計不夠好？</h4>
<p>戴爾是世界知名的電腦直銷公司，擁有 13 年的網路直銷經驗。對於這種有豐富網路直銷經驗的公司來說，系統連續發生產品標價錯誤的問題，實在是一件令人感到不可思議的事情。在戴爾發生第二次標價錯誤事件之後，筆者聽到有一位工程師出身的朋友指出，戴爾筆記型電腦的標價錯誤，是因為他們的系統設計不良。他依據新聞的報導，對比自己的網站開發經驗，認為可以確定這絕對是設計的問題。研判是促銷資料沒有正確關連產品資料，才會發生這種錯誤。</p>
<p>從戴爾回應外界連續標價錯誤事件的說法，第一次錯誤定位為人為作業疏失，第二次錯誤是因為系統異常。這麼看來朋友的說法似乎有些道理，但從系統開發流程的角度來看，卻讓筆者產生一個疑問。如果是因為設計有問題，應該是可以在系統正式運行前被測試出來，但為什麼要直到錯誤釀成災禍才被使用者發現？朋友表示要做到完整測試系統是很困難的，還不如把系統設計做好，這樣系統自然不會出錯。</p>
<p>在觀念上，我同意朋友的說法，因為好的設計的確可以減少系統發生錯誤的機會。但問題是朋友的想法在實務上卻有操作上的困難。因為設計夠好是很難被清楚定義，尤其是在專案時程及資源有限的情況下，想要設計出可以在各種情況下適用的系統是非常困難的。面對系統運作環境與需求變化無常的情況下，設計通常只是一種權衡與取捨之道；沒有可以解決所有問題的最佳設計，只有針對解決重要問題的最適當設計。</p>
<p>如果我們不能定義出具體明確的系統問題，所謂的較好的設計也只不過對未來可能變化的假設所做的設計，但實際上未來的變化可能會出乎我們的意料之外。當我們對系統的假設不再成立時，就會產生系統可能發生異常的風險。因此，戴爾出現系統異常的原因，問題的關鍵可能並不在設計的好壞，而是沒有掌握好問題的複雜度；今天系統碰上比過去更複雜的問題，是當初設計系統之時所沒有想到的情境。</p>
<h4>造成錯誤的原因</h4>
<p>從筆者過去系統開發的經驗顯示，過去長期運作正常的系統，經常會因為運作環境發生變化，而使系統在現今發生功能失常。我想戴爾的情況應該也是類似的狀況，否則如果是設計有問題，就很難解釋為什麼過去運作正常的系統，會在今天出問題。如同商業周刊的評論「<a href="http://www.businessweekly.com.tw/webarticle.php?id=37222" target="_blank">戴爾烏龍 在於沒換腦袋</a>」所提到的：</p>
<blockquote><p>戴爾系統無法偵錯的關鍵——戴爾仍以經營企業顧客的思維在做消費者生意，否則怎會沒把消費者異常下單行為納入管理流程？</p>
<p>戴爾成立以來都是以企業市場為重，占營收比重超過八成。直到二○○七年才進入消費市場，這是很大的突破，因為經營企業市場，客戶數量少，強調服務與產品穩定度，但經營消費市場，客戶數倍增，就必須靈活彈性。</p>
<p>但此次事件讓我們看到，即使經歷兩年，戴爾網路系統的「腦袋」還沒轉過來，管理階層也是一般。</p></blockquote>
<p>從戴爾大中華區中小企業處許肇元的說法，我們也可以了解戴爾系統異常的問題。網路上有一篇 <a href="http://tw.myblog.yahoo.com/jeremy-3c/article?mid=33331" target="_blank">jeremy 寫的專訪</a>中提到許肇元對短短 10 多天連續出了兩次錯誤的解釋：</p>
<blockquote><p>「因為我們成長的速度太快，而系統並沒有配合我們的成長。像是我們的訂購流程，每個零組件都可以客製化，訂一台筆電的流程換算下來就幾十個關卡，每個關卡都跟價錢有關，牽一髮而動全身。這次事件中，我們真的學到很多，也重新檢視了我們的系統。」</p></blockquote>
<p>這更讓人相信，問題的關鍵並非單純的系統單一功能失常，而是戴爾忽略了商業模式改變會對系統產生影響，而沒有做好事先預防與事後可以及時因應的準備。</p>
<p>由此可知，造成戴爾系統發生錯誤看起來並非出在各部分功能的問題，而是系統整體整合出現問題而造成系統異常。那麼台北捷運內湖線的系統異常是不是也是相同的問題？從相關新聞報導我們發現，系統發生錯誤的原因也是因為系統沒有整合好，內湖線無法順利整合木柵線舊有系統。這大概從決策當局決定採用規格無法統一的中運量的系統，以及冒險採用無線通訊新技術時就已經註定了這樣的結果。再加上測試時間不足，自然會使品質問題更加雪上加霜而惡化。</p>
<h4>造成系統失敗的條件</h4>
<p>如果戴爾電腦和台北捷運內湖線的系統異常，種種跡象都顯示是整合出現問題，那麼我們不禁要問：為什麼它們的整合都會出現問題呢？從筆者系統開發的經驗來看，我相信是因為系統整合牽涉的問題太多或是太複雜，使得開發者難以掌握。再加上人們在尚未意識到系統的複雜度之前，常會認為自己有能力解決所有的問題，但實際上他們想要這樣做卻做不到。一言以敝之，系統失敗的根源其實是來自於人性的弱點，雖然這個真相往往被硬體、作業系統或平台的功能失常所掩蔽。</p>
<p><a title="More about 溫伯格的軟體管理學" href="http://www.anobii.com/books/溫伯格的軟體管理學/9789867889720/01c7ec64f7e4bf0927/"><img style="padding: 5px;" title="More about 溫伯格的軟體管理學" src="http://image.anobii.com/anobi/image_book.php?type=4&amp;item_id=01c7ec64f7e4bf0927&amp;time=1217763761" alt="More about 溫伯格的軟體管理學" align="right" /></a>如同著名的軟體工程顧問溫伯格在《<a title="More about 溫伯格的軟體管理學" href="http://www.anobii.com/books/溫伯格的軟體管理學/9789867889720/01c7ec64f7e4bf0927/">第一級評量</a>》提到，造成軟體系統失敗的條件有八個 F，它是分別是弱點（Frailty）、愚蠢（Folly）、執迷不悟（Fatuousness）、好玩（Fun）、欺騙（Fraud）、狂熱（Fanaticism）、硬體功能失常（Failure）與運氣（Fate）。筆者發現這些造成失敗的條件，其實正是表現人性弱點的不同面向。</p>
<p><span style="text-decoration: underline;">弱點</span></p>
<p>弱點是做想做的事卻做不到，它是軟體失敗的終極源頭。因為人不是完美的，他們做不到設計所要求的，不論那是一個程式設計，或是一個過程設計。溫伯格認為管理階層的責任是設計出一個程序以規範程式如何修改，承認自然界的事實，與確保程序本身被執行。而且他認為人們傾向在發生錯誤後懲罰嫌疑犯其實很不好，因為他會讓人隱藏錯誤、浪費時間在找嫌疑犯、以及分散注意力忽略管理階層的責任；建立並執行能及早找出失敗，並預防悲慘後果的程序。</p>
<p><span style="text-decoration: underline;">愚蠢</span></p>
<p>愚蠢是做到想要做的事，但它卻是錯的事。愚蠢的基礎是無知，雖然它在當下沒有發生錯誤，卻會在以後造成錯誤。不過透過學習可以改善無知，進而將愚蠢矯正過來。溫伯格認為建立完整訓練師徒制、技術審查計劃、提供落實計劃的支援，是管理階層可以用來矯正愚蠢的職責。</p>
<p><span style="text-decoration: underline;">執迷不悟</span></p>
<p>執迷不悟是指不肯學習，一直做出蠢事，一次又一次的做。此外，想要管理好一個愚蠢的人，卻不提供他根除愚蠢所需的訓練和經驗，這也算是一種執迷不悟的行為。溫伯格認為在軟體工程機構中，除了把執迷不悟的人送到其它行業去，否則沒有什麼防護措施可以抵擋執迷不悟的人。</p>
<p><span style="text-decoration: underline;">好玩</span></p>
<p>好玩是程式設計師會寫一些奇怪的程式來為自己找樂子，溫伯格認為沒有人能夠預測別人認為好玩的事是什麼，因此好玩的心理是所有失敗的源頭中最危險的一個，因為它防不勝防。但管理者應該提供預防之道：一是開放透明的系統，另一個則是讓單單工作本身具有足夠的趣味。</p>
<p><span style="text-decoration: underline;">欺騙</span></p>
<p>欺騙是用非法的方式從一個系統中獲取個人利益。溫伯格認為好玩是在失敗的源頭中，帶來的最小的損失。因為一個系統找樂子的方法有千百種，但值得一偷的東西卻沒多少。他認為軟體工程經理要好好閱讀以資訊系統詐騙為主題的文章，並採取一切可能的預防措施來防堵它。</p>
<p><span style="text-decoration: underline;">狂熱</span></p>
<p>狂熱是試圖摧毀或瓦解一個系統，而原因不是為了個人利益，而是為了報復。溫伯格認為防範弱點而採取的行動中，多數也可以減少恐怖份子所造成的威脅與影響。</p>
<p><span style="text-decoration: underline;">硬體功能失常</span></p>
<p>溫伯格提到硬體若不能造著當初設計的目的而執行工作，就會造成功能失常的現象，這類問題多半可以用軟體來克服。他認為當人們抱怨硬體造成他所寫的軟體出問題時，我們應該找出它表達的意思，以免遺漏這句話所帶來的重要資訊：</p>
<ol>
<li>硬體沒什麼大不了的功能失常，但程式設計師需要找藉口來隱瞞一些事實。</li>
<li> 硬體功能失常問題都在一般的預期範圍內，可能程式設計師沒有採取正確的防護措施。例如將程式碼或測試腳本做備份。</li>
<li>硬體功能失常，但沒做妳硬體供應商關係的管理工作。</li>
<li>硬體功能失常是由人為錯誤所造成的，如使用者做出出乎意料的動作。</li>
</ol>
<p><span style="text-decoration: underline;">運氣</span></p>
<p>溫伯格指出運氣不好是多數表現不佳的經理愛用藉口，這不是事實。他建議當我們聽到一個經理老愛說運氣不好時，我們應該把運氣兩字換成經理，因為沒有不好的士兵，只有不好的軍官。</p>
<h4>系統異常與人性弱點</h4>
<p>從以上造成系統失敗的條件我們可以知道，系統發生異常的原因可能是系統的設計不夠好、硬體設備或作業系統出錯或是系統運作的環境太複雜了，但發生問題的真相卻都大部份是因為人性的弱點。因此，要在失敗前發現錯誤，進而採取行動防止系統失敗，重點管理好人性弱點，而非不承認它的存在，卻只在事後責備人們沒有盡到責任，但事實上最大的責任是管理階層沒有盡到管理的責任。</p>
<p>例如在台北捷運內湖線在 7/10 發生系統大當機的事件後，當外界質疑為什麼發生這麼嚴重的當機事件時，筆者注意到有一篇新聞報導提到市府官員有人表示「這個問題，80 % 是因為電腦中毒」言下之意系統異常多半是因為硬體的功能失常所致，而比較不可能是軟體的瑕疵或人為的錯誤。</p>
<p>溫伯格說過「對錯誤的直接觀察，本身並無意義，但是對『人們作何準備來面對錯誤的發生』的統合觀察就很有意義」那位市府官員的說辭，筆者相信只是為了隱瞞了一些事實，以免公布實情而讓損失更加擴大，然而這卻表現反應他們對面對系統錯誤發生的準備並不夠充分。</p>
<p>筆者再舉一位朋友的經驗為例，以前他們公司採用 .Net 開發平台開發新產品。由於他偏好 Java 的程式寫作慣例，加上當時微軟聲稱與 C# 整合不成問題，讓他很想用 J# 程式語言來開發系統。雖然他的同事擔心系統的整合會出現變數而反對，但由於他的堅持，管理階層還是照他的意思，讓他用 J# 開發他的程式，與其他同事以 C# 的程式來進行整合。</p>
<p>後來在整合時，他們發現碰到很多平台上及程式語言本身的問題。為了解決這些問題，他只好修改他的程式以處理這些問題，但也讓系統愈變愈複雜，結果使軟體問題層出不窮。但朋友仍然還是堅持要用他喜歡的方式開發系統，最後在管理階層無法忍受他的執迷不悟，並且在彼此無法達成共識的情況下，要求他離開了那家公司。</p>
<p>從這位朋友的故事中，我們看到他的弱點、愚蠢以及他和管理階層的執迷不悟。他的弱點是想實現他的設計理念並完成不同語言的整合，但後來卻發現這是個艱鉅的任務。在發現了專案時程及市場上的壓力並不允許他實現他的設計理想時，卻一再地堅持做自己想做的事而非應該做的事，這是愚蠢。而與管理階層之間一次又一次想要對方同意自己的觀點，卻又不去理性客觀地評估現實，而只是一廂情願地以為讓對方發現此路不通就會懸崖勒馬，這是他與管理階層的執迷不悟。</p>
<p>朋友的經歷並不是特例，在實際的系統開發專案中，筆者總是看到相同的故事正在持續上演。就像戴爾電腦、台北捷運內湖線發生系統異常的事件一樣，應該發揮效果的程序、流程與方法，在關鍵時刻竟然沒有發揮作用。筆者認為問題的關鍵是在於人性的弱點，我想只有在適當地管理好人性弱點之後，程序、流程與方法才能真正地落實，並且發揮出應有的效果吧。</p>
<h4>管理的重要性</h4>
<p>如果導致系統異常的關鍵是在於人性的弱點。那麼管理階層就應該負起管理人性弱點的責任，以避免專案因為人性弱點而造成系統異常的意外事件而慘遭失敗。從去年跨年夜發生的台灣大哥大行動電話用戶大當機的<a href="http://udn.com/NEWS/SOCIETY/SOC7/5110638.shtml" target="_blank">事件</a>，又再一次地讓我們看到管理對避免系統異常而造成失敗的重要性。</p>
<p>去年跨年夜，台灣大哥大發生行動電話用戶大當機，經檢調查出是台灣諾基亞西門子公司離職工程師，涉嫌以女友名義登入台灣大資料庫並刪除資料造成大當機，檢方昨天將陳依妨害電腦使用罪嫌起訴。</p>
<p>筆者看到新聞提到那位工程師，否認是遭開除而挾怨報復，只說會這麼做是因為「好玩」。讓我想到溫伯格說的，好玩的心理是所有失敗源頭最危險的一個，因為沒有人可以預測到別人認為好玩的事是什麼。</p>
<p>當然，我想事件的真相應該不是因為那位工程師基於好玩的心理，而是被公司開除而心生報復。造成台灣大哥大系統當機的原因，固然是難以預料到的惡意破壞，但這並不代表這種系統失敗是無法防止的。筆者認為問題在管理上，因為管理階層忽視人性弱點，而沒有盡到管理者應盡的責任。</p>
<p>或許有人會認為筆者這樣說對管理者要求太多了，但如果系統開發團隊沒有紀律來把事情做好，這的確是管理者的問題。管理者設計或制定流程，目的是為了幫工程師把事做好，但如果流程不能落實，那是必然代表管理出現了問題，所以管理者必然難辭其咎。</p>
<p>好比說，為什麼離職員工可以用他離職前的帳號密碼來登入系統，然後做出一些危害系統的行為？又或者，為什麼會讓人興起想要破壞系統的動機，而身為負責系統成敗的高階管理者，為什麼會不去防範可能破壞系統的行為？</p>
<p>因此，即使可能是因為好玩，管理者也要思考如何降低人們為了找樂子而影響系統的動機。如前面所提到過的，讓員工的工作更有趣，同時讓流程更透明。此外，避免員工試圖摧毀或瓦解一個系統，不是為個人利益而是為了報復。管理者應加強防範弱點而採取的行動，因為它們多數也可以減少這種攻擊。</p>
<p>以上這些都是管理者的職責，以避免系統因為人為的疏忽而失敗。總而言之，預防系統失敗，管理最重要的工作就是認清「人的不完美」，才能知道如何管理人性，進而避免發生人為錯誤而造成意外，產生系統的重大損失。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.lifeparty.idv.tw/blog/archives/2063/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>系統開發的彈性</title>
		<link>http://www.lifeparty.idv.tw/blog/archives/1113</link>
		<comments>http://www.lifeparty.idv.tw/blog/archives/1113#comments</comments>
		<pubDate>Tue, 21 Jul 2009 10:26:45 +0000</pubDate>
		<dc:creator>jim yeh</dc:creator>
				<category><![CDATA[CNet/ZDNet]]></category>
		<category><![CDATA[利害關係人]]></category>
		<category><![CDATA[問題解決]]></category>
		<category><![CDATA[思考]]></category>
		<category><![CDATA[易經思維]]></category>
		<category><![CDATA[溝通]]></category>
		<category><![CDATA[生活感觸]]></category>
		<category><![CDATA[職場]]></category>
		<category><![CDATA[開發流程]]></category>

		<guid isPermaLink="false">http://www.lifeparty.idv.tw/blog/?p=1113</guid>
		<description><![CDATA[是否代表系統開發追求速度與彈性，就必然犧牲文件與流程呢？同人認為這樣看就太過簡化了，系統開發的彈性並不是忽略系統文件與流程，而是只重視有實質效益的一切事物，當然包括文件與流程。]]></description>
			<content:encoded><![CDATA[<p>本文於 2009/07/22 經 <a href="http://www.zdnet.com.tw/members/1000103060/blog/?v=post&amp;id=10000280">ZDNet Taiwan 部落格文章專區轉載</a>。</p>
<p>在 <a href="http://www.facebook.com">facebook</a> 看到舜平學長提到「<a href="http://www.facebook.com/note.php?note_id=103671809911&amp;ref=mf">求快求彈性忽略系統文件的後果，找了兩個小時的BUG</a>」讓同人想寫一篇文章來談談系統開發的彈性。</p>
<p>舜平學長說求快求彈性，忽略系統文件重要性的後果就是使用者說沒空寫文件，如果這時我們也沒有將系統重要資訊記錄下來，那麼就算是自己也會因為時間一久而逐漸淡忘這些資訊，結果使得系統的維護變得更加困難。</p>
<p>雖然以上的現象在台灣是開發者經常碰到的問題，但那是否代表系統開發追求速度與彈性，就必然犧牲文件與流程呢？同人認為這樣看就太過簡化了，系統開發的彈性並不是忽略系統文件與流程，而是只重視有實質效益的一切事物，當然包括文件與流程。</p>
<p>「彈性－快，是之前老闆說的。常常就是邊想邊做，搞死 IT 人員」舜平學長提到他對彈性的認知。但這種對彈性的定義有沒有問題？我們追本溯源，從<a href="http://dict.revised.moe.edu.tw/">教育部國語辭典</a>可以查到「彈性」這個字詞有兩個解釋：</p>
<blockquote><p>物體受外力作用，會改變其形體，而當外力除去後，即恢復其原狀，此種性質，稱為「彈性」。</p>
<p>比喻事情無固定標準，而可隨機調整。</p></blockquote>
<p>從這裡可以看到舜平學長提到彈性的定義，是採用彈性的第二個解釋。指系統開發這件事沒有固定標準，必須隨著需求改變而機動調整，以達到讓系統快速適應變化的目標。但事實上正如學長說的，搞死 IT 人員，而系統也不斷發現問題叢生的問題，這樣其實並沒有達到彈性可隨機調整的標準。</p>
<p>開發系統臨機應變，不去根據未來可能的改變而計劃，而是面對當前需求的改變而修正系統，可以讓開發者不把時間浪費在無謂的預測上，迅速地開發出使用者真正需要的系統。但實際上為什麼卻並不是那麼一回事呢？我們可以從與系統開發相關的事、物、人來了解，彈性固然是沒有固定標準，但所謂的擁抱改變卻並不代表任由毫無限制的改變發生，那樣只會造成極度的混亂而使系統崩壞。</p>
<p>從事的角度來看，任何使用者需求都需要時間，而時間是取決於功能的複雜度。對於使用者而言，他們認為他們需要的功能都很簡單。但當許多簡單的功能集合在一起，其相互關聯的交互作用所產生的複雜度與錯誤，可能就會讓開發者很難應付。因此，開發流程的彈性，是用來決定該在什麼時候修改或增加什麼功能，以降低複雜度與錯誤的機會。</p>
<p>從物的角度來看，良好的系統架構有助於快速因應需求的變化。架構良好的系統具有強固性，不會因為系統運作環境的不同而產生功能失常。同時也具備擴充性與延展性，可以隨時依據使用者的需求變化而調整系統的功能。然而，通常在專案時間及成本的壓力之下，很難有時間能夠設計出最佳的系統架構來適應所有的情況，只能因應最主要的問題來設計適當的架構，並在必要的時候可以逐步演進系統功能，這便是系統架構的彈性。</p>
<p>從人的角度來看，專案具備來自不同利害關係人的各種面向。例如使用者通常在意的是他們作業的問題，系統能不能幫他們解決，開發者則在乎使用者提出來的需求是否精確，用來發展出良好的架構以增進開發的效率。這些不同的觀點常因為不同的需要、價值觀、專業、以及所用的語言不同而產生相當的落差。因此，溝通的彈性是在於是否能允許各種正反意見充分表達，進行對話與良性的互動，以建立資訊暢通的溝通管道。</p>
<p>因此，從系統開發相關的事、物、人來看，我們知道彈性的意義是追求快而不亂。這意謂著接受一定範圍的改變，而非任由混亂無秩序，那是一片混沌而非美其名追求彈性。彈性其實也需要計劃與紀律，只是和典型的開發方法有所不同。</p>
<p>如果開發者最早開發出來的系統，是可運作與架構簡明不容易出錯的系統，那麼隨著使用者需求的增加，為什麼會愈變愈複雜呢？這當然是因為時間緊迫與系統沒有足夠的空間可以容納新功能。</p>
<p>時間緊迫意謂開發者沒有時間增添或修改系統功能，原因通常是會干擾他正在進行的開發，而增加開發的複雜度與出錯機率。所以開發者應該延緩會影響現有功能的使用者需求，這才是追求彈性的做法，這樣就不可能邊做邊改；因為戴上修正功能的帽子時，是不應該去增加功能的。</p>
<p>那麼如果沒有足夠的空間呢？這時開發者應該著手改善系統架構而非疊床架屋勉強加入新功能。由於開發者以前可能對需求了解程度有限，所以很難開發出比較具有彈性的架構。等到對問題的了解愈來愈清楚，慢慢看清楚問題的脈絡與解決問題的模式時，這時正是改良設計的最佳時機。就是因為架構的改良，無所不包文件與繁複的流程自然不太需要，而是需要在開發過程有助溝通的基本文件與流程。</p>
<p>然而，開發者為什麼不修正架構以容納新功能呢？或許有些開發者以為架構不改變還是可以容納新功能，所以可以允許複雜度多加了一點。但等到下次，他還是會有同樣的想法，直到這樣的行為結構造成系統問題叢生的弊病才會發現事態嚴重。寫到這裡，讓同人想到《易經坤卦文言》所說的：</p>
<blockquote><p>積善之家，必有餘慶。積不善之家，必有餘殃。</p>
<p>臣弒其君，子弒其父，非一朝一夕之故，其所由來者漸矣。由辨之不早辨也？</p></blockquote>
<p>不去順應變化之道，讓系統不斷更新<sup>[1]</sup>，這樣並非追求彈性的系統開發。充其量，只能算是擠壓開發者的彈性，這是彈性的另一種解釋；承受壓力後回復原狀的性質，但結果通常是讓開發者彈性疲乏。或許這就是舜平學長說的無言吧。</p>
<p>延伸閱讀：（其它與系統開發彈性相關的文章）<br />
<a href="http://www.lifeparty.idv.tw/blog/archives/175">軟體開發是工藝還是工程？</a><br />
<a href="http://www.lifeparty.idv.tw/blog/archives/421">穩定的程式是偶然？</a><br />
<a href="http://www.lifeparty.idv.tw/blog/archives/194">程式碼的結構、迴圈與處理</a><br />
<a href="http://www.lifeparty.idv.tw/blog/archives/179">Time-Boxing 於軟體反覆演進的必要性</a><br />
<a href="http://www.lifeparty.idv.tw/blog/archives/133">資訊服務的適應性觀點</a><br />
<a href="http://www.lifeparty.idv.tw/blog/archives/87">評論「專案假設」的相關討論</a><br />
<a href="http://www.lifeparty.idv.tw/blog/archives/26">軟體開發能力的自我組織</a></p>
附註：
&nbsp;<hr/><ol class="footnotes"><li id="footnote_0_1113" class="footnote">坤卦順承變化趨勢，否則陰疑於陽必戰的結果是坤上六爻變的「龍戰于野，其血玄黃」而產生劇烈變化，最後還是會重新走向乾卦的自強不息。</li></ol>]]></content:encoded>
			<wfw:commentRss>http://www.lifeparty.idv.tw/blog/archives/1113/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>聚餐也談品質流程</title>
		<link>http://www.lifeparty.idv.tw/blog/archives/488</link>
		<comments>http://www.lifeparty.idv.tw/blog/archives/488#comments</comments>
		<pubDate>Fri, 08 May 2009 10:31:24 +0000</pubDate>
		<dc:creator>jim yeh</dc:creator>
				<category><![CDATA[CNet/ZDNet]]></category>
		<category><![CDATA[利害關係人]]></category>
		<category><![CDATA[品質文化]]></category>
		<category><![CDATA[問題解決]]></category>
		<category><![CDATA[專案監控]]></category>
		<category><![CDATA[專案規劃]]></category>
		<category><![CDATA[思考]]></category>
		<category><![CDATA[生活感觸]]></category>
		<category><![CDATA[職場]]></category>
		<category><![CDATA[開發流程]]></category>

		<guid isPermaLink="false">http://www.lifeparty.idv.tw/blog/?p=488</guid>
		<description><![CDATA[在台灣，品質最大的問題是人們習慣將品質流程獨立於設計及開發過程之外，以為兩者是可以完全分割的。然而這種思維對品質的結論就會是「把做好的東西丟到另一端去」，讓開發人員認為品質是品質部門的責任，而品質部門則認為提昇品質不是他們的責任，以為最多只能做到知道產品有問題，而不知道如何改善它們，只能退回到開發人員那邊來解決。 ]]></description>
			<content:encoded><![CDATA[<p>本周一同人在新公司 on board，公司事先訂好餐廳為我舉行迎新聚餐。在這場聚餐當中的一道菜，讓大家開啟談論品質流程的話題。</p>
<p>某同事甲指著一道菜，跟負責點菜的同事乙說：下次就別再點這道菜了。他說那道菜的肉，咬下去裡面是白的，顯見豬肉並沒有先醃過，吃起來就不夠味。這時候，有些同事表示認同，紛紛也附和同事甲的意見來評論這道菜。</p>
<p>同人對同事甲的看法沒有什麼意見，因為說實在的，我也吃不出這道菜有什麼不好。不過，如果精於美食的同事甲說得是正確的，那麼就代表這家店的這道菜品質有問題。於是我說：這其實代表他們 QA 沒做好。</p>
<p>這時候，大家好奇的眼光看著同事丙，也就是公司 QA Leader。他說 QA 沒有辦法提昇產品的品質，它只能確保有問題的產品不會送到客戶的手中。</p>
<p>顯然同事丙和同人對 QA 的定義有顯著的不同，他所說的 QA 是針對產品本身的控制流程，而非針對產出保證產品品質的執行過程，我認為那是 QC 而非 QA。但同人並不想挑戰對方對 QA 的定義，以免把吃飯的氣氛弄得太嚴肅，於是我用另一種方式來表達我的觀點：</p>
<blockquote><p>我的意思是作這道菜的流程出了問題，導致產出無法符合顧客對品質的要求。所以他們應該設法改善這道菜的製作流程，讓它更完整並且可以符合客戶的需求。</p></blockquote>
<p>終於，同事丙沒有再質疑同人的說法。不過，這也讓人看到一個現象，就是在台灣，品質最大的問題是人們習慣將品質流程獨立於設計及開發過程之外，以為兩者是可以完全分割的。然而這種思維對品質的結論就會是「把做好的東西丟到另一端去」，讓開發人員認為品質是品質部門的責任，而品質部門則認為提昇品質不是他們的責任，以為最多只能做到知道產品有問題，而不知道如何改善它們，只能退回到開發人員那邊來解決。 </p>
<p>在一個重視品質文化的公司，QA 人員對產品的意見當然可以擲地有聲，善盡到品質管制的責任。然而，在台灣軟體開發的環境，尤其是大部分以專案型態的軟體開發，品質往往是時間或成本不足第一個被犧牲的對象。同人想到我過去在一個大型政府公共建設委外 BOT 專案中，因為業主對文件要求嚴格，使開發團隊要花費相當多的心力來寫文件。想不到上層負責監督的 VP 卻說：沒有時間，品質目標只要設定為達到 30% 就夠了。</p>
<p>同人很清楚那位 VP 的說法是不會 work 的，因為品質不能妥協，否則你必將付出更大的代價。果然那個因為延誤而讓公司損失慘重的專案，在我離開那個專案兩三年後才勉強結案驗收。不幸地，這種不重視品質文化的開發方式，在今天卻還在台灣各地持續上演之中。</p>
<p>因此，與其將品質寄望在強大的 QA 人員或嚴謹的品質流程，還不如在開發過程中織入品質的面向，形成品質保證的<a href="http://en.wikipedia.org/wiki/Holographic_principle">全像圖</a>。高品質是全員參與的必然結果，在分析、設計過程就加入如何提昇品質的思維，以達到符合顧客的需求，並且創造他們的價值，這便是執行所謂的 QA，也就是真正的品質保證。</p>
<p>當然，能夠為客戶創造價值的品質流程並非絕對的，就像負責點菜的同事乙後來表示，她不覺得那道菜難吃呀。其實同人也覺得那道菜也還好，品質和你的客戶是誰有很大的關係，事實上，並不是每一個人都對吃很講究，所以那道菜的品質，其實是因人而異呀。</p>
<p><strong>後記：</strong></p>
<p>本篇文章已<a href="http://www.zdnet.com.tw/members/1000103060/blog/?v=post&#038;id=10000208">刊登</a>於 ZDNet Taiwan <a href="http://www.zdnet.com.tw/members/1000103060/blog/">部落格文章專區</a>。</p>
]]></content:encoded>
			<wfw:commentRss>http://www.lifeparty.idv.tw/blog/archives/488/feed</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>穩定的程式是偶然？</title>
		<link>http://www.lifeparty.idv.tw/blog/archives/421</link>
		<comments>http://www.lifeparty.idv.tw/blog/archives/421#comments</comments>
		<pubDate>Fri, 09 Jan 2009 06:54:09 +0000</pubDate>
		<dc:creator>jim yeh</dc:creator>
				<category><![CDATA[學習]]></category>
		<category><![CDATA[專案監控]]></category>
		<category><![CDATA[專案規劃]]></category>
		<category><![CDATA[專案風險]]></category>
		<category><![CDATA[思考]]></category>
		<category><![CDATA[編程技巧]]></category>
		<category><![CDATA[職場]]></category>
		<category><![CDATA[設計原則]]></category>
		<category><![CDATA[開發流程]]></category>

		<guid isPermaLink="false">http://www.lifeparty.idv.tw/blog/archives/421</guid>
		<description><![CDATA[如果穩定的程式真的是偶然的，程式的穩定似乎只能依賴運氣而不是人為努力，事情真的是這樣嗎？其實這位噗友太過強調環境變化的隨機性，卻忽略了適應環境變化，程式開發必然會經歷複雜演化的過程。穩定的程式是演化而來的，雖然演化的過程是偶然、但其最後結果卻是必然。換句話說，穩定的程式是偶然下的必然。]]></description>
			<content:encoded><![CDATA[<p>最近同人看到有<a href="http://www.plurk.com/p/cayqt">一則噗浪</a>提到「一個穩定的程式並非必然，而是偶然」在此噗浪的回應中，發送這則噗浪的噗友表達他對程式穩定性的看法。</p>
<blockquote><p>先問自己一個問題，一個程式有幾個 Bugs？如果是不可數，那～～它的穩定是相對非絕對，所以穩定不是絕對的，也不是必然的。</p></blockquote>
<p>同人覺得這位噗友的觀點很有趣，於是加入這則噗浪的討論。我問如果最開始的程式都是穩定的，那麼是什麼原因讓後來的程式變得不穩定？對這個問題，一些噗友提出了他們的看法，其中有一位噗友回應「是我的機器弄好的程式，跑到別人機器就不穩定了」，發送此則噗浪的噗友認為還蠻接近實際的情形。</p>
<p>他提到 Bugs 在自己的機器沒產生，在別人那邊可能會產生，問題可能是因為自己的機器有 Bugs，而不是別人的機器，他說假設機器不會出問題是會有副作用的。</p>
<p>那麼為什麼不在應用系統要運作的目標環境下直接開發程式呢？因為，嚴格來說，開發者不可能有完全一致的環境，因此開發者只能假設環境是不會改變。但實際上，在不同的機器上、甚至在相同的機器上，不同的時間可能也會出現難以預料到的變化，結果使得程式變得不穩定。</p>
<p>這位噗友認為，當我們愈信任一個系統，其實可能是一個災難，因此程式的穩定非絕對而是偶然，它是由一連串的偶然所累積而成的。同人發現這個觀點讓人很難反駁，在我過去程式開發的經驗中，並不乏遇到原來運作正常的程式，在不同時空環境出現問題的現象。正如同這位噗友提到的，作業系統與程式語言它們本身也都是程式，很難確保它們不會出問題。</p>
<p>相信很多人都曾遇到過，程式發生失常通常只因為一個令人難以捉摸的小錯誤，因此似乎真的可以說「穩定的程式是偶然的」。不過，如果穩定的程式真的是偶然的，程式的穩定就只能依賴運氣而不是人為努力，事情真的是這樣嗎？其實這位噗友太過強調<strong>環境變化</strong>的隨機性，卻忽略了適應環境變化，程式開發必然會經歷<strong>複雜演化</strong>的過程。穩定的程式是演化而來的，雖然演化的過程是偶然、但其最後結果卻是必然。換句話說，穩定的程式是<strong>偶然下的必然</strong>。</p>
<p>程式的運作通常需要處在多變的環境中，使得開發者很難確知他的程式有多少 Bugs。因此在本質上，程式開發過程是一個偶然，由一連串的隨機事件所構成的。雖然開發者可以使用與將來應用系統所運作環境相同的硬體及作業系統，但實際上終究並非同一台機器。它們可能會因為使用者的差異，而安裝了無法與應用系統相容的軟體或設定了會使系統出現問題的系統組態。就算是兩台機器能夠完全一模一樣，時空環境的變化也可能造成系統運作出現問題的關鍵，即使兩者的差異相當微小。</p>
<p>以上的現象就是程式開發的「<a href="http://en.wikipedia.org/wiki/Butterfly_effect">蝴蝶效應</a>」，就像天氣很難預測準確的道理一樣。一隻蝴蝶在巴西輕拍翅膀，會使更多蝴蝶跟著一起輕拍翅膀，最後將有數千隻的蝴蝶都跟著那隻蝴蝶一同振翅，其所產生的巨風可以導致一個月後在美國德州發生一場龍捲風。這是「<a href="http://en.wikipedia.org/wiki/Chaos_theory">混沌</a>」理論所討論的現象，未來的狀態對<strong>初始條件敏感</strong>而造成無法預測。在混沌系統中，初始條件十分微小的變化，經過不斷放大，對其未來狀態會造成極其巨大的差別。<sup>[1]</sup></p>
<p>除非所有 Bugs 都出現為止，否則程式開發者不會知道他的程式有多少 Bugs。因為他無法預測他的機器和實際運作的機器，有那些差異會造成程式運作出現功能失常。而且問題可能不是因為他的程式錯誤，有時候問題是出在作業系統或程式語言本身的 Bugs；它們都是程式，只要程式都有可能會有 Bugs。</p>
<p>現實真是令人沮喪呀，如果不確定因素使得環境變得如此難料，那要如何期待穩定的程式出現呢？我們不能期待環境不再變化，因為軟體開發的現實就是變化無常，唯一不變的真理就是變。我們也不能不去面對問題，進行規劃及管理，因為隨性地處理問題，只會讓問題變得更加混亂而讓情況失去控制。我們應該讓系統可以 <strong>適應環境的變化，發展出兼具穩定與彈性的程式</strong>，這樣我們就能在<a href="http://en.wikipedia.org/wiki/Edge_of_chaos">界於混亂與穩定的過渡地帶</a>中，逐步演進出複雜的行為來維持整體系統的動態平衡。</p>
<p>這就是<a href="http://en.wikipedia.org/wiki/Complexity_Theory">複雜理論</a>的<a href="http://en.wikipedia.org/wiki/Complex_adaptive_system">複雜適應性系統</a>的觀念，系統透過<a href="http://en.wikipedia.org/wiki/Self-organisation">自我組織</a>來穩定自己，不會因為無法控制混亂而崩解為混沌一片，同時也保有足夠變化的彈性來進化系統行為。就像同人在<a href="http://www.lifeparty.idv.tw/blog/archives/87">過去的文章</a>中，曾經提出複雜系統來比喻專案管理的隱喻一樣，複雜適應性系統的演化過程是不斷經歷觀察、假設、調整、再假設的重覆循環過程。</p>
<blockquote><p>整個專案環境所能供給的資源是有限的（成本限制），而演化競爭者會和我們爭奪這些有限資源（時間限制），所以對一個複雜系統而言，必須達成演化的使命而創造對整體系統的價值最大化（規模限制）。要做到這點，複雜系統必須能依現狀來演化系統，用最經濟的方式創造最大的價值（技術限制），演化則是先對環境的做出假設，然後依據適者生存不適者淘汰的原則來進行演化，過程中會不斷地修正對環境的假設並改變系統行為以求適應環境。</p></blockquote>
<p>因此，對於程式開發的專案當然也是相同的道理，演化的過程是無法預料的隨機行為，但演化的結果卻是必然的；發展出最適應環境及產生最大價值的穩定系統。假設的目的是為了做到將廣大的可能性限制在一小部分，這是同人在<a href="http://www.lifeparty.idv.tw/blog/archives/175">另一篇文章</a>引用 Peter Ho 所提到讓系統停留在混沌邊緣必須做的三件事之一。另外兩件事則是必須保持足夠的穩定，即使有輕微的改變，系統仍不會崩解成混沌一片、以及必須在靜止不動的死寂與過度活動的「混亂政體」間達到平衡。</p>
<p>這也就是同人提出先前穩定的程式，最後卻變得不穩定，到底問題是出在什麼地方？問題並不在我們對環境做出假設，而是無法意識到假設所帶來的風險，以讓開發程式停留在混沌邊緣以達成系統的動態穩定。因此，噗友提到假設會有副作用的問題不在假設，而在於開發者面對環境的反應的變化太慢，以致於造成局面的紊亂而使得問題無法收拾。</p>
<p>然而，如何及早反應環境的變化維持程式的穩定與彈性呢？同人推崇測試、重構與整合三項實務，誠如 Peter Ho 所說的「未經<strong>重構</strong>，系統將會沒有空間來容納新功能來適應新的變化驅力；未經<strong>測試</strong>，開發者不會知道系統邊緣在那裡；未經<strong>整合</strong>，生命火光將逐漸消散。」用紀律來維持系統彈性與穩定，雖然環境的變化是無可預料的，但我們仍將體會到穩定的程式是偶然下的必然！</p>
附註：
&nbsp;<hr/><ol class="footnotes"><li id="footnote_0_421" class="footnote">維基百科編者 (2008). <a href="http://zh.wikipedia.org/w/index.php?title=混沌理论&amp;oldid=8808028">混沌理論</a>. Wikipedia, . Retrieved 03:15, 1月 9, 2009.</li></ol>]]></content:encoded>
			<wfw:commentRss>http://www.lifeparty.idv.tw/blog/archives/421/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
