為什麼不斷做遷移,那是在還技術債

weixin_33766168發表於2019-04-07

本文來自2018年QCon舊金山大會的演講,Will Larson談到Stripe公司的程式碼劇增後,遷移是有效治理技術債的唯一機制,同時他還介紹了不斷遷移的方法。

我是Will Larson。在這裡,我要談談技術債以及實際上怎麼處理這些不斷出現的技術債。今天早上,我收到別人發來的推特訊息,“我很抱歉錯過了你的演講”。我正感到得意時,他們說,“你迄今為止可能解決了大量的技術債”,這讓人感覺有點像是反話。我不確定對此的感想,但是技術債是一種體驗,尤其是當你在事業上更進一步時,管理技術債成為一種常態,我認為學會管理技術債是很重要的。它是決定一個公司是否能長期成功的一個因素。讓我們來深入瞭解一下。

我現在工作於Stripe,在基礎SRE團隊工作了幾年。我們從事資料、開發人員生產率和基礎架構方面的工作。在此之前,我也在Uber工作了幾年。我從工程組開始做了很多和基礎架構相關的工作。不知道四、五年前你們有多少人乘坐Uber,那時,只要你一上車,司機就會告訴你,“噢,這是我的第一單”。

實際上,這是他們針對使用者的一種策略,即如果司機每次都告訴乘客這是第一單,則乘客給司機的評價會更高些。因此,我要說,今天是我的第一次會議演講。我首先會告訴大家演講的目的,接把結論提前告訴你們,然後不斷地談及這一點,就像別人告訴我的那樣。當公司變大時,技術債是影響公司快速成長的核心限制。

遷移就是完全替換掉一個工具、系統或庫。它不能被部分地替換掉,也不是變成一個半或變成三個,而是完全的替換掉某樣東西,讓其消失。要想遷移成功,正確的做法是把單個遷移都看成一個產品。

什麼是遷移?

首先要了解什麼是遷移?接下來,就可以瞭解如下的一些問題,如遷移實際上是否重要?為什麼不能忽略這一步?如何才能避免每隔幾年就更換工作?你是否真正地做好了遷移?當陷入困境時,如果不跳槽,該如何繼續?

\"\"

正如Jessica’s [泰]之前談到過的,當看到這張動物圖片時,我只會想到和大家分享這個令人激動的畫面,而不會注意這是火烈鳥的遷徙。遷移是整個地替換一個工具、系統或庫。有哪些例子呢?我想到了一個很好的例子,就是去年在Stripe,我們從工具Chronos(在Mesos上面做二次排程的工具)遷移到Kubernetes 的CronJobs。我們堅決反對再使用Chronos,對其進行了移除,並徹底遷移到Kubernetes。這是一次冒險的經歷。

\"\"

另一個遷移的經典案例則是AW或者說Netflix把他們自己的資料中心遷移到雲上。幾年前,我曾和一名Netflix員工交流,他們的做法很讓人驚奇,資料倉儲是他們最後遷移的部分。而且,他們實際上只是把他們的資料倉儲返還並賣給了其供應商,該資料倉儲後來成了一個平臺即服務。這也算徹底完工的一個方法,即重新定義什麼是成功。

\"\"

另一個很有爭議的案例則是Uber從Postgres遷移到MySQL。在黑客新聞上,有一些人對此表示不滿,這也許可以作為一個成功標準。今年早些時候,Dropbox從Python 2升級到Python 3,看起來這種遷移有點不合理,Python 3後向不相容,這是一個很大的變化,對他們而言,這也是非常重要的切換。遷移,就是完全地替換一個系統、庫或工具。

\"\"

\"\"

遷移重要嗎?

遷移重要嗎?我們已經定義了什麼是遷移,但你們為什麼要關心這個呢?你們是否應該走出這個相當擁擠的會議室呢?

生產率會隨著時間推移降低。當你第一次建立程式碼庫時,只是很小的一個創業公司,每件事都很簡單,你可以很快搞定所有事。如果你在家看到類似這樣的推特訊息,“我可以拿到你們的網站”,或者,“我可以拿到你們的產品,在週末用大約兩小時的時間重新建立它”。如果有人這麼談論過你的工作,你就會知道,如果只是一個相當小的公司或團隊,一個全新的專案,兩個小時幾乎可能搞定任何事情。

但是,當程式碼庫變得複雜,功能越來越多,當前程式碼和原本的設計出入也越來越大,隨著時間推移,要做任何修改都將變得越來越困難。團隊希望變得有效率,而不是效率低下,如Jessica之前談到的一樣,在Airbnb,部署某些時候變得異常痛苦,我覺得這是很好的一個例子。因此,團隊會竭盡全力變得更有效率。

