軟體專案規模是影響開發效率和軟體品質的關鍵因素。小型專案通常由單人完成,溝通成本低,但需要程式設計師具備全面的技能。中型專案需要團隊合作,溝通成本上升,但允許專業分工。大型專案則需龐大團隊,溝通和管理成本高,有效專案管理至關重要。不同規模的專案各有其優缺點,選擇合適的開發方法和策略才能最大化生產力。小型專案可靈活運用資源,但程式設計師技能的多樣性是挑戰。中型專案兼顧了整體理解和專業分工。大型專案允許高度專業化,但需要統一的開發方法,並可能受限於個人風格。

軟體開發專案規模探討

軟體開發專案的規模對於專案管理、開發效率以及最終產品的品質有著深遠的影響。根據專案規模的大小,可以將軟體開發專案分為小型、中型和大型三類別。

專案規模的定義

小型專案是指能夠由一位平均能力的程式設計師在合理的時間內(少於兩年)獨立完成的專案。這類別專案通常包含約5萬至10萬行程式碼。由於只有一位程式設計師,因此幾乎不需要與其他人進行溝通,生產力主要取決於程式設計師的個人能力。

中型專案則需要一個小團隊(2至5人)共同合作才能完成,專案規模約在5萬至100萬行程式碼之間。由於團隊成員之間的溝通變得重要,因此會產生額外的管理成本,但仍然在可控範圍內。

大型專案需要一個龐大的團隊(超過5人)來完成,通常包含超過50萬至100萬行程式碼。由於團隊規模龐大,溝通和管理成本大幅增加,可能會佔據工程師高達50%的生產力。因此,有效的專案管理對於大型專案的成功至關重要。

不同規模專案的特點

小型專案

在小型專案中,一位程式設計師負責系統設計、實作、測試、除錯、佈署和檔案編寫等所有任務。雖然任務繁多,但由於規模小,因此仍然可控。小型專案能夠充分利用工程資源,因為程式設計師可以採用最有效率的方法來解決問題,而不需要與其他人達成共識。

然而,小型專案也存在挑戰:程式設計師需要具備多樣化的技能,才能夠獨立完成所有任務。許多小型專案因為程式設計師缺乏適當的訓練和經驗而失敗或成本過高。

中型專案

在中型專案中,個人軟體工程涵蓋了單一工程師負責的部分,包括系統元件的設計、實作和檔案編寫等。通常,工程師還需要負責單元測試,而整個團隊則共同負責整合測試。中型專案允許一定的專業分工,因此不需要每位工程師都具備所有技能。

中型專案提供了一個介於小型和大型專案之間的平衡點:工程師既能夠對整個專案有一定程度的瞭解,也能夠在特定方面進行專業化,而不會被其他部分的細節所淹沒。

軟體工程的挑戰

軟體工程關注的是管理大型團隊開發專案所需的方法、實踐和政策。然而,適用於大型專案的方法不一定適合小型或中型專案。大型專案需要額外的管理成本和溝通協調,這些在小型專案中是不必要的。

因此,瞭解不同規模專案的特點和挑戰,有助於軟體開發團隊選擇合適的方法和策略,以提高開發效率和產品品質。

大型專案中的軟體工程挑戰與軟體工藝的實踐

大型專案的特性與軟體工程師的角色

在大型專案中,團隊成員各自扮演不同的專門角色,從系統設計到實作、測試、檔案編寫、佈署,以及系統的增強與維護等。個人軟體工程涵蓋了個別程式設計師負責的活動。與中型專案類別似,大型專案中的軟體工程師通常只負責少數任務,如編碼和單元測試,因此不需要像小型專案中的獨立程式設計師那樣具備廣泛的技能。

大型專案的規模影響了工程師的生產力。工程師可以變得非常專精,集中於他們的專業領域,從而比使用更通用的技能集更有效地完成工作。然而,大型專案必須採用共同的軟體開發方法才能有效運作,有些工程師可能不喜歡這種方法,因而影響生產力。

軟體工程的侷限性

將工程技術應用於軟體開發可以更具成本效益地生產應用程式。然而,正如Pete McBreen在《Software Craftsmanship: The New Imperative》中指出的,軟體工程的最大問題在於假設「系統化、規範化、可量化的開發方式」是唯一合理的方法。實際上,軟體開發是一種經驗過程,而非定義明確的過程,因此很難完全自動化,也難以應用工程原理。

