深入思考軟體工程,開啟 DevOps 之旅

博雲技術社群發表於2021-10-13

20 世紀 60 年代,軟體開始脫離硬體,逐漸成為一個獨立產業。至今,軟體開發過程從瀑布模型、CMM/CMMI,到 20 年前敏捷的誕生,再到今天 DevOps 的火熱,一代代軟體人在思考和探索,如何避開“焦油坑”,試圖尋找軟體交付的“銀彈”。

 

深入思考軟體工程,開啟 DevOps 之旅

 

焦油坑:複雜且讓人感覺束縛,越陷越深難以擺脫。常被軟體開發者形容軟體產品的複雜度成倍增長;銀彈:比喻詞,形容解決問題的捷徑。 圖源網路

 

DevOps 作為目前軟體工程界的集大成者,備受關注,業界也有很多討論。近年來包括博雲在內的很多廠商,也投身於 DevOps 之中,希望將更好的軟體研發管理方法與工程實踐通過產品和服務帶給客戶。

 

軟體工程的故事

軟體工程的產生:軟體危機

20 世紀 60 年代以後,硬體技術快速發展,第三、四代計算機陸續產生,軟體從計算機實驗室逐漸走向更廣闊的的軍工、商業、民用場景。在這個轉變的過程中,軟體也逐漸開始脫離硬體獨立銷售,軟體的重要性逐步提升超過硬體,其複雜性也愈發凸顯。

 

敏捷方法是需要用實踐方法來解決這些問題。比如響應變化這一點,XP 方法中,如果(迭代中的)一個使用者故事還沒有實現,允許可以考慮用另外的使用者故事替換,替換的前提的工作量是相等的;而 Scrum 強調一旦開工會定下來,需求就不允許被新增進來,並且需要 ScrumMaster 來把關。

 

●  從軟體產品(軟體開發的產物)維度來看,研發成本過高、質量不可靠、難以維護、開發效率/產能跟不上硬體的發展及使用者需求的增長,成為核心的難題;

●  從軟體專案(軟體開發的過程)維度來看,成本超預算、計劃嚴重延期成為常態。1975 年 Brooks 的《人月神話》,提出了“焦油坑”和“銀彈”的說法。時至今日,軟體工業仍然在尋找“銀彈”的路上。

 

在此等複雜性的背景下,這個階段軟體開發技術與方法的發展卻沒有跟上。愈發複雜的需求、場景與環境倒逼著軟體開發技術與方法的交織發展。

 

軟體工程第一次被提出是什麼時候呢?1968 年,北約的電腦科學家第一次提出了軟體工程的概念,希望通過系統化、規範化、數量化等工程原則和方法來實現複雜軟體系統的開發和維護。

 

深入思考軟體工程,開啟 DevOps 之旅

 

以下為《nota1968》中的部分描述摘錄,五十年前的觀點,如今看來仍然鮮活:

 

關於軟體專案管理:

軟體開發管理將繼續揹負目前的在成本和計劃有效性上的壞名聲,直到有朝一日,人們對軟體設計工程有了更全面的理解和認識。

我們開發軟體系統就像萊特兄弟製造飛機——造好整個系統,把它推下懸崖,讓它墜毀,然後重新開始。

管理工作方面,大型軟體的開發是一個令人恐怖的事情。人們的認識中,這種工作通常會成為血本無歸的泥潭,耗費財力,永無止境。人們的這種認識也許並不是偏見。

 

關於使用者需求:

使用者感興趣的是對系統提需求,而且按照需求購買系統。但這裡的潛臺詞是使用者能說出他們想要什麼。而大部分的使用者說不清楚。

我們應該在設計過程中儘早的獲取使用者反饋。

 

軟體工程的本質性難點:四個特性

軟體工程的本質性難點到底在哪裡?讓我們引用轉述一下《人月神話》作者 Brooks 在 1986 年的論文《No Silver Bullet》中的觀點。布魯克斯認為,附加性的困難會隨著工具的改善而逐漸淡化,反而是本質性的困難最難以解決,因為大部分的活動是發生在人們的腦海裡,缺乏有效的輔助工具。依照布魯克斯的說法有下列幾項:

 