\"\"

他們會採取一些措施,如程式碼審查、句法分析(linting),任何他們能做的事,他們會一點點地使他們的工作變得更輕鬆。每次,你採取其中一項措施,事情就會變得好一點。你一項一項地採取這些措施,最後會陷入低谷,你好像已經用盡了所有可供實現的好點子。現在的問題是,走到這一步該怎麼辦,停止改進嗎?

\"\"

實際上又發生了什麼呢?團隊自我授權,自下而上,為了使他們的工作變得更好而做任何他們職責範圍內的事。他們更改自己的程式碼庫、自己的服務、自己的庫、自己的工具。最終,他們會完成自我授權的所有事情。很多時候,人們認為,這裡的問題是他們之間無法達成任何一致。

\"\"

但實際上,我發現,對於大部分改動,每個人都同意去做。它有顯而易見的好處,我們只是切換到一個技術負債小的系統,對嗎?但同時,這不僅只是對一個方案投贊同票,還意味著許多其他的事情,如時間表的混合、匹配等。因此,我真正希望的可能是,先在後端團隊做,再在前端團隊做,“是的,我們下季度再做吧”。

大家可以理解,就像在有些不同尋常的情況下,每個人都認為他們想要做某些事而且很緊急,但實際上從來沒有完成它。這是因為,在某些時間節點上,有太多的團隊需要協調方法、優先順序、時序來完成這些事情。一開始就能看到這點,情況會好一些。另外,我的圖表看起來非常糟糕,和我在報告中所做的一樣。糟糕而專業的圖表。一開始看起來還不錯,後來就不行了。那我們之後可以做什麼呢?答案是可以做遷移。

\"\"

所以遷移就是,用更有效的庫、系統、工具等來徹底替換掉已經不能有效工作的部分。這也是一種簡化,對嗎?遷移並不會一夜之間帶來奇蹟,實際上,它可能在某些時候會讓情況更糟,但對於更新換代來說,這是值得的。完成一些事並扔掉所有的技術債。然後開始另一個專案,工作一段時間後,情況會變好。再開始另一個專案繼續完成它。

但有趣的是背後發生的事情,即你所在的組織不會一成不變。最開始,你有一個研發團隊來做這些變更,這會非常快。幾年之後,有3個工程團隊的時候,變更就會變得有些困難,但也還在可控範圍之內。然後團隊規模可能會突然擴大到15、50、500、5000,變更就會變得非常非常複雜了。你會發現,實際需要做的可能是3個、4個、5個或6個持續的遷移,而不是一個遷移就可以搞定。

你開始停滯不前。當你的工程團隊,如新聞、API、廣告等,耗費幾乎所有的時間來做遷移,而沒有任何多餘的時間來做使用者相關的工作。企業或公司會經常突發地需要實現基礎架構方面的需求,而員工根本沒有時間來為使用者創造價值,也不再是新奇感和創新性十足的創造型員工了。當產品工程師們把全部精力都傾注於基礎架構相關的改動時,就會陷入了這種不正常的狀態。

基礎架構團隊的情況會好一些。他們幾乎沒有對外依賴,外部的影響也不明顯,反饋環路也就相對較弱些。因此,給其他團隊帶來更多工作的基礎架構團隊通常意識不到,這對於產品工程團隊或棧頂的團隊會變成怎樣的困難。

\"\"

遷移成功和失敗的對比

現在,你已經變得忙亂,會發生什麼呢?情況會更糟。因為到現在為止都是假設遷移成功。而實際上,很多遷移是不成功的。你打算替換RPC層,替換編排層,替換前端庫或替換設計語言,就像打了個賭。突然之間,你花光了所有的時間,讓所有的團隊一起協作進行遷移,結果失敗了。這讓人感覺很難堪。你不想它失敗,但是你的生產效率持續下降,在很短的一段時間裡,甚至接近了零生產率。

\"\"

Heperbahn Uber的Hyperbahn是一個非常好的例子。Uber過去一直使用HAProxy做路由請求。這是一種簡單的路由方法,每個伺服器在Clusto上都有一個配置。Clusto有點像命令列視窗,但它來自Digg,沒有被其他公司所採用。Clusto使用Python語言,也存在一些問題,但它仍是個很酷的工具。我們構建這些HAProxy配置,然後是HAProxy的sidecar模式,我們在該模式下進行本地路由,沒有共享分佈狀態。

\"\"