軟體工程的另一個重大問題是,它將軟體工程師視為可任意替換的資源,這忽略了個人才能的重要性。管理階層試圖將工程技術統一應用於每個人,並鼓勵使用某些「最佳實踐」,這可能會限制創新和創造新方法的可能性。

軟體工藝的實踐

軟體工藝強調程式設計師在專業指導下進行培訓和實踐,是一種終身學習的過程,以成為最好的軟體開發者。按照工藝模型,程式設計師接受教育,完成學徒期,成為熟練的程式設計師,並努力創造傑作。

教育背景

大學和學院為未來的軟體工藝師提供了必要的先修課程。正式教育有兩個主要目標:首先,學生被迫學習那些如果自學可能會跳過的電腦科學主題;其次,學生向世界證明自己能夠完成已經開始的重要承諾。完成正式的電腦科學課程後,學生才真正開始學習軟體開發。

然而,無論學位多高,都不能自動使人成為軟體工藝師。擁有研究生學位的人與擁有學士學位的人一樣,需要從實習開始,然後經過多年的學徒期。

學徒期

完成正式的電腦科學課程後,學徒期的目的是獲得必要的經驗,以便能夠以多種不同的方式處理問題,並獲得盡可能多的不同經驗。典型的電腦科學課程教授程式語言、資料結構、編譯器理論、作業系統等,但並未教授如何在現實世界中進行程式設計。學徒期彌補了這一差距,讓程式設計師瞭解現實世界中的程式設計挑戰。

此圖示展示了軟體工藝師的發展路徑,從正式教育到學徒期,再到成為熟練的程式設計師,最終創造出傑作。

內容解密:

  1. 正式教育:提供必要的電腦科學基礎知識和理論。
  2. 學徒期:在現實世界中獲得程式設計經驗和技能。
  3. 熟練程式設計師:透過不斷學習和實踐,成為具有豐富經驗的程式設計師。
  4. 傑作:創造出高品質的軟體產品。

軟體工藝的階層與卓越程式碼的寫作之路

軟體開發領域中,「軟體工藝」(Software Craftsmanship)的概念強調了程式設計師的技能培養與專業成長的重要性。這種成長過程被比喻為傳統工藝的學徒制(Apprenticeship),從學徒到熟練工(Journeyman),再到大師(Master Craftsman),每個階段都有其特定的角色與責任。

軟體學徒

軟體學徒在經驗豐富的程式設計師(可能是熟練工或大師)的指導下學習先進的程式設計技術。指導者會分配任務、示範如何完成任務,並審查學徒的工作,提供適當的指導以確保工作品質。更重要的是,學徒也會審查指導者的工作,透過測試、結構化走查(Structured Walkthroughs)和除錯等方式來學習指導者的程式碼是如何運作的。

在這個過程中,學徒能夠掌握許多單靠自己無法學到的程式設計技巧。如果有幸遇到多位優秀的指導者,學徒可以從他們身上學習到不同的扎實技術。每完成一個在高階程式設計師指導下的專案,學徒就越接近學徒生涯的尾聲,並邁向軟體工藝路線上的下一個階段:軟體熟練工。

軟體熟練工

軟體熟練工負責處理大部分的軟體開發工作。他們通常會從一個專案轉到另一個專案,運用自己的技能解決應用程式問題。儘管軟體開發者的學習永遠不會結束,但軟體熟練工更專注於應用程式開發,而非學習如何開發應用程式。

另一個重要的任務是培訓新的軟體學徒。他們會審查學徒在專案中的工作,並與他們分享程式設計技術和知識。軟體熟練工不斷尋找能夠改進軟體開發流程的新工具和技術。透過早期採用新的(但已驗證的)技術,他們保持在學習曲線的前沿,跟上當前趨勢,避免落後。利用行業最佳實踐為客戶創造高效且具成本效益的解決方案,是這個工藝階段的標誌。軟體熟練工是富有生產力、知識淵博的開發者,正是大多數專案經理在組建軟體團隊時希望找到的型別。

大師級工藝師

