最近聽了ECUG大會上孫敬云老師的分享感覺受益匪淺,畢竟大學課本上只講到瀑布模型就沒有下文了,工作以后一直貫徹的都是Scrum路線,一直也沒有時間好好的去學習整理這部分的知識,直到近幾天聽到了孫老師的分享,所以就在這里記錄下孫老師的分享也總結我自己的思路。以下內(nèi)容部分摘自于孫老師的分析PPT
1 軟件工程之路
1.1 軟件工程的演進
貌似大學的那門軟件工程只給我們講到了1980年,之后的需要我們走出校門,在社會中進行學習。
先來看下面這張圖,是1980年至今的軟件工程演化路線,像瀑布模型大家應該是耳熟能詳。
進入1990年,Scrum這種,近幾年應該也是略有耳聞,可是像極限編程這種可能就很少聽說了吧。
再向后看,進入2000年,持續(xù)集成也就是CI/CD中的CI和敏捷開發(fā)近幾年炒的火熱,互聯(lián)網(wǎng)公司爭先恐后,kanban(今天公司產(chǎn)品說,我才知道kanban是日本人發(fā)明的)也有點大勢已去,不過市場上應該還有不少公司在使用kanban。
走到了2010年,我們所看到的應該就是幾個隨處可見的概念了,持續(xù)交付,DevOps,Scrumban(我們近幾年說的真正意義上的Scrum)

1.2 瀑布模型
在這里不詳細敘述,只敘述幾個痛點。
- 產(chǎn)生客戶價值周期長
- 部門、角色之間存在壁壘
- 無法及時響應需求變化
- 價值流動不可見

1.3 敏捷開發(fā)
敏捷軟件開發(fā)(英語:Agile software development),又稱敏捷開發(fā),是一種能應對快速變化需求的軟件開發(fā)能力。它們的具體名稱、理念、過程、術語都不盡相同,相對于“非敏捷”,更強調(diào)程序員團隊與業(yè)務專家之間的緊密協(xié)作、面對面的溝通(認為比書面的文檔更有效)、頻繁交付新的軟件版本、緊湊而自我組織型的團隊、能夠很好地適應需求變化的代碼編寫和團隊組織方法,也更注重軟件開發(fā)過程中人的作用。
說人話,就是更注重溝通,快速產(chǎn)出新版本,并且更適應需求變更的的適合小團體開發(fā)的方法。
下圖左方,是需求池的概念(比如jira中的backlog),比如7%的需求是現(xiàn)實中大量客戶的需求,則我們將這7%的需求作為優(yōu)先級最高的需求。
下圖右方,是迭代和反饋的概念。

