分類
architecture skill

[架構][技巧] AI UnitTest:讓ChatGPT幫助你編寫更好的測試代碼

在軟體開發領域,AI工具的演進提供新方法來編寫和測試代碼。利用AI來輔助撰寫單元測試已經成為一種新的測試策略。

透過提示AI來撰寫測試代碼,可以更快速地生成測試用例,找出潛在的缺陷並解決問題,從而提高軟體開發的效率和品質。


流程

首先對ChatGPT描述使用的技術架構和想進行的步驟,接著在步驟中和ChatGPT互動並請它提供合適的單元測試代碼。(詳細內容請參考下方的”完整流程”)


優點

採用AI輔助撰寫單元測試的效果比預計還好,特別是測試範圍和效率

前者你可以明確指定AI提供測試的範圍,也可以要求AI提供容易遺漏的測試用例,擴大合理的涵蓋範圍。

後者比起手動輸入測試代碼,透過AI產生測試代碼的速度也會有顯著的提升。

提升速度這一點相當重要,許多排斥撰寫單元測試的開發者,主要原因為在開發時程中很難再排入撰寫單元測試的時間,更不用說當業務邏輯變動之後,與其對應的單元測試也必須重構修改,因此拒絕學習或撰寫單元測試。

若透過AI產生或重構測試代碼,就能大幅降低花費撰寫單元測試的時間,也能順便開啟通往TDD的大道。


缺點

不洩漏敏感訊息的情況下,將想測試的邏輯完整表達給ChatGPT是一個可能的問題,因為我測試的邏輯很簡單,ChatGPT很清楚溫度轉換公式,提供測試代碼相當容易,但若你想測試的演算法或情境特別複雜,要完整的表達清楚就沒這麼簡單了。


總結

在現階段透過AI來輔助撰寫單元測試是一種可以探索的方式,事實上整個過程就是簡單的TDD開發流程,而ChatGPT就是我的結對編程同伴,而且這個同伴很有耐心,有求必應,沒有偏見(?)。

在測試範圍和效率都是AI的優勢,能夠大幅度縮短撰寫測試代碼的時間,但在不洩漏敏感資訊的情況下,如何表達複雜的測試邏輯會是一個可能的問題。


完整流程

以下是透過提示ChatGPT來產生測試代碼,再讓開發者來撰寫產品代碼的過程。

首先告訴ChatGPT系統簡述和技術架構和需求,包含測試庫的要求(MSTest)。

目前的系統是一個溫度轉換程式。
技術架構:C#,Winform,用於模型的測試庫(MSTest)。
所有的程式碼都應該使用上述的技術架構來撰寫。
需求:程式應該提供華氏和攝氏的溫度轉換並以測試驅動開發來撰寫。
請你只提供單元測試,我再回答是否通過你的單元測試,以這種循環方式來完成整個過程

好的,以下是使用 MSTest 撰寫的兩個單元測試。一個用於驗證華氏到攝氏的溫度轉換,另一個用於驗證攝氏到華氏的溫度轉換。

using Microsoft.VisualStudio.TestTools.UnitTesting;
using YourTemperatureConversionNamespace; // 請將此替換為您的溫度轉換類別的命名空間

namespace TemperatureConversionTests
{
    [TestClass]
    public class TemperatureConversionTest
    {
        [TestMethod]
        public void TestFahrenheitToCelsius()
        {
            // Arrange
            TemperatureConverter converter = new TemperatureConverter();
            double inputFahrenheit = 32.0;
            double expectedCelsius = 0.0;

            // Act
            double actualCelsius = converter.FahrenheitToCelsius(inputFahrenheit);

            // Assert
            Assert.AreEqual(expectedCelsius, actualCelsius, 0.01);
        }

        [TestMethod]
        public void TestCelsiusToFahrenheit()
        {
            // Arrange
            TemperatureConverter converter = new TemperatureConverter();
            double inputCelsius = 0.0;
            double expectedFahrenheit = 32.0;

            // Act
            double actualFahrenheit = converter.CelsiusToFahrenheit(inputCelsius);

            // Assert
            Assert.AreEqual(expectedFahrenheit, actualFahrenheit, 0.01);
        }
    }
}

這裡假設你有一個 TemperatureConverter 類別,並且該類別有兩個方法:FahrenheitToCelsiusCelsiusToFahrenheit。這些方法都接受一個雙精度浮點數(double)並返回一個雙精度浮點數。這些方法應該進行溫度轉換。

請注意,這些測試中的精確度為 0.01,這意味著如果實際結果與預期結果之間的差異小於或等於 0.01,則該測試將被認為是通過的。

請將此單元測試代碼添加到你的測試項目中,並運行這兩個測試。如果你的溫度轉換代碼正確,則這兩個測試都應該通過。

接著就可以建立Winform專案,並在該專案中新增一個TemperatureConversionTest測試類別並直接貼上ChatGPT提供的完整代碼。