很多情況下,這樣處理是很好的,但是當你要增加越來越多的伺服器時,缺少集中化的配置或集中化的狀態來協調路由會讓人很累。Hyperbahn專案應運而生。Hyperbahn最重要的一點是它明顯比它要替換掉的方式更先進。斷路器,非常複雜。速率限制,它有不同的搶佔式重試,它有各種類似這樣的很棒的功能。但是,介面變了,這意味著從一種服務實現切換到另一種會變得非常困難。

最後的結果是,很長時間以後,我們會有太多的服務需要維護。但是我們很難做到面面俱到,系統很可能以失敗告終。這是遷移過程中可能遇到的最壞的一種情況。我們不僅不能更快,反而會浪費時間做遷移並遷移回去。其後果就是,幾年後,它成為一個廢棄的專案。這裡主要不是為了說Hyperbahn不好,Hyperbahn本身確實是一種比它要替代的技術更好的技術,而是說遷移本身失敗了。

團隊在遷移上的賭注

接下來我們會談到怎樣做得更好。我認為,遷移失敗也像是遷移的動力。如果遷移的不好,團隊可能會得到一些反饋,如“好吧,上次我和你一起工作了很長時間,接下來我必須做回自己的事了”,或者是“毫無疑問,這次不能和你一起工作了。不過,如果你已經快做完了,也許下次我們可以一起工作,我會最後一個做遷移”。實際上,如果你一次遷移失敗,人們下次就不願意和你一起工作了,因此,你就有動力把遷移做好。

Digg v4

相反,也有好的案例。如果你做得相當好,人們覺得這會節省他們的時間,下次會願意繼續和你合作。他們知道你會減少他們的技術債。不過,遷移成功或失敗的勢頭也是很重要的影響因素,如圖上紅圈所示,如果連續好幾個遷移都失敗了,到最後生產率幾乎會降到零。如果你從來沒有在一個經歷過該階段的公司工作過,你會認為這不可能發生,好像我們能控制,能做任何事情一樣。在一個程式碼庫上怎麼會無法往前推進呢?但是,我曾在多個團隊和公司待過,他們都碰到了這種情況,最典型的是Digg v4。我之前和Randy討論過,他沒有意識到我在Digg工作,他說那是差勁的遷移例項。很諷刺的是,那是我感到很自豪的工作之一,但它同時也是一個完完全全的災難。之前的程式碼庫叫LOLcat,即使我們是專業人員,也很難基於它開展工作。它是一個PHP單體應用,我們最終替換掉了它,但我們決定不只是讓它看起來被替換掉了。

\"\"

五六年後,如果我們盡力做到最好,會出現什麼情況呢?我們知道,MySQL比較舊,有時候會出現問題,Facebook推出了一種新型資料庫Cassandra。不知道是否有人聽說過下面的玩笑話,Cassandra就像Facebook釋出的一個特洛伊木馬病,用來毒害新一代創業公司。2018年,Cassandra實際上已經是一款非凡的軟體,對嗎?但在2011年,時間還有點早。我們切換到Cassandra,後端切換到Python語言,我們切換到Thrift協議。我們做了所有這些切換。只是SQL,對嗎?我們把每個演算法刪除,然後重新開始,沒有任何理由。新的總是好的,但沒有效果,人們討厭它,不再訪問我們的網站。我們花光了錢,關閉了公司,每個人都回家了。如果陷入遷移混亂中,就會出現的情況,耗盡生產率,重寫整個系統。這是非常非常危險的,接下來就只能回家並丟掉工作了。遷移重要嗎?答案是肯定的,因為我們不想回家並丟掉工作。

介面

如果你不喜歡遷移,不喜歡做任何類似遷移的工作,最好的方法就是擁有強大的介面。強大的介面意味著它們之間重疊的部分要儘可能得少。這樣,團隊才能夠不斷地自我授權,提高生產率。同時,依靠那些聰明縝密而且可以在自己的工作範圍內自我授權的人員來抑制任何生產率的大幅下降。

\"\"

有效的遷移

相反,如果介面弱,彼此間重疊很多,作為一個整體,邊界定義的不清楚,我們能做的事情不多,則只能儘快做遷移。好了,我們知道什麼是遷移,也知道它意味著什麼。現在,怎麼實際地開展遷移呢?每個遷移都是一個產品,我會反覆強調這一點。這個過程分為三個風險階段或三個過程,即去風險、使能、完成。因為每個遷移都非常重要,同一時段,只能做部分遷移。能夠並行進行遷移的數量是有限制的。

