l

2012年1月20日 星期五

Single Code Base

January 20 10:11~11:51


昨天傍晚收到出版社編輯的通知信,告訴 Teddy 出版社已經通過編輯小姐所提的出書提案。雖然書本的內容還是需要加以增修,不過 Teddy 預估如果沒什麼意外的話今年內出版應該是沒問題了。想捧場買一本的鄉民們可以把小豬撲滿拿出來開始存錢了...大概一天存個3-5塊新臺幣也就夠了。或是考慮把年終獎金尾牙抽到的獎金、過年領到的紅包留個幾百塊下來,不要全部都「梭哈」在牌桌上...XD


***


話說 Teddy 昨天還在擔心無料可寫,想來想去還是回頭找「老朋友」,從以前看過的書中擠一些東西出來。今天來談一下 Kent Beck 所寫的 Extreme Programming Explained, 2nd 這本書第 67 頁所提到的 Single Code Base 這個做法。還記得當年第一次看到 Single Code Base 這個做法其實沒什麼特別的感覺,但是剛剛拿出來又看了一次之後,突然聯想到這個做法回答了一個軟體開團隊發經常會遇到的問題:


不同的客戶,對於公司所開發的產品有不同的客制化需求


對於這個問題基本上有兩個解法:

  • One code base for one customer: 只要有一個新客戶的客制化需求,就把 source code 從原本的開發主幹中複製一份(或是產生一個分支)出來。
  • Single code base: 不論有多少個客戶,多少種客制化需求,全部都放在一個單一的開發主幹中。

第一種作法看起來比較簡單,在剛開始客戶還不多的時候問題也不大,反正就是把原本的 source code 複製一份,然後在加上客制化的功能,最後打包給客戶驗收過後就沒事了。就算事後出包也是交給所謂的「維護合約」以及倒霉的「維護人員」去處理。


但是當客戶數量變多的時候,第一種作法就很有可能會產生很嚴重的「維護問題」。假設這個產品有 30 個客制化的版本,開發團隊某一天解決了一個在最原始那個版本中的 bug,請問這個 bug fix 要不要套用到其他 30 個客制化版本上?


感覺起來好像也許可能應該似乎要套用,但是,這 30 個客制化的版本已經各自「演化」一段時日了,要確定這個 bug fix 可以安全的套用到這 30 個客制化版本而不會產生新的 bugs 會是一件很花時間的工作。聰明的鄉民們可能會說:「不是有自動化測試案例可以用來驗證新的 bug fix 嗎?」套句賭神電影裡面的對白:「年輕人終究是年輕人」,果然是涉世未深。這裡是哪裡?台灣耶,就算是只有單一個軟體版本都少有人在寫自動化測試案例了,誰在跟你寫 30 個客制化的版本的自動化測試案例啊。想要知道血淋淋的慘案,請參考「600 多個 bugs 要怎麼修?」。

***

在課本第 68 頁,Kent Beck 叔叔告訴我們:


Rather than add more code bases, fix the underlying design problem that is preventing you from running from a single code base.


鄉民們可以把不同客戶功能差異的部分移到設定檔中,設計軟體系統使其可以經由讀取設定檔的方式來產生不一樣的行為。接著修改建構系統(build system)讓它可以從 single code base 裡面生出不同客戶所需要的客制化產品(安裝程式)。


要達到 single code base 會是一件相當有挑戰性的工作,但是如果想要有效率的開發出高品質的軟體,那麼請問有那一件軟體開發的活動不是這樣呢?有一些剛開始看起來好像很簡單或是很省事的做法,其實反而是造成後續軟體持續開發困難的原因。例如,因為「專案太敢 太趕」沒時間而省略不寫自動化測試,因為客制化要求而捨棄 single code base,因為沒人懂而不做持續整合,因為有人不爽而不做 pair programming 等等。


***


友藏內心獨白:這一篇是在 MacBook Air 上用 Apple  的外接藍牙小鍵盤所完成的。Bye bye,Ubuntu...

2012年1月19日 星期四

Scrum 是什麼(8):Sprint Review Meeting

January 19 09:40~10:42

螢幕截圖 2015-06-11 21.33.34

前情題要:


也不知道是那根筋不對勁沒事訂定什麼「2012 年每天寫一篇部落格文章」的目標,現在想一想真的是頭殼壞去。前幾天赫然發現,農曆新年快到了耶,還是要寫(當初「許願的時候怎麼沒把這點考慮進去啊...Orz)。到底能不能撐過第一個月都還不知道,算了,撐一天算一天,反正最近瘦了2-3公斤,還有一點食言而肥的本錢...XD。

剛剛想了老半天也擠不出什麼新東西出來,那今天就來談一下 Sprint Review Meeting 好了。這個會議有兩種可能的開法:

正常版本

  • 團隊中有專職的 Product Owner。
  • Sprint Review Meeting 時 Product Owner 、Scrum Master、Developers 都必需要參加,最好也邀請stakeholder 一起參與並提供意見。
  • 會議可由 Product Owner、Scrum Master 或是協調一位團隊成員主持。
  • 會議前一天 Scrum Master 要先 email 一份 Sprint review agenda 給與會人員,範例如下(或參考「Scrum 是什麼(3):三種補充文件」):

  • 會議前一天負責示範的人要花點時間準備(不要為了review花太多時間,最多最好不要超過 1-2 小時),千萬不要沒準備然後當場才發現要展示的功能都不能動
  • 會議開始時,主持人(Product Owner、Scrum Master或是Team都可以) 說明一下這個 sprint 的目標,然後依據會議議程逐一請相關人等展示功能。例如:
    • 主持人:「大家好,歡迎參加 E-Com 團隊第 5 個 sprint 的 review。我們已經完成了這個 sprint 的目標 xxxx,現在開始 sprint review會議。第一個 story 是 yyy,我們先請 David 示範一下這個功能要如何使用」。
    • David:...(開始展示這個功能)(在展示過程中,如果與會者有問題可以隨時發問)。
    • David:(展示結束)
    • Scrum Master:各位關於這個功能還有沒有其他問題?如果沒有的話我們謝謝 David,接下來請 Tim 展示這個功能的測試結果。
    • Tim:....
  • 主持人要幫忙注意每一個展示項目所花的時間,並且要避免與功能無直接相關且過於冗長的討論。
  • Product Owner 或是 stakeholder 如果對於功能不滿意,或是有修正意見,可以當場反應給 Developers 了解。至於後續要如何修改的細節,則不需要在會議中討論。
  • 如果經費許可團隊可以準備一些零食給與會者享用,可以把 sprint review當成是一個小小的「驗證與慶功場合」...當然如果團隊要是連一個 story 都沒有完成那就另當別論了...XD。

異常版本
 
  • 團隊中有沒有專職的 Product Owner(由其他團隊成員兼任),或是團隊在開發一個新產品,Product Owner 也正在摸索需求(翻成白話文就是 Product Owner 也還搞不太清楚狀況...Orz)
  • 會參加 sprint review會議的人(包含圍觀鄉民)大多是比較偏技術的開發人員。
  • 其他內容與前述正常版本都很像,但是在異常版本中,sprint review的項目可能還會包含「施工的 tasks」。也就是說,正常版本的review比較像是「black-box review」,與會人員只關心每一個做完的 story 要如何使用以及是否真的做完,不去管 Developers 倒底是如何做完這些 stories。而異常版本的review比較像是「black-box  + white-box review」,除了包含正常版本的review內容以外,團隊也可能會利用機會review技術性的 tasks,例如「database schema design」、「新的 DAO 」、「某某 bug 如何解決」等。

 

***

為什麼會有兩種不同的 sprint review 進行方式?照道理講應該是只有第一種正常版本,只要review功能如何使用以及讓 Product Owner 「驗收」這些功能這樣就可以了。但是實務上有些團隊所面對的問題(專案)在某些階段技術細節相當程度會決定專案的成敗 ,或是團隊想利用「大家都在」的機會「快速同步一下一些技術性的核心議題(與會者可能大多是技術背景的人)。在這種情況下,在 sprint review 時如果有需要的話也順便談一點「內幕消息(技術性議題)」應該是可以接受的。

***

***
 
友藏內心獨白:又在逆練九陰真經了

2012年1月18日 星期三

Sustainable Pace

January 18 09:58~11:25

這幾天作息有點不正常,晚上睡不著,白天爬不起床。有點年紀了,作息不正常這件事對於「生產力」和「身體健康」都有很大的殺傷力。昨天晚上也是弄到凌晨2點多才睡著,但是今天下定決心要把「時差」調整回來,早上8點好不容易趕跑周公之後吃完早餐就準備開工了。