當然,執行測試一定會失敗,因為根本還沒有TemperatureConverter類別。

(如果對測試驅動開發有點了解,應該可以發現這也是驅動測試開發的流程,甚至也可以把ChatGPT當作結對編程的對象)

最後再根據ChatGPT提供的單元測試,實作被測代碼。為了避免洩漏敏感訊息,被測代碼不會提供給ChatGPT。

被測代碼如下

    public class TemperatureConverter
    {
        public double FahrenheitToCelsius(double fahrenheit)
        {
            if (fahrenheit < -459.67)
            {
                throw new ArgumentOutOfRangeException("The Fahrenheit temperature cannot be less than -459.67");
            }
            return (fahrenheit - 32) * 5 / 9;
        }

        public double CelsiusToFahrenheit(double celsius)
        {
            if (celsius < -273.15)
            {
                throw new ArgumentOutOfRangeException("The Celsius temperature cannot be less than -273.15");
            }
            return (celsius * 9 / 5) + 32;
        }
    }

接著回答ChatGPT,已可通過單元測試,請再提供更多的單元測試。而且在這裡明確指定測試範圍為極限值


我已可通過你提供的單元測試,請再提供更多的單元測試,主要測試範圍為極限值
---
using Microsoft.VisualStudio.TestTools.UnitTesting;
using YourTemperatureConversionNamespace; // 請將此替換為您的溫度轉換類別的命名空間

namespace TemperatureConversionTests
{
    [TestClass]
    public class TemperatureConversionTest
    {
        [TestMethod]
        public void TestFahrenheitToCelsiusAbsoluteZero()
        {
            // Arrange
            TemperatureConverter converter = new TemperatureConverter();
            double inputFahrenheit = -459.67;
            double expectedCelsius = -273.15;
            
            // Act
            double actualCelsius = converter.FahrenheitToCelsius(inputFahrenheit);

            // Assert
            Assert.AreEqual(expectedCelsius, actualCelsius, 0.01);
        }

        [TestMethod]
        public void TestCelsiusToFahrenheitAbsoluteZero()
        {
            // Arrange
            TemperatureConverter converter = new TemperatureConverter();
            double inputCelsius = -273.15;
            double expectedFahrenheit = -459.67;
            
            // Act
            double actualFahrenheit = converter.CelsiusToFahrenheit(inputCelsius);

            // Assert
            Assert.AreEqual(expectedFahrenheit, actualFahrenheit, 0.01);
        }
        
        [TestMethod]
        public void TestFahrenheitToCelsiusHighTemperature()
        {
            // Arrange
            TemperatureConverter converter = new TemperatureConverter();
            double inputFahrenheit = 212.0;
            double expectedCelsius = 100.0;
            
            // Act
            double actualCelsius = converter.FahrenheitToCelsius(inputFahrenheit);

            // Assert
            Assert.AreEqual(expectedCelsius, actualCelsius, 0.01);
        }

        [TestMethod]
        public void TestCelsiusToFahrenheitHighTemperature()
        {
            // Arrange
            TemperatureConverter converter = new TemperatureConverter();
            double inputCelsius = 100.0;
            double expectedFahrenheit = 212.0;
            
            // Act
            double actualFahrenheit = converter.CelsiusToFahrenheit(inputCelsius);

            // Assert
            Assert.AreEqual(expectedFahrenheit, actualFahrenheit, 0.01);
        }
    }
}

把上述的單元測試新增到TemperatureConversionTest類別,並執行所有的單元測試,被測代碼可以成功通過單元測試!

接下來想對輸入的不合法數值做一些處理,所以要求ChatGPT對溫度低於絕對零度撰寫測試代碼

我已經可以通過你提供的單元測試,但如果輸入的溫度低於絕對零度,我想丟出一個異常,你可以寫出這種測試嗎?

當然可以。在這裡,我將為溫度低於絕對零度的情況寫出兩個測試。我們期望方法在這種情況下拋出一個異常。

using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using YourTemperatureConversionNamespace; // 請將此替換為您的溫度轉換類別的命名空間

namespace TemperatureConversionTests
{
    [TestClass]
    public class TemperatureConversionTest
    {
        [TestMethod]
        [ExpectedException(typeof(ArgumentOutOfRangeException))]
        public void TestFahrenheitToCelsiusBelowAbsoluteZero()
        {
            // Arrange
            TemperatureConverter converter = new TemperatureConverter();
            double inputFahrenheit = -500.0; // Less than absolute zero

            // Act
            converter.FahrenheitToCelsius(inputFahrenheit);

            // Assert is handled by the ExpectedException attribute
        }

        [TestMethod]
        [ExpectedException(typeof(ArgumentOutOfRangeException))]
        public void TestCelsiusToFahrenheitBelowAbsoluteZero()
        {
            // Arrange
            TemperatureConverter converter = new TemperatureConverter();
            double inputCelsius = -300.0; // Less than absolute zero

            // Act
            converter.CelsiusToFahrenheit(inputCelsius);

            // Assert is handled by the ExpectedException attribute
        }
    }
}