我們必須考慮遷移帶來的好處。這意味著幾件事,第一個就是“這麼做值得嗎?”。坦白地講,我認為,大多數遷移沒有解決已有的問題。很多遷移是由錯誤原因引起的。下面給出一些策略,它們將從遷移的角度來看是否值得。

尋找支持者。經常會出現這種情況,我覺得很有意思,就是當你決定做某些事情,如針對新的資料庫、新的後端,新的Digg v4進行重寫,卻找不到一個團隊真正地願意支援你。這不是指高管層的支持者,對於高管層,只要付出足夠的努力就能說服他們。我們需要尋找的是工程團隊,雖然忙但願意把你的工作的優先順序排在他們當前正在做的工作之前的團隊。

如果找不著這樣的團隊,也許這是一些不值得做的事。找到一個信任你工作的另一個團隊是非常重要的。一旦你找到一個,可能還有第二、第三個。

第二,機會成本。選擇一個遷移不代表它有價值。因為有諸多限制,我們必須識別出能做的最有價值的事情是什麼。這點很重要。同一時間段,只能做其中的一部分,因為很佔用時間。這是策略性的賭注。

比如,公司會採用的一些最重要的策略性賭注,它們決定了你的實際能力,是重寫新功能,還是以零交付告終。我們經常會像這樣,“哦,如果我們遷移到這個新事物上就太酷了。”但是,真正的機會成本,這是你本次可以做的最有價值的東西呢?作為資深人士,做這樣的選擇是你的職責,它決定了你公司的未來,你會因此而受人尊敬。

第二條不是我發明的。最近,在我和別人聊天時,他們談到了正在構建自己的高一致性或強一致性分佈資料儲存的初創公司的數量。事實證明,這是一個很難的問題。畢竟他們和資料庫公司不一樣,這更像是一些創業公司,試圖建立另一個Spanner或另一個Cosmos DB。如果沒有祕密武器,這幾乎是不可能解決問題的。所以說,這不只是一件很好的事,也是一件很酷的事。很多時候,人們容易被一個問題的有趣性所吸引,從而忽略了這個事情本事是不是值得去做。不要這樣做。

再一個是為工具找問題。第一次練習本次演講時,我列舉了一堆我認為是糟糕選擇的例子,事實證明,如果想要不冒犯至少一半的聽眾,那就沒有辦法做出選擇。因此,我希望大家做的就是,想象一下,你看到別人採用了而進展非常不順利,然後你可能會想,“哦,他們週末剛做的,現在要拿到這裡來討論,這正是我在談論的東西。他們為什麼這麼做呢?”

設計文件

如果你認為某件事有價值,值得去做,接下來的問題是解決方案是否有效?設計文件是第一步。這裡有三個步驟。第一是你要先讓自己相信,這個方案有效,寫下具體細節,最終讓你自己相信這種方案切實可行。

第二,你需要讓使用者相信該方案對他們而言是可行的。第三,人們通常容易忽略這一點,而我認為更重要的是,讓持有不同意見的人相信該方案的可行性。搞定那些可能贊同你想法的人是一方面,但是,如果你沒有試圖找到那些討厭你的想法,認為該方案很糟糕或對你持懷疑態度的人,從他們那裡得到反饋,你就沒有做好前期的準備。

一個好的設計文件不會包含你應該做某事的原因。如果有,它同時還會有更長的篇幅來寫為什麼不應該做其他事。如果你不能說清楚為什麼不做其他事,你就不能說清楚你的解決方案是否有效。

原型很多時候,原型對人們來說就是第一個版本V0。但原型並不是指構建第一個版本或是快速版本。它不是用來驗證正確的實現方法。實際上,存在多種解決方案時,原型可以降低風險。這就像在兩個小時內找到可以做的事情。如果你試圖將伺服器配置從Puppet遷移到Dockerfiles,可以選擇Puppet中的一個角色,重寫成Dockerfile。這可能很糟糕,但你的任務就是花幾小時看看這是否可行。你並不是要試圖建立一個可以工作的版本,也不是建立一個一直執行的實現,你只是要確認你的方案是否很可行,“我們花了兩個小時來驗證,然後停下。”

讓前期使用者參與

讓前期使用者參與進來是更深入原型的一個方法。確保能夠滿足他們所有的需求。為了實現好的產品設計,要儘快同客戶建立良好關係。他們能看到那些你看不到的邊緣情況。我認為,當你經常操作一個系統時,對於使用它會有一種抽象的感受,這也是為什麼需要實際地參與,加入會成為早期使用者的團隊,設法讓原型為他們工作。