傳統上,要成為大師級工藝師,需要創作出一件傑作(Masterpiece),這件作品能夠讓你脫穎而出。一些高階的軟體傑作包括 VisiCalc[^8]、Linux 作業系統,以及 vi 和 emacs 文字編輯器。這些產品最初都是由單一開發者構思和建立的,儘管後來涉及到了數十甚至數百名不同的程式設計師。一件傑作不一定要像 Linux 或某些 GNU 工具那樣聞名於世,但必須得到你周圍同行的認可,被視為對某個問題的有用且創新的解決方案。傑作也不一定是獨立的原始程式碼,編寫一個複雜的作業系統裝置驅動程式,或是以多種有用的方式擴充套件其他程式,同樣可以被視為傑作。創作傑作的目的,是在你的作品集裡新增一件能夠告訴世人的作品:「我有能力開發出優秀的軟體,請認真對待我!」

大師級工藝師的主要任務是確定當前的最佳實踐,並創造新的最佳實踐。最佳實踐描述的是完成某項任務的最佳已知方法,而不一定是絕對最佳的方法。大師級工藝師會研究是否有更好的方法來設計應用程式,認識到某種新技術或方法論在廣泛的應用領域中的實用性,並驗證某種實踐是否最佳,然後將這些資訊傳達給他人。

內容解密:

  1. 軟體工藝的階層結構:文中描述了從學徒到大師的三個階段,每個階段都有其特定的職責和成長路徑。
  2. 學徒階段:重點在於學習和技能培養,透過與指導者的互動來提升自己的程式設計能力。
  3. 熟練工階段:專注於應用程式開發,並承擔培訓新學徒的責任,同時不斷尋找新的技術和工具來提高開發效率。
  4. 大師階段:創作傑作以證明自己的能力,並致力於發現和推廣最佳實踐。

程式碼範例

def fibonacci(n):
    """
    計算 Fibonacci 數列的前 n 項。
    
    :param n: 需要計算的項數
    :return: Fibonacci 數列的前 n 項
    """
    fib_sequence = [0, 1]
    while len(fib_sequence) < n:
        fib_sequence.append(fib_sequence[-1] + fib_sequence[-2])
    return fib_sequence[:n]

# 示例用法
print(fibonacci(10))

內容解密:

  1. fibonacci 函式:用於計算 Fibonacci 數列的前 n 項。
  2. fib_sequence 初始化:從 [0, 1] 開始,因為 Fibonacci 數列的前兩項是固定的。
  3. 迴圈擴充套件數列:持續將最後兩項的和新增到數列中,直到達到所需的長度 n
  4. 傳回結果:擷取數列的前 n 項傳回,確保結果符合需求。

圖表範例

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

title 軟體開發專案規模與生產力探討

package "時間序列分析" {
    package "資料準備" {
        component [時間索引] as time_idx
        component [重採樣 Resample] as resample
        component [缺失值處理] as missing
    }

    package "特徵分析" {
        component [趨勢 Trend] as trend
        component [季節性 Seasonality] as season
        component [殘差 Residual] as residual
    }

    package "預測模型" {
        component [ARIMA] as arima
        component [指數平滑] as ets
        component [Prophet] as prophet
        component [LSTM] as lstm
    }
}

time_idx --> resample : 時間聚合
resample --> trend : 分解
trend --> season : STL分解
season --> residual : 殘差分析

trend --> arima : 自迴歸
season --> ets : 季節調整
residual --> prophet : 預測
residual --> lstm : 深度學習

note right of arima
  p: 自迴歸階數
  d: 差分階數
  q: 移動平均階數
end note

@enduml

內容解密:

  1. 圖表結構:使用 Plantuml 圖表表示軟體工藝的不同階段及其之間的轉變關係。
  2. 節點說明
    • A[學徒]:代表初學者階段,主要任務是學習和技能培養。
    • B[熟練工]:代表能夠獨立承擔專案開發的階段,專注於應用程式開發和培訓新學徒。
    • C[大師]:代表達到頂尖水平的階段,負責創新和推廣最佳實踐。
    • D[業界領袖]:代表在業界具有重要影響力的角色,通常由大師級人物擔任。

生產力:軟體工程的核心

軟體工程的概念源自於1960年代末期,當時單純增加程式設計師數量已無法解決軟體危機。提高程式設計師的生產力成為當務之急,而這正是軟體工程領域誕生的契機。因此,理解生產力是研究軟體工程的基礎。