敏捷開發(fā)中有敏捷宣言,可以更好地闡述敏捷開發(fā)的概念。
- 個體和互動高于流程和工具(敏捷開發(fā)的站會落實了這一點)
- 工作的軟件高于詳盡的文檔(jira、miro等更好地代替厚重的需求文檔)
- 客戶合作高于合同談判(公司內(nèi)各部門甩鍋情況,難道不應該用合作為公司創(chuàng)造價值嗎)
- 響應變化高于遵循計劃(快速響應時長需求,而不是錯過市場變化)
1.3.1 Scrum實踐
下圖右上方是Scrum中的角色定義。
- Product Owner(產(chǎn)品負責人,主要負責產(chǎn)品設計,需求篩選等)
- Scrum Master(敏捷主管,主要負責項目迭代跟進等)
- Scrum Team(敏捷團隊,主要負責需求的研發(fā)測試部署等,包括Dev、Test、Ops等)
下圖左方是用戶故事(user story),其實就是我們傳統(tǒng)意義上的需求,只不過以一種需求方的更委婉擬人的語氣來講述該用戶人群的需求。
例如:我是一個買家,我希望我的購物車能通過價格排序,這樣我就能根據(jù)我卡中的錢進行合理的消費。
下圖中部,則是Scrum的核心流程。有很多公司光注重了其中的流程(比如站會),卻沒有得到其中的精髓。Scrum中把一個迭代叫做一個沖刺(Sprint),這也是很多地方把計劃會議叫做沖刺會的由來,一般一個Sprint為1~4周。核心流程包括4個會議,如下所示:
-
計劃會議(Product Owener、Scrum Master、Scrum Team)
- 從Backlog中按照優(yōu)先級選擇這個Sprint要做的User Story
- 向團隊解釋澄清User Story的需求,并決定是否將User Story拆解為更細粒度的
Sub Task - 團隊估計User Story的
Story Point(用以評估story的大小,有一種好玩的方式叫做Scrum Poker) - 團隊決定是否將User Story拆分Sub Task來進行跟蹤
- 決定這個Sprint的目標和交付的User Story
-
每日站會(Product Owener(可選)、Scrum Master、Scrum Team)
- 站會維持在15分鐘以內(nèi),分早晚
- 團隊成員講述圍繞3點:我做了什么,我將要做什么,我遇到什么困難
- 每人陸續(xù)進行講述,為了快速響應,維持最新消息,包括需求調(diào)整等
- 以及進行高效溝通,傳遞信息,拒絕信息發(fā)散
- 確定相關問題后,團體相關人員小范圍討論
-
評審會議(Product Owener、Scrum Master、Scrum Team)
- 類似于傳統(tǒng)意義上的驗收階段
- 介紹Sprint結果,按User Story順序演示新功能
- 回答相關人員對展示的疑問,并記錄其所期望的更改,收集反饋
- 如果遇到一些還沒解決的障礙,則將障礙加入障礙Backlog
- 以User Story作為是否成功交付的標準來評價任務完成情況
-
回顧會議(Scrum Master、Scrum Team)
- 回顧這個Sprint,收集Sprint的相關數(shù)據(jù)
- 產(chǎn)生見解,多問為什么,找到各個方面的優(yōu)缺點,進行復盤分析
- 進行頭腦風暴分析解決方案,投票選出下期的改進項
- 探索提高效率和質(zhì)量的方式

1.3.2 極限編程(XP)實戰(zhàn)
極限編程是敏捷開發(fā)中最具成效的幾種方法之一,如同其他敏捷方法,它和傳統(tǒng)方法的本質(zhì)不同在于它更強調(diào)可適應性能性以及面臨的困難。
它的基礎和價值觀是:
- 交流(加強交流,解決信息不同步導致的問題)
- 樸素(秉持最小可用,勤于迭代,不做拍腦袋的無用功擴展)
- 反饋(多接受反饋,以進行快速調(diào)整修改)
- 勇氣(在該重構時重構,當狀態(tài)不對時,放棄思考,調(diào)整狀態(tài)后重新思考)

它認為任何一個軟件項目都可以從四個方面入手進行改善:加強交流;從簡單做起;尋求反饋;勇于實事求是。
下圖是極限編程的13個最佳實踐。

1.3.3 思考自己的團隊是不是敏捷
問問自己下面的幾個問題:
- 成員是否氫氣的知道團隊的目標?
- 成員是否可以預測結果并且充滿信心?
- 成員是否主動做事并且為此負責?
- 成員是否愿意持續(xù)改進團隊?
如果你對上述幾個問題的回答時肯定的,那么恭喜你,你們的團隊是關注發(fā)展的敏捷團隊,如果你對上述問題有部分或全部否定,那么你可能需要調(diào)整你的團隊,你們只不過是在關注敏捷的形式,而沒有精髓。
1.4 DevOps模型
DevOps是一種開發(fā)、測試和運維之間文化溝通,通過自動化的方式來進行軟件交付和架構變更的流程,使構建、測試、發(fā)布軟件能更快捷、頻繁、可靠。它的出現(xiàn)是因為軟件逐漸的認識到,開發(fā)、測試和運維的緊密合作可以更好的交付軟件產(chǎn)品和服務。
下圖是DevOps的標準化流程,通過建立一個完備的團隊來建立一條IT服務供應鏈,通過自動化實現(xiàn)高效率交付,不固定需求管理、工具鏈等,只專注于持續(xù)的穩(wěn)定的價值交付