在這兩個測試中,我們使用了 ExpectedException 屬性來指定我們期望方法在給定的情況下拋出的異常類型。在這種情況下,我們期望方法在溫度低於絕對零度時拋出 ArgumentOutOfRangeException。如果你的溫度轉換方法正確地處理了這種情況,那麼這些測試應該會通過。

把ChatGPT新增的測試代碼貼到測試類別之後,執行單元測試應該會失敗。因為在TemperatureConverter沒有根據輸入的不合法數據丟出異常。

回頭修改TemperatureConverter,主要針對華氏低於-459.67和攝氏低於-243.15,丟出異常

public class TemperatureConverter
{
    public double FahrenheitToCelsius(double fahrenheit)
    {
        if (fahrenheit < -459.67)
        {
            throw new ArgumentOutOfRangeException("The Fahrenheit temperature cannot be less than -459.67");
        }
        return (fahrenheit - 32) * 5 / 9;
    }

    public double CelsiusToFahrenheit(double celsius)
    {
        if (celsius < -273.15)
        {
            throw new ArgumentOutOfRangeException("The Celsius temperature cannot be less than -273.15");
        }
        return (celsius * 9 / 5) + 32;
    }
}

完成後順利通過單元測試!

最後想請ChatGPT提供它覺得缺乏的單元測試。

我已經可以通過單元測試了,最後我想請提供你覺得缺乏的單元測試

很好,你已經涵蓋了一些重要的測試案例。除了我們已經討論過的測試案例,我們還可以添加一些邊界測試和特殊值測試。以下是一些我們可以添加的額外測試:

  1. 輸入為零的情況:零度華氏對應的攝氏溫度為 -17.78°C,零度攝氏對應的華氏溫度為 32°F。
  2. 一些隨機值的測試:選擇一些隨機的華氏和攝氏溫度值,檢查轉換是否正確。

以下是相應的單元測試:

using Microsoft.VisualStudio.TestTools.UnitTesting;
using YourTemperatureConversionNamespace; // 請將此替換為您的溫度轉換類別的命名空間

namespace TemperatureConversionTests
{
    [TestClass]
    public class TemperatureConversionTest
    {
        [TestMethod]
        public void TestFahrenheitToCelsiusZero()
        {
            // Arrange
            TemperatureConverter converter = new TemperatureConverter();
            double inputFahrenheit = 0.0;
            double expectedCelsius = -17.78;

            // Act
            double actualCelsius = converter.FahrenheitToCelsius(inputFahrenheit);

            // Assert
            Assert.AreEqual(expectedCelsius, actualCelsius, 0.01);
        }

        [TestMethod]
        public void TestCelsiusToFahrenheitZero()
        {
            // Arrange
            TemperatureConverter converter = new TemperatureConverter();
            double inputCelsius = 0.0;
            double expectedFahrenheit = 32.0;

            // Act
            double actualFahrenheit = converter.CelsiusToFahrenheit(inputCelsius);

            // Assert
            Assert.AreEqual(expectedFahrenheit, actualFahrenheit, 0.01);
        }

        [TestMethod]
        public void TestFahrenheitToCelsiusRandom()
        {
            // Arrange
            TemperatureConverter converter = new TemperatureConverter();
            double inputFahrenheit = 45.0; // A random Fahrenheit value
            double expectedCelsius = 7.22; // Corresponding Celsius value

            // Act
            double actualCelsius = converter.FahrenheitToCelsius(inputFahrenheit);

            // Assert
            Assert.AreEqual(expectedCelsius, actualCelsius, 0.01);
        }

        [TestMethod]
        public void TestCelsiusToFahrenheitRandom()
        {
            // Arrange
            TemperatureConverter converter = new TemperatureConverter();
            double inputCelsius = 20.0; // A random Celsius value
            double expectedFahrenheit = 68.0; // Corresponding Fahrenheit value

            // Act
            double actualFahrenheit = converter.CelsiusToFahrenheit(inputCelsius);

            // Assert
            Assert.AreEqual(expectedFahrenheit, actualFahrenheit, 0.01);
        }
    }
}

這些測試將增加對你的溫度轉換程式碼的信心。如果所有這些測試都通過了,那麼你的溫度轉換程式碼在這些測試覆蓋的範圍內應該是正確的。請注意,單元測試能幫助我們提高對程式碼的信心,但並不能保證程式碼百分之百的正確。例如,有可能存在我們尚未考慮到的邊界情況或特殊情況。因此,雖然這些測試是一個很好的起點,但可能仍需要進一步的測試和審查來確保程式碼的健壯性。

分類
architecture skill

[架構][技巧]軟體估算系列 – 基本估算技術 – 先計數再計算

在軟體估算中有一些原則可以優先考慮,其中一項就是先計數再計算。以下分別說明如何進行先計數再計算


