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