昨天晚上睡得晚,睡前拿起一本買了一年半但卻幾乎沒看的書想說能不能幫助睡眠,沒想到愈看精神越好...Orz。這本書叫做 Kanban: Successful Evolutionary Change for Your Technology Business,Teddy 今天想談一下書中提到的 Sustainable Pace (穩定的步伐,可持續的速度)這個觀念。

Sustainable Pace 是 agile community (敏捷社群)很早就提出來的一個觀念,講單的來講就是說:「每週工作40小時」。看過 Teddy 部落格的鄉民們應該知道 Teddy 是不相信每天加班可以提高生產力這種作法。Teddy 年輕的時候也是幾乎天天加班,睡在公司也是常有的事。現在回想起來,雖然偶爾靈感來了加一下班可以有「驚人的生產力」,但是很多時候加班其實只是因為東西一直做不完,或是遇到問題卡住了,但是公司或是團隊(有團隊嗎?)也沒有提供一個機制協助 Teddy 解決問題,而 Teddy 也沒有能力可以只依靠自己就可以有效率地解決所有的問題。再加上專案的時程都是業務或是老闆「跟著感覺走」而訂定出來的,身為小小工程師的 Teddy,「事情沒做完怎麼好意思下班」呢?(迷之音:如果老闆叫你去煮沸海水,那不是永遠都不用下班了?!)所以不管有沒有實際的進度,也只能「撐在那邊」,至少「沒有功勞也有苦勞」,老闆看你這麼「拼」,就算是時程到了結不了案也不好意思對 Teddy 過於苛責。

Teddy 很小的時候家裡開了一家小小的塑膠加工工廠,趕出貨的時候除了工廠員工要加班以外,連 Teddy 也被「動員」去幫忙。加班和增加人力對於生產力有沒有幫助?絕對有,因為這是一間「代工工廠」,工人只是做著重複性的工作。

***

一般江湖上預估專案開發時程的作法,不外乎:

  • 分析需求:看看有多少個 use cases,function points,或是 stories。
  • 分析人力:看看可以投入多少人到專案中。
  • 分析風險:...誰那麼認真啊...XD。

假設有 100 個 stories,團隊有 5 個人,平均一人一個禮拜可以做完一個 story,所以這個專案需要二十週也就是大約五個月可以做完。

問題來了,「平均一人一個禮拜可以做完一個 story 這是怎麼估出來的?這裡講得「一個人」是指怎樣的人?剛畢業的菜鳥,還是已經有5-6年以上工作經驗的工程師,還是寫得一嘴好程式的待退資深員工?這「一個人」的技術背景是什麼?對 Java 很熟?很抱歉,這個案子要用 VB.NET...XD。

如果今天問題換成「下個球季洋基隊打入季後賽的機率有多少」?請問鄉民們會如何去「估算」這個機率?當然是要看「洋基和大聯盟其他球隊的實力」。也許這個比喻並不是很洽當,因為開發軟體並不是打球,但是軟體畢竟是要靠「人(一群人,也就是團隊)」所開發出來的,如果預估專案時程的時候所思考的只是「一個個沒有面孔的派遣人力」,那麼用這種方式所組成的團隊其「生產力」與「品質」都是很難預估的。
***

扯了老半天這和 Sustainable Pace 有何關係?鄉民們應該都有打籃球的經驗,打球的時候最重要的就是要選對 team members,持續的訓練,加上有機會就去參加各種比賽以提高團隊的實力。同一批人馬在一起打球,合作久了之後團隊的「戰力」就變得可以預估。開發軟體也是一樣,敏捷方法強調「人(團隊)」與「溝通,合作」的重要性。一個團隊在導入 Scrum 或是 XP 這種敏捷方法之後,大概六個月左右這個團隊的 Pace (腳步,生產力)就可以慢慢地穩定下來。當一個團隊找到自己的 Sustainable Pace 之後,「預估時程」這件事就變得「不是那麼討厭」,而且可以越估越準。

「培養團隊」是一件費時、費力、又費心的工作,不見得每個公司或是組織的文化都支持這樣的作法。還是那句老話,畢竟「加班」對於公司與員工而言,是一種最不花腦筋(更棒的是又不花錢...因為責任制啊...Orz)又可以「保護」雙方的一種工作模式,幹麼花時間去建立團隊的 Sustainable Pace 呢。
***
友藏內心獨白:我不當派大星,誰當派大星。