先計數

具體做法為蒐集對目前專案進行估算最有幫助或最相關的資訊

舉個簡單例子,對於網站開發可以蒐集網站包含的網頁數量,若該網站包含10個頁面,10個頁面就是計數的結果。

另一個例子為釐清需求後,發現系統有10個特性(或功能)需要完成,這也可以當作計數的結果。

先計數的重點是盡量挑選對估算專案最有幫助或最相關的資訊,以下列出幾個優先考慮的方向

1.      和專案規模(時間,成本,人力)最相關的目標

2.      挑選在統計學具有代表性的數量,如計數的數量包含或超過20個

3.      了解計數的相關資訊,資訊代表甚麼意思? 適合目前的專案嗎?

4.      容易取得的計數資訊,在相同的條件下以容易計數的資訊為優先


再計算

接著再對計數的結果進行計算,若計數的結果有長久穩定的歷史紀錄或具體數據,效果會越好

繼續以上述的範例來說明,假設計數出網站包含10個網頁並根據已存在的歷史紀錄評估出1個網頁大約2.5天,那麼10個網頁大約就是25天。

再計算的重點為計算過程盡量透過歷史紀錄和具體數據來評估,特別要避免憑空推測或單憑經驗來計算,原因之一就是個人單體經驗未必適用於所有情況

這點對於經驗老練但不謹慎的開發者反而容易犯錯。

以下列出可用來計數的目標和後續計算所需的歷史資料

可計數的量化目標後續計算所需的歷史資料
特性每個特性用於開發和測試的平均時間
使用案例每個使用案例的平均工作時間
使用者故事每個使用者故事的平均工作時間
功能點每個功能點的平均工作量
頁面數每個頁面平均工作時間
資料層每個資料表的平均工作時間
類別每個類別的平均工作時間
可計數量化目標及後續計算歷史資料


總結

先計數再計算為軟體估算的原則之一,先計數的做法是對於估算目前專案最有幫助的資訊進行計數。請注意幾個優先考慮的方向

再計算的做法為對於計數結果進行計算,當計數的結果有長遠的歷史紀錄或數據,效果會最好。特別注意避免憑空或只憑經驗進行計算

分類
architecture skill

[架構][技巧]軟體估算系列 – 軟體估算的基礎 – 估算技術介紹

不同的估算技術適用於不同的估算環境,以下為選擇估算技術的主要因素


估算的目標

估算的目標分為兩種情況,第一種情況是你已經知道有哪些特性並可針對這些特性來估算時間和工作量,稱為”專案規模“。

第二種情況為你已被限制在固定的預算或工作時間內,必須在這個條件範圍內估算可以交付那些特性,稱為“估算特性”


專案規模

專案規模分為小型,中型,大型專案。

小型專案:

包含5個或5個以下的人員,因為個體生產力的可變性會明顯的影響其他因素,所以一些適用大型專案的估算技術不適用於小型專案。

對於小型專案來說比較好的估算技術為根據實際執行該專案的人員所做的估算(也稱為從下而上)。

中型專案:

中型專案一般為5到25人組成,專案週期為3到12個月。中型專案的優點為幾乎適用於所有大型專案和一些小型專案的估算技術。

大型專案:

大型專案為25人以上,專案週期為6到12個月或更長的時間。在大型項目中根據不同階段選擇不同的估算技術是最準確的。

大型項目的前期階段適用於根據統計或演算法延伸的估算技術(也稱為從上而下),較不適用根據個人提出的估算技術(從下而上)。

大型項目的中期階段可以根據已經產生的專案數據結合從下而上或從上而下的方法來評估,可以到較準確的結果。

大型項目的後期階段適用從下而上的估算技術。


開發風格

開發風格指的是專案的進行方式為直線或迭代,但對於估算技術而言直線和迭代差別在於前者在專案前期需求定義的佔比大,後者為軟體建構之後再進行的需求定義佔比大。

無論是直線或迭代都傾向於從上而下或根據統計的估算技巧開始,最後轉移為從下而上的方式結束,但因為迭代的進行方式會參考自身所產生的數值來改進,所以相比之下更能強化估算準確度。


開發階段

開發階段也可分為三種,各為前期,中期,後期。

前期:

直線式開發風格的前期通常為從問題定義開始到需求完成階段。迭代式開發風格的前期指的是專案初始規劃階段。

中期:

直線式開發風格的前期通常為從需求到初期架構完成階段。迭代式開發風格的中期指的是專案前2~4個迭代。中期的重點是已可取得一些關於專案生產率的數值。

後期:

無論是直線式或迭代式,後期都是指軟體建構中期到最終發布的範圍。


可能的準確度

估算技術的準確度會受3個因素影響,首先是技術本身提供的功能,第二為技術是否應用在合適的情境,第三是技術適用的專案階段。