Kubernetes就是一個很好的例子。因此,我們有了以它為基礎的Chronos,一個Mesos排程程式。結果在運營上,有點被拋棄了,我們內部並沒有很好的擁有它。但總的來說,這個專案在最初建立它的公司裡就沒有得到太多的使用,所以有一點衰敗。我們內部有個團隊使用了大量的約有90%的Chronos cron任務。我們加入進去和他們一起工作,把每一個都移走了。

\"\"

這也是讓我們很吃驚的地方,在將Chronos替換為Kubernetes的遷移中,驗證了我之前關於遷移的、也是唯一的定義。突然之間,Chronos被完全的廢棄掉,Kubernetes上的CronJob,那時不被人看好,它早期有一些問題,現在已經好了。我們成功做了遷移,那時仍然考慮二次遷移,即把無狀態服務遷移到Kubernetes。

一難一易

當你想看某件事是否能正常工作,先讓容易的部分可以工作。這很重要。做簡單的事情會簡單些,否則,團隊成員不願意接受。但人們也包括我犯過好多次的一個錯誤是,去做了第二、第三簡單的事情。從度量資料來看,似乎進展不錯。然後,當遇到很難的部分而做不下去時,能做的就是撤回已做的遷移工作。

當前階段,我們的目標不是儘快的完成任務,而是確保任務完成的可行性。所以應該先易後難。如果難的部分不工作,反而更好,此時只需撤回簡單部分的實現和整合。這可以節省我們和使用者的很多時間。做好這一步,就不容易失敗,導致使用者失去信心。在大公司裡,你可以有很多這樣的經歷。

在Stripe,一個很好的例子就是MongoDB的升級。在很長的一段時間裡,Stripe在舊版本的MongoDB上是世界頂級專家。我覺得自己比實現MongoDB的工程師們瞭解的還要多。在這個特殊的版本上,我們有很多技巧,在決定升級時,是有些壓力的。這是我們所有的資料,非常重要的資料,有足夠多的資料都在這個資料庫裡。每個人都會問,“你們有備份嗎?”。當然,我們有備份,但是當資料量非常大時,恢復這些資料,僅僅是資料的傳輸就會花費大量的時間。所以問題不僅是“你備份了嗎?”而可能是你有兩個資料備份嗎?你願意花雙份的錢嗎?這裡需要取捨權衡。

當我們決定做遷移時,我們首先做了能想到的最簡單的事情。比如,只有幾行程式碼的營銷網站停止工作了,我們可以恢復,畢竟只有很小的資料集合,可以在一兩小時內恢復,對使用者的影響不會太大。現在,輪到最難、最棘手、最讓人畏懼的部分,如訪問模式非常古怪的資料集合。我們遷移過去了,雖然有點困難,有時會來回切換,但最後我們還是徹底讓它工作了。那時,我們就知道,其他的每件事都可以迎刃而解了。

在我們完成這些比較簡單的部分時,可能會碰到失敗的點。當在新版本中遇到不同的可擴充套件性問題時,我們不得不做一些回滾。這樣會更安全、便捷和簡單。消除風險。現在我們知道了,正在做的首先應該是值得做的。第二,方法可行的,能正常工作。真讓人很激動。所以,下一步是識別出你能做什麼,讓遷移變得簡單。

使用者測試

使用者測試可以說是最重要的事情。這就像你看到了一個小公司,就像這個樣子,“我們做出了令人驚歎的產品,但沒人願意使用它”。你必須儘快找到使用者。必須實際地測試遷移。至此,一個重要的區別是這樣做並沒有測試產品,而只是測試了產品的使用。遷移對於要切換到該產品的人來說要儘可能得簡單。測試介面,讓人們真正使用你的介面來解決問題。觀察他們對介面的不滿。我們就是這樣學習的,不是嗎?觀察人們如何使用你的介面。這能讓你進入快速迭代週期。就這樣做。

文件

文件工作經常容易被忘掉。我認為在Stripe早期的成功和持續發展中,文件工作非常重要,優質的文件讓員工可以很容易使用。在每次遷移中,如果給人們可用的文件,他們就可以根據自己的時間安排幾小時實施遷移,而不需要等你有空閒時間去協助他們解決。但是,只有你坐下來,觀察別人怎樣使用你的文件來進行遷移工作,你才能知道文件是否實際有用。

對於Email來說,也是如此。很多時候,Email像是世界上最糟糕的東西。我不記得在讀寫電子郵件上到底花費了多少時間,但是很多很多。但如果你不對電子郵件先做測試就發出去,那是在浪費人們的時間。因此,對你的電子郵件也進行A/B測試吧。讓小部分人先閱讀,看看他們是否明白需要做什麼。郵件吸引人嗎?讓人興奮嗎?這些並不是必需的,但是可以讓郵件起到它的作用。 “真正的技術難點在哪裡?”,又或者是“什麼是成功的必要因素?每件事的質量體現在哪裡,電子郵件的質量,文件的質量,而不僅僅只是程式碼的質量”,這些問題看起來有些奇怪,但可以讓你在職業發展中上一層樓。