敏捷方法是需要用實踐方法來解決這些問題。比如響應變化這一點,XP 方法中,如果(迭代中的)一個使用者故事還沒有實現,允許可以考慮用另外的使用者故事替換,替換的前提的工作量是相等的;而 Scrum 強調一旦開工會定下來,需求就不允許被新增進來,並且需要 ScrumMaster 來把關。

 

●  複雜性(complexity):軟體要解決的問題,通常牽扯到計算步驟,這是一種人為、抽象化的智慧活動,多半是複雜的。

●  隱匿性(invisibility):尚未完成的軟體是看不見的,即使利用圖示說明,也常無法充分呈現其結構,使得人們在溝通上面臨極大的困難。

●  配合性(conformity):在大型軟體環境中,各子系統的介面必須協同一致。由於時間和環境的演變,要維持這樣的一致性通常十分困難。

●  易變性(changeability):軟體所應用的環境常是由人群、法規、硬體裝置、應用領域等,各因素所彙集而成,而這些因素皆會快速變化。

 

對軟體工程的定性:綜合性學科

Brooks 在他的論文《No Silver Bullet》中表示:不存在任何一個單一的開發技術或管理技術能夠解決軟體工程所面臨的所有問題。因而軟體工程是一個包括一系列概念、理論、模式、語言、方法以及工具的綜合性學科。

所以,軟體工程,或者說軟體的實現過程,實際上是一個綜合性的學科。涉及到技術手段、管理方法和內外部環境等因素。

 

舊世界的軟體工程:瀑布與 CMM

洛克希德軟體技術中心的 Winston W. Royce,在其 1970 年的論文“Managing the Development of Large Software System”中提出了一個長得很像瀑布(waterfall)的流程。

 

深入思考軟體工程,開啟 DevOps 之旅

 

按照科學管理的理念,只要按照標準流程、標準動作來,就能產出高質量軟體、解決軟體危機,這就是最佳實踐。然而真的是這樣嗎?舊世界的軟體工程並沒有給出答案:

 

敏捷方法是需要用實踐方法來解決這些問題。比如響應變化這一點,XP 方法中,如果(迭代中的)一個使用者故事還沒有實現,允許可以考慮用另外的使用者故事替換,替換的前提的工作量是相等的;而 Scrum 強調一旦開工會定下來,需求就不允許被新增進來,並且需要 ScrumMaster 來把關。

 

●  很多軟體工程著作並沒有對軟體開發的日常實踐給出具體的指導,結果到實際工作中就變成了“邊做邊改、邊改邊做”;

●  CMM 評估流於形式,例如評估小組只詢問文件在哪裡而並不真正關注文件的內容是否恰當以及文件本身的價值;

●  CMM 所關注的“成熟度”與軟體開發過程的“有效性”脫節,通過了 CMM5 級認證的團隊照樣會開發出沒人買單的糟糕軟體。

 

舊世界軟體工程的靈感來源:泰勒主義

說到這裡不得不提的是科學管理之父泰勒和他的科學管理理論。科學管理、泰勒主義的理念是:

敏捷方法是需要用實踐方法來解決這些問題。比如響應變化這一點,XP 方法中,如果(迭代中的)一個使用者故事還沒有實現,允許可以考慮用另外的使用者故事替換,替換的前提的工作量是相等的;而 Scrum 強調一旦開工會定下來,需求就不允許被新增進來,並且需要 ScrumMaster 來把關。

 

少數精英負責制定標準的工作流程、工具操作、工藝質量,再由管理層完成監督和管理工作,大量的藍領工人可以不用動腦子、不思考地就可以完成符合質量標準的工作,從而產生合格的產品。我想大家一定都記得 K12 課本里出現過的一句話:“我需要的是一雙手,而不是一個人”。

 

客觀上來講,科學管理的提出和應用確實帶動了工業製造的發展提速,解決了生產力不足、無法滿足市場需求的問題,這也是工業革命後不可逆的趨勢。但科學管理也並非永遠是萬能的。

 

後科學管理時代的探索:敏捷

20 世紀以後,行業面臨的挑戰從“生產力不足、無法滿足市場的功能性需求”逐漸轉變為“靈活性不足,無法滿足消費者對創新和定製需求”。“快速、殘酷與不確定性的變化”將是未來製造業面對的常態挑戰。每個行業都開始強調變化,這個問題的本質直到今天仍然適用。

 