當然,通常會期望使用最準確的估算技術,但準確度和成本為正比,高準確度的估算勢必付出較高的代價。另一個要注意的準確度會受不確定錐形的影響,考量成本的情況下在可變性低的範圍使用低成本的估算技術也是可以接受的。


總結

選擇估算技術的考慮因素有3個,分別為專案規模,開發階段,開發風格。

最好根據規模,階段,風格選擇不同的估算技術。若有成本的顧慮,就必須考慮可能的準確度和所付出的成本是否合適。

分類
architecture skill

[架構][技巧]軟體估算系列 – 軟體估算的基礎 – 影響估算的因素

影響估算的因素從高到低分別為專案規模,專案類型,人員因素


1.專案規模

專案規模為影響估算的第一要素,主要原因為專案規模的影響最大,在規模差距過大的情況下用歷史紀錄來評估新的專案最好謹慎一些。

一個簡單的例子就是十萬行(Lines of Code)和一百萬行專案的工作量比例並不等比於十倍(總行數)一樣簡單。

換句話說以兩百人月可完成十萬行的專案,不等於以兩千人月可完成一百萬行的專案。

主要原因就是溝通和連續性問題,而且規模越大的專案其問題會越嚴重。這引起了軟體開發領域領域的有趣現象,”規模不經濟效應”(Diseconomies of scale)。

一般來說生產製造提到的”規模經濟效應(Scale Economies Effect)”,指的是在某個規模中工廠越大,生產線越多,生產成本就會越下降。

軟體開發的”規模不經濟效應(Diseconomies of scale)”卻正好相反,專案規模越大,生產成本反而會越上升。

規模不經濟效應通常只會發生在規模差距過大的專案上,在規模相似(一到五倍之間)的專案上使用平均值來推算工作量,基本上是可以接受的。

假設十萬行的工作量為兩百人月,則平均人月為五百行(十萬除以兩百)。若新專案規模預估為二十萬行(五倍之內),則可以估計大約是落在390~410人月。

最後要注意當規模差距在合理的範圍(一到五倍之間),可以使用平均值做為參考基準來推算工作量。但若規模差距過大(五倍以外),則最好拉大並謹慎評估。


2.專案類型

專案類型為影響估算的第二要素。

類型通常和安全性,可靠性,軟體生命週期等等相關特性有密切的關係。

高度要求安全性和可靠性的軟體(如飛航系統,心臟調節器,公共運輸)比對公司內部的員工網頁,開源軟體等等。對系統各種面向的要求截然不同,其人月平均產出代碼量的差距從十倍到二十倍都有。

還好,通常在同一間軟體公司或同個部門,其開發專案類型的變化不會太大。

原則上也是別把看起來規模相似,但不同類型的歷史紀錄當作參考的唯一基準。換句話說不同類型的專案最好不要混為一談。


3.人員因素

人員因素為影響估算的第三要素。

人員因素包含以下幾種特性表現,需求分析能力,開發者能力(通用),人員流動頻率,業務領域經驗,開發語言和工具經驗,開發平台經驗,團隊凝聚力。

下方是各項特性的比例圖(原本的比例圖不太好理解,我重新繪製了圖表)

橫軸為上述提到的各項特性種類,縱軸為平均工作量,以50為基準點,代表完成該項工作的期望值。分別以兩種不同顏色的直條代表最好和最差的情況。

先以單項能力來看,差距最大的為需求分析能力,以期望值50為基準點,最好的需求分析能力可以減少29%(79-50)的工作量,最差的需求分析能力必須增加42%(50-8)的工作量。

換句話說若需求分析預期完成的天數為50天,最好的情況只需要35.5天(50 – 14.5)。最差的情況需要71天(50 + 21)

差距最小的為團隊凝聚力,以期望值50為基準點,最好的團隊凝聚力可以減少14%(64-50)的工作量,最差的團隊凝聚力必須增加11%(50-39)的工作量。

換句話說若專案預期完成的天數為50天,最好的情況只需要43天(50 – 7)。最差的情況需要55.5天(50 + 5.5)

從各項數據得到一個結論,專案負責人最好能了解誰是某項工作的最佳人選,特別是在需求分析,開發能力,業務領域,這三項可以掌控的因素盡量挑選合適人選。

另外當專案不得已發生以上各項變動,特別是排名越前面的特性對專案評估的結果影響越大。最好重新評估專案,按照新的排程進行。


結論

影響估算的因素從高而低有3種,分別為規模,類型,人員。
在規模要注意的是差距不大(1~5倍之間)可以推算平均值來評估,反之,最好謹慎進行估算。
在類型要注意的是別把規模類似但類型不同的歷史紀錄做為唯一的參考點。
在人員要注意的是因各項特性變動所造成的影響,最好了解誰是這項工作的最佳人選並對排名前段的特性進行篩選,也要注意當特性發生變動後最好重新進行評估。

分類
architecture skill

[架構][技巧]軟體估算系列 – 軟體估算的基礎 – 準確估算的好處