操作

我認為操作是一個常規步驟,很容易切換。實際上,一旦切換,人們不會再使用不好的系統。確保人們真正使用系統。我覺得混沌工程是個很好的方法,早期人為注入故障,迫使使用者習慣系統操作,讓使用者在實際使用前或至少在我們自己宣稱系統可以工作前,對系統有信心。

除錯

除錯也一樣。注入故障進行測試,最終讓人們習慣這個系統。經常會有工具幫助人們完成切換,但這會讓人們失去除錯系統的能力。

這是很糟糕的,因其突發性,人們不清楚切換是否存在問題,可能在哪裡,是在遷移中少了一個標識或其它的東西,還是底層系統工作異常。出現這種情況後,人們很快就會不信任新系統。他們會相互談論這個問題,說來也奇怪,他們會從其他使用過新系統的人那裡瞭解到新系統無法正常工作,這會妨礙其他團隊的採用。而實際上,新系統是工作的,這只是一個誤解。但是,對於已經認為你的產品不可靠或有問題的人來說,你完全無法說服他們。你只能確保他們在早期就獲得了這樣的資訊,有了這樣的理解。

欲速則不達

現在談下欲速則不達。重新回到之前的例子,早期做很多簡單的事情,讓你獲得遷移勢頭。努力識別出完成全部遷移所需要構建的工具。不需要在早期就全速前進,這點並不重要,重要的是識別出怎麼開展工作,怎麼構建工具,編寫文件?怎樣進行宣貫才能處於可以儘快完成遷移的有利位置?

自助服務

有關這一點,自助服務是一個很好的例子。很多時候,如果文件不夠好,或者即使文件非常棒,執行遷移的團隊也可能會變成整個生產環節中的瓶頸。這很糟糕,因為有個組織可能就正好準備好了遷移和完全切換。但是,團隊正處於一系列的麻煩中,為個人提供幫助,回答各種問題,整天在Slack中回覆這些問題,可能就是告訴他們,“答案就在文件中。”這就是前期的準備工作沒做好,沒法讓人們自行解決問題。自助服務,從這個工作流程中脫穎而出,讓人們可以自行解決問題。

自動化遷移

最好的遷移是不需要任何人做任何工作。通常,這還不夠。我認為Sorbet是一個很好的例子。Stripe有著上百萬行Ruby程式碼。Ruby是非型別化語言。它有時候有點神奇。我們一直在做的就是努力把漸進式型別策略引入Ruby,使用了正在開源過程中工具Sorbert。修改1億行程式碼是不現實的,但老實說,讓我們大約300個工程師來做所有的型別化工作也不可能,因為他們都忙著做相當重要而且非常價值的事。

因此,我們在遷移中所做的幾乎所有工作就是一系列的指令碼,通過程式設計重寫抽象語法樹,然後提交程式碼。在Googel ClangMR裡有一篇很棒的論文,提到怎樣在大得多的規模上進行這項工作。但是怎樣能不手工做這件事呢?你可能會想,“啊,我們有600個工程師精通這個,可以都來參與。”但如果只有一個員工通過編寫指令碼來重寫程式碼,你實際上可以跳過整個遷移,還跳過了除錯,比如7000多個因為胖手指引起的打字錯誤。

如果公司沒有諸如Codemod之類的工具,遷移之初,手動工作量都會被嚴重低估。如果工具使用得當,能夠節省工程師們幾百個小時甚至幾百年的時間。

增量和回滾工具

遷移中的另一個典型問題是遷移有最後期限。通常是在週五,因為我們不能很好地按時間來安排相應工作。在週五4點、5點、6點或其他時間切換到新的系統上,就會有一個系統中斷。

如果我們不給客戶提供回滾工具讓他們可以使用,遇到突發事件,就需要週五晚或週末加班進行除錯。如果讓他們可以撤銷遷移,遇到失誤後就可以返回到上一版本。這樣,人們就會信任遷移,建立遷移過程中的心理安全感,他們會相信自己得到了很好的支援,而不只是強迫他們這麼做。