2012年1月17日 星期二

Scrum 是一組餐具

January 17 03:57~04:38


今天無緣無故又失眠了,看來明天又要晚起,翻來覆去睡不著乾脆起來吃點東西順便寫篇文章看看等一下會不會比較好睡。


前幾天一位非資訊科系的人(簡稱「非資訊人」)問 Teddy 是在怎樣的機緣之下開始實施 Scrum,為了回答這個問題 Teddy 就在想要怎麼跟這位非資訊人」先解釋一下 Scrum,突然之間 Teddy 有一個靈感:Scrum 就像是吃高級日本料理的時候所看到的一組餐具,讓我們把各式各樣的食物可以放到合適的餐具中,然後呈現給顧客 (Teddy 沒有高級日本料理的照片,只能用平價日本料理代替..XD)





Teddy 所說得「餐具」,就代表一種「框架(framework)」,而「食物」就是各種「實務作法(practices)」或是「軟體工程方法」經過大廚(團隊)實踐之後的產物。餐具的種類有限,但食物卻可變化無窮。餐具容易獲得,但要做出好食物卻需要一番功夫。

回想 Teddy 當年一開始也只不過是看了 Scrum and XP from the Trenches 這本書就跑去幫人家帶一個 Scrum 團隊,當然後來 Teddy 很認真看了好幾本 Scrum 的書也去上了 Certified ScrumMaster 課程,但總的來講最重要的成功因素還是:
  • 以前有過很多開發產品與做專案(失敗)的經驗...Orz。
  • 看了很多,很多軟體開發與設計的書還有論文。

翻譯成白話文就是說「有九陽神功護體,學什麼都快」。只要軟工底子好,配合上合適的「餐具」與大廚團隊,要端出一盤盤的好料理也是順其自然的事情。

***

友藏內心獨白:現在 Teddy 的生理時鐘是在活在那一國的時區啊?

2012年1月16日 星期一

Scrum 是什麼(7):Daily Scrum

January 16 10:08~11:24

螢幕截圖 2015-06-11 21.27.29

前情題要:

 

Daily Scrum 是從 sprint 開始之後的每個工作天 Scrum 團隊必須舉辦的一個活動,假設鄉民們的 sprint 為期兩週,那麼就會舉辦 9 次 Daily Scrum。依據 Scrum 課本上的講法,Daily Scrum 的進行方式為:

  • Daily Scrum 通常是一種「站立會議(standing up meeting)」,也就是說與會人員必須要站著開會。
  • Daily Scrum 進行時間以不超過 15 分鐘為原則。
  • 與會人員包含 Scrum 團隊(開發人員)與 Scrum Master,至於 Product Owner 可自行決定是否參加。
  • 舉辦時間(幾點舉辦)並沒有規定,假設公司上班時間為 9:00~18:00,有的人會在早上 9:20 舉辦,有的人喜歡在 17:00 下班前舉辦。
  • 會議開始,由開發人員逐一報告三件事:昨天做了什麼,今天預計要做什麼,有沒有遭遇的什麼困難阻礙。
  • 開發人員報告這三件事的「對象」並非 Scrum Master,而是其他開發人員。
  • 舉辦的地點在 task board 前面,開發人員報告的過程中,可以順便更新 task board(移動 task 的狀態)。

Daily Scrum 的目的除了可以讓 Scrum 團隊成員彼此了解整個團隊的狀態與工作進度(工作進度透明化),最主要的還是可以達到「險(曝露風險)」的目的,以便提早擬定排除風險或是應付風險的對策(不要等 sprint 最後一天,甚至是專案快結束前才突然冒出大問題來)
  • 如果有團隊成員報告說自己有遭遇的問題(call for help),則其他團隊成員可以主動協助。
  • 如果團隊成員遇到的問題無法在團隊中解決(例如牽涉到其他部門),則 Scrum Master 要出面幫忙協調。
  • 如果有團隊成員每天都說沒有問題,但是被困在同一個 task 上面已經好幾天都沒辦法做完,此時其他團隊成員或是 Scrum Master 可以主動了解看看該位團隊成員無法完成 task 個原因。

***

