# 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;
}
```