而對於軟體工程領域來說,Agile/敏捷,就是關於企業如何在一個動盪的、競爭激烈的經營環境中獲得利潤。跟“科學時代的軟體”的區別在於:

 

敏捷方法是需要用實踐方法來解決這些問題。比如響應變化這一點,XP 方法中,如果(迭代中的)一個使用者故事還沒有實現,允許可以考慮用另外的使用者故事替換,替換的前提的工作量是相等的;而 Scrum 強調一旦開工會定下來,需求就不允許被新增進來,並且需要 ScrumMaster 來把關。

 

● 對市場的需求和認知。承認變化、接受變化,不認為客戶需求可以一次性集中研究然後完全獲取。

● 對生產的態度和工作方法。接受錯誤、願意試錯、快速調整、持續優化。

● 對生產資料的態度。承認個人的能力與價值,“讓一線呼喚炮火“,而不是“精英定規則,生產線上只要一雙雙手”。

 

當然,以上並不是完全否定瀑布與 CMM 的價值和意義。只是,在現下的世界與環境中,CMM 與瀑布並不能完全解決我們的問題,所以我們仍然不得不繼續尋找“銀彈”。

 

認識 Agile,敏捷的誕生

眾所周知,2001 年 2 月,在鹽湖城外 25 公里的雪鳥城(Snowbird),一個滑雪勝地,17 位業界先鋒聚在一起,制定並簽署了行業歷史上最重要的檔案之一:敏捷宣言。隨後,他們共同商討出 12 個敏捷原則,對宣言進行進一步的解讀。

 

深入思考軟體工程,開啟 DevOps 之旅

 

敏捷,另一類軟體工程方法論

方法論,是關於“如何開發一個軟體系統”的行為框架,包含如何組建團隊、如何做專案計劃、團隊成員及跨團隊如何交流、如何管控專案進度與質量。

在“雪鳥會議”之前,已經存在了很多這種被稱為“輕量級(Lightweight)”的軟體開發方法,雪鳥會議的結果是正式地將這些方法統稱為“敏捷(Agile)”軟體界開發方法。

 

一些“敏捷”的軟體工程實踐方法

 

深入思考軟體工程,開啟 DevOps 之旅

 

不同敏捷方法的共同特徵

不同的新方法有著一些共同的特徵。例如,強調適應性而非預見性。所以敏捷主義者會說“擁抱變化”,敏捷宣言中也有那句“響應變化重於遵循計劃”。

 

可能有人會說,既然這樣,那充滿變化、不按計劃、沒有邊界,那專案還怎麼做?敏捷方法是需要用實踐方法來解決這些問題。比如響應變化這一點,XP 方法中,如果(迭代中的)一個使用者故事還沒有實現,允許可以考慮用另外的使用者故事替換,替換的前提的工作量是相等的;而 Scrum 強調一旦開工會定下來,需求就不允許被新增進來,並且需要 ScrumMaster 來把關。

 

再比如,強調面向人,而非程式導向。強調人的作用與價值。所以敏捷宣言中會說“個體和互動重於過程和工具”。

 

也可能有人會說,那是不是敏捷就可以不寫文件了?其實並不是完全不寫文件了,而是為了減少浪費,減少準備文件、溝通傳遞資訊、理解文件這個過程話費的時間和人力。不在對客戶沒有價值的東西上面浪費時間,這是“敏捷不寫文件”的真實含義。用這個原則指導,使用者故事/需求、架構圖、複雜業務/計算邏輯等都是無法從程式碼中看出來的,這就是需要寫的。而高保真設計、資料庫表結構、虛擬碼等就可以精簡或者不寫。

 

敏捷的出發點

敏捷方法的出發點,是運用一些更有效的方法(工程實踐),交付高價值、高質量的軟體。

敏捷方法是需要用實踐方法來解決這些問題。比如響應變化這一點,XP 方法中,如果(迭代中的)一個使用者故事還沒有實現,允許可以考慮用另外的使用者故事替換,替換的前提的工作量是相等的;而 Scrum 強調一旦開工會定下來,需求就不允許被新增進來,並且需要 ScrumMaster 來把關。

什麼是高價值的軟體?產生商業價值的能力更強。所以在使用使用者故事這一工具時,它的描述規格是“可以實現 xxx 商業價值”。

 

什麼是高質量的軟體?更穩定、更靈活、易維護、易擴充套件。

 