增量遷移也很重要,它讓人們可以一次只遷移一小部分。它讓人們可以做得心應手或有足夠的時間。遷移過程中一個不斷重複的現象就是大家總有很多的工作要做,非常的忙。幾乎沒有終端使用者會說類似這樣的話,“我實際需要你們做的是遷移Cassandra叢集環境”。你的外部使用者對此並不關心。這種情況經常發生在邊緣情況下或者暗處,在20%的時間裡或120%的時間裡.。確保每次只做一點點遷移是很有價值的。

灰度上線

一個可回退釋出的例子就是灰度上線。通過灰度上線,可以頻繁釋出面向使用者的產品,基礎架構、庫和遷移。可以在新舊版本之間通過開關功能標誌位或配置設定來進行切換,這對於風險消除是很安全的一種做法。而且,也增強了信心,因為這不僅增強了當前做遷移的人員的信心,也增加了下一年會和你共事的人員的信心。

最後是介面。介面有點獨特,因為從表面看並不明顯。好的介面遵循一定的規則,差的介面亦然。好的介面正確限定了問題範圍,讓遷移簡單易行,而差的介面讓遷移變得非常非常困難。Mongo有些獨特的屬性,在Stripe,我們被它困擾就是一個很好的例子。使用者必須根據他們不同的寫一致性需求來構建永續性保護。

雖然不明顯,但有一種情況可能會丟失資料。如果主備份先於從備份不可用,Mongo中的主備份是否會從丟失資料將取決於一致性級別。這是相當令人討厭的,人們不得不保護這些資料,到處增加寫前日誌。

這只是一個例子,說明如果介面有一點錯誤,哪怕是一點點的疏忽,都會讓人們花費很大的精力來獲得正確的輸出。少點愛,多點愛,少點使用者測試,多點使用者測試。所以要努力讓介面達到100%正確,從而不會因為一些遷移中古怪的邊界情況或問題停滯不前。

完成遷移

我們知道所做的事是值得的,方案也是可行的,可以稍微提前想想加快速度的工具。現在,我們需要完成遷移。完成是指100%完成,不是99%,也不是99.9995%,也不是指17個9或7000和9。它也不是指其他團隊在維護舊系統,所以對舊系統沒有任何影響。怎麼才能實際棄用舊系統呢?怎樣完全替換需要摒棄的系統、庫或工具,從而得到完全的勝利並減少技術債呢?

uContainer

通過很多很多的遷移,我發現最重要的事就是停止壓榨。我能想到的最好的例子就是在Uber做過的uContainer遷移。5年前,大約在2013年,Uber的道路服務已經提供,和無狀態服務類似,有大量的Puppet變更, Clusto作為Digg的優質技術被移植到Uber。一系列的Clusto變更,也被新增來執行命令。增加單個服務,會花6到20個小時時間,因為系統總是存在一些錯誤,通訊異常,從而導致輸出不是預期的結果。

6-20個小時的工作對於SRE組員來說,是很折磨人的。實際上,人們還想要更多的更新,比如我們中的三個人,想一週有三到四次的遷移。現在突然的,我們需要花費大部分的時間來提供服務,併為此付出艱辛的勞動,如損壞的Puppet配置,但對公司來說沒有任何價值。採用它們並並不好。但那時,在Uber,最恐怖的是公司人員以每年四倍的速度在增長。我們都覺得“這實在不好。”我們預期情況很快會變得更糟。我們所做的就是將所有配置從Puppet遷移到Docker。

我們採用了完全自助的服務方式,每個新的服務與SRE組的互動為零。這讓人讚歎不已,其一,我們不需要在遷移上花費全部的精力。其二,我們可以把之前花費在遷移上的時間用來處理其它還未完成的事務,這真的是太棒了。其三,我們的度量資料表明,在沒有人員介入的情況下,每天可以提供20個新服務。

看起來我們做了件了不起的事,讓每個人都能提升效率。但實際上,我們什麼也沒有做。人們可以專注於已有的服務。要注意的是,使用絕對值而不用百分比。百分比資料會有欺騙性,當演講變得乏味時,可以試著想象,這裡插入7到8個貓咪表情包,如果正好和和你頭腦中想象的貓咪影象一致,會讓你覺得激動,有了活力,就不再覺得枯燥了。

跟蹤

跟蹤,這又是一類沒有趣味但又不得不做的事情,因為我們需要從中受益,減少實際的技術債,並完成遷移。一個大體的方法是建立已做未做事宜的後設資料。這些可以填到JIRA表單中,但不是手動來做,可以通過專門的工具來實現。不要手填,因為生命短暫。專案經理的生命也很短暫,每個人的生命都很短暫。構建一個工具吧。