關於 Daily Scrum 有幾點心得 Teddy 想分享一下:
  • Teddy 個人比較喜歡在早上(9:20 或是 9:30)舉辦 Daily Scrum,但是 Teddy 也曾經聽過有的公司採取「彈性上班時間」,所以團隊成員可能在 9:00~10:30 甚至是 11:00 之後才會陸續到齊。Teddy 個人是不建議 Scrum 團隊採取這樣的「彈性上班時間」,但是如果這是既定無法更改的事實,也許把 Daily Scrum 移到下班前的 17:00 也是一種方法。
  • 如果有團隊成員表示今天不知道要做什麼的時候,可以先給他一點時間看看有那些 tasks 是可以拿來做的。如果真的都沒有,Scrum Master 可以「建議」幾個可能的 tasks,或是讓他跟別人 pair。但是,如果這種現象持續發生,則表示該名成員可能遭遇的一些問題,例如他的技能可能過於侷限在某個特定領域,此時 Scrum Master 要思考如何解決這樣的問題。
  • 雖然 Scrum 課本告訴大家不要在 Daily Scrum 時討論技術問題(解決方案),如果有需要進一步討論則在 Daily Scrum 之後再把相關人等留下來討論,無關的人就可先行離開。但是,如果鄉民們的團隊人數不多(例如 3-6人),或是幾乎每個人都會與所要討論的主題有關的話,那麼 Teddy 覺的並不一定要那麼嚴格規定都不可以在 Daily Scrum 中討論任何技術問題或是解決方案。不過大原則是,Daily Scrum 不可以開太久,15-25 分鐘 Teddy 覺的還可以接受,如果有一些討論會導致 Daily Scrum 超過  25 分鐘那就應該要另闢戰場。
  • 有些團隊成員在報告的時候會習慣看著 Scrum Master,為了被免搞到最後團隊成員好像都在跟 Scrum Master 報告,所以有些 Scrum Master 會選擇站在團隊成員的背後。

***

下一集:〈Scrum 是什麼(8):Sprint Review Meeting


***
友藏內心獨白:最近越睡越晚...XD。

2012年1月15日 星期日

防弊還是興利(2)

January 15 11:06-12:18


最近突然對「防弊還是興利」這件事情有很大的感觸。幾年前 Teddy 還在學校念書的時候,常常在期刊雜誌與研討會的論文集看到很多國外公司(例如 IBM、Microsoft、Google、Sun,以及一些記不得名子的小公司)的員工所發表軟體領域的論文,但是印象中好像幾乎沒有看過台灣民間公司的員工(大學或中研院這種研究單位不算)發表過類似的論文。為什麼?以下是 Teddy 的猜測:

  • 洩漏公司機密:身為公司員工,發表論文是否會有「洩漏公司機密」的疑慮。為了解除這樣的疑慮,公司還要找人先審查論文內容,浪費公司資源。
  • 對公司有什麼好處:員工發表論文對公司有什麼具體的好處?
  • 你很閒喔:員工居然還有時間可以去寫論文發表,代表這位員工太閒了,多派一些工作給他吧。
***

前幾天 Teddy 看到 Hadoop 1.0 版正式發表,如果稍微有聽過 Hadoop 的鄉民,應該會知道 Hadoop 是根據 Google 工程師所發表的論文中所談到的 BigTable、MapReduce 以及 Google File System 等概念的實做。目前 Hadoop 由 Apache 軟體基金會 (Apache Software Foundation) 所管理,其中 Yahoo 與 Cloudera 等公司都投入開發人員參與 Hadoop 的開發。

奇怪了,Google 沒事幹麼讓自己的員工發表論文「洩漏公司機密」,搞得其他競爭對手都可以學習模仿,開發類似的雲端計算架構?這麼一來雲端市場不是多了很多競爭對手?

Teddy 猜想(公堂之上假設一下,應該不犯法吧...XD),Google 的立場可能是:
  • 對自己公司很有信心。因為 Google 公司與員工不斷的在進步,所以把一些「既有的」知識透露一小點分享給社會大眾,不但不會對公司造成負面的影響,反而會對公司的形象加分。為什麼,因為自此之後,每當搞雲端計算的人提到 BigTable、MapReduce 以及 Google File System 這些觀念的時候,幾乎都一定會提到 Google(免費幫公司打廣告啊)。
  • 雲端計算是一種「生活習慣的改變」,這樣的改變所帶來的市場太大、太大了,Google 一家公司也不可能全部通吃。話說回來,光靠 Google 一家公司的力量要很快地讓大眾達到這樣的改變也是有一定的難度。所以,還不如分一點小甜頭給市場上的其他公司,一起把這個市場做起來,這樣子 Google 反而會是受益最大者。