敏捷實踐的總結:通過可落地的、緊密配合的一系列敏捷實踐,來產生高價值、高質量的軟體。

敏捷方法是需要用實踐方法來解決這些問題。比如響應變化這一點,XP 方法中,如果(迭代中的)一個使用者故事還沒有實現,允許可以考慮用另外的使用者故事替換,替換的前提的工作量是相等的;而 Scrum 強調一旦開工會定下來,需求就不允許被新增進來,並且需要 ScrumMaster 來把關。

什麼是緊密配合?舉例來說,簡單設計後如何進行進一步的優化?那就必須使用重構。如何能讓重構成為一種經常性的、快速有效的開發方法?單元測試程式碼的開發就是必不可少的步驟,而測試驅動開發就是其最佳實踐。如何讓持續整合跑的順利?必須推廣統一的編碼規範,並且還得有自動化測試用例集。

 

敏捷常見工程實踐

● 使用者故事,讓需求認知過程成為一種協作方式

敏捷軟體開發方法的需求梳理工作,一般是通過使用者故事的形式來開展的。

在《使用者故事實戰》一書中,將 US 的特性概括為”3 個 C”:Card、Conversation、Confirmation。後面兩個 C 體現的就是 US 作為一種寫作方式的定位(而非傳統需求檔案試圖去描述完整需求)。

 

另外,從需求的傳遞與跟蹤來看,Story Backlog 和 Kanban 都是常用的工具,但各有其應用場景。從最初產生的場景來看,Kanban 更適合連續性的、源源不斷的工作項跟蹤,而使用者故事 Backlog 是以迭代為單位,分批去消化不同的需求。

 

● 重構,在不改變軟體行為的前提下優化軟體內部結構

重構是敏捷軟體開發方法裡非常核心的實踐之一,強調的是在不改變軟體行為的前提下優化軟體的內部結構。一個小小的重新命名工作,通過“如烹小鮮”般的一系列動作來完成。看似瑣碎,卻嚴謹、小巧、可控。通過這一系列的操作,使得重構的難度(對原有程式碼可觀察行為的影響)大大降低。特別是在複雜的軟體專案中,這種極高的可操作性會體現它的巨大價值。

 

在 IDE 高度進化的今天,類似這種重構的場景,甚至更多複雜的重構場景,都可以藉由現代 IDE 的內建功能去完成,重構變得更加簡單、快捷、好用。

 

● 單元測試/自動化測試,讓不斷重構成為可能

重構可以在保持行為不變的前提下改善軟體內部質量。但如何檢驗軟體的行為是否變化,只能靠測試。試想一下,如果你在通過重構來完成程式碼的開發,這個過程如何檢驗軟體在修改前後是否真的“行為不變”?只有通過測試。而高頻重構/修改,則意味著需要高頻的測試。高頻的測試,自動化是必然。

 

那是否自動化了就夠了,或者說如何更好的做自動化測試、單元測試、契約測試等等自動化測試工作,它們之間又有什麼區別與聯絡?這又是另外關於自動化測試的敏捷實踐方法。

 

● 持續整合,以配置/原始碼管理為基礎

如何保障軟體質量?XP 的方法是,從可觀察性區分,外部質量靠自動化測試,內部質量靠重構。但對於一箇中大性專案團隊,在通常為期數月甚至以年計的交付過程中,如何堅持下來?這就要靠持續整合。持續整合的工程實踐包括一系列的工具和方法:

 

敏捷方法是需要用實踐方法來解決這些問題。比如響應變化這一點,XP 方法中,如果(迭代中的)一個使用者故事還沒有實現,允許可以考慮用另外的使用者故事替換,替換的前提的工作量是相等的;而 Scrum 強調一旦開工會定下來,需求就不允許被新增進來,並且需要 ScrumMaster 來把關。

● 通過程式碼/配置管理工具,將程式碼集中版本化管理起來;

● 通過程式碼分支管理策略,建立起適配專案特點的分支/版本管理方法;

● 通過 CI 工具,完成構建過程的自動化,建立持續整合流水線;

● 通過單元測試、自動化測試工具和方法,保證持續整合的質量和可靠性;

● 通過將 CI/CD 過程中的非程式碼部分的必要產物(如 Shell 指令碼)也版本化,來保證整個持續整合過程的自動化、穩定性和可重現性。

 

● 迭代,舊時代瀑布模型的升級版

