道法之間——軟工第2次部落格作業

軟體工程究竟是什麼?懷著這樣的疑問,我仔細地翻閱了《構建之法》這本書,也算是系統性地對軟體工程的各個環節與組成有了一個大致的了解,這才終於得到了一個大致滿意的答案:

\[\text{軟體工程} = \text{軟體}-\text{程式}
\]

其實這個公式只是把鄒老師在本書一開始給出的\(\text{軟體} = \text{程式}+\text{軟體工程}\)作了簡單的移項變換,就從對軟體的定義變成了對軟體工程的定義:所謂軟體工程,即是指在軟體的開發過程中,把與演算法程式有關的部分剝去之後所剩下來的內容,這部分內容與程式的編寫者直接相關。

於是說到底,軟體工程這門課程所真正想要討論的核心是」人在軟體開發過程中所應該扮演的角色「,而非」如何去編寫一段高品質的程式碼「;儘管這兩者看起來似乎有些因果的味道。

如果將」人的立身之本「看作【道】,將程式的撰寫比作【法】,那麼軟體工程所處的位置,正是這道法之間

《構建之法》疑問梳理

Q1. 什麼樣的功能才有實現的必要?

  • 問題出處:P7 ~【Ch01 概論——航空業與軟體業的類比】

在這部分內容中,為了說明商用軟體和愛好者寫的程式的區別,作者以航空業類比進行了如下的論述:

說到商用軟體和愛好者寫的程式的區別,我們還可以看看這個例子:

如果一架民用飛機上有一個功能,用戶使用它的概率是百萬分之一,你還要做這個功能么?你會選擇:

  1. 根本不考慮

  2. 如果沒時間實現這個功能就算了

  3. 做了,但是不用告訴用戶

  4. 做了,而且不厭其煩地告訴用戶如何使用

你會如何選擇呢?選擇之後,這個功能究竟是什麼呢?

謎底是:

飛機的安全功能

我認為作者在這裡的論述存在著明顯的誤導性,似乎想要證明無論是什麼功能,只要用戶有可能要用到,即便可能性很小,一個合格的商用軟體開發者也必須要在一開始加以考慮並予以實現。否則萬一這個功能就像飛機的安全功能一樣在關鍵時刻可以救人一命的話,到時候再後悔就為時已晚了。

首先需要承認的是,確實存在一些用戶平時甚少使用但卻極為重要的功能,就如同飛機里的安全功能一樣。但對於這些功能,我們之所以在程式中需要加以實現,顯然不是因為它被使用到的概率很低,而是因為它的重要性不容忽略。同樣是民用飛機上一個使用概率為百萬分之一的功能,甚至比這個高一些,可能有萬分之一,比如給飛機上的電影庫中更新一部最近剛剛上映的小眾電影,難道也需要開發者在一開始就要去考慮實現它嗎?

因此,我想作者在這裡真正想要強調的其實是對一個功能重要性的判定問題,即如何在開發一個軟體之前就能預先判定哪些功能是必不可少的,哪些功能是可有可無的,且這種判定本身與這一功能的使用頻率之間不應構成直接的相關關係。這也正是取分商用軟體和愛好者的關鍵所在。

至於具體應該如何衡量這一重要性,則是第8章【需求分析】所討論的內容了。

Q2. 單元測試應該由誰來寫?

  • 問題出處:P25 ~【Ch02 個人技術和流程——單元測試】

在討論」好的單元測試的標準時「,作者給出了以下觀點:

單元測試必須由最熟悉程式碼的人(程式的作者)來寫。

程式碼的作者最了解程式碼的目的、特點和實現的局限性。所以,寫單元測試沒有比作者更適合的人選了。

對此我並不是完全認同。一方面,進行測試工作的人必須是熟悉程式碼的人,這點毋庸置疑:如果一個人之前從未閱讀過這份程式碼,那麼他也很難對程式碼的細節和功能有一個清晰的認識。但另一方面,寫單元測試的一定得是最熟悉的人(也就是作者)嗎?