這些令人驚奇的後設資料可以用到其他的任何事上,比如,報告。執行遷移時,如µContainer遷移,剛開始有50個服務,等我們完全完成後,有2000多個服務。跟蹤每年所有的50到2000個服務是件很混亂的事。但也是很有趣的一年。但我們需要報告來指出做的好的地方。我們有基於每個服務的後設資料,可以明確指出哪些遷移失敗了,給出最典型的例子。為了產品的研發,我們也可以建立經典案例,哪個組有困擾,哪類服務困難較大,從而做理論分析,瞭解可以在介面上做怎麼的改進,哪些的地方介面和使用者的實際需求不匹配,哪些地方是使用者太忙了沒有實際花時間和我們一起工作的。報告,並不令人激動,但老實說,如果你想讓基礎架構不斷改進,想讓公司儘可能成為最有產能的公司,而不是零生產率的公司,想要從零重寫所有的東西,想要不帶工作回家,跟蹤報告是非常重要的。

增加推力

現在,談下增加推力。很多時候,當人們做遷移時,是自頂向下的,比如“你有六週的時間,如果不能按時完成的話,你就成了壞人,我們的CTO也會對你怒吼。”事實上,很多時候這可以奏效,但是,你總不能一直疏遠你的同伴們。可以改進這一點的思想就是增加推力。怎麼能給人們儘可能多的資訊,讓他們能自發地做你希望他們做的事情,而不是告訴他們必須去做呢?例如,在Stripe時,我們做過AWS賬單的成本核算,使用了一種不同的方法。我們會說“嘿,你現在花了大筆的錢,很酷哦,不過你的花銷是最多的。你的平級團隊只用了大約一半的錢”。這只是一點點的背景資訊,但人們會突然意識到“可能我做的不合規範”。不用告訴他們那些事情做的不正確。你不是把關的人,只需要給他們提供一點資訊即可。

遷移也是同樣的。“嘿,你知道,你現在是最後一名。不過很酷哦,我們明白。你做的是很重要的部分,不過它的確太晚了,其他所有人都在16周前就結束了”。只是一小點的推力,一小點的資訊。最重要的一點是,如果團隊沒有和你一起做遷移,這很多時候並不是因為他們討厭你,而是因為他們根據領導的任務優先順序安排他們在做的其他事情。所以,提供事情進展的足夠資訊作為一種推力,讓他們可以自己去和領導進行討論,並重新進行任務排序。

自己完成遷移

遷移的最後一步就是自己完成它。通常,人們不願意做這一步,他們會想,“噢,這不是我的程式碼。開發團隊應該負責遷移,完成他們自己的工作”。但是,我們知道,應該自己深入進去並完成它。在我的整個職業生涯中,最好的例子就是在Strip時做的供應商遷移。我們替換了可觀測性供應商。我們不得不做完整的RIP,替換所有的度量資料和資料表。

我們還有一個合同要到期的供應商,如果同時更新它們成本會很高。而且,如果不能搞定,我們就會鬧笑話。規則一就是不要鬧笑話。也許是規則四,我記不太清楚了。但實際上,我們讓大部分的團隊自己進行了遷移,但當我們到最後期限時,可觀測性團隊加入進來,自己完成了所有的遷移。事實證明這是一段相當好的經歷。我們在資料表中看到了很多常見錯誤。我們看到很多團隊不清楚新的可用功能。他們需要暫停,進行升級,採用平臺上更多的功能。最後我們做到了。這是最重要的,我們做到了100%。

完成後的慶祝

完成的最後一步是在結束時進行慶祝。有兩種慶祝遷移的方式。第一個是針對你的使用者的,開工時,你可能會說,“嘿,我們要實際開始做遷移了”。你會嘗試勸說你的使用者們準備好,你不會把它搞得和以前一樣糟。所以這是給他們準備的,不是給你的。你只需要在結束時進行慶祝,這點很重要,也是你需要在公司裡推行的一個文化準則。

如果不這樣,人們會開始遷移,可能會得到這樣的資訊,“哈,我完成大升級了”,團隊被解散了。突然之間,他們到另一個團隊了開始新的遷移,這讓你產生一種錯覺,公司裡最好的工程師們在不斷地產生技術債。我們必須設定公司文化,否則會陷入困境。很多公司沒有很好地認識到這一點。但是如果去做,它會帶來令人難以置信的價值。

演講即將結束。重點是什麼呢?技術債是發展道路上最重要的制約。不斷進行遷移是管理技術債的唯一方法。唯一的,最簡單的,最直白的解決方案就是,把每個獨立的遷移都當作一個產品來做。謝謝!

檢視演講原文Paying Technical Debt at Scale - Migrations

相關文章