- 普遍的軟體專案的風險:
- 先天的時程規劃錯誤(schedule flaw)
- 需求膨脹(requirement inflation)
- 人力流失(employee turnover)
- 規格崩潰(specification breakdown)
- 低生產力(poor productivity)
- 常見的五個普遍的軟體專案風險,只有最後一項真正跟員工的工作表現有關,其餘四項都跟做得多辛苦、多精明幹練無關
- 預先為無法捉摸的事物進行合理的準備,正式風險管理的核心,預先準備並不能讓妳為失敗開脫,只能在風險造成威脅時保有緩衝的餘地
- 時程規劃錯誤是指時間的安排有缺陷,這是時程本身的錯誤,而非專案執行的錯誤。時程錯誤不僅是真正的風險,也是五個核心風險中對專案衝擊最大的。
- 七個月的專案最後卻做了十二個月,生氣的高階主管很少會怪時程規劃的人,相反的,他們會怪人員沒有盡力
- 從專案的角度來看,刪除某些已經做好的功能,也算是一種需求膨脹,因為這同樣增加了額外的工作負擔
- 在規劃時程時,常會忽略需求膨脹的議題。高階主管也不會管,類似的說詞是:客戶要X,預估十個月後交付;若你發現還要X以外的東西,那就是你的問題了
- 處理人員流失,你必須知道:
- 公司技術人員的每年平均流動率
- 接替人員完全進入狀況時間(ramp-up time)的預估值。想辦法做好教育訓練與技術文件,降低進入門檻
- 規格崩潰往往溯及專案初期談判過程的失敗,而談判正是需求界定的中心
- 界定需求時,為了避免與利害關係人有衝突,常會將一些關鍵的、有爭議的的問題模糊化。被掩飾的問題暫時不見,不代表永遠不見。界定需求也許可以搞得模糊,但是開發階段可模糊不得。最糟糕的是,這些問題都是專案後期才會發生,這個節骨眼幾乎是將預算與時間耗盡的階段,常給專案致命一擊
- 閉口不談風險,並不會讓風險消失
- 在工作上,我們都被要求保有can do 的態度,問題就在這;討論風險是一種cannot do 的思維,往往風險探索會背離組織的主流意見。組織要有開放的文化,讓負面言論有說出來的可能
- 「明確過程」有明確步驟、可預測的程序,相當於white box 。經驗過程則相當於black box,是經由不斷的觀察與驗證後,以經驗來調整輸入,進而產生滿意的輸出
- 風險舒緩是預先採取的一套作為,萬一風險成形,便可有效的、快速的處理風險
- 有風險認知的管理者,會優先考量含有重大技術風險的部分,這非常合理,不過也有違大部分管理者的本性,因為會太早暴露他們在這場競賽中的弱點。這就是為什麼對如此棘手的問題,他們總是秘而不宣,一拖再拖
- 如果系統有哪個部分必須仰賴技術上的突破才能完成,就該把它納入早期版本,到時候就算突破不了,應變選擇也會擁有最大的彈性。假如夠早,也許私下承受一點損失即可;反之,若把相同的挫折放到專案後期,影響層面將會擴大到每一個人
- 漸進式交付是降低軟體專案風險的好方法,其是由三種artifacts所構成:
- 設計藍圖:顯示要識做的最低階模組或類別,以及彼此之間的關係
- WBS(Work Breakdown Structure):顯示要完成的工作,以及彼此之間的相依關係
- VAT (Version Acceptance Test):把產品的總驗收測試按照版本細分,顯示哪個測試可用在哪個中間產品上
- 冒險的積極程度必須由報酬而決定。你願意冒多少風險,端視你可以得到多少報酬而定
- 對複雜、不確定性頗高的問題,建立模型來估算各種變因對結果的敏感性,即為敏感性分析
- 軟體專案常會呈現規模不經濟(diseconomieis of scale)的特徵:若系統規模增加為兩倍,則實際系統開發耗費的心力會超過兩倍
- 擴大產品規模,成本增加的幅度就會更大;那麼縮小產品規模,便很可能可以大幅節省開支。刪除系統中屬於低價值 / 成本比的部份,也許是紓解時程和預算壓力最簡單且直接的方法
- 有些軟體專案,都是以專案的重要性來為死亡行軍的正當性辯護:這次的任務太重要了,請全體專案人員放棄個人生活、超量加班,就算是最後一滴血也要榨乾。但是,既然這個專案有這麼重要,為什麼公司不願意分配合理的時間與金錢來做呢?
- 公司很排斥將軟體專案價值量化,隱瞞做這個專案會產生的價值的期望值,如此才有可能在縮減開發成本的情況下完成專案,這是很重要的理由,「我們要把成本壓低,低到不論做出多少價值的東西都划得來」
Total Pageviews
2017/05/01
[閱讀筆記] Waltzing with Bears (2/2)
Labels:
Project Management,
Reading
2017/04/30
2017/04/10
[webMethods] 如何在 Java Service 中,將資料寫入 Integration Server 中的 Cache
Problem
假設我們已經在 Integration Server 中,在 Public Cache Managers 中建立一個名為 Test 的 Cache Manager,並於 Test 中建立一個名為 hello 的 cache
若我們希望在 Java Service 中,用 webMethods 提供的套件來:
該如何做?
How-to
以下是 sample code:
假設我們已經在 Integration Server 中,在 Public Cache Managers 中建立一個名為 Test 的 Cache Manager,並於 Test 中建立一個名為 hello 的 cache
若我們希望在 Java Service 中,用 webMethods 提供的套件來:
1. 新增資料到 cache
2. 從 cache 中取出資料
3. 移除 cache 中的資料
該如何做?
How-to
以下是 sample code:
// --- <<IS-BEGIN-SHARED-SOURCE-AREA>> --- private static final String cacheManagerName = "Test"; private static final String cacheName = "hello"; public static void addValueToCacheByKey(IData pipeline, String key, Object value) throws ServiceException { IDataCursor cursor = pipeline.getCursor(); IDataUtil.put(cursor, "cacheManagerName", cacheManagerName); IDataUtil.put(cursor, "cacheName", cacheName); IDataUtil.put(cursor, "key", key); IDataUtil.put(cursor, "value", value); try { Service.doInvoke("pub.cache", "put", pipeline); } catch (Exception e) { throw new ServiceException(e); } cursor.destroy(); } public static Object getValueFromCacheByKey(IData pipeline, String key) throws ServiceException { IDataCursor cursor = pipeline.getCursor(); IDataUtil.put(cursor, "cacheManagerName", cacheManagerName); IDataUtil.put(cursor, "cacheName", cacheName); IDataUtil.put(cursor, "key", key); IData data = null; try { data = Service.doInvoke("pub.cache", "get", pipeline); } catch (Exception e) { throw new ServiceException(e); } Object result = IDataUtil.get(data.getCursor(), "value"); cursor.destroy(); return result; } public static void removeCacheByKey(IData pipeline, String key) throws ServiceException { IDataCursor cursor = pipeline.getCursor(); IDataUtil.put(cursor, "cacheManagerName", cacheManagerName); IDataUtil.put(cursor, "cacheName", cacheName); IDataUtil.put(cursor, "key", key); try { Service.doInvoke("pub.cache", "remove", pipeline); } catch (Exception e) { throw new ServiceException(e); } } // --- <<IS-END-SHARED-SOURCE-AREA>> ---
Labels:
webMethods
2017/04/09
[webMethods] 如何設定在 Integration Server 啟動時,執行 Java Service
Problem
假設我們有一個 OPC Connection 的 Java Service,我們希望這個 Java Service 在啟動 integration server 時,順便啟動此 OPC_Connection Java Service,取得OPC connection,完成初始化的相關動作
How-to
步驟1. 點選要設定的 Java Service 所屬的 package,按下右鍵,並選擇Property
步驟2. 在 dialog 中,點選最後一個選項 (Startup / Shutdown Services),在 Startup Services 處,將 OPC_Connection Service 選取到 Selected services,按下 OK (若要將 flow service 設定為 startup service,其程序相同)
步驟3. 重啟 Integration Server,可以到 administration console 中看到 OPC_Connection 印出的 log
若日後有更動 folder 位置,記得要重做步驟 1 與步驟 2,Designer不會自動更新 service 的位置,會出現以下錯誤
假設我們有一個 OPC Connection 的 Java Service,我們希望這個 Java Service 在啟動 integration server 時,順便啟動此 OPC_Connection Java Service,取得OPC connection,完成初始化的相關動作
How-to
步驟1. 點選要設定的 Java Service 所屬的 package,按下右鍵,並選擇Property
步驟2. 在 dialog 中,點選最後一個選項 (Startup / Shutdown Services),在 Startup Services 處,將 OPC_Connection Service 選取到 Selected services,按下 OK (若要將 flow service 設定為 startup service,其程序相同)
步驟3. 重啟 Integration Server,可以到 administration console 中看到 OPC_Connection 印出的 log
若日後有更動 folder 位置,記得要重做步驟 1 與步驟 2,Designer不會自動更新 service 的位置,會出現以下錯誤
Startup service acme.albert.adapters:OPC_Connection was not found in Acme package
Labels:
webMethods
2017/04/08
[webMethods] 如何在Designer 中 create 的 Java Service,使用已經寫好的外部的Java Class
步驟1. 先將所寫好的程式包成一個 jar file
步驟2. 將包好的 jar file 與其相依賴的 jar files 放到 ESB 特定package的目錄中 (即 [integration_server_dir]/packages/[your_pacakge_name]/code/jars ),並重啟 webMethods Integration Server
步驟3. 在出現編譯錯誤的程式碼,按下修正錯誤,點選 Fix project setup...
步驟4. 點選 here
步驟5. 點選 Add External Jars
步驟6. 選取所需用到的 jar files,按下開啟舊檔
步驟7. 按下 OK
步驟8. 此時即可 import 我所寫好的 Java class
若日後有更動外部的Java code,步驟如下:
步驟1. 先將所寫好的程式包成一個 jar file
步驟2. 將包好的 jar file 放到 ESB 特定 package 的目錄中,並重啟 webMethods Integration Server
步驟2. 將包好的 jar file 與其相依賴的 jar files 放到 ESB 特定package的目錄中 (即 [integration_server_dir]/packages/[your_pacakge_name]/code/jars ),並重啟 webMethods Integration Server
步驟3. 在出現編譯錯誤的程式碼,按下修正錯誤,點選 Fix project setup...
步驟4. 點選 here
步驟5. 點選 Add External Jars
步驟6. 選取所需用到的 jar files,按下開啟舊檔
步驟7. 按下 OK
步驟8. 此時即可 import 我所寫好的 Java class
若日後有更動外部的Java code,步驟如下:
步驟1. 先將所寫好的程式包成一個 jar file
步驟2. 將包好的 jar file 放到 ESB 特定 package 的目錄中,並重啟 webMethods Integration Server
步驟3. 將 jar file 更新到你的 Designer 會用到的 library directory
步驟4. 點選目前正在開發的 package,點選右鍵並按下 Reload Package,這樣 Designer 裡就可以用到最新版本的 Java code
Labels:
webMethods
2017/04/07
[webMethods] 如何安裝 Designer 工具
Installation Steps
1. 執行 installer,等待出現 installation window 以後,點選 Advanced Options
2. 點選 Images tab、勾選 Use installation image、選取要安裝的 image file,先點選 Validate Image 確定所選取的 image file 是否完整,確認 image file 是正確的才點選 OK
3. 選擇要安裝的 Installation directory,點選 Next
4. 選擇要安裝的 packages,點選 Next
5. 點選 checkbox,按下 Next
6. 選取 trial license file,按下 Next
7. 直接按下 Next
8. 直接按下 Next
9. 直接按下 Next
10. 此步驟需要等待數分鐘
11. 安裝完成畫面,按下 Close 即可完成安裝步驟
Designer Configuration Steps
1. 啟動 Designer 工具
2. Window => Preferences
3. 點選 Integration Servers 的選項
4. 輸入你要連線的 Integration Server 的 IP、Port、User name、Password,點選 Verify Server 可以檢查是否可以正常連上 Integration Server,按下 OK 即可完成設定
5. 完成設定後,若可以正常連上 Integration Server,Status 會顯示 Connected
Reference
[1] http://techcommunity.softwareag.com/pwiki/-/wiki/Main/Guide+to+Downloading+and+installing+the+webMethods+Free+Trial+Version
1. 執行 installer,等待出現 installation window 以後,點選 Advanced Options
2. 點選 Images tab、勾選 Use installation image、選取要安裝的 image file,先點選 Validate Image 確定所選取的 image file 是否完整,確認 image file 是正確的才點選 OK
3. 選擇要安裝的 Installation directory,點選 Next
4. 選擇要安裝的 packages,點選 Next
5. 點選 checkbox,按下 Next
6. 選取 trial license file,按下 Next
7. 直接按下 Next
8. 直接按下 Next
9. 直接按下 Next
10. 此步驟需要等待數分鐘
11. 安裝完成畫面,按下 Close 即可完成安裝步驟
Designer Configuration Steps
1. 啟動 Designer 工具
2. Window => Preferences
3. 點選 Integration Servers 的選項
4. 輸入你要連線的 Integration Server 的 IP、Port、User name、Password,點選 Verify Server 可以檢查是否可以正常連上 Integration Server,按下 OK 即可完成設定
5. 完成設定後,若可以正常連上 Integration Server,Status 會顯示 Connected
Reference
[1] http://techcommunity.softwareag.com/pwiki/-/wiki/Main/Guide+to+Downloading+and+installing+the+webMethods+Free+Trial+Version
Labels:
webMethods
2017/04/06
[Failsafe] Retry with backoff
Problem
I would like do retry if I fail to connect to database.
My requirement is
How-to
Here has the sample code:
Execution log:
I would like do retry if I fail to connect to database.
My requirement is
- retry 5 times at most and sleep 5 second then retry again
- Sets the delay between retries (i.e. 5 seconds), exponentially backing off to the maxDelay (i.e. 120 seconds) and multiplying successive delays by the delayFactor (i.e. 2).
How-to
Here has the sample code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | package albert.practice.retry; import java.sql.Connection; import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.TimeUnit; import lombok.extern.slf4j.Slf4j; import net.jodah.failsafe.Failsafe; import net.jodah.failsafe.RetryPolicy; @Slf4j public class RetryTest { private int count = 0; public static void main(String[] args) throws ConnectionException { new RetryTest().connectWithRetry(); } public Connection connectWithRetry() { // create a retry policy with 5 max retries and have 2 seconds delay among retries RetryPolicy retryPolicy = new RetryPolicy(); // create a retry policy and sets the delay 5 seconds between retries, exponentially backing off to the maxDelay 120 // seconds and multiplying successive delays by the delayFactor 2. retryPolicy.retryOn(ConnectionException.class).withMaxRetries(5).withBackoff(5, 120, TimeUnit.SECONDS, 2); // Using Fallbacks allow you to provide an alternative result for a failed execution. // In this example, it will retry again after 5 seconds. Connection conn = Failsafe.with(retryPolicy).withFallback(() -> retryIfFail()) .get(() -> connect()); return conn; } public void retryIfFail() throws InterruptedException { log.debug("GG at " + getCurrentTime()); Thread.sleep(5000); log.debug("retry....." + getCurrentTime()); connectWithRetry(); } public Connection connect() throws ConnectionException { log.debug(" time = " + getCurrentTime()); Connection conn = null; if (count < 9) { count++; throw new ConnectionException("connection fail!"); } else { log.debug("get connection successfuly..."); } return conn; } private String getCurrentTime() { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss.SSS"); return dateFormat.format(new Date()); } } |
Execution log:
1 2 3 4 5 6 7 8 9 10 11 12 13 | [main] DEBUG albert.practice.retry.RetryTest - time = 2016/11/23 17:31:51.495 [main] DEBUG albert.practice.retry.RetryTest - time = 2016/11/23 17:32:01.509 [main] DEBUG albert.practice.retry.RetryTest - time = 2016/11/23 17:32:21.515 [main] DEBUG albert.practice.retry.RetryTest - time = 2016/11/23 17:33:01.498 [main] DEBUG albert.practice.retry.RetryTest - time = 2016/11/23 17:34:21.499 [main] DEBUG albert.practice.retry.RetryTest - time = 2016/11/23 17:36:21.539 [main] DEBUG albert.practice.retry.RetryTest - GG at 2016/11/23 17:36:21.539 [main] DEBUG albert.practice.retry.RetryTest - retry.....2016/11/23 17:36:26.545 [main] DEBUG albert.practice.retry.RetryTest - time = 2016/11/23 17:36:26.545 [main] DEBUG albert.practice.retry.RetryTest - time = 2016/11/23 17:36:36.554 [main] DEBUG albert.practice.retry.RetryTest - time = 2016/11/23 17:36:56.564 [main] DEBUG albert.practice.retry.RetryTest - time = 2016/11/23 17:37:36.571 [main] DEBUG albert.practice.retry.RetryTest - get connection successfuly... |
Labels:
Failsafe
2017/04/05
[Failsafe] Retry with Fallback
Scenario
If we cannot get connection, then we will retry 5 times with 2 seconds delay.
If we fail to retry with 5 times, then we will pause X minutes then retry again.
How to implement it?
How-to
You can make good use of failsafe to fulfill this implement.
In this exampe, it will demonstrate:
1. create a retry policy with 5 max retries and have 2 seconds delay among retries
2. If you got failed execution, it will retry again after 5 seconds.
Console log:
Reference
[1] https://github.com/jhalterman/failsafe#fallbacks
If we cannot get connection, then we will retry 5 times with 2 seconds delay.
If we fail to retry with 5 times, then we will pause X minutes then retry again.
How to implement it?
How-to
You can make good use of failsafe to fulfill this implement.
In this exampe, it will demonstrate:
1. create a retry policy with 5 max retries and have 2 seconds delay among retries
2. If you got failed execution, it will retry again after 5 seconds.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | package albert.practice.retry; import java.sql.Connection; import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.TimeUnit; import lombok.extern.slf4j.Slf4j; import net.jodah.failsafe.Failsafe; import net.jodah.failsafe.RetryPolicy; @Slf4j public class RetryTest { private int count = 0; public static void main(String[] args) throws ConnectionException { new RetryTest().connectWithRetry(); } public Connection connectWithRetry() { // create a retry policy with 5 max retries and have 2 seconds delay among retries RetryPolicy retryPolicy = new RetryPolicy(); retryPolicy.retryOn(ConnectionException.class).withDelay(2, TimeUnit.SECONDS) .withMaxRetries(5); // Using Fallbacks allow you to provide an alternative result for a failed execution. // In this example, it will retry again after 5 seconds. Connection conn = Failsafe.with(retryPolicy).withFallback(() -> retryIfFail()) .get(() -> connect()); return conn; } public void retryIfFail() throws InterruptedException { log.debug("GG at " + getCurrentTime()); Thread.sleep(5000); log.debug("retry....." + getCurrentTime()); connectWithRetry(); } public Connection connect() throws ConnectionException { log.debug(" time = " + getCurrentTime()); Connection conn = null; if (count < 9) { count++; throw new ConnectionException("connection fail!"); } else { log.debug("get connection successfuly..."); } return conn; } private String getCurrentTime() { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss.SSS"); return dateFormat.format(new Date()); } } |
Console log:
1 2 3 4 5 6 7 8 9 10 11 12 13 | DEBUG albert.practice.retry.RetryTest - time = 2016/11/05 11:42:13.832 DEBUG albert.practice.retry.RetryTest - time = 2016/11/05 11:42:15.837 DEBUG albert.practice.retry.RetryTest - time = 2016/11/05 11:42:17.837 DEBUG albert.practice.retry.RetryTest - time = 2016/11/05 11:42:19.837 DEBUG albert.practice.retry.RetryTest - time = 2016/11/05 11:42:21.838 DEBUG albert.practice.retry.RetryTest - time = 2016/11/05 11:42:23.839 DEBUG albert.practice.retry.RetryTest - GG at 2016/11/05 11:42:23.839 DEBUG albert.practice.retry.RetryTest - retry.....2016/11/05 11:42:28.839 DEBUG albert.practice.retry.RetryTest - time = 2016/11/05 11:42:28.839 DEBUG albert.practice.retry.RetryTest - time = 2016/11/05 11:42:30.839 DEBUG albert.practice.retry.RetryTest - time = 2016/11/05 11:42:32.839 DEBUG albert.practice.retry.RetryTest - time = 2016/11/05 11:42:34.840 DEBUG albert.practice.retry.RetryTest - get connection successfuly... |
Reference
[1] https://github.com/jhalterman/failsafe#fallbacks
2017/04/04
[Angular2] fail to load data items in ng2-select component
Problem
I am using ng2-select to implement a dropdown list function.
But my dropdown list cannot be showed just like the example:
Here is my code snippet:
How-to
Owning to the data items in ng2-select example is static:
The data items in my function which will retrieve them from database, so we need to wait until data items had been finished then create the DOM element.
Owing to I do not know how long I need to wait, so I add *ngIf in the first div tag.
Reference
[1] https://valor-software.com/ng2-select/
I am using ng2-select to implement a dropdown list function.
But my dropdown list cannot be showed just like the example:
Here is my code snippet:
<div class="row"> <div class="col-sm-12"> <div class="form-group"> <label> 負責人員 </label> <div style="width: 300px; margin-bottom: 20px;"> <ng-select [allowClear]="true" [items]="items" [data]="selectedItem" (data)="refreshValue($event)" (selected)="selected($event)" (removed)="removed($event)" placeholder="請選擇負責人員"> </ng-select> </div> </div> </div> </div>
How-to
Owning to the data items in ng2-select example is static:
The data items in my function which will retrieve them from database, so we need to wait until data items had been finished then create the DOM element.
Owing to I do not know how long I need to wait, so I add *ngIf in the first div tag.
<div class="row" *ngIf="items && items.length > 0"> <div class="col-sm-12"> <div class="form-group"> <label> 負責人員 </label> <div style="width: 300px; margin-bottom: 20px;"> <ng-select [allowClear]="true" [items]="items" [data]="selectedItem" (data)="refreshValue($event)" (selected)="selected($event)" (removed)="removed($event)" placeholder="請選擇負責人員"> </ng-select> </div> </div> </div> </div>
Reference
[1] https://valor-software.com/ng2-select/
Labels:
Angular2
Subscribe to:
Posts (Atom)