在現如今許多成熟的科技公司中,面向編程開發人員往往會設置多種崗位,其中就包括演算法崗、運維崗、測試崗——這也就意味著在一個成熟的商業公司中,【開發】與【測試】這兩步往往都是分離的,並不是由同一個人甚至是同一個團隊來完成,而是會交給專門的測試人員去做。之所以這樣,乃是因為一般來說程式碼的編寫者本身往往會陷入到自己的思維定勢中,很難跳出自己的視角徹頭徹尾地審視這段程式碼,正所謂」不識廬山真面目,只緣身在此山中「。

當然,要想確保測試人員能夠清楚地理解開發者編寫的程式碼,對程式碼進行良好的封裝與抽象以及一份清晰的說明文檔是必不可少的,這就對一個商業公司的規範與文化提出了較高的要求。對於普通的業餘編程愛好者而言,或許還是自己來寫單元測試的好——誰讓沒有經費呢。

Q3. 專和精的關係到底是什麼?

  • 問題出處:P53 -【Ch03 軟體工程師的成長——軟體工程師的職業發展】

在這部分內容中,作者舉了街頭賣藝的單人樂隊、只研習某一樂器的交響樂團中的樂手和編寫交響樂的作曲家的例子,似乎想要引發讀者對於【專和精的關係】的思考。(原文較長,故在此不再引用)

這裡我對於作者的論述過程感到非常疑惑。在我的認識中,」專「和」精「這兩個概念應該是同義詞,【專】即專業,【精】即精通——一個人的專業自然就是一個人精通的內容,這有什麼思考的必要嗎?

鑒於作者所舉的3個音樂方面的例子,我認為可能他真正想要討論的是【專和」全「的關係】,即對於彈奏一種樂器的樂手而言,精通這一種樂器似乎要比對每種樂器都略知一二要更受人們的歡迎;但對於作曲家、指揮家們而言,則必須要對所有可能用到的樂器都要有一定程度的理解。那麼,對於一名工程師而言,究竟應該是更」專「一點好,還是更」全「一點好呢?

對於這個問題,我認為單純強調」專「更好或者」全「更好都過於片面而不夠客觀,歸根結底還是對於【軟體工程師】這一職位地定義太過模糊與寬泛。同樣都是工程師,不同人的分工也有所不同:有人專精於前端或後端相關技術棧,有人則需要統籌全局地架構和部署。此外,在一個人的職業生涯中,其承擔的職責也是在不斷流動的。也許他在入門時可能只是擅長某一特定方向的程式設計師,但在不斷的磨礪與成長後也完全有可能去獨自領導一個團隊;或者也許他在入門時各方面基礎都比較紮實,但在不斷的探索中他也完全有可能找到自己擅長且感興趣的方向,最終成為這個方向上的開拓者。

Q4. 」技能的反面「與提高技能的方法

  • 問題出處:P57 -【Ch03 軟體工程師的成長——技能的反面】

在這部分內容中,作者舉了他小時候玩魔方的例子,意在說明絕大部分人口中的技能其實遠遠達不到真正意義上的技能的標準。而為了說明什麼才是真正的技能,作者援引了比爾·塞克斯頓關於」技能的反面「的相關論述:塞克斯頓認為,技能的反面是【解決問題】;因此一個真正擁有某項技能的人在完成相應的任務時應當不再像一個生手一樣把這一切還當作是一系列解決」新問題「的過程,而是能夠超越低層次的問題,將主要的精力放在更高層次更抽象的思考上。

而關於如何提高技能,作者給出的答案是:不斷地練習。原文如下:

那怎麼提高技能呢?答案很簡單,通過不斷的練習,把那些低層次的問題都解決了,變成不用經過大腦的自動操作,然後才有時間和腦力來解決較高層次的問題。

對此我有不同的看法。首先,對於某一特定技能的熟練程度在很大程度上決定了勞動的生產效率,一名熟練工人單位時間內可以生產出更多的工件,從而創造更多的利潤。但是否對於我們所嘗試的每一件事都要達到如此熟練的程度,甚至連玩魔方這樣的興趣愛好都不放過呢?我認為大可不必。

