---
# System prepended metadata

title: eXtreme Programming
tags: [軟體工程]

---

# eXtreme Programming

###### tags: `軟體工程`
[91APP的軟體開發之道—從20人到200人的組織發展旅程](https://happylee.blog/91appway/?fbclid=IwAR3oIxpmNMTmaHd6_XK8yRqdsye132yhDMkSVqwp15xAHKevc66Kdns6xBA)

## Target
* 降低因需求變更而帶來的成本(wiki)
* ==提高錯誤的檢出率==

## Main 
4 Activities, 5 Values, 3 Principles, 12 Practices

## Activities
* Coding
    coding is basic, beautiful code is beautiful document, beautiful code improve system.
    [ffs](####ffs)
    :::success
    鼓勵撰寫良好程式技術
    * 專案每部份都分派兩人(pair programming)
    * 逐行複查程式碼(code review)
        * 除了相互驗證錯誤, 還可以讓其他人了解程式, 可降低錯誤的修正週期
    * 要求程式碼簽名
    * 提供良好的程式碼當作範例參考
    * 強調程式碼是公有財產(Collective code ownership)
    * 獎勵良好的程式碼
    * 一份簡單的標準
        * e.g. coding rule
    >[name=Code complete in chap28.1]
    :::
* Testing
    Test is important, if code is not passing the test, that means these code is fail
* Listening
    傾聽客戶(該系統使用者)需求, 了解背後的故事背景
* Designing
    完善系統分析與設計可以增加程式的開發速度與未來的維運穩定度

## Values
* Communication
    頻繁的==溝通==和==簡單的文件==取代複雜的文件，目的是讓所有開發人員對於系統有同一個視角概念
* Simplicity 
    鼓勵用==最簡單==的解決方式出發，再通過==不斷重構==達到更好的結果。
    對當前的需求來進行設計、編碼，而不去理會明天、下周或者下個月會出現的需求。
* Feedback
    * Feedback from system
    Using unit test to get the feedback when modify the code
    :::warning
    通過編寫單元，簡單的證明某一段代碼是否存在問題
    :::
    * Feedback from customer
    Unit test case is from by customer, make sure the customer knowing the development progress
    * Feedback from team
    新需求透過開發小組的討論後，即可評估出新需求所需要的工作時間
* Respect
    XP倡導團隊，因此團隊中的任一人皆可修改程式，==但要對其結果負責==，亦須尊重團隊成員的貢獻
* Courage
    要勇敢重構程式，而隨時可以用更好的設計來改寫你的系統。
    :::success
    Refactor is not rewirte
    如果重構是造成大規模的修改，就該回頭看看，是否該重新把這部份程式碼推倒重來，重新設計開發
    >[name=Code complete]
    :::
:::info
Simplicity/ refactor/ testing the code and system
:::

## Principles
* Feedback
頻繁快速的修改重構且利用 ==CI== 的方式進行feedback
* Assuming simplicity
注重當下的簡易的設計，==好的設計可以更好的維護系統==
* Embracing Change 
不要對變化採取反抗的態度，而應該包容它們。
    :::success
    需求變更設計
    * 累積分類後處理變更請求
        * 累積變更需求後, 做一次通盤規劃
    * 評估變成的成本
    * 提防大量的變更請求
    >[name=code complete chap 28.2]
    :::
## Practices
### Fine Scale Feedback
:::spoiler Pair programming 
* 兩人以上一起寫程式，互相討論、code review
    e.g [bug](#RAII) 
    :::success
    * pair porgramming 可以增加開發的效率
        單元測試平均缺陷檢出率約30%
        整合測試平均缺陷檢出率約35%
        對設計和程式碼進行詳細檢查, 防止錯誤出現率約 55%/60%

    * 全程採用pair programming 比單人開發成本約高10%~15%, 但開發週期卻會縮短45%
    >[name=Code complete in chap21.1]
    :::
    :::success
    pair porgramming 不一定要採用兩人為一組的實做方式
    而是可以採用 Collective code ownership 的方式交叉配對, 達到人員對於程式碼的覆蓋率
    >[name=Code complete in chap21.1]
    :::
    :::success
    pair programming rule
    * 用程式碼規範來支援
        * 減少因程式碼風格造成的爭吵
    * 不要讓結對程式變成旁觀
        * 另一個配合的人應該是配合規劃接下來要做的事情, 而不是在一旁觀看
    * 不要強迫使用在簡單的問題上
    * 有規律的進行任務輪替
    * 相互調整進度
        * 若進度差距太大, 則需要考慮拆散或將另一人進度暫緩
    * 避免新手組合
    * 指定一人做任務調配
    >[name=Code complete in chap21.2]
    :::
:::
:::spoiler TDD
* 在開發程式以前先寫測試，確保每次新增的程式有一定程度的檢核與確保品質
* 也可以藉由累積下來的測試，驗證bug fix or feature 是否有改壞其他的功能
:::
:::spoiler Whole Team
* 客戶與團隊一同開發
:::
:::spoiler Planning Game
* Release planning
    * Exploration
        * Write a story
        設計使用情境(故事設計)
        * Split a story
        若故事太大或太長，則需要分割故事
        * Estimate s story
        評估每個故事所需時間
    * Commitment
        * Sort by value
        照價值為使用者故事排序
            :::info
            Critical / Significant Business Value/ Nice to have 
            :::
        * Sort by risk
        按風險為使用者故事排序
            ::: info 
            Completeness(完全度): 我們是否已經瞭解所有的故事細節
            Volatility(發散性): 可能會發生變化嗎
            Complexity(複雜度): 是否難以建構
            :::
        * Set velocity
        決定以怎樣的速度開展專案
        * Choose scope
        挑選在下一個發布中需要被完成的使用者故事
    * Steer 
        控制軟體開發節奏
* Iteration planning
    * Exploration
        * Translate the requirement to tasks 
        指派工作
        * Combine/Split task
        處理合併或切割的任務
        * Estimate task
        預測任務的處理時間
    * Commitment 
        * A programmer accepts a task
        程式設計師來接任務
        * Programmer estimates the task
        由負責的人，給初預估開發時間
        * Set load factor
        設定負載係數，有點像是實際工時
        ::: info
        e.g: 一周工作40小時，其中5小時用於開會，則負載係數不會超過35小時
        :::
        * Blancing
        資源調配，調整過重或過輕的 load factor
    * Steering 
        * Get a task card
        * Find a Partner
        Pair programming
        * Design the task
        與你的伙伴一同設計功能
        * Write unit test
        * Write code
        * Run test
        * Refactor
        * Run Functional test
        執行功能測試
:::

### Continuous Process
:::spoiler Continuous integration 
* CI rule
    :::info
    * Maintain a Single Source Repository
        使用版本控制來管理程式碼，像是 SVN, CVS等。
    * Everyone Commits To the Mainline Every Day
        每天都 Commit 撰寫的程式碼，減少程式碼衝突的情況。
    * Every Commit Should Build the Mainline on an Integration Machine
        每一次 Commit 都進行建置 (Build)與整合，提早發現程式錯誤。
    * Automate the Build
        專案自動建置，每一個 Commit (程式碼上傳) 都進行建置工作。
    * Make Your Build Self-Testing
        程式碼進行有效的切割，使得程式變得更容易維護。並且撰寫單元測試，將片段式的程式在建置後進行自我測試。
    * Test in a Clone of the Production Environment
        提供一個穩健的建置與測試環境，這個環境必須相當接近真實的執行環境
    * Automate Deployment
        透過一些 Script 的執行達到自動佈署與安裝測試
    * Everyone can see what's happening
        透過程式碼更新與建置的過程中所產生的報表，讓所有人清楚發生了什麼事？程式碼做了哪些修正？
    * Make it Easy for Anyone to Get the Latest Executable
        建置的時間越快越好，理論上不得超過十分鐘，且提供一個可以穩定運作的最新版本。
    :::
:::

:::spoiler Design improvement (Refactor)
* 系統開發久了，常出現很多奇怪徵狀，這時候都需要將系統做refactor
    :::info
    * 許多 Func. 在做同一事情
    * 有很多骯髒(多餘)的程式碼
    * 未使用的程式碼
    * 改善設計的演算法
    :::
    eg. [get number of bits](#get_num_bit)
:::
:::spoiler Small releases 
* 小規模的釋出可正確使用的系統
:::

### Shared understanding 
:::spoiler coding standard
* 建立良好的程式撰寫標準，一致的程式編碼風格與習慣，提高後續維護的便利性。
    :::info
    ```clike=
    if (foo->next==NULL && totalcount<needed && needed<=MAX_ALLOT
    && server_active(current_input)) { ...
    ```
    Might be better as
    ```clike=
    if (foo->next == NULL
        && totalcount < needed 
        && needed <= MAX_ALLOT
        && server_active(current_input)){
    ...
    ``` 
    ref: https://www.doc.ic.ac.uk/lab/cplus/cstyle.html
    :::
:::
:::spoiler Collective code ownership 
* 程式碼是大家共有的，每一位程式設計師都要對所有程式負責，當然也可以任意修改程式，儘管這行程式不是你自己寫的
    :::success
    * 眾多人一起檢查, 一起協同撰寫, 可以提昇程式碼品質
    * 若其中一人離開團隊, 對專案的衝擊很小
    * 錯誤的修正週期縮短, 因任何一人都可以被指派去修正錯誤
    >[name=Code complete in chap21.1]
    :::
:::
:::spoiler Simple design 
* 使用簡單的方式設計系統
:::
:::spoiler System metaphor
* 利用程式語言的特型，讓任何人可以從你的程式碼看出這段程式的目的
    :::info
    ```clike=
    long int freq = 0;
    freq = device.get_freq();
    ```
    may be
    ```clike=
    long int fsk_freq = 0;
    fsk_freq = device.get_fsk_freq();
    ```
    :::
    :::info
    ```clike=
    add_data(name, address, phone, zip_code);
    ```
    may be
    ```clike=
    add_employee_data(employee);
    ```
    :::
    [你了解C嗎?](#understanding c ???)
:::

### Programmer welfare
* Sustainable pace
開發工程師==不得工作超過40小時==，若當週超過時數，下週需要扣回來。要用較短的開發循環持續推進，更頻繁的交付，已獲得更高質量的成果
    :::success
    把程式設計師當人看
    * 尊重程式設計師的個體差異
    * 有寬敞, 安靜, 隱私的環境
    * 盡量少的干擾
    >[name=Code complete in chap28.5]
    :::
## Reference        


:::info
[Extreme Programming](https://ithelp.ithome.com.tw/articles/10217544)
[軟體開發之極限編程(極限開發) eXtreme Programming, XP](https://blog.toright.com/posts/697/%E6%A5%B5%E9%99%90%E9%96%8B%E7%99%BC-extreme-programming-xp.html)
[wiki](https://zh.wikipedia.org/wiki/%E6%9E%81%E9%99%90%E7%BC%96%E7%A8%8B#%E6%A0%B8%E5%BF%83%E5%AF%A6%E8%B8%90)
:::

## 同場加映

C 語言中的 O.O.

從 RAII 看到 destructer
```
RAII_VARIABLE(char *, variable, NULL, free);
```
free 換成 C++ 中的 ~variable()

[用C寫的oo](#COO)





#### ffs
```clike=
static inline int ffs(int x)
{
    int r = 1;
    if (!x)
        return 0;
    if(!(x& 0xffff)){
        x >>= 16;
        r += 16;
    }
    if(!(x& 0xff)){
        x >>= 9;
        r += 8;
    }
    if(!(x& 0xf)){
        x >>= 4;
        r += 4;
    }
    if(!(x& 0x3)){
        x >>= 2;
        r += 2;
    }
    if(!(x& 0x1)){
        x >>= 1;
        r += 1;
    }
    return r
}
```

#### get_num_bit
```c=
int get_num_bit(unsigned int x){
    int pop = 0;
    for(int i = 0;i < sizeof(x)*8 ; i++){
        if (x & 1) pop += 1;
        x = x >> 1;
    }
}
int get_num_bit2(unsigned int x){
    int pop = 0;
    while(x){
        pop = pop + (x & 1);
        x = x >> 1;
    }
}


int get_num_bit3(unsigned int x){
    int pop = 0;
    while(x){
        pop = pop +1;
        x = x >> 1;
    }
}

```
```
        mov     DWORD PTR [rbp-8], 0
        jmp     .L2
.L4:
========== if (x & 1) pop += 1 ; ========
        mov     eax, DWORD PTR [rbp-20]
        and     eax, 1
        test    eax, eax
        je      .L3
        add     DWORD PTR [rbp-4], 1
.L3:
        shr     DWORD PTR [rbp-20]
        add     DWORD PTR [rbp-8], 1
.L2:
        mov     eax, DWORD PTR [rbp-8]
        cmp     eax, 31
        jbe     .L4
------------------------------------------        
        jmp     .L6
.L7:
========== pop = pop + (x & 1); ==========
        mov     eax, DWORD PTR [rbp-20]
        and     eax, 1
        mov     edx, eax
        mov     eax, DWORD PTR [rbp-4]
        add     eax, edx
        mov     DWORD PTR [rbp-4], eax
        shr     DWORD PTR [rbp-20]
.L6:
        cmp     DWORD PTR [rbp-20], 0
        jne     .L7
------------------------------------------                
        jmp     .L9
========= pop = pop +1; ==================
.L10:
        add     DWORD PTR [rbp-4], 1
        shr     DWORD PTR [rbp-20]
.L9:
        cmp     DWORD PTR [rbp-20], 0
        jne     .L10
```
```c=
int get_num_bit4(unsigned int x){
    x = x -((x >> 1)) & 0x55555555;
    x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
    x = (x + (x >> 4))& 0x0F0F0F0F;
    x = x + (x >> 8);
    x = x + (x >> 16);
    return x & 0x0000003F;
}

```

#### RAII

```c=
int net_pkt(int *para){
    char *buf = get_pkt_buff(para);
    if (procesing_data1(buf)){
        do_something(para);
    }
    if(checking_proc(buff)){
        error_exception1()
        free(buf);
        return size
    }
    .....
    
    .....
    
    .....
    
    .....
    
    .....
    procesing_data2(para);
    
    if(doing_proc(buff)){
        error_exception2();
    }
    free(buf);
    return size;
}

```


```c=
int net_pkt(int *para,char *para2){
    char *buf = get_pkt_buff(para);
    if (procesing_data1(buf)){
        do_something(para);
    }
    if(checking_proc(buff)){
        error_exception1()
        free(buf);
        return size
    }
    .....
    
    .....
    
    .....
    
    .....
    
    if (checking_para2(para2)){
        return size;
    }
    
    .....
    
    procesing_data2(para);
    
    if(doing_proc(buff)){
        error_exception2();
    }
    free(buf);
    return size;
}

```

```c=

#define RAII_VARIABLE(vartype, varname, initval, dtor) \
    void _dtor_ ## varname (vartype * v) { dtor(*v); } \
    vartype varname __attribute__((cleanup(_dtor_ ## varname))) = (initval)
    
int net_pkt(int *para,char *para2){
    RAII_VARIABLE(char *, buf, get_pkt_buff(para), free);
    if (procesing_data1(buf)){
        do_something(para);
    }
    if(checking_proc(buff)){
        error_exception1()
        return size
    }
    .....
    
    .....
    
    .....
    
    .....
    
    if (checking_para2(para2)){
        return size;
    }
    
    .....
    
    procesing_data2(para);
    
    if(doing_proc(buff)){
        error_exception2();
    }
    return size;
}

```
array size problem

```c=
#define getNumberInArray(a) \
	(sizeof(a) / sizeof(a[0]))
```


```c=
char array[50];
char *ptr = NULL;
printf("array size of array = %d\n", ARRAY_SIZE(array));
printf("array size of ptr = %d\n", ARRAY_SIZE(ptr));
return 0;
```
:::spoiler Ans
```c=
#define ARRAY_SIZE(arr) \
  (sizeof(arr) / sizeof((arr)[0]) \
  + sizeof(typeof(int[1 - \
                      2*!!__builtin_types_compatible_p(typeof(arr), \
                                                       typeof(&arr[0]))]))*0)
```
```
sizeof-new.c:13: error: size of array 'type name' is negative
```
:::

#### understanding c ???

```c=
unsigned int add(unsigned int a, unsigned int b)
{
  return a + b;
}
```
Q: What is the result of add(UINT_MAX, 1)?

```c=
int add(int a, int b)
{
  return a + b;
}
```
Q: What is the result of add(INT_MAX, 1)?

```c=
void func(int *x) { return *x; }
int n = 0;
printf("%d %d %d\n", n++, n++, n++);
printf("%d %d %d\n", func(&n), func(&n), func(&n));
```
Q: print result ??

:::spoiler Ans
printf("%d %d %d\n", n++, n++, n++)
is undefine behavior

printf("%d %d %d\n", func(&n), func(&n), func(&n));
unspecified behavior
:::
```c=
#include <stdio.h>
int foo (int a) {
    if (a + 100 > a)
        printf("%d GT %d\n", a + 100, a);
    else
        printf("%d LT %d\n", a + 100, a);
    return 0;
}
int main () { 
    foo(100); 
    foo(0x7fffffff); 
    return 0; 
} 
```

200 GT 100
-2147483549 LT 2147483647
or ???
200 GT 100
-2147483549 GT 2147483647
:::spoiler Ans
-O0 -O2
:::


#### COO
定義一個通用的通訊界面:

```clike
typedef struct {
	int (*open)(void *self, char *fspec);
	int (*close)(void *self);
	int (*read)(void *self, void *buff, size_t max_sz, size_t *p_act_sz);
	int (*write)(void *self, void *buff, size_t max_sz, size_t *p_act_sz);
} tCommClass;

tCommClass commRs232; /* RS-232 communication class */
commRs232.open = &rs232Open;
commRs232.write = &rs232Write;

tCommClass commTcp; /* TCP communication class */
commTcp.open = &tcpOpen;
commTcp.write = &tcpWrite;
```

對應的通訊實做: [TCP subclass]

```clike
static int tcpOpen (tCommClass *tcp, char *fspec) {
	printf ("Opening TCP: %s\n", fspec);
	return 0;
}
static int tcpInit (tCommClass *tcp) {
	tcp->open = &tcpOpen;
	return 0;
}
```

當然你也可以定義 HTTP subclass

```clike
static int httpOpen (tCommClass *http, char *fspec) {
	printf ("Opening HTTP: %s\n", fspec);
	return 0;
}
static int httpInit (tCommClass *http) {
	http->open = &httpOpen;
	return 0;
}
```