有些開發人員或專案經理已經習慣失準的估算,高估或低估皆會對專案造成不同的影響。

在萬不得已的情況下是選擇高估好還是低估好?


低估帶來的問題

1.低估的影響有連貫性

通常專案早期階段的活動(釐清需求,規劃原型,設計架構)影響專案的程度較大。這些早期活動的時程被低估壓縮而產生的負面效應,也會影響中後期活動(實作功能,測試除錯)的時程。

2.低估的影響難以補救

低估專案時程通常也代表專案延期完成,若組織營收和專案時程有直接關係,除了導致獲利下降,也會連帶影響品牌形象。


高估帶來的問題

1.高估容易導致拖延

若專案實際需要2個月,但估算預計3個月。那麼專案在第1個月的實際完成率會遠低於預計完成率。(因為可以拖延到第2個月才開始動手)

2.高估造成資源浪費

無論高估人力或時間都是多餘的成本浪費,且高估程度越多,浪費也越多。

另一個需要謹慎觀察的情況是組織的高估風氣,在組織中通常高估專案獲得的評價會優於低估專案。因為至少專案是在”高估”的時程內完成,其造成的成本浪費比低估導致時程延期的負面影響來的輕微。

為了獲得較高評價或避免列入黑名單,大家就會傾向高估。特別是管理者不具有相關產業知識的情況下,只看結果而不看過程,高估風氣會更明顯。


了解高估和低估的不同影響之後,在不得已的情況下要怎麼選擇?

當然是看情況。

若低估導致專案延期是殺頭重罪,在無法準確估算的情況下只能選擇高估。(特別是有高估風氣組織)。

至於選擇低估的情況應該有兩個,首先是根本不知道是低估,如相關專案經驗不足,相關技術不熟悉,需求釐清沒有做到位,需求變化太大。

其次是被迫選擇低估,也就是說專案時程已經決定,沒辦法改變。但還是可以透過協商來調整是否能提供部分功能而不是整體功能,讓低估所造成的負面效應降到最小。


最後來看看準確估算可以帶來什麼好處?

1.減少專案風險

專案的風險其實就是專案的理想目標和實際估算的差距,差距越大,風險就越大。

能夠早期處理風險代表可以考慮更多的應變措施,如早期增加人手,縮減規模,改變時間交付功能等等。


2.強化進度評估

準確估算通常代表專案的預計進度,透過比較預計進度和實際進度就可以清楚了解專案進度的狀態是提前還是落後。


3.減少錯誤發生

大約40%的軟體錯誤來自於緊急的進度壓力,這些進度壓力其實可以透過準確的估算進度來避免。在急迫壓力下通常只能選擇盡快完成功能,趕上進度。沒辦法採用單元測試或TDD來驗證正確性,也導致錯誤的可能性大增。


4.提高協調合作

專案的估算不準確,也會連帶影響專案中其他相關活動的時程,這些其他相關活動包含製作文件,市場銷售,後續的預算或人力編制等等。準確的估算能提高專案相關活動之間的協調與合作。

分類
architecture skill

WinForm DataGridView 和 BindingSource 的簡單綁定

Situation

資料和介面的同步一向是實作技術的關鍵點。特別在有多個改變資料的位置,手動編寫兩者的同步不僅繁瑣且難以維護。

WinForm 提供的資料綁定 (Data Binding) 提供非常簡便的方式以自動同步控件屬性和資料屬性。其目的就是當資料或控件兩者其中之一改變時,另一者會同步變化。

資料綁定在使用方式上可以簡單也可以複雜,想要詳細了解的朋友可以參考 Windows Forms 2.0 數據綁定。

以下只說明如何綁定 DataGridView 和 BindingSource。

Action

3個步驟就能簡單綁定 BindingSource 和 DataGridView。

1.宣告並初始化 BindingSource

private BindingSource bindingSource = new BindingSource();


2.指定 BindingSource 的 DataSource 屬性所使用的資料型別

bindingSource.DataSource = typeof(MaintainData);//指定使用的自定義資料型別(MaintainData)


3.把 BindingSource 賦值給目標 DataGridView 的 DataSource 屬性

dataGridViewTest.DataSource = bindingSource;//綁定目標DataGridView(dataGridViewTest)


Result

完成以上綁定步驟之後,dataGridViewTest 會根據 bindingSource 的內容自動更新,不必手動同步。如下

bindingSource.Add(new Maintain()); //dataGridViewTest會自動更新
bindingSource.Clear(); //dataGridViewTest也會自動更新


而當你操作 dataGridViewTest 之後, bindingSource也會自動更新。

Extends

事實上資料綁定還可分為簡單資料綁定 / 複雜資料綁定 / 單項資料源 / 清單資料源。

在不同的情境中結合這些技巧,使用資料綁定會更加得心應手。

分類
architecture skill

[架構][技巧] 軟體估算系列 – 軟體估算的基礎 – 什麼是軟體估算