因為,技能。或者說熟練度,其本質上其實是一個人在勞動中逐漸異化的過程。他越熟練於某一特定的工作,就越有可能囿於這項工作所設下的囹圄中。而反過來,解決問題,也就是技能的反面,所體現的才恰恰是一個人真正意義上的真實能力,是人之所以為人的證明。

以程式語言為例,現在市面上的程式語言多如牛毛,比較有名的就有C、python、java、C#、Rust、Go……而每一種語言都有它自己的語言特性,那麼對於一個優秀軟體工程師的衡量標準,難道是看他所精通的程式語言的多少嗎?再比如,隨著軟體生態的日趨成熟,很多領域都已經有了一套成熟的軟體體系和自己定義的各種API與函數介面,像在科學計算領域,就有Matlab,Mathmetica以及Python語言支援的scipy庫等等,它們的很多函數雖功能類似,但名稱、調用形式卻千差萬別,難道我們也需要對這裡面的每一個API都爛熟於心嗎?

如果真是這樣的話,那還要機器幹什麼,都讓人來做好了。

所以,我始終認為,衡量一個科班出身的CS人的能力究竟如何,不是看他會調多少包,而是看他能否舉一反三、快速把握問題的關鍵和本質,至於具體的實現細節,還是交給文檔的好。

當然,如果一個人連一門程式語言都不會就敢說自己是CS人,那就不在本文的討論範圍之內了。

Q5. goto到底該不該用?

  • 問題出處:P69 -【Ch04 兩人合作——程式碼設計規範】

這裡作者在討論什麼是良好的程式設計規範時,認可了對goto語句的使用,原文如下:

函數最好有單一的出口,為了達到這一目的,可以使用goto。只要有助於程式邏輯的清晰體現,什麼方法都可以使用,包括goto。

而荷蘭電腦科學家Dijkstra在很久以前就提出了著名的”goto有害論”,反對在程式中使用goto語句,這顯然與本書作者的觀點不合。對此,我本人也更傾向於Dijkstra的觀點,即一旦允許使用goto,那麼很有可能就會破壞程式原有的清晰邏輯結構,造成多出口的結果,這一過程往往是不可控的。因此,有必要在源頭上斷絕goto被使用的可能。

Q6. 商業價值與開源精神是否矛盾?

  • 問題出處:P134 -【Ch07 實戰中的軟體工程——MSF基本原則】

這部分內容在書中主要以阿超、二柱等人的對話的形式加以展開。當涉及到團隊項目在完成後是否應該開源這一問題時,二柱、阿超等人均在不同程度上表達了反對,因此也可以認為作者本人也更傾向於以「閉源」的形式對待商業軟體。

但另一方面,我們也應該看到,如果沒有當初Linus等人將最初的Linux系統以郵件的形式開源發布出來,那麼也不會有今天整個Linux大家族的繁榮;同樣,現在的GitHub社區也充分倡導開源精神,鼓勵更多人把他們的idea分享出來;就連微軟也在不久前公開了其Office家族的一部分程式碼介面,以允許其他文本編輯軟體與之兼容;至於Google、Facebook開源的Tensorflow、Pytorch等深度學習框架就更是如此了。

現如今,開源已成為CS界的一種潮流,這與傳統製造業可以說是大相徑庭,但至於這種趨勢究竟是否會促進IT公司商業價值的提升還是會在一定程度上影響其(特別是小公司)商業化產品的成功落地,現在也是眾說紛紜。對此,我本人也沒有一個明確的答案——但我相信,未來一定會出現一種基於開源的全新商業模式,從而可以兼顧對個體勞動貢獻的肯定與對社區繁榮的維繫。

Q7. 「只先一步」最終一定會帶來「顛覆式」創新嗎?

  • 問題出處:P357 -【Ch15 IT行業的創新——創新的時機】

