作者 | James Simone
譯者 | 彎月
出品 | CSDN(ID:CSDNnews)
在過去的三個月里,我們團隊的效率達到了驚人的高度。我們完成了許多過去的目標,而且還超出了預期,提交了很多新功能,而且我們每個人都沉浸在工作的樂趣中。我們是怎么做的?我們是如何保持這種狀態的?在過去的幾個月里,我一直在思考這些問題,我也與隊友探討了其中一些問題。
簡而言之,只要積極地分享成功,并承擔起失敗的責任,整個團隊就能有優異的表現。在過去的幾個月里,我們團隊取得了一系列的成功:
- 我們的產品負責人獲得了晉升的機會。當然他們也付出了巨大的努力,此次晉升受之無愧。
- 我們的一位團隊成員的項目(Nebula Logger)在GitHub上斬獲200顆星。去年這個時候,他已經為這個項目付出了多年努力,但是給星一直在50 顆左右徘徊。最近,整個社區突然意識到該工具為管理員和開發人員帶來的巨大價值,真是令人難以置信!
- 我們的新隊友成長為了真正的工程師。我們這個團隊負責10多個應用程序,壓力非常大,但我們的新隊友迅速成長,熟悉了復雜的邏輯,為團隊做出的貢獻越來越多。
- 最后,我也得到了晉升,非常感謝我的團隊給予的支持!
在慶祝之余,我也明白我們這個項目的壓力非常大,如果不是我們一絲不茍、兢兢業業地努力,可能這個項目就要走向失敗。
深度剖析工程上的問題
在從事軟件開發的八年中,我看到了很多失敗的項目,數量甚至超過了成功的項目。長時間這一行業里摸爬滾打,就會覺得自己的人生也是失敗的一部分,有時項目的失敗是我們無法控制的,就像創業公司燒錢是不可避免的。然而,一般情況下,項目會慢慢走上失敗的道路。下面,我們就來回顧一下其中的原因。
裁掉最優秀的員工帶來的危險
項目的長期健康會受到一些因素的影響,其中之一就是裁掉最優秀的員工。英文中,有一個專門的詞匯來描述這種情況:brightsizing,詞典的定義是:
Giving redundancy to the hardest-working / most talented employees of a company。
裁掉公司里最勤奮/最有才華的員工。
許多定義都出給了員工被解雇的例子,但在疫情之前,我以為出現brightsizing的原因不是因為公司發展不景氣,而是拒絕承認和分享個人或群體的表現。許多公司幾乎沒有激勵措施來留住員工,或者獎懲條例不夠清晰。在許多傳統觀點中,勞動力只是一種投入,在其他條件相同的情況下,如果一個人離開,那么很容易找到另一個人來代替。
疫情肆虐期間,人們開始意識到人與人是不能相提并論的,想要真正“取代”有才華和知識淵博的專業人士往往需要數年時間。多年來,brightsizing一直是公司人員配置的一個熱門話題,而如今由于“辭職大潮”頻發這種困境更勝從前,尤其是在科技領域。換句話說,失去最有才華的員工是導致軟件項目失敗的原因之一。在過去的八年里,我看到許多項目失敗就是因為這個原因。
我們很容易認為,糟糕的架構和質量不過關的代碼是導致項目失敗的原因,但我認為實際上這些只是brightsizing的癥狀。換句話說,如果你能留住和獎勵有才華的人,就不會面臨糟糕的架構和質量不過關的代碼等問題。我們公司曾有一名“技術主管”提交了如下代碼:
const removeLastNCharacters = (string: string, number: number) => {var stringToRemove = new RegExp(".{" number "}$", "i");return string.replace(stringToRemove, "");};
我不得不進行重構:
const removeLastNCharacters = (string: string, number: number) =>string.slice(0, string.length - number);
我想說明一點,這個函數的第一版是直接從一個網上的帖子中復制出來的。從網上搜索答案很容易,但這位“技術主管”并不了解基本的庫函數。
有很多問題都是由brightsizing導致的,在此我想特別強調兩個:
- 如果你的態度是“這是我寫的代碼,能有什么問題?”(就像上面這個例子一樣),那么你的手下就不可能取得成功。
- 如果項目的核心人員離職,那么剩下的人也不可能取得成功,他們或者根本無法推進項目,或者對問題置之不理,通常都會造成災難性的后果。
我再舉一個例子來說明這兩點之間的區別。五年前,我前后花費了幾個月的時間才順利通過一系列行政上的繁文縟節,拿到了另一個部門的代碼庫的訪問權。當時的我天真地以為這只是一個“簡單的變更”,然而幾個月過去了問題還是沒有得到解決,因為那個團隊中只有一個人了解他們的應用程序,而且他很“忙”。其實,我只是想給一個對象添加一個屬性……
/ this should have been a type, not an interface// but hey, let's pick on one thing at at time, right?interface ImageProps = {path: string;title: string;// my new awesome property!!label?: string;// etc ...}// the question mark indicates a property is able,// which would later on allow us to write something like ...const createProps = (path: string, title: string, label?: string) : ImageProps => {const imageProps = {path,title,label: label ?? title // fallback to title when label not provided// etc ... other assignments};return imageProps;}
但是,當我在其他地方訪問這個新的label屬性時,TypeScript 告訴我該屬性不可用。我有點糊涂了,我剛剛不是才添加好這個屬性嗎?后來我看到了如下代碼,每個使用ImageProps的文件中都重復定義了ImageProps:
interface ImageProps = {path: string;title: string;}
對于那些拒絕承認有才華、勤奮工作的人帶來的價值的組織來說,brightsizing 是一個非常現實的問題。代碼臃腫、Copypasta(指人們經常在論壇或社交網絡上剪切、復制、粘帖文字)、糟糕的架構等等,各種問題開始慢慢浮現,然后迅速蔓延。在我看來,只需要變更兩行代碼(第一行,將屬性添加到接口;第二行,將新創建的label屬性正確分配給創建好的<img>元素),結果卻不得不修改幾十個文件中的100多行代碼。沒有標準化,沒有檢查。我在代碼審查期間提出了一個建議,集中處理有問題的接口,并創建 img 標簽,但他們對我的建議一笑了之。沒多久我就離職了。
那家公司的人力資源一直有問題,他們不懂得挽留人才,經常迫使優秀的員工離職。員工的平均就職時間不足兩年。許多人未到兩年就匆匆離職了。盡管上面我提到了如今總體的趨勢,但總有公司落伍,因此才出現了“辭職大潮”。
我總是拿我的第一家公司做比較,雖然那家公司也比較失敗,當時我們的CRM系統已有20多年,面臨淘汰(該系統的整個生命周期花費了數千萬美元)。雖然該系統只使用了幾年,但他們決定重新組建團隊,構建自定義的CRM系統,而且他們認為這比改進已有的系統更劃算。然而,他們試圖增加員工并維護的產品其實與核心業務無關,我實在找不到合適的詞匯來描述這種行為。當然,確實有Hubspot這樣的公司真正做到這一點,但關鍵性的區別在于 Hubspot 正在尋求進入一個全新市場的機會,以出售他們的勞動成果。HubSpot開出的薪水很有吸引力,而且部分原因是大波士頓地區創業文化的復蘇。我敢肯定他們創建CRM系統并不僅僅為了支持內部業務,否則真正的高層或股東肯定無法負擔得起這項長期的成本。
有時,裁掉優秀員工的后果不會立刻顯現,尤其是當你剛剛加入新組織時。但逐漸地,你就會覺察到有異樣的代碼、緊密耦合的架構、不夠嚴密的測試以及缺乏組織知識積累。這些都是裁掉優秀員工的后遺癥。這也是為什么面試如此重要,因為你可以借機了解面試你的人是否喜歡自己的公司。換句話說,如果團隊成員都很討厭自己的工作,他們聘用你是為了填補其他人離職留下來的空缺,那你肯定也不想加入他們。
代碼審查的注意事項
最近,我在讀一本書《Accelerate》,書中提到不健康且低效率的團隊面臨的最大問題之一是不必要的審查流程。我反思了我們現在所做的審查以及我見過的一些審查流程,很明顯審查的方式以及審查的內容同等重要。我們應該引入非暴力溝通原則。這些是代碼審查的先決條件,審查過程中的任何精神/語言敵意都會導致審查全面崩潰。
一些不良習慣很容易摧毀審查流程,《Accelerate》展示了與其忍受糟糕的變更審查流程,還不如不要任何審查流程,但我更傾向于說,折中才是最理想的。
代碼審查可以為團隊提供機會,讓每個人達成一致的看法。有時,你需要重新回顧自己過去的看法,我經常對自己的開源工作進行代碼審查,即使有些功能簡單到一天就能改完,也會在審查時經常做出修改。過一段時間再來審查代碼,更有可能發現問題。對于Mob編程,我最喜歡的就是它非常重視接收反饋。最近,我們正在開發一個具有解耦架構的功能,但最終一些測試出了問題,我們開始重構。有人建議我們應該深入調查Jest API,如果我們無法通過現有的測試,那么解耦就沒有任何意義了。加快反饋循環為我們節省了幾個小時的時間,并讓我們迅速回滾到以前可以正常工作的代碼,然后進行測試。
關于代碼審查,有幾個關鍵的問題需要注意:
- 如果按原樣部署這段代碼會有問題嗎?
- 這段代碼是否與其他地方采用的模式一致,還是說這段代碼是一個獨特的存在,并無先例?
- 代碼審查提出的建設性反饋是否會成為提交者的學習機會?
如果你的建議不屬于上述任何一種,則沒有必要提出來,吹毛求疵只會讓大家痛恨代碼審查,浪費時間,而且還會讓大家對代碼審查失去信心。代碼審查應該是大家期待的環節,不要過分挑剔!
遙想當年,在我離開第二家公司后,一位朋友向我透露,我們的代碼庫是他所見過的最干凈的代碼庫。多年以來,他的稱贊一直困擾著我,尤其是我想到了一些我們的不足之處,以及一些仍然可以改進的領域。其中之一便是我們的風格指南。當初我花了大量時間來調整代碼的格式,就是為了讓我的代碼更加貼近風格指南,每次想起這些,我就覺得很痛苦。我們每個人都有自己的小怪癖,即使完全遵從結對編程,我們也必須在提交代碼之前檢查自己下意識的編寫風格。如果你正在審查代碼,而且對代碼的格式有疑問,那么就應該找一款可以自動調整代碼格式的工具。
摩擦 -> 倦怠 -> 災難
遇到摩擦時,第一時間解決是最有效的方式。否則,摩擦會導致倦怠,而倦怠會導致災難。這里我所說的“摩擦”指的是:
- 繁重的代碼審查;
- 困難的部署;
- 所有的部署只能由一個人完成;
- 部署之后需要做回歸測試,有人害怕因此被指責。
- 不鼓勵休假的企業價值觀;
- 企業的價值觀與你自己的價值觀截然不同;
- 公司的管理極其糟糕,以至于價值觀根本沒有任何作用。
- 無法平衡工作與生活;
- 孤島式的工作環境;
- 不清楚職業發展的下一步。
- 不切實際的預估與時間計劃。
以下是消除摩擦的一些例子:
- 代碼審查只關心必要的清理工作和架構問題;
- CI/CD;
- 項目總結不會互相指責;
- 沒有限制的帶薪休假,并鼓勵休假(每年5~6周);
- 良好的調度,開始與結束的時間統一;
- 高度可見、易于理解的晉升渠道以及晉升目標;
- 樂于助人的團隊;
- 根據新信息修改時間計劃;
- 專注“改善”,降低維護的難度。
我會經常遇到哪些摩擦?人們以為工程師總是想接受艱巨的挑戰,使用尖端的技術。我認為,如果問及工程師在工作中對他們來說什么最重要,大多數工程師會從第二重要的事情說起,因為在他們心目中第一位是人。如果工作中的每一項任務都困難重重,那么倦怠將不可避免。如果一切都需要使用尖端技術,那么就意味著你需要花費大量時間來解決沒有任何文檔記載的錯誤、處理工具之間的功能差異,還需要費盡苦心來隔離新的基礎設施、平臺和代碼,直到系統穩定。
我看到過很多團隊和人員快速迭代,采用尖端技術來解決當今的問題,并解決了很多棘手的問題,但我相信他們有能力做這些事情是因為他們在低摩擦的環境中工作。如果你感覺公司或團隊的文化與自己格格不入、與你的工作目標脫節、看不到職業發展的機會,那么就不太可能承受每一天的嚴酷考驗,更不用說長期堅持直到項目取得成功。
我們再來思考一下“失敗”的項目。
在惡劣的環境中工作,團隊成員團結在一起的例子并不罕見,他們相互扶持,共度難關。這種結構,就像雪花形成的格子一樣,無法長期維持,如果有一天破裂了,那么HR就會面臨大決堤。我前面提到的那家失敗的公司就有過這樣的經歷,我們的兩位最有才華的工程師離職了,后來在半年之內,團隊中的其他成員(包括我自己)都相繼離職了。
項目成功的已知因素
那么,什么讓工程團隊變得強大,又是什么讓高績效團隊始終保持健康和快速?
高績效團隊能夠享受工作的樂趣
在疫情期間,我曾參加過一個項目,所有人都是遠程工作,但我們在一起很開心。在我剛加入團隊之際,項目經理打電話給我,熱烈歡迎我的加入。
雖然后來我離職了,但我非常懷念那段快樂的時光,我至今仍與他們保持著聯系。我覺得自己很幸運能有機會與他們合作,并不是每個團隊都能彼此信任,主動承擔責任。所以,我認為,快樂地一起共事,優雅地歡迎新人加入,這樣的團隊更有彈性,更有效率。
信任的重要性
長期以來,我一直在思考“信任”這個詞(這是我們公司的第一核心價值)。雖說這是一個簡單的詞語,對不同的人來說有著不同的含義,但我發現彼此信任的團隊會更加團結,相處融洽,積極溝通,并超越其他團隊。信任有多種不同的形式:
- 愿意表現出脆弱,承認自己不知道;
- 能夠傾聽某人的心聲,不會被打斷;
- 能夠學習新技術,相信投入的時間不會白白浪費;
- 沒有太多微管理。
- 在相互協作的工作環境中,信任以及沒有微管理對團隊的工作流程有著很大的積極影響。
信任與摩擦是相互對立的。彼此信任的團隊可以將社交互動的復雜性降到最低,整個團隊就像一臺運轉良好的機器。團隊成員之間會為彼此的成就而感到高興,而不是相互嫉妒。
只有對團隊或個人有足夠的信任,權力才能被下放。成功的團隊可以由具有許多差異的人組成,只要他們相互信任,而且能夠在工作中存同求異。這不禁讓我想起了一位老同事,雖然在工作之外他的性格和興趣與我有著天壤之別,但我們依然能夠愉快地共事。志同道合的一群人在一起工作固然好,但天南海北的人們聚在一起,為了某個共同的目標而團結努力也不失為一段佳話。我們有著共同的目標,而且與企業的價值觀一致,這是高效團隊充滿活力的因素之一,即便:
- 需求不斷變化;
- 待完成的工作不斷積累;
- 有很多錯誤需要修復;
- 每個人都面臨著工作和生活上的巨大變化。
但我們依然會為著崇高的目標而努力,能夠幫助我們抵達彼岸的正是信任。
指導的重要性
適當的指導對項目的成功也有一定的輔助作用。經驗豐富的人可以通過指導的方式將知識傳遞給新人。在實踐中,良好的指導是雙向的,因為雙方都可以得到成長。我很幸運,在工作中遇到的第一位老師非常了不起。在我看來,提供技術指導的組織更有彈性,因為他們的團隊有更大的共享機構知識的能力。
最關鍵的是,導師與管理有很大的不同,讓經理擔任導師并不是一個明智的選擇,因為導師的角色與經理的角色不同。導師會給你介紹瀏覽應用程序中的概念和案例,還可以看看實現這些概念的代碼。如果經理有時間和精力擔任導師,而學生也愿意,那么也可以讓同一個人擔負起兩個角色。但是指導與管理的性質完全不同,并非每個經理都適合指導他人。同樣,也并非每個新人都適合由經理指導。
學習的過程分為兩種:第一,通過不斷犯錯成長(你的代碼會出bug,或者編寫出的代碼并不能按照預期運行);第二,通過觀察別人學習。指導可以讓我了解決策背后的批判性思維,從而更快速地前進。
在工作中,我也曾有幸指導幾位優秀的初級工程師。我非常喜歡結對編程,因為我們可以在這個過程中通過指導學習到更多知識。作為一個團隊,我們之所以成功,是因為我們能夠探索有趣的模式或軟件架構,分享學習成果的過程能夠促使我們改變。下面,我來講述一個結對編程的例子:
type Data = {fieldOne: string;fieldTwo: string;// etc, other properties};type DataOverrideConfig = {fieldOne?: string;fieldTwo?: string;// etc, other properties};const updateBasedOnConfig = (data: Data, config: DataOverrideConfig) => {data.fieldOne = config.fieldOne ? config.fieldOne : data.fieldOne;data.fieldTwo = config.fieldTwo ? config.fieldTwo : data.fieldTwo;// etc ...};
我和同事在一次偶然的機會中發現了這段代碼,當時我們打算將幾個不同的代碼路徑合并到一個類中,而該類負責處理數據的配置變更。我們只是打算將函數移動到同一個類中,并沒有打算從根本上改變代碼。然而,在看到上面的兩個三元組時,我忍俊不禁,想借此機會展示如何修改這段代碼才更有意義:
const updateBasedOnConfig = (data: Data, config: DataOverrideConfig) => {if (config.fieldOne) {data.fieldOne = config.fieldOne;}if (config.fieldTwo) {data.fieldTwo = config.fieldTwo;}// etc ...};
注意這段代碼本來不是用JavaScript編寫的(否則可以用更為冪等的方式來實現這個功能)。我對變量讀取進行了修改,當配置覆蓋中的某個字段不存在時,避免不必要的寫入操作:
- 第一段代碼中定義的三元組的意思是:“無論何時都必須設置此屬性,即使沒有配置覆蓋,也要用該屬性的原始值寫入”。
- 加上if語句后,這段代碼的意思是:“如果配置覆蓋包含匹配的屬性,則使用它來覆蓋數據的現有值”。
后者更符合業務規則的描述,我們可以回顧一下為什么提高代碼的可讀性對每個人都有好處。
正確的指導可以讓新人迅速成長,并提高他們的自學能力。我有幸在自己的職業生涯中遇到過多位優秀的導師,在此我想向他們表示感謝。
疫情期間的相互協作
疫情期間,每個人的工作和生活都發生了巨大的變化,這一時期的團隊合作尤為重要。各個公司對科技人才的需求達到了空前的高度,再加上他們也意識到遠程辦公不僅能讓開發人員隨時隨地工作,而且人才庫也不會受地域的限制,擴大了幾倍。當然,所在城市也不再是影響薪資待遇的重要因素,因為開發人員有可能居住在城市之外,或者住在生活成本更低的小城市里。疫情期間人們的思想也發生了變化,大家都希望各個公司能夠重視人才,而不是他們所在的位置,每個人都希望回到自己的家鄉或居住在其他城市,然后通過遠程辦公。
能夠不受地域的限制,自由選擇工作和居住的城市,員工就會更有動力,更有追求成功的欲望。《Accelerate》一書中也提到:
……擁有高度信任工作環境的公司的股票表現優于市場指數三倍……
如果員工能夠全身心地投入工作,保持愉快的心情,而且有能力做到最好,那么每個人都會受益。此外,心理健康專業人士還指出,在疫情期間,由于工作中的摩擦以及家庭心理的綜合影響,心理健康服務的需求也達到了空前的高度。這是一個持續的變化,正在不斷滲透我們的文化和價值觀。雖然我不知道疫情究竟對我們的辦公室文化和個人的生活方式帶來了怎樣的影響,但我們知道:
- 人們都在談論“視頻會議疲勞”。
- 雖然許多軟件開發人員喜歡單獨工作,但他們也是辭職大潮中重要的一部分。
與以往相比,似乎如今的我們更加無法“忍受”企業價值觀與自己價值觀之間的差異。我們對工作越來越沒有耐心,越來越挑剔。再加上強烈的與世隔絕的感覺,很容易理解為什么這個時期辭職的人數創下了記錄。
我們虛需要保持積極的心態。雖然我與有些團隊成員從未見過面,但我們每天都在視頻通話,我對他們一點都不會感到陌生,工作中總是充滿了歡聲笑語。
這種感覺很重要,這一切都建立在信任之上。我們已經成功地發布了很多功能,還曾在兩個沖刺內啟動了兩個項目。有時,我們的壓力很大,有時還不得不連夜修補程序。雖然很辛苦,但我很高興加入了這個團隊,
重視“改善”
“Kaizen Focus”中的Kaizen來自日語,寫成漢字就是改善,意思是持續改進的過程。這是敏捷框架從日本制造業中汲取的一個思想。在實施敏捷方法時,這是一個常見的做法,即讓團隊在每個沖刺確定一個可以幫助他們加快行動速度的改善事項。我與一位朋友曾經感嘆道,我們花費在維護代碼上的時間甚至超過了編寫代碼的時間,所以說通過“改善”降低維護的難度是如此重要。說到這里,我想到了以下兩個問題:
- 代碼在編寫完成后,就會立即進入維護模式(還有一種說法“技術債務不僅僅來自繼承的代碼,就連你剛剛編寫完的代碼也是技術債務”)。
- 讀代碼比寫代碼難。
我們想一想成功的項目與失敗的項目之間的區別。有些項目之所以成功,并不是說一開始編寫的代碼就很出色,之后就再也沒動過。相反,這些代碼經歷了許多次迭代。換句話說,成功的項目得到了良好的維護。
結束語
我前后花了一個月的時間來撰寫這篇文章,在此過程中我反思了個人職業生涯中積極和消極經歷。我認為,在某種程度上,我們所有人都在尋求“希望看到的改變”,但是在錯誤的公司、項目和/或團隊工作會讓人感覺深陷泥沼,不堪重負。
至于如何提高個人或團隊編寫的代碼的質量,我認為我們應該保持積極的心態,為團隊做出有意義的貢獻。反思我們在軟件工程方面哪些做對的事情和做錯的事情,也許就是一個很好的起點。
原文地址:https://www.jamessimone.net/blog/joys-of-apex/the-life-and-death-of-software/
版權聲明:本文內容由互聯網用戶自發貢獻,該文觀點僅代表作者本人。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如發現本站有涉嫌抄襲侵權/違法違規的內容, 請發送郵件至 舉報,一經查實,本站將立刻刪除。