老闆:
各位,這個專案必須在第三季前完成,完成的意思包含正式上線,所有的測試和驗收。你們先安排一下時程再來跟我報告!

你 :
(心裡盤算了一下)
老闆,我們預計會安排Peter和John進行開發,但在第三季前完成還是有點困難,不知道是不是有機會可以…

老闆:
不行!這是客戶要求的。Mary不是快有空了嗎? 可以在第三季前加入啊

你 :
好吧,我試試看…(根據人月神話,在後期加入人手通常對速度沒有幫助)


目標和計畫,估算和承諾

對於估算的認識通常來自於上方對話,對話中包含估算的基本知識,目標,計畫和承諾。

在開始進行估算之前必須先瞭解且能分辨這些基本知識,估算才能代表某些意義。


1.目標

最常聽到的通常是”目標“,目標包含目的物,時間和狀態

這三者也代表對理想商業願景的描述,對話中就是“這個專案(目的物)必須在第三季前(時間)完成(狀態)”。

目標通常聽起來很理想,有願景,但未必能完成。


2.計畫

計劃是針對目標進行一系列規劃的過程,計畫的最終結果就是滿足目標。

很多人會把計畫直接當作估算,但兩者完全不同。

計畫是主觀追求目標,估算是客觀分析目標。

通常比較好的做法是把估算作爲計畫的基礎,而不是直接把計畫當作估算。

在對話的“預計會安排Peter和John…”就是根據目標進行的簡單計畫。


3.估算

估算是對項目花費時間或成本的分析預測,也是公正客觀的分析過程,不受其他外在壓力影響。但實際上估算和目標,承諾,管理卻會互相作用。


4.承諾

承諾是根據特定日期以特定標準可提供功能的保證。 迫於各種壓力,很多人對目標會直接給予承諾,但沒有經過估算的承諾,其實對任何人都沒有好處。

在對話的“好吧,我試試看“,就是簡單的承諾。如果老闆記性不錯,這可能就是下一次對話的起點。

為什麼分開判斷目標,計畫,估算,承諾很重要

很多人會把老闆提出的”目標“當作唯一的選擇,把它視為不可違抗,不可變動的。其實站在專案管理的角度,這是沒有幫助的。

大多數的老闆通常沒有技術背景,即使有技術背景也無暇顧及每個專案的細節,因此也不可能知道他所提出“目標”的困難點在哪? 為何困難?。

因此身為專案負責人必須清楚分辨”目標的計畫(理想)“和”目標的估算(現實)“,判斷兩者的差異在哪?差距有多大?

試著對兩者的差距進行管理,將理想和現實拉近,讓專案可以順利完成。也讓你的承諾具有實質意義。

這才是專案管理的本質之一,也是良好估算的定義 :

估算目的在於清楚反映專案的實際狀態,讓管理者能做出良好決策來控制專案以達到目標。

良好估算的定義
分類
architecture skill

[架構][技巧] 軟體估算系列

一個良好估算的價值在於提供實際和理想的差距,讓你能針對差距做出良好決策以達到目標。

軟體估算的價值

對於每位工程師或專案經理而言,無可避免的工作就是估算。
估算目標可能是功能,特性,時間,成本。
估算方式可能是單憑經驗或精心計算。

但你了解估算嗎? 你的估算準確嗎? 

我們都希望提升估算準確率,讓預期和實際越接近越好。但無論在教育體系或就業環境很少出現專門教你了解估算或學習估算的課程。

《軟體估算的藝術》說明進行估算必須了解的基本知識,教你如何提升估算能力以及面對估算難題。讓你下次面對老闆或客戶提問不再啞口無言或隨便說說。

接下來我將列出一系列關於《軟體估算的藝術》中必須知道的知識或技巧,透過這些知識內容,可以更清楚容易的學習軟體估算。

軟體估算可分為三大部分,各為軟體估算的基礎,軟體估算的技術,軟體估算的挑戰


1.軟體估算的基礎

先說明什麼是估算,估算的價值以及影響估算的各種因素。這些基礎知識會讓你對估算有更多的了解。

1.1 什麼是軟體估算
1.2 軟體估算的好處
1.3 影響估算的因素


2.軟體估算的技術

透過範例清楚介紹各種估算的技巧,告訴你在哪種情況下選擇哪種估算技巧最準確以及各種估算技術所帶來的優缺點。

2.1 估算技術介紹
2.2 計數,計算,判斷


3.軟體估算的挑戰

描述實際進行估算所面臨的問題和相關解決方法,包含專案規模,工作量,進度,談判等等。

3.1 估算專案規模的問題
3.2 估算工作量的問題
3.3 估算進度的問題
3.4 估算的展示
3.5 政治和談判


最後,軟體估算的知識和技巧其實並不限於軟體工程師,也不限於軟體領域。其應用範圍涵蓋各種領域層面。

分類
architecture skill