在本書第16章的開始【創新的迷思】部分,作者提出了兩種不同的創新方式(P342),分別是改良式(Incremental)創新與顛覆式(Disruptive)創新;緊接著,在【創新的時機】部分,作者又以黃金點遊戲為例,試圖說明最終真正能夠成功的創新往往每次都是比大眾的平均值先走了一小步,實現所謂的「相對優勢」;而那些一開始最激進的創新卻反而有可能歸於失敗。

對此我不敢苟同。應該看到,很多對人類文明產生重大影響的發明創造往往對人們的認知都是顛覆性的,遠的有牛頓三定律直接打破了人們過去所認可的【力是維持物體運動狀態的原因】這一錯誤觀念,近的有喬布斯首創的iPhone系列手機徹底顛覆了人們過去對手機的功能定位。如果任何事都只追求一點一點的迭代式創新的話,那麼整個人類文明的發展甚至很有可能會逐漸「收斂」,最終停滯不前。

Q8. PM與開發測試人員的關係和分工應當是怎樣的?

  • 問題出處:P185 -【Ch09 項目經理——PM做開發和測試之外的所有事情】

正如標題中所言,這部分內容中作者的基本觀點就是【PM做開發和測試之外的所有事情】。那麼問題來了,是不是開發和測試人員要做的事情PM就統統不用管了呢?他們具體都寫了哪些程式碼,這些程式碼究竟能不能實現客戶的需求,難道這些問題PM就不用去考慮了嗎?而另一方面,開發和測試人員是不是也無需關心客戶的需求了呢?假設客戶後面又提出了更複雜的功能要求導致軟體全部需要重構,那這個鍋應該是PM背還是開發人員背呢?

因此,我認為,單純地把PM的工作定義為【開發和測試的補集】是非常不合理的。一個合格的PM他在一個團隊中所起到的作用更像是【粘合劑+方向標】,他既需要隨時把握各個開發測試崗位的當前進度(這如果沒有基本的技術功底的顯然無法做到),同時也需要及時將客戶的需求傳達給團隊的其他成員們,從而共同協商出下階段合理的任務計劃與分工進度安排。

Q9. 「殺手」 + 「輔助」 = 「維持」?

  • 問題出處:P165 -【Ch08 需求分析——功能的定位和優先順序】

這裡作者將產品的功能從實現和需求兩個角度進行了劃分,得到了如下圖所示的功能象限圖。

image-20210312200900023

可以看到,對於【殺手功能+輔助需求】這樣的組合,作者給出的建議是採取「維持」的辦法進行實現,而對於【殺手+必要】這樣的組合,作者則給出了「差異化」的建議。

對此我有所異議。在如今的互聯網時代,整體上來看,多數公司與產品所採用的商業模式已經與傳統製造業截然不同——傳統製造業銷售的是產品本身,而互聯網公司們銷售的則往往是產品周邊與服務。比如Google公司的核心是搜索引擎演算法,但它所採用的盈利方式則是靠廣告投放:假如當時Google把全部重心都放在優化它的搜索引擎的性能上的話,那麼缺少有效盈利手段的它有可能擁有如今的市場佔有率嗎?

再比如,現在非常火熱的《王者榮耀》等一系列手游,它們的核心功能無疑是遊戲的玩法本身,但很遺憾這部分功能通常是免費的,而真正收費的卻是各種【皮膚】等看起來並無任何實際用處的外圍功能:那這是否意味著遊戲公司就只需要提供最基本的皮膚給玩家就夠了呢?

因此,我認為在資金有限的前提下,一些必要需求,哪怕是殺手功能,反而只需要「維持」即可;而恰恰是一些看似不那麼重要也不難實現的輔助需求,才是真正需要做到「差異化」的關鍵所在。

源程式碼版本管理軟體調研

本部分將逐一對GitHub、Gitlab與Bitbucket這三個目前主流的用於源程式碼版本管理的軟體進行簡要介紹,最後再對它們之間的異同加以分析。

GitHub