團隊需要在指定的時間段內完成一系列工作項的過程稱為迭代。迭代的持續時間一般由團隊和產品所有者決定。

 

在 Scrum 框架中,迭代被稱為 Sprint(衝刺)。而 Scrum 的專案過程有一系列的 Sprint 組成。Sprint 的長度一般控制在 2-4 周,通過固定的週期保持良好的節奏。產品的設計、開發、測試都在 Sprint 期間完成。在 Sprint 結束時交付可以工作的軟體。整個 Sprint 的過程中(計劃會議確定下工作項內容後)不允許發生變更。

 

Agile 和 CMM 的本質不同是什麼?

一般意義來講,CMM 更多的是一種預定義的、固化的模型。而 Agile 是一種經驗性的、彈性的模型。軟體開發,本質上是一種解決複雜未知問題的過程,解決這種問題用經驗性過程會更加有效。

 

但在 CMMI 目前較新的版本中,也增加了對使用敏捷方法的組織的指南。以下 為 CMMI V1.3 中對於敏捷的描述片段。

 

深入思考軟體工程,開啟 DevOps 之旅

 

Scrum

 

深入思考軟體工程,開啟 DevOps 之旅

 

XP

 

深入思考軟體工程,開啟 DevOps 之旅

 

使用者故事

 

深入思考軟體工程,開啟 DevOps 之旅

 

認識 DevOps

 

從 Agile 到 DevOps,區別是什麼?

從研發管理的視角來說,Agile 已經涵蓋了流程、方法、實踐、文化,甚至一部分工具。Scrum 框架其實就是一些輕量級的流程方法的集合。而 XP 則更多的強調了很多好的工程實踐。敏捷宣言本身就是敏捷文化的體現,甚至自帶敏捷先驅們的浪漫主義情懷。而持續整合這一工程實踐不可能脫離工具而存在。但 Agile 的各家言論並沒有真正把“儘快交付業務價值”的最後一公里走完,所以完整的工具鏈,以及生產環境管理要到 DevOps 中去尋找答案。

 

而從應用管理的視角來看,我們在實踐 DevOps 的時候,多半也會去使用很多 Agile 中的流程、方法、實踐。比如我們做需求的時候經常會用到使用者故事、看板;做設計的時候也會強調簡單設計,避免過度設計;整個開發過程使用迭代的方式去管理、測試和部署時會使用持續整合並遵循一定的分支及流水線管理策略等等。而在上線包括上線之後,則會多出一些 Agile 中 沒有說的特別清楚的重要內容,Ops。

 

深入思考軟體工程,開啟 DevOps 之旅

 

通過上面的觀察,我們會發現 Agile 和 DevOps 是密不可分的,並且他們本質上都是在做一件事情,就是軟體工程。

 

當然,對於不同的行業,DevOps 還是可能會有很多不一樣的地方。比如在強監管的金融機構,測試環境和生產環境之間難以逾越的高牆可能一直會存在。而這在很多網際網路公司或普通企業來講就不是一個特別要命的問題。

 

無論有什麼樣的差異性,無論我們把正在做的事情是叫 Agile 還是 DevOps ,甚至未來又出現什麼新的名字,其實我們都在解決軟體工程領域的問題,質量、效率、成本鐵三角是這個領域永遠的命題。

 

工具鏈整合,建立高效開發協作體系

工具鏈的整合構建是 DevOps 落地過程中非常重要的一項工作。整個工具鏈的串聯會涉及到很多工具。我們可以從兩個維度列舉一些常見的工具:

 

研發工程維度(軟體交付過程):

  • 專案協同:Jira、BeyondDevOps Synergy;

  • 程式碼託管:GitLab、Bitbucket;

  • 製品管理:Artifactory、Nexus、Harbor;

  • 構建管理:Maven、Docker、NPM;

  • 程式碼質量:Sonar、Junit;

  • 自動化測試:JMeter、Selenium;

  • 流水線工具:Jenkins、JFrog。

 

基礎設施維度(周邊配套系統):

  • IaaS 平臺:VMware、Openstack、BeyondCube;

  • 容器管理:Kubernetes、BeyondContainer;

  • 部署平臺:Ansible、Python、Shell、BeyondStage;

  • ITSM:CMDB、CMP、BeyondCMP、BeyondCMDB;

  • 日誌平臺:ELK、EFK;

  • 自動化監控:Zabbix、Prometheus。

 