生產力的定義

儘管「生產力」一詞常被視為軟體工程的基礎,但令人驚訝的是,許多人對其有著扭曲的理解。問任何程式設計師關於生產力的看法,你可能會聽到「程式碼行數」、「功能點」、「複雜度指標」等。然而,軟體專案中的生產力概念並沒有什麼神秘之處。我們可以將生產力定義為:

在單位時間內完成的單位任務數量,或是以給定的成本完成的單位任務數量。

這個定義的挑戰在於如何界定「單位任務」。一個方便的單位任務可能是整個專案,但專案的大小和複雜度差異很大。程式設計師A在給定的時間內完成了三個專案,而程式設計師B只完成了一個大型專案的一小部分,這並不能告訴我們兩位程式設計師的相對生產力。因此,單位任務通常遠小於整個專案,通常是某個功能、單行程式碼或甚至更小的專案組成部分。只要在不同專案之間保持一致,並且單一程式設計師在任何專案上完成單位任務所需的時間相同,那麼具體的衡量標準並不重要。

一般來說,如果我們說程式設計師A的生產力是程式設計師B的n倍,那麼在相同的時間內,程式設計師A可以完成n倍於程式設計師B的專案數量。

程式設計師生產力 vs. 團隊生產力

1968年,Sackman、Erikson和Grant發表了一篇令人矚目的文章,聲稱程式設計師之間的生產力差異達10到20倍。後續的研究和文章甚至將這一差異推得更高。這意味著某些程式設計師的產出可能是某些能力較低的程式設計師的20倍(甚至更多)。一些公司甚至聲稱,在他們的組織中,不同軟體團隊之間的生產力差異達到兩個數量級。這是一個驚人的差異!

如果某些程式設計師能夠比其他人高出20倍的生產力(所謂的「大師級程式設計師」,Grand Master Programmers, GMPs),是否有某種技術或方法可以提高一般(或低生產力)程式設計師的生產力?

由於不可能將每位程式設計師都訓練成GMP級別,大多數軟體工程方法採用其他技術,如更好的管理流程,以提高大型團隊的生產力。本文採取了另一種方法:不是試圖提高團隊的整體生產力,而是教導個別程式設計師如何提高自己的生產力,並朝著成為GMP的方向努力。

雖然個別程式設計師的生產力對專案交付時間表的影響最大,但現實世界更關心專案成本——完成專案所需的時間和成本,而不是程式設計師的生產力。除小型專案外,團隊生產力優先於團隊成員的生產力。團隊生產力並不是簡單地取團隊成員生產力的平均值;它根據團隊成員之間複雜的互動。會議、溝通、個人互動和其他活動都可能對團隊成員的生產力產生負面影響,將新成員或經驗較少的成員納入團隊,以及重新修改現有程式碼,都可能降低團隊整體效率。

工時與實際時間

前面的定義提供了兩種衡量生產力的方法:一種根據時間(單位時間內完成的單位任務數量),另一種根據成本(以給定的成本完成的單位任務數量)。有時成本比時間更重要,反之亦然。為了衡量成本和時間,我們可以使用工時和實際時間。

從企業的角度來看,與程式設計師生產力相關的專案成本部分與工時(每個團隊成員在專案上花費的時間總和)直接成正比。一個人一天的工作量約等於8工時,一個月的工作量約等於176工時,一年的工作量約等於2000工時。專案的總成本是花費在該專案上的工時總數乘以每個團隊成員的平均小時薪水。

實際時間(也稱為日曆時間或掛鐘時間)是指專案進行期間的時間流逝。專案進度和最終產品交付通常根據實際時間。

工時是實際時間與同時參與專案的團隊成員數量的乘積,但最佳化其中一個變數並不總能最佳化另一個變數。例如,假設你正在開發一個市政選舉所需的應用,最關鍵的變數是實際時間;無論成本如何,軟體都必須在選舉日期前完全功能化並佈署到位。相比之下,一個「地下室程式設計師」正在開發下一個殺手級應用,可以花更多時間在專案上,從而延長交付日期(實際時間),但無法僱用更多人員來更快完成應用。

專案經理在大型專案中常犯的一個錯誤是將工時與實際時間混淆。如果兩名程式設計師可以在2000工時內(1000實際小時)完成一個專案,你可能會得出結論,四名程式設計師可以在500實際小時內完成該專案。然而,由於增加了溝通和管理複雜性,四名程式設計師可能無法實作如此高的效率增益。