GitHub 是首個供「用Git進行版本控制系統的軟體開發項目」使用的基於Web的程式碼託管服務,是目前全球最大的開源社交編程及程式碼託管網站。GitHub 於 2008 年 4 月 10 日正式上線,除了基本的服務以外,還提供了訂閱、討論組、文本渲染、在線文件編輯器、協作圖譜(報表)、程式碼片段分享(Gist)等功能。

Gitlab

GitLab 是一個利用 Ruby on Rails 開發的開源應用程式,實現一個自託管的 Git 項目倉庫,可通過 Web 介面訪問公開或者私人的項目。

Bitbucket

BitBucket 是 2008 年創建的源程式碼託管網站,採用 MercurialGit 作為分散式版本控制系統,同時提供免費賬戶商業計劃。2010 年被 Atlassian 收購,與 Atlassian 的其他服務(Git GUI SourceTree、HipChat、Cloud9)順利集成,主要面向慈善企業和企業用戶,其主要市場是大型企業。

團隊協作流程

由於這三者均為基於Git的分散式版本管理系統,因此在團隊協作流程上基本上也是大同小異。以GitHub為例,整體的協作流程大致可分為以下10個基本階段:

  1. 在 Github 上創建 organization
  2. 邀請隊友加入 organization 並創建 team
  3. 建立團隊項目倉庫,在設置中將 team 的許可權設置為 read
  4. 創建開發分支,讓隊友 fork 到個人倉庫
  5. clone 項目到本地
  6. 添加 REMOTE 關聯到團隊遠程倉庫
  7. 切換到 dev 分支
  8. 提交commit到自己的遠程倉庫
  9. 和團隊遠程保存同步
  10. push 到自己的遠程倉庫 & 請求 pull request 到團隊遠程

相同點分析

  • 都是分散式版本管理系統,基於Git實現
  • 目前都已支援私人的免費倉庫,但通常存在容量和人數的限制
  • 三者都支援CI\CD功能
  • GitHub 和 GitLab 都是基於 web 的 Git 倉庫

不同點分析

  • Gitlab在GitHub的功能之外,還支援更多的優秀特性,比如許可權設置。因此一般企業內部軟體產品多使用Gitlab,而開源產品則一般放在GitHub上。
  • Gitlab的「Snippet support」支援用戶分享一個project的部分程式碼,而不是整個project。
  • Gitlab的源碼本身是開源的,而GitHub、Bitbucket雖然社區開源,但其本身卻是閉源的。
  • 在支援倉庫類型上,除Git外,GitHub還支援SVN,HG,TFS,Bitbucket還支援CodePlex,Google Code,HG,SourceForge,SVN。

持續集成/部署工具調研

Gitlab CI

GitLab CI 是GitLab內置的進行持續集成的工具,只需要在倉庫根目錄下創建.gitlab-ci.yml文件,並配置GitLab Runner;每次提交的時候,GitLab將自動識別到.gitlab-ci.yml文件,並且使用GitLab Runner執行該腳本。

本部分採用大二下OO課程的Unit3_task2作業作為樣例,引入Maven框架,展示GitLab CI/CD的基本過程。

倉庫地址

項目根目錄

image-20210314205518945

Pipeline 運行記錄

image-20210314205646729

ci各階段詳細執行流程