***

有遠見的人會思考如何「把餅做大」,可惜大多數的人所想得還是如何「把別人的
餅搶過來(這算是一種紅海戰爭嗎?!)」。還記得 Teddy 在幾個月前所寫得「防弊還是興利」,其中引用前任衛生署長楊志良先生關於全民健保部份負擔這個議題所說得一段話:

由於不可能針對每個人訂定部份負擔,因此不管如何訂,對某些人總是太低,而某些人則是太高,那麼我們就要問:健保的目的是什麼?如果是減少浪費,那就訂部份負擔愈高越好,甚至100%負擔。天下沒有完美的制度,有利就有弊,兩害相權取其輕,兩利相權取其重,去掉弊常也把利取消掉了。若健保的目的是要達到全民健康照護,那麼部份負擔就只能訂得低,但接受某種程度的浪費

Teddy 真的很喜歡上面用藍色標注的這段話,同樣的觀念其實在很多地方都可以看到。例如,豐田汽車和他的經銷商合作關係,或是在 ted.com 上面的這段演講 Howard Rheingold: The new power of collaboration (有中文字幕)所要傳達的觀念。不知道是不是華人社會這種「多一事,不如少一事」的心態已經根深蒂固的默默存在許多人的心中(還是怕被爆料、抹黑?),所以當有些「不同的想法」出現的時候,主事者第一時間想到的往往不是「興利」,反倒是如何「防弊」,防到最後就如同楊志良先生所說得:「去掉弊常也把利取消掉了

***

2011 年 9 月 Teddy 去李國鼎故居參觀,看了李國鼎先生的生平簡介之後才知道原來他在經濟部長任內曾經因為「東亞紡織公司貸款案」被監察院提出彈劾。站在經濟部長的立場,原本就是要幫助經濟發展,協助企業經營。即使李國鼎先生應該算是公認的「清官」,但是他還是在「防弊還是興利」的戰爭中輸給了防弊,辭去經濟部長職務。時空回到現在的社會環境,想要有勇氣大刀闊斧去興利而又不怕被扣帽子的人恐怕只會越來越少了

***

友藏內心獨白:講這麼多,是你自己太反骨了吧你。

2012年1月14日 星期六

安裝先行

January 14 11:52~12:24


Test-driven development 或是 test-first development 中文有人翻譯成測試先行」,大意是說還沒開始寫 production code (就是一般所謂的程式)之前,先把 test code 給寫出來。奇怪了「待測程式(production code)」都還沒個影子,如何寫 test code 去測試它(不存在的東西如何測試)箇中細節暫且不談,總之「測試先行」的目的是希望開發人員能夠先從「end users(可能是最終的客戶,或是呼叫你的程式的其他程式或是其他開發人員)」的角度來看看自己等一下所準備撰寫的程式應該長成什麼樣子,之後再實際逐一把這些程式實做完成。


Teddy 今天要談的是另一個類似的觀念,姑且稱之為「安裝先行」。傳統採用 waterfall 開發流程的專案或是產品,安裝與佈署的問題通常都是最後才被考慮的問題。為什麼?因為東西都還沒開發完畢,幹麼去考慮安裝和佈署的問題啊(東西都還不能用啊)。但是如果團隊採用的是 agile methods(敏捷方法),那麼「理論上」每個 iteration 結束時,都應該要有一個可以執行的軟體(running software),所以對於敏捷團隊來說,在專案初期便應該需要考慮軟體安裝與佈署的問題。


關於這個問題 Teddy 和指導教授曾經發表過一篇持續整合的論文,其中有介紹這樣的觀念(如下圖所示)。






這兩天 Teddy 把 97 Things Every Programmer Should Know 這本書翻了一下,發現書中第 40 頁也有提到類似的觀念。


Deploy Early and Often
Starting your project with an installation process will give you time to evolve the process as you move through the product development cycle, and the chance to make changes to the application code to make the installation easier.


有興趣的鄉民們可以參考一下。
**


友藏內心獨白:當遇到防弊與興利的抉擇時,大部分的人還是會選擇比較不會被質疑的防弊