內容解密:

本段主要闡述了工時和實際時間之間的區別,並指出專案經理容易犯的一個錯誤,即假設增加人手就能線性地縮短實際時間。然而,隨著團隊規模擴大,溝通和管理複雜度也會增加,這使得簡單地增加人手來縮短開發時間並不總是有效。

此圖示說明瞭軟體工程與生產力之間的關係,以及個人生產力和團隊生產力的不同最佳化方向。

生產力

隨著技術的不斷進步和軟體開發方法的演變,未來軟體工程中的生產力將繼續受到關注。人工智慧、大資料和DevOps等新技術的發展將為提高軟體開發效率和品質提供新的機會。同時,如何有效地整合這些新技術,並最佳化團隊協作和管理流程,將是未來軟體工程領域的重要研究方向。

2.4 概念複雜度與範圍複雜度對生產力的影響

在軟體開發專案中,隨著專案變得更加複雜,程式設計師的生產力會下降。這是因為更複雜的專案需要更深入(和更長時間)的思考來理解正在發生的事情。此外,隨著專案複雜度的增加,軟體工程師將錯誤引入系統的可能性更大,而早期引入系統的缺陷直到後期才被發現,那時的修復成本要高得多。

概念複雜度

概念複雜度指的是專案中具有複雜、涉及或錯綜複雜的部分安排,使得理解變得困難。例如,在高階語言(HLL)如 C/C++ 中的單個算術表示式,可能包含複雜的函式呼叫、幾個奇怪的算術/邏輯運算子,具有不同的優先順序,以及許多使表示式難以理解的括號。概念複雜度可能發生在任何軟體專案中。

對生產力的影響

概念複雜度透過兩種方式影響程式設計師的生產力。首先,複雜的結構需要比簡單的結構更多的思考(因此需要更多時間)。其次,複雜的結構更有可能包含缺陷,這些缺陷必須在稍後進行修正,從而導致相應的生產力損失。

範圍複雜度

範圍複雜度發生在專案的資訊量太大,人類難以輕易消化時。即使專案的個別元件很簡單,但專案的龐大規模使得一個人無法理解整個專案。範圍複雜度通常出現在中型和大型專案中(事實上,這種形式的複雜度使小型專案與其他專案區別開來)。

對生產力的影響

範圍複雜度引入了不同的問題。當專案達到一定規模時,專案中的程式設計師可能完全不知道專案其他部分正在發生的事情,並可能重複編寫已經存在於系統中的程式碼。很明顯,這降低了程式設計師的生產力,因為程式設計師浪費了時間編寫那段程式碼。

// 示例:重複的程式碼
int calculateArea(int width, int height) {
    return width * height;
}
// 在專案的其他地方可能存在相同的函式
int calculateRectangleArea(int w, int h) {
    return w * h;
}

內容解密:

上述程式碼展示了範圍複雜度的一個問題:由於對專案其他部分的未知,程式設計師可能會編寫重複的程式碼。函式 calculateAreacalculateRectangleArea 實際上執行相同的操作,但由於缺乏溝通或程式碼函式庫的可見性,它們被獨立編寫。為瞭解決這個問題,可以透過建立一個集中的程式碼函式庫或指定一個「圖書管理員」來追蹤可重用的程式碼元件,從而減少這種生產力的損失。

預測生產力

生產力是一個可以衡量和嘗試預測的專案屬性。當一個專案完成時,假設團隊在專案開發過程中保持了對任務完成的準確記錄,那麼確定團隊(及其成員)的生產力是相當容易的。雖然過去專案的成功或失敗並不能保證未來專案的成功或失敗,但過去的表現是預測軟體團隊未來表現的最佳指標。

如何提高預測能力

為了提高軟體開發過程,需要跟蹤哪些技術有效、哪些技術無效,以便知道在未來專案中該做什麼(或不該做什麼)。為了跟蹤這些資訊,程式設計師及其支援人員必須記錄所有軟體開發活動。這是軟體工程引入的純粹管理費用的典型例子:檔案幾乎對當前專案的完成或品質沒有幫助,但它是一項對未來專案有幫助的投資,可以預測(並提高)生產力。