本部分將結合.gitlab-ci.yml中的相關程式碼予以說明,其中Maven相關的配置可參考倉庫中的pom.xml文件。

  1. 所選鏡像

    image: local-registry.inner.buaaoo.top/image-dev/java:8u201
    
  2. 宏定義變數

    variables:
      USER_INFO: tcyhost
    
  3. before_script

    before_script是用於定義一些在所有任務執行前所需執行的命令, 包括部署工作,可以接受一個數組或者多行字元串。

    before_script:
      - java -version
      - javac -version
      - mvn -v
    
  4. 各階段定義

    階段是對批量的作業的一個邏輯上的劃分,每個 GitLab CI/CD 都必須包含至少一個 Stage。多個 Stage 是按照順序執行的(但同一個stage的多個jobs則會並發執行),如果其中任何一個 Stage 失敗,則後續的 Stage 不會被執行,整個 CI 過程被認為失敗。

    stages:
      - build
      - run
      - test
    
    • build

      build階段的核心為mvn compile命令,對項目進行編譯,並列印編譯成功後的目錄結構。

      mvn_build:
        stage: build
        script:
          - echo "[${USER_INFO}] Build Project"
          - mvn compile
          - tree -I .git
        artifacts:
          paths:
          - target/
      

      注意這裡利用artifacts欄位將mvn編譯得到的結果傳遞給接下來的jobs,從而使後續任務無需重新進行編譯。

      運行結果如下:

      image-20210314210800824

    • run

      run階段主要任務為執行一個簡單的測試樣例,從而初步判斷項目的正確性。

      java_run:
        stage: run
        dependencies:
          - mvn_build
        script:
          - echo "[${USER_INFO}] Running"
          - cd target/classes
          - java Main < ../../src/test/in0.txt
        only:
          - se
      

      注意這裡通過添加only欄位,限制該job只能在se分支發生變動時運行master或其他分支出現變化時,該job不會被執行。

      運行結果如下:

      image-20210314211600133

    • test

      test階段主要任務為對原始項目進行Junit單元測試,並計算測試的覆蓋率。

      mvn_test:
        stage: test
        dependencies:
          - mvn_build
        script:
          - echo "[${USER_INFO}] Test JUnit4"
          - mvn cobertura:cobertura
          - mvn cobertura:dump-datafile
        coverage: '/coverage line-rate="\d+/'
      

      注意由於這裡是利用cobertura介面實現Junit測試,因此在正則匹配時應關注line-rate的結果。

      運行結果如下:

      image-20210314211646328

GitHub Actions

GitHub Actions與GitLab CI在功能上類似,都是用於對程式碼進行持續集成操作;但GitHub Actions將這些操作進行了解耦合,從而允許開發者把每個操作寫成獨立的腳本文件存放到程式碼倉庫中,使得其他開發者也可以引用。因此,如果你需要某個 action,就不必再自己寫複雜的腳本,而是直接引用他人寫好的 action 即可。整個持續集成過程,也就變成了各種actions的組合。

本部分以一個簡單的基於Python實現的M/M/n隊列模擬器程式為例,利用pytest和flake8等包,展示GitHub Actions的基本過程。

項目倉庫

項目根目錄

image-20210314212121520

Workflows 運行記錄

  • 所有workflows的執行情況

image-20210314212227615

  • 某一特定workflows中各個job的執行情況

image-20210314213231061

  • jobs中各個action的執行情況

image-20210314213338989

Actions各階段詳細執行流程

本部分將結合.github/workflows/test.yml中的相關程式碼予以說明。

  • 基本配置與環境初始化

    name: GitHub Actions demo
    
      on:
      - push
      - pull_request
    
      jobs:
        build:
    
          runs-on: ubuntu-latest
          strategy:
            matrix:
              python-version: 
                - '3.8'
          steps:
            - uses: actions/checkout@v2
            - name: Set up Python ${{ matrix.python-version }}
              uses: actions/setup-python@v2
              with:
                python-version: ${{ matrix.python-version }}
    

    運行結果如下:

    image-20210314213712332

  • 安裝依賴包

    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install flake8 pytest pytest-cov
        if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
    

    運行結果如下:

    image-20210314214211795

  • flake8靜態語法測試

    - name: Lint with flake8
      run: |
        # stop the build if there are Python syntax errors or undefined names
        flake8 --count --ignore F403,F405,W504,E226 --max-line-length=127 myQueue/ --statistics
    

    運行結果如下:

    image-20210314214257709

  • pytest單元測試

    - name: Test with pytest
      run: |
        tree -I .git
        pip install -e .
        pytest ./test --cov-report term-missing --cov=./myQueue/ -sv
    

    運行結果如下:

    image-20210314214325414

  • 安裝部署包並驗證是否安裝成功

    - name: install myQueue
      run: |
        pip install -e .
    - name: verify installation
      run: |
        python -c "import myQueue; print(myQueue.__version__)"
    

    運行結果如下:

    image-20210314214348354