[架構][技巧] 以人月評估工作量的問題和可能解法

人月(man-month)通常用來預估或排定時程,也是一般用來評估工作量的單位。

可以單純使用人月來預估工作量的前提有以下兩個 : 
1.工作者彼此之間不需要溝通(包含訓練和交換資訊)
2.工作可以完全切割(被切割的部分沒有連續性)

可以套用人月來計算工作量的簡單範例就是收割作物。
假設1個人採收1公畝的作物需要1個月,佔地4公畝需要4個人月。你可以讓1個人工作4個月來完成,也可以請4個人工作1個月來完成。

也可以說在這種情況下人月是可以互換的。(當然,前提是忽略工作者不會在工作中閒聊或割錯範圍)

但軟體開發很難只透過人月來預估工作量。

1.首先是溝通,溝通包含訓練和交換資訊。

每位投入的開發人員都必須接受技術的訓練,目標和策略的規劃等等
(雖然收割作物也需要訓練,但對比軟體開發而言成本相對小了多)

另外無論元件的交換資訊(API)或開發人員的交換資訊(討論資料格式,架構規劃,程式流程等等),所佔的成本其實是相當可觀,可以計算看看開了多少會議來討論這些問題。
(交流量公式 : n(n-1)/2,n為人數,根據這個公式,3個人的交流量是2個人的3倍,4個人的交流量則是2個人的6倍,以此類推)

2.至於工作是否可以完全分割,以一個很簡單的4個模組來說明,登入模組,主畫面模組,加密模組,儲存模組。依賴關係如下

簡單的依賴關係

假設儲存模組發生儲存資料的內容錯誤,除了查找本身的缺陷(fault)和功能失常(failure)以外,它還必須通知加密模組以及主畫面模組或登入模組查找問題。

因此以測試或除錯的角度來看軟體模組不僅具有連續性而且透過這種連續性引發的行為會大大增加所需的成本,在大型系統(模組數量大於100)的連續性成本更加嚴重。

事實上不單是軟體開發,想單純透過人月來評估工作量都應該至少考慮以上兩個前提(溝通和連續性)

回過頭來,在軟體開發領域是否有其他方式可以減緩溝通和工作連續性所引發的成本?

敏捷開發可能就是個解方。

1.首先就技術的訓練而言,可以透過結對編程(Pair programming)讓老手來帶領新手來減輕新進人員的技術負擔和專案的陌生程度

各種交換資訊所消耗的成本則可以透過Scrum會議(衝刺計劃會議/每日站立會議/評審會議/回顧會議)來減緩

2.工作連續性所消耗成本,背後原因其實是個別元件沒有確實驗證而導致錯誤難以釐清定位。基本上可以透過單元測試或測試驅動開發(TDD)來確實驗證元件的正確性(行為和資料)

分類
architecture skill

[架構][技巧] 優秀的工程師就是優秀的管理者嗎?

前同事阿強是開發部門的技術強者,最近因為大功一件加上考績滿分,被老闆提拔為經理。

看起來阿強走路有風。沒想到在聚會時卻跑來跟我訴苦。

“唉我喜歡技術也專精技術,可是我從沒管過人,也沒想過要管人”

“你有跟你老闆談過你沒意願嗎?”

“有! 但老闆說技術強,其他人就會聽你的,自然就可以管人。我相信你的技術,也期待你管理他們!!”

“好吧…(難道技術能力強也代表管理能力強嗎)”

技術能力強也代表管理能力強,應該是不少企業組織的慣性思維。

這個思維其實引發另一個較為廣義的問題 :

某個職位的優秀人才也是另一個職位的優秀人才嗎?

答案是不一定!

根據個人在不同組織的觀察以及”溫伯格的軟體管理學第三卷-觀照全局的行為”一書中提到,

許多企業組織以一種錯誤思維挑選管理階層,這種錯誤思維稱為”單一面向的挑選模型”,該模型有三個錯誤假設!

錯誤假設 1. 管理者特質是天生的,不是後天養成的
錯誤假設 2. 以單一標準來評量員工
錯誤假設 3. 專業職的職級等於管理職的職級

具體來說這些錯誤假設其實代表著以下隱藏的意義

錯誤假設 1. 管理者特質是天生的,不是後天養成的

明顯的偏見就是認定管理者是天選之人,因此管理技巧訓練大多數是浪費時間和成本! 重點是只要挑對的人來管理就可以了!

錯誤假設 2. 以單一標準來評量員工

雖然現況有許多評量標準,先不論評量標準如何。在企業組織文化,辦公室政治的影響下要進行所謂的公正評量,基本上難度很高!

錯誤假設 3. 專業職的職級等於管理職的職級

這就回到開頭的問題,”技術能力強也代表管理能力強嗎?”,我想重點在於不同職位須具備的特質是否存在衝突。當互相衝突的特質越多,兩者職位相近的程度也越低!

請記得,優秀的工程師未必是優秀的管理者,反之亦然。