2 三部工作法
2.1 第一工作法(工作流)
這一步工作法是關于從開發(fā)到運再到客戶的自左向右的工作流
2.1.1 定義工作流
下圖展示了自循環(huán)工作流的流程,其中前4項屬于Dev范疇,后4項屬于Ops范疇:
- plan(計劃)
- code(編碼)
- build(構建)
- test(測試)
- release(發(fā)布)
- deploy(部署)
- operate(操作)
- monitor(監(jiān)控)

2.1.2 工作流實踐
下圖左方是我們針對上面的工作流的一種實踐,是一種工作日志的方式,這種工作方式同樣適用于敏捷開發(fā)(jira中的工作日志),其實DevOps的本質(zhì)就是敏捷開發(fā)。
- 過程名稱
- 輸入
- 輸出
- 工具
- DoD(Definition of Done,完成的定義,可以理解為完成的標準)
- 平均前置任務等待數(shù)(完成當前任務,依賴等待了多少任務)
- 手工比例(手工操作的步驟占據(jù)了總量多少)
- %C/A(返工指標,完成時間/總花費時間,總花費時間=完成時間+修復時間)
- 處理時間PT(真正處理該任務所花費的時間)
- 流動時間FT(從接收到需求到需求完成所花費的時間)
通過上方工作日志所記錄的數(shù)據(jù),我們可以對我們的工作進行階段性的分析,比如:
-
平均前置任務等待數(shù)可以用來判斷我們的任務分配是否合理 -
手工比例可以用來提示我們是否需要使用自動化來優(yōu)化流程 -
%C/A可以用來判斷一個人的工作質(zhì)量是否需要優(yōu)化 - 等等

2.1.3 版本和分支策略
2.1.3.1 普通版本管理
- 新需求拉一條新的分支進行開發(fā),命名
xxx feature branch - 新bug拉一條新的分支進行修復,命名
xxx fix branch - master分支保持永遠可構建成功狀態(tài)
- 每當新需求或新bug完成合并到主分支,觸發(fā)主分支的pipeline構建流程

2.1.3.2 GitFLow實踐
這種方案是基于GitFLow的標準來進行版本控制的。
該方案采用develop、feature、hotfix、release、prod等分支進行實踐,比較適合版本并存的團隊
- develop:主開發(fā)分支,包含所有發(fā)布到下一個release的代碼
- feature:新功能開發(fā)分支,最后會合并回develop分支
- hotfix:prod發(fā)現(xiàn)bug時,用來熱修復分支,最后會合并回develop分支
- release:發(fā)布版本時,基于develop創(chuàng)建的分支
- prod: 用于發(fā)布到生產(chǎn)環(huán)境的代碼的分支,只能合并不能修改。
這種方式很靈活,代碼隔離也很好,只是過于繁瑣。

2.1.3.3 tag版本實踐
該方案是基于版本打tag的方式來進行版本控制的。
該方案采用master、feature、fix等分支進行實踐,比較適合小版本滾動升級團隊
- master:主分支,該分支不允許修改,只允許合并,該分支永遠保持可構建狀態(tài),發(fā)布時通過tag來進行版本發(fā)布
- feature:新功能開發(fā)分支,最后會合并回master分支
- fix:線上發(fā)生bug后,用于修復的分支最終會合并到master分支
這種方式很簡單,但是對多環(huán)境的支持不是很好。

我們推薦使用GitFlow+Tag的方式來進行版本控制。以觸發(fā)多環(huán)境下的pipeline自動化流程。
2.1.4 規(guī)劃流水線
這里主要是我們的CI/CD的流水線的結構,可已使用Jenkins的pipeline也可以使用Gitlab的pipeline。
這里鏈接下之前寫的Jenkinsfile教程(Jenkins Pipeline 教程)
自動:
- 代碼檢測(推薦使用SonarQube,教程)
- 單元測試(很重要,java可使用junit,其他的未調(diào)研)
- 制品(過去的jar包,如今的docker image)
- 集成測試(很重要,屬于自動化測試中的一個重要環(huán)節(jié))
- 性能測試(大部分場景下可能不會每次都做)
- 安全測試(跟性能測試差不多)
- 部署預發(fā)布(這里泛指線上以下的所有環(huán)境)
手動:
- 驗收測試
- 部署線上