CI/CD工具使用小結

在充分調研並實踐後,我認為無論是Gitlab CI還是GitHub Actions,其背後的核心宗旨無外乎兩點:自動化標準化

首先是自動化。我們知道,CI/CD工具關注的對象並非是程式碼的創造過程本身,而是程式碼在編寫出來後對其進行編譯、測試、提交、部署的這一系列過程。不難發現,這些過程其實都是非常機械的,不僅不需要什麼創造力,而且過於繁瑣的環境配置過程反而還會耗費開發者大量的精力,造成團隊整體的生產效率低下。因此,將這一部分內容從程式碼開發中分離出來,並在伺服器上自動加以實現,無疑是諸多軟體開發團隊的普遍需求,也是一類工具的核心使命。

其次是標準化。既然希望將這部分內容交由機器去完成,那麼一方面,為了讓機器的執行流程更加高效清晰,另一方面也是為了進一步減輕開發者的編程負擔,標準化就顯得至關重要了。

這裡的標準化有三層含義,首先是執行流程的標準化。無論是Gitlab CI還是Github Actions,都利用YAML文件將整個持續集成過程分成了多個階段,各階段之間相互獨立、順序執行,從而使得執行流程清晰簡明;其次是執行指令的標準化,這一點GitHub Actons做的明顯要更好些——它將原本一條條原子化的指令進行了進一步封裝,構成了一個個action,從而允許開發者彼此之間進行調用,進一步降低了編程負擔;最後是執行環境的標準化,比如Gitlab中的Runner,通過在團隊之間進行共享,就可以實現統一的運行環境,從而便於最後的集成和部署。

若從需求分析、技術支援與產品實現的角度進行歸納的話,那麼CI/CD工具的核心特性可整理如下:

需求分析:許多軟體開發團隊為了使集成開發的環境、流程做到統一、規範,個人開發者希望實現測試、部署等過程的自動化

技術支援:Docker等虛擬環境技術的成熟,基於web的分散式版本管理技術的推廣,yaml工具的出現等

產品實現:GitLab CI/CD、GitHub Actions、Travis……

GitLab CI與GitHub Actions的系統化優劣對比如下表所示:

image-20210314230507360
image-20210314230519547
YES 支援非第三方的嵌入式CD NO
YES 自託管 NO
YES 有自己的生態系統(//about.gitlab.com/partners/) NO
NO 有自己的市場平台 YES
YES CI/CD 充分集成 – 無需第三方插件或工具 NO
YES 內置Kubernetes部署和監控管理 NO
YES 自動CI/CD Pipeline配置 NO
YES 嵌入式CI-CD安全審查 – 無需第三方插件或工具 NO
YES 安全儀錶板(Dashboard)可實現安全團隊協作 NO
YES 容器註冊表集成在CI-CD Pipeline中 – 無需第三方插件或工具 NO

當然,應該要認識到很多問題是有兩面性的。比如究竟是否應該支援第三方插件這件事,支援的話有助於提升市場開放性和靈活性,但也會出現魚龍混雜的情況;不支援的話有助於進行統一的管理和優化,但可能缺少足夠的個性化服務。

總之,從我個人的角度來看,我會更傾向於使用GitLab CI進行團隊項目的集成開發,使用GitHub Actions進行個人開源項目的測試部署。

參考文獻

  1. 《構建之法——現代軟體工程》鄒欣著,人民郵電出版社,第三版.
  2. GitHub、GitLab與BitBucket應該怎麼選?
  3. GitHub & Bitbucket & GitLab & Coding 的對比分析
  4. Bitbucket vs GitHub vs GitLab
  5. Git、GitHub、GitLab三者之間的聯繫以及區別
  6. GitLab CI介紹——入門篇
  7. Building and Test Python
  8. 超詳細!Github團隊協作教程(Gitkraken版)
  9. GitLab CI vs GitHub Actions