可編排的流水線

如果沒有流水線,就無法完成自動化的 CI/CD 過程,“持續”二字也就無從談起。以博雲的 BeyondDevOps 產品來說,基於 Jenkins 提供了產品、服務兩級流水線自定義編排能力。

 

深入思考軟體工程,開啟 DevOps 之旅

 

深入思考軟體工程,開啟 DevOps 之旅

 

同時通過任務模板,通過“流水線、步驟、任務”多級模板、提供“樂高”式的流水線搭建體驗。並且平臺已經內建各種常用模板:從程式碼構建、程式碼掃描、製品/映象拉取、自動化測試、到製品部署各個環節。如果已有模板並不滿足使用,也可以通過自定義模板功能來實現新模板的開發和接入。

 

深入思考軟體工程,開啟 DevOps 之旅

 

程式碼分支管理規範

程式碼分支策略及分支管理規範,是 DevOps 中 SCM 領域的核心實踐。常見的程式碼分支策略包括 Git Flow、GitHub Flow、TBD Flow 等等。不同型別的公司、產品、系統、專案,適用的分支策略也未必完全相同。好的 DevOps 平臺,可以適配不同的程式碼分支管理策略,幫助客戶通過平臺和工具鏈完成 SCM 的整體管理,比如在某些場景下,可以將某種分支管理規範形成一個 Template 固化到平臺中,幫助研發團隊降低複雜分支策略的使用門檻。

 

深入思考軟體工程,開啟 DevOps 之旅

 

製品管理規範

製品是軟體開發過程的核心輸出物,也是產生最終業務價值的載體。對於企業來說,建立組織級的統一製品管理體系是非常必要的。其中包括製品的命名標準、二方包管理規範、三方包管理規範、生產/非生產製品包管理規範,還包括製品如何晉級,以及相應的安全策略等。

 

深入思考軟體工程,開啟 DevOps 之旅

 

自動化生產級應用釋出過程

生產環境的釋出和運維,是一個非常嚴謹的工作。相對於大多數企業和網際網路公司來說,金融機構對於生產釋出的要求會更高。包括上線申請單、資源協同開通、製品管理、可定義的自動化釋出過程、應用配置中心等。特別是在很多傳統金融機構中,生產環境釋出還意味著多部門配合、多系統呼叫、多網路環境協同等等工作。博雲的 BeyondDevOps 整體解決方案就是在這種複雜的業務場景中產生的。

 

博雲 DevOps 整體解決方案概覽

 

諮詢+產品+實施

DevOps 作為目前軟體工程界最佳實踐的集大成者,確實是炙手可熱的研發管理方法。但只要是管理,就有萬千不同。對於向落地 DevOps 的團隊和組織來說,所處行業、組織文化、軟體形態、團隊現狀、內部環境等等因素的不同,都會導致其研發管理和工程實踐的不同。

 

深入思考軟體工程,開啟 DevOps 之旅

 

如何在充分考慮現實因素(目標、時間、成本、資源)的情況下,去選擇一個切實有效的路徑和方案,是落地 DevOps 專案成功的關鍵。所以對於一個 DevOps 專案來說,並非買一套產品就萬事大吉。博雲基於自身在金融、工業、企業、政務等行業的經驗,總結了一套可以為不同行業客戶落地的交付方案,願意與各個行業的朋友持續交流切磋。

 

專案建設路徑 

羅馬不是一天建成的,DevOps 作為研發管理之重器,也並非一朝一夕就可以建設好、運用好。對於大多數的組織來說,完整 DevOps 體系的建設和應用之路需要做好規劃,特別是有一些企業對於 DevOps 除了實踐應用之外還有過信通院能力成熟度模型的需求,還要為過級評估工作留出充分的時間。

 

深入思考軟體工程,開啟 DevOps 之旅

 

在目前很多企業年度預算制的開支框架下,DevOps 專案需要做好規劃與路徑,做到有明確的短期、場景的目標與實現路徑,才能更好地完成專案,達成預期。

 

博雲 BeyondDevOps 平臺開放試用,歡迎訪問bocloud.com.cn,或掃描下方二維碼或點選文末閱讀原文申請試用。

 

深入思考軟體工程,開啟 DevOps 之旅

相關文章