2.1.5 Scrum+XP+DevOps流程
看過了上面的部分,你一定嗤之以鼻,因為下面的圖其實就是上面的Scrum圖+Pipeline的圖。其實下面你的這張圖才能真正意義上的指導我們?nèi)绾卧诠ぷ髦袑嵺`Scrum+DevOps。我將下面的圖分層了4塊,讓我們一起看看下面的圖吧
Scrum+XP流程
第一部分的需求池的這個概念我們在上方的Scrum中已經(jīng)看到了,在這里不做詳細解釋,如果記不太清了,可以回到上方#1.3.1 進行復習。
第二部分的Scrum流程需要我們再來回顧一下,一個迭代中的4個會議還記得嗎,不記得就回去看看吧,通過小迭代來進行可持續(xù)的速度小型發(fā)布,其中要落實XP的13個實踐,包括集體所有權、簡單設計、重構等等。
CI/CD
通過代碼版本控制來觸發(fā)我們的的CI和CD的構建。
通過發(fā)布編碼標準,測試驅動開發(fā),代碼riew等來提升我們的代碼質(zhì)量以進行優(yōu)質(zhì)的代碼持續(xù)集成。
在持續(xù)集成之后,迎面而來的是構建、測試、部署,這幾個步驟的才是真真正的表現(xiàn)我們的團隊的持續(xù)交付的質(zhì)量。
在3和4兩個步驟,我們會看到下方有包含對號和錯號的表格,這個我們會在接下來的地方講到,這個是個CI/CD反饋表,用來表示我們某個階段的CI/CD的縮略情況,可以通過這個反饋來進行相關的分析。

2.2 第二工作法(反饋流)
2.2.1 持續(xù)反饋
下方的圖是CI/CD的持續(xù)反饋圖,可能做過Jenkins的Pipeline的小伙伴已經(jīng)能看出它了。
頂部的“表頭”其實就是我們的Pipeline的流程,而每一行是我們的歷史構建記錄,通過這張圖我們可以清洗的看到我們在某一階段的pipeline的執(zhí)行情況,就可以看到我們在哪一個節(jié)點發(fā)生錯誤的情況比較高。
在這個流程中最重要的就是測試部分,如果我們的團隊對包括單元測試在內(nèi)的測試環(huán)節(jié)如果不是很重視,那這個反饋將對我們的團隊毫無意義。
在DevOps中,我們推崇測試驅動開發(fā),通過先寫單元測試,集成測試等用例來驅動我們的開發(fā)進行編碼。

2.2.2 查看在制品
這里的制品的含義就是我們所構建的,比如java的jar,golang的native,docker的docker image等。
通過DevOps的反饋,我們可以查看制品所在的story的目前階段

2.3 第三工作法(學習)
2.3.1 不斷嘗試和重復學習
讓我們再來看下面這張圖,對比上面的那種圖,在我們的編碼、測試、部署三個階段多了個小錘子的標志。
這里的編碼、測試、部署,分別代表著開發(fā)、測試、運維,三個崗位需要不斷的嘗試、配合和重復學習來讓這條IT服務供應鏈更快速更穩(wěn)定更自動化,讓信息反饋更精準、更全面的覆蓋到整個服務生命周期。

2.3.2 測試四象限
首先畫一個由x軸支持評價和y軸業(yè)務技術導向組成的四象限,我們將我們DevOps中的所有種類的測試流程放入其中,來將每一個測試落實在一個二維區(qū)間內(nèi),再在每一種測試上標識一個工作投入程度,組成下面的圖。
我們之前提過,在XP極限開發(fā)中,我們推崇測試驅動開發(fā),因為測試驅動開發(fā)可以讓我們在開發(fā)之前更加深入的理解業(yè)務,并且基于接口定義程序,更好的組織我們的軟件架構。
所以下圖是我們的測試流程應在我們的工作中所占有的工作比例,如果所有的工作經(jīng)歷為100%的話,那么6顆星則為60%,每顆星占據(jù)10%。
良好的開發(fā)習慣,和標準的測試流程可以讓我們的代碼質(zhì)量更上一層樓。

2.3.3 運維四象限
上面我們說了測試的四象限,這里我們說說運維的四象限,我們以緊急性為x軸,重要性為y軸,這個四象限其實是很多工種的人都會使用到的,會將我們每天的任務放到里面,用來確定任務的優(yōu)先級,我們知道基于這種四象限,我們的優(yōu)先級會有下方四種:
- 緊急且重要
- 緊急不重要
- 重要不緊急
- 不重要不緊急
我們將運維的工作放在上面,如果大部分的工作都落實在緊急且重要的第一象限上的話,那么說明我們的DevOps流程是有問題的,比如我們的運維的大部分精力都在每天的線上緊急修復之類的任務,就說明我們的開發(fā)和測試的質(zhì)量是有問題的。
運維的工作不應該重點在右方,而應該重心左移,偏向于重要不緊急,多做一些規(guī)劃性的工作,比如從傳統(tǒng)部署方式轉向k8s容器編排等工作。

3 工程化指南
3.1 實踐整合
在上面我們將上面說講述的知識合并起來,組成我們真正在工作中實踐所要用到的東西。Scrum+XP+DevOps

3.2 建立工程師文化模型
小團隊內(nèi)部要做到:
- 可視化面板和主動領取任務(信息扁平化,加速效率,共同目標幫助隊友完成任務)
- 成為用戶故事的負責人(每個人都要有主人翁意識,認為自己是UserStory的主人)
- 20%的非功能性需求(給開發(fā)一點非業(yè)務的技術需求,提升多巴胺,產(chǎn)生樂趣)
- 合入主干的代碼需要審核(合代碼需要組內(nèi)review,降低代碼出問題的概率)
- 周期性的技術分享(每個人都要分享,對自己所學到的東西進行沉淀,同時也吸取組內(nèi)其他人的分享)
公司級別:
- 舉辦黑客馬拉松(選擇一個主題,讓公司的開發(fā)進行參與,進行開發(fā),討論,提升技術激情)
- 舉辦技術沙龍

3.3 目錄結構
定義每種語言的標準的目錄結構,比如下方的目錄結構就是Node.js的標準目錄結構,我將一些語言級通用的結構用紅框畫了出來。
- src
- test
- .env
- Compilefile
- Dockerfile
- Jenkisnfile

3.4 版本控制規(guī)劃
下圖使用的是基于Tag的版本控制,我這里看推薦使用GitFLow+Tag的方式來進行基于Tag多多環(huán)境的版本控制的方式進行構建使用。

3.5 快速失敗的流水線
下圖中的快速失敗的概念是指當pipeline中某一節(jié)點未達到通過的要求,則不再運行之后得節(jié)點,以當前節(jié)點的失敗為整個pipeline的失敗。
像jenkins原生就兼容快速失敗。下圖是jenkins最新的Blue Ocean界面,很友好的。


3.6 可視化反饋平臺
在第二工作法中,我們學習到了反饋工作流,如果使用jenkins構建pipeline的時候,我們就可以通過jenkins原生支持的可視化結果來了解到我們近期的CI/CD情況。

3.7 持續(xù)改進
在我們的產(chǎn)品設計中,有一個MVP的概念,它的意思是最小可行產(chǎn)品,這個概念來自于《精益創(chuàng)業(yè):新創(chuàng)企業(yè)的成長思維》這本書中,書中提倡首先定義一個面向市場的最小可用的極簡原型產(chǎn)品,然后再不斷的試驗和學習中,以最小的成本和最有效的方式來驗證產(chǎn)品是否符合用戶需求,靈活調(diào)整方向,以達到“快速失敗,廉價失敗”的方式來驗證產(chǎn)品是否符合市場需求。這是一種不斷學習,挖掘用戶需求,迭代優(yōu)化產(chǎn)品的方式。
我們對待我們的團隊,其實也要像對待我們的產(chǎn)品一樣,不停地學習,不停地嘗試,不停地優(yōu)化,這樣讓我們的團隊快速成長,要允許我們的團隊犯錯(但是不能重復掉進相同或相似的坑中)。

3.8 更多的質(zhì)量保證
3.8.1 CI/CCD
通過良好的測試+自動化流水線來提高我們的代碼的質(zhì)量
在部署前:
- 集成測試
- 性能測試
- 安全性測試
部署后測試:
- 自動化測試
- 驗收測試

3.8.2 環(huán)境與配置管理
通過配置中心來區(qū)別應用在各個環(huán)境中的配置,以防止出現(xiàn)踩到帶著開發(fā)測試的配置上線的這種老舊坑。

3.8.3 制品庫管理
制品庫推薦也同樣隔離開,預發(fā)和生產(chǎn)使用同一個制品庫,開發(fā)和測試使用同一個制品庫,在兩個制品庫之間,需要人為來審核和同步。

3.8.4 流程控制
這里的流程控制主要指的是CI/CD的流程控制。因為我們都知道jenkins單節(jié)點在同一時間自由一個pipeline能構建,也就是說單節(jié)點jenkins不能多條pipeline并發(fā)構建。所以我們需要搭建分布式的jenkins集群,來對pipeline的構建進行調(diào)度。
另一種更好的方式就是通過gitlab或其他支持并發(fā)pipeline構建的工具進行構建。
下方的pipeline中在測試環(huán)節(jié)分成了三個分支,這個特性我們再使用jenkins的pipeline時是有完美支持的,名字叫并行流,是根據(jù)條件來判斷三個分支是否進行的。

3.8.5 數(shù)據(jù)收集和監(jiān)控
我們的服務上線后,我們需要使用兩種方式來確保我們線上使用的服務能夠健康的提供服務。
- 日志
- 監(jiān)控
通過采集我們的線上的服務的日志,來對線上日志進行分析,達到實時監(jiān)控服務健康情況的需求。這里推薦使用市場較為開放通用的開源方案ELK
我們同時還要對我們的中間件,流量,物理硬件等進行相關監(jiān)控,以確定基礎環(huán)境的實時監(jiān)控情況,這里我推薦使用Prometheus+Granfa進行監(jiān)控。

3.8.6 容災
當我們的應用日益復雜且用戶量逐漸提高后,我們需要對我們的服務進行容災配置以及周期醒的混沌工程演練。我們的服務應該像下圖一樣逐漸進階為可用性更高的部署方式。

3.8.7 緊急事件處理
我們永遠都不能問心無愧的拍著胸口說我們的服務非常的問題,可用性能達到100%。因為100%是我們的服務的可用性極限,就算我們的服務可用性做到6個9,8個9,但是永遠也達不到100%,永遠只能無限的趨近于100%。因為我們永遠都沒辦法避免黑天鵝事件。但是我們能做的是出現(xiàn)黑天鵝事件后,我們要快速響應,將我們的損失降到最低。下面就說說當出現(xiàn)線上故障的時候,我們應該怎么做才能更好的減少我們的損失。
事故發(fā)生前(凡事有預案):
- 提前準備可能出現(xiàn)的事故
- 全員參與容災演練
- 多做混沌工程,提高服務的可用性和健壯性
事故發(fā)生時(先通報,后處理):
- Ops和Dev相互通報
- Ops和Dev各自向上級匯報
- 主管決定是否繼續(xù)向上級匯報
事故處理中(先止損,后查因):
- 是否有處理預案
- 有預案,Ops主管10分鐘內(nèi)做回滾決定
- 無預案,Ops主管和Dev主管決定回滾和補救方案
事故處理后(反思,定級):
- Ops通報事故處理結果
- 24小時內(nèi)主要責任部門牽頭矩形Case Study
- 對事故進行定級