<?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, 20 Jan 2012 11:19:52 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.1.1</generator>
		<item>
		<title>在人後道人短的時盤</title>
		<link>http://www.lifeparty.idv.tw/blog/archives/6681</link>
		<comments>http://www.lifeparty.idv.tw/blog/archives/6681#comments</comments>
		<pubDate>Fri, 28 Oct 2011 17:49:38 +0000</pubDate>
		<dc:creator>jim yeh</dc:creator>
				<category><![CDATA[占卜]]></category>
		<category><![CDATA[專案管理]]></category>
		<category><![CDATA[時盤分析]]></category>
		<category><![CDATA[開發流程]]></category>

		<guid isPermaLink="false">http://www.lifeparty.idv.tw/blog/?p=6681</guid>
		<description><![CDATA[最近太陽進入同人命宮，也該繼續寫文章在網誌上發表了，不過同人最近碰到了一件事，讓我停下來正在寫作的文章，先來寫一篇分析這個事件時盤的文章。這個時盤是某個人在網路上在人後評斷是非的時盤。]]></description>
			<content:encoded><![CDATA[<p>好久沒有網誌上發表文章了，其實同人一直都有文章正在寫，只是最近在工作太忙碌，加上也許是上個月流月太陽會合壓在同人本命的十二宮，直到十月底太陽才離開十二宮，而讓人提不起勁來寫東西。最近太陽進入同人命宮，也該繼續寫文章在網誌上發表了，不過同人最近碰到了一件事，讓我停下來正在寫作的文章，先來寫一篇分析這個事件時盤的文章。</p>
<p>這個時盤是某個人在網路上在人後評斷是非的時盤。上個禮拜在 Facebook 的社群中看到有人提到拿 CMMI 和 Scrum 比較的討論。雖然同人並不看好在台灣導入 CMMI 對軟體發展的效益，但敏捷開發方法並不適用在 CMMI 的導入，這並不是關於 CMMI 的一項事實，即使在台灣成功導入 CMMI 的公司通常都是採用傳通的瀑布模式或是 V-model 的開發方法，但那並不代表 Scrum 和 CMMI 是互相排斥的。</p>
<p>在同人參與討論的過程中，我發現有網友對 CMMI 的觀念有問題，他大談所謂的能力和成熟，卻搞不懂什麼是 ML2 和 ML5。後來他才承認自己沒有學習過 CMMI 的理論，也沒有 CMMI 的實務經驗。不過，他還是很堅持他認為 CMMI 要成熟，就不應該用瀑布法這個不成熟的開發模式。</p>
<p>同人對他的看法回應成熟等級並不代表成熟度，而且提高 CMMI 成熟等級不等同品質比較高，而是因為問題的複雜度昇高和客戶有更高的要求，不管軟體模式的成熟等級多高，所開發出來的軟體都必須要具有相同的品質－能夠解決問題和符合客戶的要求，這才是真正的成熟度。品質不是由軟體模式決定的，因為它總是無法客觀衡量，而是由符合某個主觀價值判斷來認定。</p>
<p>或許成熟等級較高的軟體模式可以開發出更完美的軟體，但可能會因為不能符合客戶的主觀價值判斷，而無法讓系統有用處；反過來說，採用成熟等級較低的軟體模式來開發軟體會碰到更多的困難和障礙，但如果系統的問題與客戶的需求並不複雜，採用成熟等級較低的軟體模式反而是更靈活而彈性，能夠為客戶帶來更高的價值。</p>
<p>事實上，瀑布模式不是不成熟的開發模式，只是它要解決的問題和敏捷開發模式。因此不同軟體模式沒有成不成熟的問題，而是開發者是不是能夠因應解決問題的需要、和客戶的要求來採行適當的軟體模式，溫伯格說過評定某種軟體模式比其它模式成熟，那不是成熟而是幼稚。</p>
<p>這篇文章要談論的主角不是這位跟我辯論軟體模式成熟度的網友，而是另一位網友看到同人和這位網友討論的留言，讓他覺得不能認同。但他沒有在討論區提出他的質疑，卻在自己的塗鴉牆意有所指道同人之短。尷尬的是他這種在人後道人短的行為，很不巧讓同人在第一時間就發現了，卻還沾沾自喜的表現出自命不凡的樣子。儘管同人對他認識並不多，但他的行為卻讓我透過星盤的呈象來認識他。既然他喜歡憑個人的感覺任意的批評人，故弄玄虛地針對人的貶抑而非事情的討論，那麼同人也來從占星時盤超現實的角度，來看一看在人後道人短的行為是怎麼回事吧！</p>
<p>在 23 日凌晨 0:23，那個網友在 Facebook 的塗鴉牆中提到好像是看到同人的留言讓他想扁人，他說在網路不要扯些有的沒有的比較好。後來自己又在塗鴉牆的那則訊息中留言提到他所指的人話說太多而漏餡了。從他寫下訊息的時間來看，他八成是在說我，但令人疑惑的是網路上討論如果覺得我說的不對，為什麼不跟我討論而要私下放話，先前表面上跟我討論敏捷開發方法在台灣導入 CMMI 的情形，卻又私底下說我閒話，到底他是因為心虛還是把自以為了不起，我排出了他留言時候的星盤，看到他的技術能力其實也沒有什麼深入的本事呀！</p>
<p><a href="http://www.lifeparty.idv.tw/blog/wp-content/uploads/2011/10/11AN0036.png"><img src="http://www.lifeparty.idv.tw/blog/wp-content/uploads/2011/10/11AN0036.png" alt="" title="11AN0036" width="621" height="453" class="alignnone size-full wp-image-6682" /></a></p>
<p>時盤命宮在落在獅子座，這個星座有輕鬆、幽默、趣味的意味。再看到太陽和木星的對相，太陽的娛樂對上木星的深入，很清楚地顯示出想要在談笑之中表現出深入智慧的意向，這從他的網誌的名稱就能夠了解。只可惜太陽和木星的衝突相位，反而表現出誇大而輕忽的特質。</p>
<p>太陽落在天平座會合土星顯得表相化而嚴肅刻板，木星則是落在金牛座顯得現實而利益導向，在三九宮的對立代表在表達觀點的強調以自我為中心的經驗並沒有辦法將想法化為行動來身體力行，木星的凶相位常會讓人有說話不負責的態度，堅持以我為尊的權威態度只會得他人自大狂的評價。</p>
<p>火星是命宮中第一顆星，它是和技術有關的星曜，所以可以知道這位網友對技術的重視。但可惜火星只有凶相位，問題也是出在獅子座的以自我為中心，火星刑傷四宮的水星和金星，代表技術上的基礎有淺薄和無法聚焦的問題，主要的原因還是個性的衝動使話語衝口而出、以及想法未盡周詳的缺憾。尤其看到同人的長篇留言就覺得講那些有的沒有的讓他想扁人，這不正像極了火星在獅子座凶象的舉動呀！火星的凶象讓他沒有時間和耐性去弄清楚別人的觀點，那只是所謂「有的沒有的」。</p>
<p>至於命宮的月亮對衝七宮的海王星，正是以情緒反應以逃避的方式應對。月亮在室女座，是故以挑毛病的態度來表現自己的不滿，但又不說對方那裡說得不對，那是海王星正衝的矛盾情結做祟，轉以情緒批判人，而非面對歧異討論事。</p>
<p>同人後來聽說他還想把我的長篇留言拿來當負面教材，這讓我想到以前 Tom 先生也說喲哪桑學長的文章是負面教材。當年我還在《<a href="http://www.zdnet.com.tw/enterprise/column/softwaredev/">名家專欄：軟體開發見聞錄</a>》發表文章談到這是 Tom 先生<a href="http://www.lifeparty.idv.tw/blog/archives/391">信仰偏見的迷思</a>。我知道學長認識那個在人後道人短的網友，他的自大狂妄就好像當年的 Tom 一樣，自我膨脹成這樣真是不可思議。和學長一樣，我會說我從來沒想到我的留言要拿來當教材，我可沒那麼好為人師。同人只會好心提醒：如果為人不謙虛一點，只會讓生命的成長機會隨著傲慢而消逝呀！</p>
<br /><div class="googlePlusOneButton"><g:plusone href="http://www.lifeparty.idv.tw/blog/archives/6681"  size="standard"   ></g:plusone></div><br />]]></content:encoded>
			<wfw:commentRss>http://www.lifeparty.idv.tw/blog/archives/6681/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>不要把 TDD 和做測試混為一談</title>
		<link>http://www.lifeparty.idv.tw/blog/archives/6126</link>
		<comments>http://www.lifeparty.idv.tw/blog/archives/6126#comments</comments>
		<pubDate>Tue, 05 Jul 2011 11:22:29 +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=6126</guid>
		<description><![CDATA[最近讀到一篇文章〈不要盲目的 BDD / TDD，我對寫測試的看法〉，看完作者 XDite 反對不論如何都要導入 TDD 的理由，讓同人想提出我對這篇文章的看法。]]></description>
			<content:encoded><![CDATA[<p>最近讀到一篇文章〈<a href="http://blog.xdite.net/?p=2478">不要盲目的 BDD / TDD，我對寫測試的看法</a>〉，看完作者 <a href="http://blog.xdite.net/">XDite</a> 反對不論如何都要導入 <a href="http://en.wikipedia.org/wiki/Test-driven_development">TDD</a> 的理由，讓同人想提出我對這篇文章的看法。</p>
<p>XDite 在文章中前半段，提到他對做測試的看法：</p>
<blockquote><p>我個人的看法是認為在大多數的情形下，你需要對你的「軟體」寫 "Test"，而且是要先寫 "Test" 再行撰寫程式，也就是  Test-Driven Development。因為程式碼會逐漸龐雜，沒有人可以 write code without bug，也不可能每次都有辦法用手測出來，加上有時候寫錯程式時的損失不是事後修理就有辦法彌補的，所以我們必須要寫 Test  Case，及早抓出問題。</p></blockquote>
<p>XDite 說這是所謂寫<a href="http://en.wikipedia.org/wiki/Test_case">測試</a>的重要性。但他強調這卻不是程式設計師「可以」不合時宜的盲目在「任何類型」的專案強行導入 <a href="http://en.wikipedia.org/wiki/Behavior_driven_development">BDD</a> / TDD 的藉口。XDite 指出寫測試程式來確保程式正確性的解決方案，存在著額外多付成本的代價，亦即：</p>
<ol>
<li>「 寫測試 + 寫程式」 所花的時間，大概是純寫程式的 1.5 -2 倍時間。</li>
<li>「會寫 Test」、「正確的測到該測的部份」、「寫出好的測試」，都需要學習時間以及功力。</li>
</ol>
<p>於是 XDite 提出了四點原因，用來當成反對盲目 BDD/TDD 的理由。即使是程式設計師因為厭倦了維護在之前的專案後期的維護，因為修改程式碼而一再發生的問題（無論是小問題，大問題），而決定在下個專案，無論什麼專案類型都要導入 TDD / BDD，XDite 認為這在他眼中認為這是相當不正確的事情。</p>
<blockquote><p>1. 每個專案類型不盡相同，有的是要求高正確性且牽扯到金流問題且開發時間充裕。有的純粹只是 event，用過極丟。有的是幫人外包，只要求規格正確，開發時間不寬裕。有些則是混合型。有些部分的程式碼則是相當難以寫測試（如 View），C/P 值極低。應該考慮每個專案的類型甚至是 component 去決定哪部分該嚴厲的規定寫測試，而哪些部分可有可無。</p>
<p>2. Startup 前期應不應該導入 TDD/BDD ? 我認為不應該。為什麼，很多人都認為 TDD / BDD 是為了確保「程式的正確性」，所以無論如何我們都應該執行。卻忽略了 Startup 的成功要素是「快速驗證你的 Idea 的正確性」、「快速應付市場變化調整的速度」、「在市場廝殺中節省成本存活下來」。</p>
<p>3. 寫測試只是為了要能自動驗證「程式的正確性」、降低「程式出錯的機率」，但團隊合作開發程式最重要的是團隊中的「溝通」，大家對 function 和架構要有一定程度以上的理解，共同撰寫程式要有一定程度以上的 convention。變更任何重大架構（如 core function, db schema, 底層設計前）都要提醒大家。</p>
<p>如果每個人都只寫自己的，然後想改什麼東西照自己心情，沒有人想舉手之勞通知大家、跟隊友溝通。坦白說，那寫測試有個屁用，可能只是不會爆 production code，development 拉下來還是爛光光，還是要修到死。</p>
<p>完全不溝通、不制定規範，卻期待寫測試能夠解決一切，這樣的想法不是很奇怪嗎？</p>
<p>4. 無論如何，就算寫了再完美的測試，再完美的程式碼。程式還是可能在你完全預期不到的狀況爆掉，所以應該做的是要接受無論如何就是要修 bug 的這件事，然後想辦法把修 bug 的成本儘量壓低，也把因為 bug 會產生的損失也儘量壓低。</p>
<p>不要期待測試是萬靈丹。否則期待越大，失望也大。</p></blockquote>
<h4>到底是評論 TDD、還是做測試？</h4>
<p>同人覺得 XDite 這篇文章的論點令人混淆；他到底是在評論盲目導入 TDD 的行為、還是在探討開發者該不該多花時間寫測試程式，以確保「程式的正確性」？這兩者完全是不能相提並論的事情，同人很不能理解在前面提到寫測試程式來確保「程式的正確性」需要花更大的代價，而下一段就拿寫測試程式必然會忽略其它開發活動（如溝通、修正程式 <a href="http://en.wikipedia.org/wiki/Computer_bug">bug</a> 等活動）來否定 TDD/BDD 的價值。</p>
<p>難道 XDite 以為 TDD 就只是在做確保「程式的正確性」之測試而已嗎？假如 XDite 真的是這樣認為，那麼顯然他把 TDD 誤解成是在<a href="http://en.wikipedia.org/wiki/Software_testing">做測試</a>。不過，對於同人在文章後面留言提出的質疑，XDite 回應否定他把 TDD 誤解成寫測試，而是不喜歡一些沒有把 TDD 搞清楚的人，強行要把「TDD」和「測試」混在一起，綁在一起要求無論如何都要「TDD」。他提出了一種典型把測試跟 TDD 混淆綁在一起的狀況，是遇到下面這種混蛋邏輯：</p>
<blockquote><p>因為程式會爆炸，或者是以後改來改去又會暴很麻煩 =&gt; 所以我們需要寫測試 =&gt; 既然要寫測試，也許我們不應該事後補寫 Test Case，而是應該來試著 TDD。</p></blockquote>
<p>另外，XDite 還提到理想狀況開發者可以用 TDD 來開發產品，先寫測試去制定出規格，然後寫出實際程式通過測試，達到 <a href="http://en.wikipedia.org/wiki/System_design">System Design</a>。接著藉由不斷的寫新測試、新規格，然後寫出更多的程式，邊修邊測到 boundary。但現實狀況是，就算是熟悉 TDD 的開發者，他卻不一定是「<a href="http://en.wikipedia.org/wiki/Software_specification">規格</a>」、「<a href="http://en.wikipedia.org/wiki/Software_design">設計</a>」的主導者。</p>
<p>XDite 說在這樣的「現實狀況」，很多時候， Team 裡面有開發者不能強迫導入 TDD 的環節。強行推動只是互相絆倒和拖垮彼此的時程。XDite 認為 TDD 只在某種「單純」（專案成員素質接近一致，沒有 highly dynamic factor，目標單純）的專案、環境、Component 下適用。</p>
<p>雖然 XDite 對他的觀點，似乎並沒有留下和同人繼續討論的空間；我發現我後來的留言被當成 spam 處理。同人猜想也許他的文章說歡迎指教，但我的留言讓他感到威脅、或是覺得再繼續討論壓力太大，於是想中止和我就這個議題繼續討論。同人不想責怪他對我留言的反應，即使他這樣做只是要表達內心情緒的不滿，但我仍然不會停止對 TDD 的這個討論主題的思辨，並且會透過網誌來表達我的觀點。</p>
<h4>評論 XDite 的四點理由</h4>
<p>XDite 提到他不喜歡把「測試」和「TDD」綁在一起的混蛋邏輯，但它反對不管怎樣都要導入 TDD/BDD 的原因，卻又以寫測試案例的「極端」現象來反駁 TDD 的價值，顯得不合邏輯。</p>
<p>在 XDite 寫的四點原因中，除了第二點有明白地表達不該在 startup 前期導入 TDD 之外，其它三點看起來都好像是在批評寫測試程式的問題（還不論是不是先寫測試程式）。因此，這樣看來就很令同人費解；如果真如 XDite 所說的他不喜歡人們把測試和 TDD 綁在一起，怎麼會把寫測試程式出的問題都算在盲目導入 TDD/BDD 的頭上？如果連自己在談 TDD 的時候，都會和寫測試程式的問題牽扯不清，那麼又怎麼能責怪別人把「測試」和「TDD」綁在一起呢？</p>
<p>同人認為在 XDite 所提到的四點原因，它們並非錯誤，而是並沒辦法當作反對不管如何都要導入 TDD/BDD 的原因。雖然在第一點原因中 XDite 並沒有說錯，有些程式像是使用者界面程式的測試程式就很難寫，因此的確不大適合強行對他們進行 TDD。然而，當遇到這種情況的時候，使用者還是可以選擇用 Mock Object 把那些沒辦法寫測試程式的物件區隔起來，然後針對最多部分可測試具有演算邏輯的程式來進行 TDD。</p>
<p>因此，使用者界面程式沒辦法建構 TDD 的測試程式對於 TDD 而言，並不是什麼太大的問題。因為基本上 TDD 不是用來當做測試的工具，而是用來設計可以驗證符合使用者真實需要的工具。所以開發者只要針對可測試、或該測試的部分建構測試案例就夠了。TDD 的目的是要確認花費最小的努力來滿足使用者的需求，測試案例所要求的不是全面的完整，而只是確認達到剛好達到滿足需求的邊界，這將會促使開發者先以界面的思維來整合系統各要件。對 TDD 測試案例比較重要的問題，是如何設計可測元件與非可測模組之間的界面。</p>
<p>至於 XDite 提到對於某些程式沒有必要寫 TDD 的測試程式，同人倒會很好奇有什麼客觀標準、或是有誰可以決定什麼程式沒有建構 TDD 測試案例的必要？在同人導入 TDD 的經驗中，就曾經有開發者要求 Business Object 不要寫 TDD 的測試案例，當時他們的理由是它們沒有什麼太複雜的邏輯、而且不大容易會變動，所以應該不需要為它們建構 TDD 的測試程式。</p>
<p>然而，事實證明，接受開發者這個要求的決定，是整個系統發生苦難的開始。因為所有的問題都從 Business Object 的瑕疵開始引爆，直到把 Business Object 也納入 TDD 測試案例的範圍，系統程式的品質才能達到令人接受的水準。</p>
<p>再來，對於 XDite 唯一有討論到導入 TDD 的第二點，他認為 startup 前期不應該導入 TDD，因為 startup 的成功要素是「快速驗證你的 Idea 的正確性」、「快速應付市場變化調整的速度」、「在市場廝殺中節省成本存活下來」。然而，同人對此卻認為恰好相反，TDD 正是「快速驗證你的 Idea 的正確性」、「快速應付市場變化調整的速度」、「在市場廝殺中節省成本存活下來」的利器。</p>
<p>為什麼？不用 TDD，人們多半傾向會以自己所相信的基本假設來解答問題，也就是以自己的經驗或熟悉的事物，從核心出發來發展解決方案，以期待把事情做正確。在這種情境下，人們通常會想得太多，而容易有過度設計（over engineering）的傾向，而 TDD 則可以讓人回歸到從問題來決定系統的邊界，再以最符合經濟效益的方式來解決問題。所以自然能夠以最省時及省力的方式來做正確的事情，更可以在市場上獲得致勝關鍵。</p>
<p>XDite 說他不同意「快速驗證你的 Idea 的正確性」、「快速應付市場變化調整的速度」、「在市場廝殺中節省成本存活下來」，是「對  TDD 再適合不過」。但同人很清楚這「對於熟悉 TDD 的開發者而言」是很自然的，顯然 XDite  是忽略了我認為這句話成立的前提，在於開發者是否能熟悉使用 TDD 的節奏。</p>
<p>如果開發者熟悉 TDD  的節奏，當他沒辦法寫出測試程式的時候，代表他對系統所要解決的具體問題還不夠清楚。為了要弄清楚他必須更清楚去溝通，而且 TDD  的習慣會促使他傾向以使用者的情境去溝通，而不是拿功能要使用者告訴他該做什麼。只有在開發者瞭解使用者的情境之後，開發者才能具體而正確地寫出驗證程式的測試案例，這說明<strong>在 TDD 寫的測試案例不是做測試而是在做設計，而且不可能因為寫 TDD 的測試案例而讓開發者不做溝通</strong>。</p>
<p>所以 XDite 提出的第三點和第四點原因，或許對於做測試是可能成立，但在 TDD 的情況下卻是可能性很低。因為除了建構 TDD 的測試案例需要了解使用者情境更需要溝通之外，TDD 的測試案例本身就是有用的溝通工具。TDD 的測試案例是能夠經過驗證的具體規格、同時它也是相關元件、API、或是模組的範例程式。</p>
<p>當然 TDD 更不會有太要求測試程式的完美，而忽略修正 bug 的問題，因為 TDD 的測試案例並不要求完美，而只是夠用就好。而且 TDD 在實際上可以用較低的成本來修正 bug，因為它能夠自動發現錯誤，再加上重構能夠逐漸改善程式碼的品質，TDD 沒辦法確保程式一定不會出現錯誤，但它總是能夠以較低的成本得到較高品質的程式碼。</p>
<h4>TDD 必須很「單純」？</h4>
<p>XDite 說 TDD 只在某種「單純」（專案成員素質接近一致，沒有 highly dynamic factor，目標單純）的專案、環境、元件下適用。但在同人導入 TDD 的成功經驗中，很不巧就有專案、環境、元件不單純的例子。</p>
<p>在一個國內知名金控公司的銀行外匯系統建置專案，表面上專案的目的是為了建置新一代的外匯系統，而不想受制於舊有系統和廠商的牽制，但實際上客戶高層的想望是要架構能夠整合機構其它系統的基礎建設平台。然而，當詢問客戶高層對系統目標的意見時，他們給的回答竟是「把系統做到令客戶咋舌」這種有說等於沒說的答案。</p>
<p>專案的成員並非來自開發組織的單一部門，而是從各個組織挑選各種經驗和背景的人，雖然有專業能力資深的人，但也有對相關技術不熟悉的成員。可以想見，在專業、技術、能力、背景如此懸殊的情況下，因為「人的問題」而發生團隊衝突是很難避免的，這個專案的團隊衝突的課題，正好提供我<a href="http://ndltd.ncl.edu.tw/cgi-bin/gs32/gsweb.cgi/login?o=dnclcdr&#038;s=id=%22095NTUS5396084%22.&#038;searchmode=basic">碩士論文</a>的研究動機。</p>
<p>當時開發團隊過去沒有接觸過相關問題領域的經驗（事實上是客戶指定不希望找對有金融經驗的人來開發，公司沒有開發過相關領域的專案，而有不錯的系統建置專案，才是客戶會選擇我們的主要原因）、開發環境也是用開發團隊過去完全沒有經驗的技術。</p>
<p>客戶指名這個專案要使用 <a href="http://en.wikipedia.org/wiki/Service-oriented_architecture">SOA</a> 架構，由前端 <a href="http://en.wikipedia.org/wiki/JavaServer_Faces">JSF</a> 以 <a href="http://en.wikipedia.org/wiki/Webservice">Web service</a> 呼叫到 <a href="http://en.wikipedia.org/wiki/Application_service">Application Service</a>，呼叫後端直接存取 <a href="http://en.wikipedia.org/wiki/POJO">POJOs</a> 或是傳送訊息的服務、而中間物件的傳遞則透過 <a href="http://en.wikipedia.org/wiki/XStream">XStream</a> 的序列化以 <a href="http://en.wikipedia.org/wiki/JAX-RPC">JAX-RPC</a> 進行遠端傳送、至於元件包括軟體架構也都是從無到有按照系統需求逐漸演化成型。</p>
<p>專案團隊大部分的人都沒做過 TDD，加上這麼多「高度動態的因素」，而導入 TDD 竟然能夠成功，是因為我們運氣好嗎？這麼說必須忽略開發組織守舊勢力對新方法論、架構和技術的反撲，否則就很難解釋公司總經理的質疑和反對。實際的情況是我們不是運氣好，而是用對的方法讓我們做好準備。當公司高層開始質疑的時候，我們早就已經透過 TDD 快速驗證我們的方法沒問題，而讓總經理無法否決我們的設計成果。這讓人目睹 TDD 的真正價值所在，不是確認「程式的正確性」，而是讓人有勇氣並保持簡單的節奏。</p>
<p>同人認為 TDD 的成功並不在 TDD 自動確認「程式的正確性」，而在於TDD 讓人有勇氣把事情做好，讓人遵循良好解決問題的節奏和紀律。那就是：1.確認問題；2.依照問題情境，發展用來驗證方案的標準；3.設計方案並執行驗證，以達符合驗證標準；4.視組織需要改善方案，再次執行驗證以確認符合標準。</p>
<p>依照這樣的節奏和紀律，可以讓人在確認方案做正確之前，已經驗證方案是用來解決正確問題，以避免過度設計和設計不足的兩難。這正是傳統方法很難避免的問題；即使開發者擁有熟練的開發技術，但少了在事情做對之前先做正確事情的紀律，品質問題正是因此而叢生。</p>
<h4>現實的真相</h4>
<p>XDite 用「理想狀況」來反駁用 TDD 來瞄準、射擊、修正後再射擊的觀念，他說很多時候， Team 裡面有開發者不能強迫導入 TDD 的環節，強行推動只是互相絆倒和拖垮彼此的時程，這是所謂的「現實狀況」。這麼說似乎是意味了 XP 敏捷方法的 TDD 實務無法在「現實狀況」實行，而是「理想狀況」。這種說法對同人其實並不陌生，就像我在〈<a href="http://www.lifeparty.idv.tw/blog/archives/163">羅馬不是一天造成的</a>〉這篇文章所提到的情形：</p>
<blockquote><p>同人偶爾會與 X 君分享我的工作心得，他很羡慕我能夠堅持設計的理想，遵循好的設計原則及開發方法發展出軟體架構，像他就沒有這樣的機會。而和同學分享我的工作成果時，他則認為我堅持設計理念可以成功，是因為我們公司有足夠的資源可以讓我們玩，換成是小公司，就不會有這樣的成功條件。</p></blockquote>
<p>其實同人在那篇文章所提到的概念、架構和技術的驗證，所用的方法正是 TDD。但同人分享成功經驗看到人們的反應卻發現，除了羡慕和佩服之外，多半是認為我剛好遇到足夠的條件來支持我實踐理想，而他們的「現實狀況」一定不允許他們這樣做。</p>
<p>假如真的是「現實狀況」不允許開發者導入 TDD，那麼面對開發出來的軟體品質不彰，他們採取了什麼行動來面對現實呢？以同人在一家開發軟體產品的 startup 公司看到的「現實狀況」來看，他們只是試著更努力而辛苦地把過去的工作做得更好，可是彷彿都一直不能如願。</p>
<p>同人在那家公司分享過我的 TDD 的經驗，RD 經理認為或許應該要做 TDD，可是因為使用資料庫的關係，會讓 TDD 很難做到或是做了沒有太大的效益。同人並不太瞭解為什麼他會有這種顧慮，因為按照我過去的經驗，資料庫的部分可以用 Mock Object 或是 Hash Map 版本的實作來區隔開來，而且這樣做有優化設計的好處。不過，同人不便公開質疑主管對 TDD 的顧慮，而是選擇尊重他在「現實狀況」下的決定；等到適當的時機再導入 TDD，除了眼下的問題需要被克服之外，還要讓開發者們的步調都能一致。</p>
<p>直到同人離開那家公司之前，TDD 都是一直被人們掛在嘴巴上說，但從來未曾真正去做。然而，隨著需求愈來愈多，程式碼也愈來愈複雜。這時候，關於軟體品質愈來愈殷切的議題便是元件很難被獨立測試，而是必須整塊合在一起才能測試，但這在除錯上很沒有效率。於是各個元件要獨立測試變成一項非常重要的需求，但滿足此項需求最大的問題便是軟體架構需要動大刀，而更緊急的嚴重問題便是有一大堆在客戶端的 P1 和 P2 的 bug 需要解決。</p>
<p>於是在龐大的時程和品質壓力下，常讓 RD 和 QA 沒辦法在時限內產出符合品質要求的軟體。我們用的方法，就是一次又一次的讓 RD 以<a href="http://en.wikipedia.org/wiki/BDUF">大規模前置設計</a>（BDUF），然後等程式開發完成之後再 handover 給 QA。雖然表面上公司採用敏捷的開發流程，每天在固定時間召開 <a href="http://en.wikipedia.org/wiki/Stand-up_meeting">Daily Stand-Up Meeting</a>，但是看起來專案管理者的腦袋並沒有換成敏捷的思維，仍然是期待以更精確的預測來改善專案的進展，即使是他們對專案狀況的預估從來沒有準確過。</p>
<p>在這種「現實狀況」下，雖然同人已經提醒 <a href="http://en.wikipedia.org/wiki/Scrum_%28development%29">Scrum</a> 偏重管理面而非工程面，但由於 RD 經理對 TDD 仍然有疑慮而不敢貿然採用它來提昇程式的品質。整個開發團隊只是一遍又一遍地用和昨天用過的方法，來試圖解決今天相同的問題，但我發現專案管理者好像不合邏輯地希望得到不一樣的結果。它正符合愛因斯坦對精神錯亂所下的定義：</p>
<blockquote><p>一遍又一遍做同樣的事，卻期待不同的結果。</p></blockquote>
<p>同人看到專案管理者對「現實狀況」的理解是，因為開發者在事前（不用心而）想得不夠多，所以不能在一開始就把事情做對，而導致軟體運行發生錯誤。但同人認為「現實狀況」的真相卻是，關鍵在於開發者沒有辦法做正確的事情，在很多時候設想了太多無謂的問題，而把心力分散在處理瑣碎的事情，而無法專注在關鍵問題上思考。</p>
<p>這是因為無法快速驗證方案對解決問題的效果，往往在開發者花費大量心力脫離「<a href="http://zh.wikipedia.org/zh-tw/%E4%BA%BA%E6%9C%88%E7%A5%9E%E8%AF%9D#.E7.84.A6.E6.B2.B9.E5.9D.91">焦油坑</a>」之後，才發現真實情況有很多地方是無先無法預料到的。換句話說，這不是開發者做的東西不對或是不好，而是面對適應變化，沒有把握「動作要快、行動要小」<sup>[<a href="http://www.lifeparty.idv.tw/blog/archives/6126#footnote_0_6126" id="identifier_0_6126" class="footnote-link footnote-identifier-link" title="曾昭屏譯（2006年），《溫伯格的系統管理學：系統化思考（第一卷）》，經濟新潮社。">1</a>]</sup>的原則。</p>
<p>當然在「現實狀況」下，導入 TDD 的實務不見得真的能夠有效地解決問題，對於任何一種開發方法論，我們都不應該盲目導入，而是要弄清楚它的適用範圍和限制，TDD 當然也不例子。然而，對於把 TDD 和做測試混為一談，然後藉此推測團隊中「一定」有 TDD 不能強迫導入的環節，強制推行只會互相絆倒和拖垮彼此時程，同人認為這是扭曲 TDD 當成做測試的繆誤。</p>
<p>使用 TDD 來開發軟體，開發者實際花費在測試案例的時間並不會很長。因為程式開發者不可能只寫程式而不驗證自己的程式碼，否則無法確認程式符合規格，就不能認定他已經寫完程式了。既然驗證程式碼是他的責任，那麼測試先行只是確認程式碼符合規格的一種方式，這樣只是把後面應該執行的工作先做好，並不會增加開發者額外的工作量。</p>
<p>因此建構 TDD 的測試案例並不需要額外的工作量、也不大可能會因為執迷於測試而沒有花時間溝通共通架構、或是程式的 <a href="http://en.wikipedia.org/wiki/Debug">debug</a> 時間。又能節省開發者因為想太多而多花費過度設計的時間，而且可以支持驗證程式的重構來加強程式的結構，不會因為增加功能而影響程式碼的品質。</p>
<p>TDD 在開發程式之前先寫測試案例的用意，並不是為了及早自動抓出問題。雖然這通常是採用 TDD 能夠產生的附加價值，但 TDD 先寫測試程式的精神是為了驗證未來方案能夠符合需求，以確認方案是在正確的命題方向上發展。開發者發展解決方案，每前進一小步，都能夠知道這一步離他的目標更接近或是遠離，這讓他可以馬上採取行動來調整方向以減少偏差。於是開發者可以做到真正的面對現實，這正是掌握適應改變行動要快、動作要小的原則。</p>
<h4>不理究理者的未嘗知義</h4>
<p>採用 TDD 來開發軟體，並不能做到確保程式的正確性、或是降低程式出錯的機率。因為要做到這個目的你就必須「預測」程式會發生問題的一切可能性，並且預先把它們都放到測試案例中，才有可能達到這些目的。然而，事先做預測已經違反了敏捷開發的基本原則，也違反了 TDD 的基本精神，認為 TDD 的目的是為了確保程式的正確性、或是降低程式出錯的機率而把它和做測試混為一談，這只是不明究理的人未嘗知義的偏見。</p>
<p>就像在網路社群中，<a href="http://www.artima.com/weblogs/viewpost.jsp?thread=216434">James O. Coplien 以「宗教信仰」形容 TDD 的迷思</a>算是最典型的代表，它以 TDD 不可能達到測試良好的涵蓋率來批評它的效率不如傳統的軟體工程方法。同人曾經<a href="http://www.lifeparty.idv.tw/blog/archives/266">評論過這種觀點的繆誤</a>：</p>
<blockquote><p>品質的問題並不是測試效率太低；而是開始測試的時間拖得太久，使得分析設計階段的缺陷不斷地擴散而使開發人員窮於應付。例如開發者在設計的過程中有沒有去思考設計驗證的問題，還是把這個責任推給後續的品管作業，例如 code inspection，或是各種形式的審查作業？</p>
<p>這個答案無關乎方法論本身的優劣，而是關乎開發者面對問題的態度，這對品質有著直接而深遠的影響。沒錯，我們需要專注於思考，但思考的重點並不在用那一種檢驗方式比較有效率，因為那樣我們將會落入品質是檢驗出來的迷惘當中。而是應該思考，對這個問題為什麼我會這樣設計？我如何驗證它是可行的？有沒有可能它會無法解決問題或造成其它我沒有想到的問題？</p></blockquote>
<p>把做測試和 TDD 混為一談的觀點，忽略了 TDD 的價值不在檢驗程式的正確性，而是找出足以解決問題的系統邊界的設計過程、以及面對目標採用有效的節奏把事情做好。對於 TDD 而言，測試不是用來確認程式沒有錯誤的目的，而是定義系統邊界的手段與過程，因為品質的關鍵不在於確認沒有錯誤的程式，而是從程式接口之間發現軟體自然演化的脈絡；學習庖丁解牛的精神，以游刃有餘的技法來體會軟體開發之道。</p>
<p>至於 XDite 在意以「混蛋邏輯」把 TDD 和測試綁在一起，同人覺得根本就是沒有必要地在鑽牛角尖。當程式設計者說他想要寫測試程式來確認程式的品質，並不見得他真的是把測試和 TDD 綁在一起；而且就算是他真的把測試和 TDD 綁在一起，同人也認為無傷大雅。有時候，管理者不見得會瞭解程式開發者所表達 TDD 的正確的觀念，所以開發者必須學會用不太精確但表達到重點的說法對管理者提出需求。</p>
<p>這也就是說，對於程式設計者而言，他所需要的是擁有能夠提昇他程式品質的方案，而 TDD 是他想要可能可以符合需要的候選方案之一。提出測試的附加價值是讓管理者比較能夠接受的說法，當然程式開發者也有可能真的把測試和 TDD 綁在一起，但即使這樣也沒有關係。因為當他實際採用 TDD 開發程式後會發現程式品質的提昇不是測試，而是在<a href="http://en.wikipedia.org/wiki/Edge_of_chaos">系統邊界附近</a>發生<a href="http://en.wikipedia.org/wiki/Self-organization">自我組織</a>的演化過程。</p>
<p>很多時候，我們真的沒辦法期待人們觀念正確才開始動手執行，因為那樣等於你什麼都不用做，而且軟體開發的重點不是理論怎麼說，而是實務上怎麼做，程式開發者從實務上的執行才能真正瞭解，TDD 不能跟做測試混為一談。</p>
<br /><div class="googlePlusOneButton"><g:plusone href="http://www.lifeparty.idv.tw/blog/archives/6126"  size="standard"   ></g:plusone></div><br />附註
&nbsp;<hr/><ol class="footnotes"><li id="footnote_0_6126" class="footnote">曾昭屏譯（2006年），《<a href="http://www.books.com.tw/exep/prod/booksfile.php?item=0010341309">溫伯格的系統管理學：系統化思考（第一卷）</a>》，經濟新潮社。</li></ol>]]></content:encoded>
			<wfw:commentRss>http://www.lifeparty.idv.tw/blog/archives/6126/feed</wfw:commentRss>
		<slash:comments>16</slash:comments>
		</item>
		<item>
		<title>從公共建設的系統失常看系統開發的複雜性</title>
		<link>http://www.lifeparty.idv.tw/blog/archives/6092</link>
		<comments>http://www.lifeparty.idv.tw/blog/archives/6092#comments</comments>
		<pubDate>Fri, 24 Jun 2011 05:31:29 +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=6092</guid>
		<description><![CDATA[要認識系統開發的複雜性，這並不是一件很容易的事。不過，忽略它會讓我們和湊熱鬧的外行人一樣，從他人系統失敗的經驗中只能看得到表相；以為這只是犯了離譜的技術或方法論的錯誤。]]></description>
			<content:encoded><![CDATA[<p>談到系統開發，很多人都不反對它具有複雜性，至少不會認為它是很容易的一件事。然而，當聽到公共建設的系統失常的消息之後，人們似乎又會忘了系統開發存在的複雜性。我們經常會看到許多相關的評論，認為系統會發生錯誤是因為方法或是技術的問題，卻甚少探討有關系統開發的複雜性問題。</p>
<p>當然，系統發生錯誤的原因，也許真的很可能和方法或技術有關。但是否所謂的方法或是技術，就是一般對系統發生錯誤的評論中經常提到的方法或技術；不是開發者的技術觀念有問題，就是沒有遵照正確的流程及方法來做事？筆者覺得這樣的假設似乎把系統開發看的太容易了。</p>
<p>如果系統失敗的真相，真的是技術觀念、流程、或方法論的問題，那麼只要在系統開發過程中，加強這些因素就可以確保系統開發的成功。但從筆者系統開發的經驗顯示，這些因素雖然很重要，但它們並不是系統開發能得以成功的關鍵，而是要等到逐漸適應系統開發的複雜性之後，才能逐漸地改善增加系統成功的機會。</p>
<p>要認識系統開發的複雜性，這並不是一件很容易的事。不過，忽略它會讓我們和湊熱鬧的外行人一樣，從他人系統失敗的經驗中只能看得到表相；以為這只是犯了離譜的技術或方法論的錯誤。我們應該學習像內行人一樣，<a href="http://www.lifeparty.idv.tw/blog/archives/58">從系統失敗的教訓中看到成功的門道</a>，了解到系統開發複雜性本質的真相；不天真地以為技術或方法論可以解決系統失敗，而是深入省思系統開發的複雜性。基於筆者過去對系統失常的觀察及體會，想以這篇文章從公共建設的系統失常看系統開發的複雜性。</p>
<h4>從文湖線的系統品質談起</h4>
<p>自從捷運文湖線通車之後，筆者就常聽到有人對它的系統品質提出批評的意見。筆者記得以前在上班的途中，在路上遇到同事就曾和他討論到捷運文湖線的系統品質。這位同事林君的看法是以工程技術觀點看待系統失敗的典型，雖然他的觀點並不能夠讓我們看到系統失敗的全貎，但對系統開發的複雜性本質，倒是不失為一個不錯的思考起點。</p>
<p>林君知道筆者平常上下班都是搭捷運文湖線，然後再轉乘公車接駁。當時他問筆者搭乘捷運文湖線的人潮，筆者回答和過去文湖線未通車前搭乘捷運新店線相比，文湖線的人潮似乎並沒有新店線的人那麼多，雖然不見得會有位置坐，但文湖線的擁擠程度確實比不上新店線。林技術長猜測也許是人們對文湖線沒有什麼信心，所以搭乘的人比較少。</p>
<p>林君長年在國外定居和工作，因此以歸國華人的身份看待捷運文湖線的系統品質，會認為人們對捷運文湖線的系統沒信心是很自然的。文湖線在開始營運之初，發生過很多次嚴重的當機事件，因而讓人們覺得系統的品質不佳，這的確是不爭的事實。不過文湖線現在的狀況，確實已經比剛上線的時候要穩定許多。</p>
<p>依據<a href="http://www.google.com/url?q=http%3A%2F%2Fwww.trtc.com.tw%2Fct.asp%3FxItem%3D1129326%26ctNode%3D24508%26mp%3D122031&amp;sa=D&amp;sntz=1&amp;usg=AFQjCNFmHJfUYIIJjNdy24H8906j3-XYtg">台北捷運公司所提供的資料</a>，文湖線系統可用度與國際穩定度指標 MKBF 的兩項指標，可以客觀地證明，文湖線已是一條符合國際水準的捷運線。以筆者每天親身搭乘的實際體驗，我認為文湖線現在的狀況，其實並沒有像林君說得那麼糟糕。</p>
<p>筆者以過去參與的大型公共建設的系統開發經驗，讓我比較能夠理解文湖線剛開始上線的系統失常，直到後來才逐漸穩定的狀況。以筆者過去的系統開發經驗顯示，很多系統在剛開始上線的時候，總是會碰到一些偶然的意外，讓系統功能無法正常運作。直到專案團隊耗費足夠的時間與心力，找到系統異常的根本原因，才能針對問題對系統改善或調整使其趨於穩定。</p>
<p>因此筆者認為系統從剛開始上線到逐漸穩定，需要足夠的時間。剛開始是由於開發人員經驗不足，以致於不夠了解狀況而難以掌控系統以因應問題。等到開發人員從系統失敗中得到寶貴的經驗及教訓，並努力費心解決問題並改善系統，系統自然就會開始漸入佳境。很多系統並不像捷運文湖線或是高鐵售票系統那樣受到大家的注意，但其實每個系統在上線後，會出現問題的機率都是一樣的。</p>
<p>不過，林君倒是不這樣認為，而是認為由於開發者的觀念錯誤，才導致捷運文湖線的品質不良。以林君長期在國外開發系統的經驗，他很推崇老美做事的方法，按步就班地照著正確的方法來進行開發的工作，不求速成只求「把事情做正確」。反觀在台灣，他認為人人都是為了趕進度而求快速，經常省略了一些不應該省略的作業，為了節省時間，卻得到系統品質不穩定、甚至是系統失敗的代價。</p>
<h4>品質就是沒有錯誤？</h4>
<p>林君認為開發者為了讓捷運文湖線提早上線，沒有確實地把開發工作做正確，因而使系統品質變得不穩定而不斷發生系統異常的事故，所以應該歸咎於開發過程的觀念錯誤。林君的論點似乎是認為「品質就是沒有錯誤」，但這種觀念必須把系統開發的錯誤當成道德問題，否則它將會造成邏輯推論上的繆誤。</p>
<p>「品質就是沒有錯誤」會造成什麼邏輯推論上的繆誤呢？的確，當系統出現大量的錯誤時，毫無疑問地，不管系統功能如何，人們還是會因為品質缺失而認為它沒有價值可言。但反過來說，即使系統沒有發生任何的錯誤，我們也無法確認它的價值為何。<sup>[<a href="http://www.lifeparty.idv.tw/blog/archives/6092#footnote_0_6092" id="identifier_0_6092" class="footnote-link footnote-identifier-link" title="曾昭屏譯（2006），《溫伯格的軟體管理學：第一卷（系統化思考）》，p.294，經濟新潮社">1</a>]</sup></p>
<p>事實上，在追求系統不會出錯的完美之外，要讓系統有價值還需要對特定的人有用處。開發者致力於工程技術的完善，開發出不會發生任何錯誤的系統，即使盡到開發沒有瑕疵系統的道德責任，但這並不代表系統能夠為客戶或是使用者產生價值，而能夠贏得他們讚揚的肯定。</p>
<p><img alt="客戶真正的需要" src="http://andinspired.files.wordpress.com/2008/02/analogy.jpg" title="what the customer really needed" class="alignnone" width="449" height="336" /></p>
<p>這是因為追求系統在工程技術的完美、和系統符合客戶及使用者在問題領域真正的需要，根本就是兩回事。就像系統開發者常會碰到幾種導出使用者需求的障礙。尤其對於大型規模或複雜的系統，客戶和使用者通常要等待較久的時間，才能接觸到實際的系統。經過長時間對環境所產生的變化，加上他們在想法上的蘊釀，讓系統在認知和實際上的差距變大，於是便出現「是的，但是&#8230;」症候群的現象。開發者照著客戶或使用者提出的需求來開發系統，但等到系統開發出來之後，他們反而表示「是的，系統功能看起來很酷，但是它不是我想要的功能」。</p>
<p>「是的，但是&#8230;」症候群顯示系統開發在導出使用者需求的一種常見障礙；由於系統在被開發出來之前的不可見和不可觸摸性，客戶和使用者在還沒看到和接觸系統之前，根本很難說清楚他們需要什麼，而且有時候連他們都不知道自己要什麼。</p>
<p>除此之外，系開開發在導出系統需求還有兩種常見的障礙，一種是在系統範圍內存在「未發現的廢墟」，當開發者愈深入認識系統，愈會發現很多原來沒有被發現的系統問題；另一種是開發者和客戶或使用者的語言不同，因為彼此背景、專業知識、技能、以及關注的焦點不同，使雙方在溝通上採用不同的方式，增加彼此誤解和衝突的機會。<sup>[<a href="http://www.lifeparty.idv.tw/blog/archives/6092#footnote_1_6092" id="identifier_1_6092" class="footnote-link footnote-identifier-link" title="Dean Leffingwell &amp;#038; Don Widrig (2003), 《Managing Software Requirements: A Use Case Approach》, Second Edition, Addison Wesley Professional.">2</a>]</sup></p>
<p>由上面幾種導出使用者需求的障礙，我們可以了解僅僅系統開發要把需求弄清楚，就是一項艱鉅的任務。很多時候系統發生錯誤，問題的關鍵並不是開發者開發的系統有錯誤，而是沒有開發客戶或使用者真正需要的系統。因為在具備規模和體制的系統開發專案中，系統不大可能在層層工程驗證和確認過程把關之下，還會發生沒有把事情做正確的繆誤。但倒是很有可能因為系統開發在各種因素交互作用下產生的複雜性，讓人無法在混亂和秩序之間適當地調適而讓情況失去控制。</p>
<h4>系統開發的模糊地帶</h4>
<p>從工程技術的觀點來看，系統開發應該需要客觀及明確，其中不應該有模稜兩可和高度的不確定因素存在。但實際上在系統開發的過程中，卻讓我們發現要開發被嚴謹定義的系統，處處出現無法具體而明確清楚定義的難題。系統開發顯然存在模糊地帶，只是假設它不存在，更加容易讓人忽略系統的複雜性，而在系統出現混亂的時候無所適從。</p>
<p>系統開發為什麼會存在模糊地帶呢？理論上，只要能夠發展出足夠具體的需求規格，系統應該就不存在任何的模糊地帶才是。然而，即使沒有前面提到導出使用者需求的三大障礙，人也沒辦法藉由發展嚴密的系統規格，讓系統開發過程的模糊地帶因此而消失。</p>
<p>因為雖然定義具體的規格可以用來規範系統的一致性，減少系統發生模稜兩可情況發生的機會，然而，規格所規範的一致性愈高，也就代表系統的完備性愈容易受到限制；在系統規格沒有錯誤的情況下，不可能不出現任何邏輯上的矛盾。</p>
<p><a href="http://zh.wikipedia.org/wiki/%E5%93%A5%E5%BE%B7%E5%B0%94%E4%B8%8D%E5%AE%8C%E5%A4%87%E5%AE%9A%E7%90%86">歌德不完備定理</a>已經證明在理性的世界中，沒有任何的公理系統可以表現它的正確性，而在它所涵蓋的範圍中不發生任何的矛盾。只要公理系統所涵蓋的範圍足夠廣大，可以蘊含自然數所定義的成員，那就必然可以找到沒辦法證明也不能夠證否的命題。此外，也沒有任何的公理系統可以證明自己的正確性。</p>
<p>從歌德不完備定理可以讓我們理解系統開發的模糊地帶是必然存在的。對系統運作可能會碰到各種問題，如果開發者對問題領域沒有足夠的經驗，藉由數理公式及邏輯推演，沒有人可以定義出沒有模糊地帶的完備具體系統規格。當規格具有相當的涵蓋範圍，就代表系統存在更多的模糊空間，不然就代表了規格一定存在邏輯上的矛盾。</p>
<p>歌德不完備定理並不是說沒有系統的規格是完備的，而是指只要規格的涵蓋範圍夠廣，就沒辦法透過計算或是推論來證明它的完備性，否則將會以破壞規格的一致性作為代價。當然對於系統運作將來會面臨的某些情境，開發者可以增加規格的細節來擴大規格的完備性，但通常這樣做會影響到系統其它部分的功能，於是還是無法提昇規格整體的完備性，或是因此喪失規格的一致性。</p>
<p>因此，系統開發的模糊地帶是因為人們經驗的侷限，它們讓我們不可能用規格的正確無誤來證明系統對人的用處，也就無法確保系統的價值為何。因為單單要定義什麼是「完全正確無誤」就是個大問題，很多時候人不是不知道應該把事情做對，而是缺乏可以減少或避免犯錯的經驗。</p>
<p>一般而言，系統開發的錯誤包括缺少兩種典型的經驗；第一種錯誤是缺乏問題領域的經驗，使人沒辦法在規格中清楚定義系統，因為不清楚系統未來將面對的情境，沒辦法問正確的問題以做正確的事、第二種經驗則是缺乏工程技術的經驗，使人不了解系統可以採用的技術及方法，沒辦法回答正確的解答來把事情做正確。</p>
<h4>工程技術的對策</h4>
<p>在工程實務上，面對系統開發的模糊地帶，開發者並非無技可施。要避免沒有問對問題和沒有提供正確解答的缺失，開發者可以透過驗證（Validation）流程來做正確的事情，然後再運用確認（Verification）流程把事情做正確。這是面對系統開發的模糊地帶，工程技術所提供的對策，只是運用<a href="http://en.wikipedia.org/wiki/Validation_and_verification">工程實務的驗證和確認流程</a>，是否真能有效降低或避免系統開發的模糊空間？</p>
<p>從筆者實際參與系統開發的經驗來看，在系統開發過程中，開發者經常花費很多的心力在系統的驗證和確認上，但實際上它們對降低或避免系統開發的模糊地帶其實非常有限。主要的原因並非開發者的能力或是他們的使用的流程或方法不對，而是開發者終究會發現他們沒辦法完全忽略、或是排除工程技術以外有關人的因素。</p>
<p>筆者再舉另一個著名公共建設發生系統失常的例子。就像在幾年前，通過 CMMI ML3 的神通電腦開發高鐵售票系統，被人們詬病像登機的售票系統是忽視真正的使用者需求。以通過 CMMI ML3 的公司水準來看，人們很難能夠接受他們會開發出這樣的系統，不敢相信他們具有相當的軟體開發能力成熟度。</p>
<p>CMMI 工程領域中的驗證過程，它的目的是為了證實未來的系統，在預期中的環境中運作可以滿足預期的使用者需求，因此如果系統沒有滿足實際使用者真正的需求，很可能是因為開發者並沒有做好需求的驗證，並且以「用廣泛的方式驗證需求」來發展需求。</p>
<p>然而，高鐵售票系統沒有辦法滿足使用者真實需求，原因不盡然是需求驗證的問題。相信有參與過大型軟體系統開發專案經驗的人都會非常清楚，在開發過程中，開發者能夠主導需求方向的能力是很有限的。面對系統的真實情況，開發者並不如其他的利害關係人更能了解需求，可能很難分辨什麼樣的系統擺在預計的環境，才能滿足使用者需求。</p>
<p>當然，開發者可以用各種方法來探索各個候選的系統解決方案，然後運用各種廣泛的方式來確認需求；諸如用展示、雛型、模擬、或概念驗證等技術來驗證使用者需求。然而，除了筆者在前面提到的三大導出使用者需求的三大障礙之外，系統開發還會常碰到其它原因，讓「用廣泛的方式確認需求」也不能獲得使用者的真實需求。</p>
<p>例如負責需求確認的人員，並不是實際操作系統的使用者。他們可能會誤解使用者的需求、或是因為利害關係而忽略使用者的需要，等到系統開發完成後，系統使用者才發現系統不符合他們的需求。</p>
<p>上面的情況在大型的系統開發專案中很常見，開發者經常會碰到提出需求和使用系統並非同一個人的困擾。跟開發者談需求和確認需求的是資訊部門，當開發者把系統開發出來之後，交付使用單位操作以後，使用者卻又表示系統並不符合他們的需求。</p>
<p>為什麼客戶不讓開發者面對使用者直接溝通需求，而要透過資訊部門的中間人的角色？這個問題其實很複雜，很多時候客戶很難找到確切的使用者來讓開發者訪談需求。即使排除像高鐵售票系統的使用者是一般的民眾的狀況，其它企業內部的系統的使用單位，很經常會橫跨多個部門，要找到所有的利害關係人一齊開會，其實是非常不容易的。</p>
<p>其實還有更主要的原因是客戶在策略觀點、和使用者作業觀點的不同調。客戶的決策高層可能不只是希望系統將現行作業自動化提昇效率，他更希望系統能夠運用資訊科技的優勢來改進作業流程，並改變使用者習慣來創造效益。所以並不希望開發者對系統需求的認知，被使用者的作業習慣框住。客戶端高層對系統的看法，經常會與實際操作系統的使用者的需求南轅北轍，很難能夠從當中找到讓每個人都滿意的系統需求，而只能從當中做出某種程度的取捨。</p>
<p>雖然系統開發在驗證和確認流程並不是對人而是應該針對事，可是忽略人的因素就很難能夠得到執行它們應有的成效。記得筆者曾經經歷過系統經過客戶的 Power User 測試需求的系統，最後在系統驗收之前，卻還是被客戶要求變更需求。這對開發者也許是難以理解並接受的情境，但其實這正是系統開發複雜性本質的展現。即使對企業需求更了解的資深使用者也很難發現自己提出的需求是有問題的，更何況有時候需求的變化是來自人力所不能抗力的環境因素所造成的，開發者很難能夠對他們來多加以責難。</p>
<h4>社會技術的關鍵因素</h4>
<p>因此，從上面工程技術經常碰到的困難我們可以清楚知道，系統開發即使透過嚴謹的工程技術，也不可能忽略人的因素，因為即使工程技術沒有出現錯誤，也不能證明它是對人有用處而展現價值。這正說明了系統開發的複雜性並不是單獨以工程技術的觀點就可以理解，而是更需要正視有關於社會技術的觀點。</p>
<p>社會技術觀點和工程技術觀點最顯著的差異，正是前者並不如後者的客觀，這也代表是非對錯沒有固定不變的標準，常常同一件事在不同的專案或基於人的不同立場而會有不同的結論。其實系統開發的複雜性正是因為無法限定在單一的觀點，而是必須考量各種不同面向的多重觀點。</p>
<p>因此，系統開發需要考量多重觀點，要先從取捨系統需求出發，這代表系統開發者必須做好利害關係人管理，它對系統的成敗具有積極的決定因素。利害關係人管理需要識別出專案的利害關係人，然後挖掘、管理、以及影響他們的需要及期望。專案的利害關係人包括主動參與專案的人員、或是他們所感興趣的事情，會對專案的產出或成敗有影響的個人或組織。</p>
<p>系統開發應該要儘可能地滿足每一位利害關係人的期望和需要，然而，一旦利害關係人之間，彼此意見衝突而且無法協調共識時，那系統就必須要以滿足專案的客戶為主。也就是說要把客戶當成是關鍵的利害關係人，為了滿足他的需要和期望，其他人的需要及期望都可以被放棄。</p>
<p>然而，有些時候系統開發者眼前直接面對的客戶、或是專案的贊助者，他們的意見不見得會讓系統變得更有用處，而且經常還會讓系統變得更不容易使用，反而讓人們沒辦法接受系統。因此，在這種情況下，可能會促使<a href="http://jonathanspeaking.blogspot.com/2007/02/stakeholder-management.html">開發者認為應該思考使用者實際的需要</a>，因為可能他們才是最重要的利害關係人，而不以滿足客戶或專案的贊助者的需要和期望為目標。</p>
<p>不過，這種想法的問題是開發者通常沒有能力判斷誰的期望和需要比較重要。當然使用者是直接使用系統的人，他們的期望和需要或許應該優先考慮。但客戶和專案贊助者是真正出錢的人，當他們和使用者的意見不一致、甚至是發生衝突的時候，考量現實的因素，開發者真的很難為了符合使用者的期望和需要，而違逆客戶或專案贊助者的要求。</p>
<p>其實開發者運用他所熟悉的工程技術，通常是不能分辨誰才是真的利害關係人，因為對於客戶所關心的問題領域，有太多他不清楚的事情。有時候，開發者基於工程技術感覺應該是對的事情，往往最後才會發現它可能是錯的。當然，其它像客戶、或專案管理的單方向觀點，可能會受到對解決方案領域的認識有限，也沒辦法分辨誰才是真的利害關係人。當我們聽到有人主張「客戶永遠是對的，所以你應該照我說的這樣做」就會知道這絕對不是真理而是偏見，忽略了系統開發的複雜性來自多重面向。</p>
<h4>在混亂中找秩序</h4>
<p>既然系統開發的複雜性不能忽略多重面向，那麼我們就不可能在問題領域和解決方案領域之間選擇一邊來強調系統開發的核心思想是什麼，而是應該在各種不同面向觀點的交界之處，去找到適應系統開發複雜性的關鍵所在。換句話說，系統開發必須調合各種陰陽面，從未知和已知之間、混亂和秩序之間、結構和非結構之間，去尋找可以融合各種觀點的最大可能性。</p>
<p>要調合系統開發的各種陰陽面，以融合各種觀點最大的可能性，依複雜適應性系統（complex Adaptive System）的觀念來說，停留在會發生自我組織的混沌邊緣以適應變化，可以從三個方向著手。首先必須要把廣大而無所不包的可能性，限制在一小部分、其次是必須保持允許輕微改變的穩定性、最後是必須要在靜止不動的死寂和過度活動的混亂之間維持平衡。</p>
<p>這三個方向讓我們看到了適應系統開發複雜性的三大重點，那就是系統必須是可測試的、擁有允許改變的彈性、以及必須能夠持續的整合以維持動態的平衡。測試是為了知道系統的邊界在那裡、彈性是為了容納更多需要的變化、整合則是讓演進系統的火光能夠迸現。</p>
<p>於是，面對環境變化的無常，開發者需要自我組織來演化出適應複雜性的能力。筆者認為這種能力才是系統開發品質的關鍵，以工程技術和社會技術之間的調適，來尋求不同領域觀念之間的融合。這樣就能夠使系統在變化的環境中，能夠保持穩定的動態平衡。</p>
<p>就好像《太極拳論》提到「偏沉則隨，雙重則滯」的觀念一樣。工程技術重視規律和秩序，而社會技術則強調適應變化和彈性，兩者各有所長而無法偏廢。在開發過程採用（adopt）、調適（adapt）、並熟練（adept）不同著重點的轉移，是讓人學習在混亂中找秩序的法門與修煉。當然筆者承認這實際做起來真的不大容易，但至少它不會讓人用簡化的思維來看待系統開發。</p>
<h4>後記</h4>
<p>這篇文章本來是準備投稿 <a href="http://www.zdnet.com.tw/">ZDNet Taiwan</a> 的文章，從去年八月和同事的對話引發同人的寫作動機開始，到最近才把整篇文章完成，歷經了將近一年的時間。在寫作過程中，文章經過無數次的修改，希望能夠寫出不同以往談論系統開發複雜性的文章，沒有深澀的術語、也能用非技術專業的觀點來看到我對複雜適應性系統的領會。</p>
<p>這正是同人想突顯和另一篇有關系統開發複雜性的文章〈<a href="http://www.lifeparty.idv.tw/blog/archives/175">軟體開發是工藝還是工程</a>〉最大的不同，也算是對我最近兩年在系統開發所行、所思、所得，做一個比較全面的整體論述。系統開發真的需的有效的方法，但沒有任何一套方法可以忽略人的因素而能夠正確無誤地運作。</p>
<p>很多時候，讓人們沒辦法流程把事情做好的原因其實很簡單，但人的情緒卻通常把它變得很複雜。尤其是高階主管的情緒，不去觀察現象來瞭解執行和計劃的落差，以調整計劃來做正確的事情，卻以批評與責難來反應自己的情緒，這正是顯示高階主管在系統開發管理的無能為力。這種無能代表他們除了只會讓人「聽命辦事」之外，其它對管理其實是一無所悉。儘管他們能夠一直用口號來「教育」員工，並藉此檢查工作者的工作態度對不對，但他們的管理能力只能讓他們「簡化」系統開發的複雜性。</p>
<p>這篇文章比〈軟體開發是工藝還是工程〉更擴展它的詮釋領域，系統開發不應該只限制在軟體開發上。雖然軟體的抽象性會增加系統開發的困難度，但系統開發複雜性的本質所在，在於技術和業務觀點相互撞擊而產生各種變化，它不只是存在於軟體開發的系統中，對其它非軟體的系統開發也是相同的狀況。</p>
<p>同人並不想讓這篇淪為整理過去文章的新瓶舊酒，而是以這兩年系統開發實務得到的經驗，融合複雜適應性系統的觀念，內化成系統開發調適變化與紀律的動態平衡之道。雖然這篇文章也許寫了太久，以致於錯過了投稿 <a href="http://www.zdnet.com.tw/enterprise/column/softwaredev/">ZDNet Taiwan 專欄</a>的時機，但發表在網誌也算是對自己有交待和對喜歡《<a href="http://www.lifeparty.idv.tw/blog">同人的生活派對</a>》<a href="http://www.lifeparty.idv.tw/blog/archives/category/%e8%bb%9f%e9%ab%94%e9%96%8b%e7%99%bc">軟體開發</a>和<a href="http://www.lifeparty.idv.tw/blog/archives/category/%e7%ae%a1%e7%90%86/%e5%b0%88%e6%a1%88%e7%ae%a1%e7%90%86">專案管理</a>文章讀者的回饋。讀者的支持是作者寫作最大的動力，不管是在網路專欄或是個人網誌，同人將會持續寫作來分享我在軟體開發和專案管理領域所體會的點點滴滴。</p>
<br /><div class="googlePlusOneButton"><g:plusone href="http://www.lifeparty.idv.tw/blog/archives/6092"  size="standard"   ></g:plusone></div><br />附註
&nbsp;<hr/><ol class="footnotes"><li id="footnote_0_6092" class="footnote">曾昭屏譯（2006），《<a href="http://www.anobii.com/books/013ad41f7a862e80dd/">溫伯格的軟體管理學：第一卷（系統化思考）</a>》，p.294，經濟新潮社</li><li id="footnote_1_6092" class="footnote">Dean Leffingwell &#038; Don Widrig (2003), 《<a href="http://safari.oreilly.com/032112247X">Managing Software Requirements: A Use Case Approach</a>》, Second Edition, Addison Wesley Professional.</li></ol>]]></content:encoded>
			<wfw:commentRss>http://www.lifeparty.idv.tw/blog/archives/6092/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<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>
<br /><div class="googlePlusOneButton"><g:plusone href="http://www.lifeparty.idv.tw/blog/archives/2784"  size="standard"   ></g:plusone></div><br />]]></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>
<br /><div class="googlePlusOneButton"><g:plusone href="http://www.lifeparty.idv.tw/blog/archives/2764"  size="standard"   ></g:plusone></div><br />]]></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>
<br /><div class="googlePlusOneButton"><g:plusone href="http://www.lifeparty.idv.tw/blog/archives/2737"  size="standard"   ></g:plusone></div><br />]]></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>
<br /><div class="googlePlusOneButton"><g:plusone href="http://www.lifeparty.idv.tw/blog/archives/2716"  size="standard"   ></g:plusone></div><br />]]></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>
<br /><div class="googlePlusOneButton"><g:plusone href="http://www.lifeparty.idv.tw/blog/archives/2669"  size="standard"   ></g:plusone></div><br />]]></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>
<br /><div class="googlePlusOneButton"><g:plusone href="http://www.lifeparty.idv.tw/blog/archives/2114"  size="standard"   ></g:plusone></div><br />]]></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）。筆者發現這些造成失敗的條件，其實正是表現人性弱點的不同面向。 弱點 弱點是做想做的事卻做不到，它是軟體失敗的終極源頭。因為人不是完美的，他們做不到設計所要求的，不論那是一個程式設計，或是一個過程設計。溫伯格認為管理階層的責任是設計出一個程序以規範程式如何修改，承認自然界的事實，與確保程序本身被執行。而且他認為人們傾向在發生錯誤後懲罰嫌疑犯其實很不好，因為他會讓人隱藏錯誤、浪費時間在找嫌疑犯、以及分散注意力忽略管理階層的責任；建立並執行能及早找出失敗，並預防悲慘後果的程序。 愚蠢 愚蠢是做到想要做的事，但它卻是錯的事。愚蠢的基礎是無知，雖然它在當下沒有發生錯誤，卻會在以後造成錯誤。不過透過學習可以改善無知，進而將愚蠢矯正過來。溫伯格認為建立完整訓練師徒制、技術審查計劃、提供落實計劃的支援，是管理階層可以用來矯正愚蠢的職責。 執迷不悟 執迷不悟是指不肯學習，一直做出蠢事，一次又一次的做。此外，想要管理好一個愚蠢的人，卻不提供他根除愚蠢所需的訓練和經驗，這也算是一種執迷不悟的行為。溫伯格認為在軟體工程機構中，除了把執迷不悟的人送到其它行業去，否則沒有什麼防護措施可以抵擋執迷不悟的人。 好玩 好玩是程式設計師會寫一些奇怪的程式來為自己找樂子，溫伯格認為沒有人能夠預測別人認為好玩的事是什麼，因此好玩的心理是所有失敗的源頭中最危險的一個，因為它防不勝防。但管理者應該提供預防之道：一是開放透明的系統，另一個則是讓單單工作本身具有足夠的趣味。 欺騙 欺騙是用非法的方式從一個系統中獲取個人利益。溫伯格認為好玩是在失敗的源頭中，帶來的最小的損失。因為一個系統找樂子的方法有千百種，但值得一偷的東西卻沒多少。他認為軟體工程經理要好好閱讀以資訊系統詐騙為主題的文章，並採取一切可能的預防措施來防堵它。 狂熱 狂熱是試圖摧毀或瓦解一個系統，而原因不是為了個人利益，而是為了報復。溫伯格認為防範弱點而採取的行動中，多數也可以減少恐怖份子所造成的威脅與影響。 硬體功能失常 溫伯格提到硬體若不能造著當初設計的目的而執行工作，就會造成功能失常的現象，這類問題多半可以用軟體來克服。他認為當人們抱怨硬體造成他所寫的軟體出問題時，我們應該找出它表達的意思，以免遺漏這句話所帶來的重要資訊： 硬體沒什麼大不了的功能失常，但程式設計師需要找藉口來隱瞞一些事實。 [...]]]></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>
<br /><div class="googlePlusOneButton"><g:plusone href="http://www.lifeparty.idv.tw/blog/archives/2063"  size="standard"   ></g:plusone></div><br />]]></content:encoded>
			<wfw:commentRss>http://www.lifeparty.idv.tw/blog/archives/2063/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>

