入行 14 年,我還是覺得編程很難

infoq 發佈 2024-05-02T07:41:43.379125+00:00

2020 年,我在公司小組內做了一個分享,當時的 PPT 標題是《編程十年後的十個感觸》。如今 3 年過去了,我總算是兌現了自己的承諾。

作者 | 朱雷

策劃 | Tina

2020 年,我在公司小組內做了一個分享,當時的 PPT 標題是《編程十年後的十個感觸》。將資料分享在內網後,有位同事看到,評論說光看 PPT 不過癮,希望我能將其擴展成一篇文章,我回覆說沒問題。如今 3 年過去了,我總算是兌現了自己的承諾。當時的 PPT,最後一頁,我用純白色背景給出了一行黑體大字:「十年很短,編程很難」。如今,第二個十年也已快行至中途,而這句話的後半部分好像對我仍然適用。


很多年前,當我還是一名學生的時候,偶爾也會點開一些「高級工程師」的招聘帖,這些帖子裡,寫著讓人眼花繚亂的技術名詞,但讓我印象最深的是常出現在第一行的崗位年限要求:「本職位要求:工作經驗 5 年以上」。作為一隻一天班都沒上過的小菜鳥,這些年限要求在我眼裡簡直長到誇張。不過,望洋興嘆之餘,我有時也會在心中暗暗憧憬一下:「五年工作經驗的程式設計師,那該多厲害啊?寫代碼對於他們來說,是不是像吃飯一樣簡單?」


時光荏苒,一晃十幾年過去了。如今回頭一望,自己也成了一名有著 14 年工作經驗的光榮打工人。在軟體開發行業摸爬滾打這些年後,我發現很多事情,與我在大四時所想像的大不相同,比方說:


  • 隨著經驗增長,編程並不會變簡單太多,「像吃飯一樣簡單」只出現在夢裡
  • 給許多「大項目」寫代碼不光沒意思,還很危險,遠不如在 LeetCode 上做一道算法題有趣
  • 只從技術角度思考問題,成不了好程式設計師,有些東西遠比技術更重要


細想起來,這類關於編程的感觸還有許多。我整理了其中 8 條,寫成了這篇文章。如果其中某些觀點引起了你的共鳴,我會非常高興。


1. 寫代碼很簡單,但寫好代碼很難


編程曾經是一項門檻很高的專業技能。從前,一個普通人想學編程,最常見的做法就是通過教材和書本學習。不過大部分編程專業書,十分艱深晦澀,對於初學者來說很不友好。因此不少人在嘗到編程的樂趣前,就早早地半途而廢。


但如今,學編程正在變得越來越容易。學習不再像以前那樣,只能硬啃書本,而是多了許多新途徑。觀看教學視頻、參加 Codecademy 的交互式課程,甚至直接在 CodeCombat 通過玩遊戲來學編程,每個人都能找到適合自己的學習方式。



「媽,我真沒在玩遊戲,我在學編程呢!你看屏幕右邊!」


此外,程式語言也在變得越來越易用。經典的 C 和 Java 不再是大多數初學者的首選,許多更簡單、更易上手的動態類型語言如今大受歡迎,與之相關的 IDE 等工具也變得越來越完善。這些因素進一步降低了編程的學習門檻。


總而言之,編程早已褪去了它的神秘面紗,從只有少數人才能掌握的神秘技能,變成了一門人人皆可學習的普通手藝。


但更低的學習門檻、更友好的程式語言,並不意味著人人都能寫出一手好代碼。如果你已經工作,參與過一些項目,那我很想問你一個問題: 」你日常接觸的這些項目的代碼質量如何?是好代碼多,還是爛代碼多?」


不知你會怎麼回答,我先來說說我的答案。


好代碼還是很少


2010 年,我跳槽到了一家總部位於北京五道口的大型網際網路公司。


加入這家公司前,我只在十人規模的小公司待過,因此,我對新公司在各方面都有著很高的期待,尤其是軟體質量方面。當時,我心裡想的大概是這樣:「這可是支撐了有著千萬用戶量的產品的『大』項目,代碼質量跟之前那些比,肯定有質的飛躍吧!」


等到在新公司工作了一周後,我才發現自己實在是錯得離譜。所謂「大」項目的代碼質量同我的預期相去甚遠。打開 IDE,數百行的函數和神秘的數字字面量比比皆是,開發任何一個小需求都難如登天。


後來,在待過更多公司,接觸了更多軟體項目後,我總結出一個道理:不論公司多大、項目多牛,在實際工作中遇見好代碼,仍然是小概率事件。


好代碼有哪些要素?


話說回來,到底怎樣的代碼才算是好代碼?在這方面,Martin Fowler 有一句話常被大家引用:


「Any fool can write code that a computer can understand. Good programmers write code that humans can understand.」


「任何傻瓜都能寫出計算機能理解的代碼。優秀程式設計師寫人類能理解的代碼。」


我認為它可以作為評價好代碼的原點:好代碼一定是可讀、易讀,且容易理解的。寫出好代碼的第一原則,就是把人類讀者放在第一位。


除了可讀性以外,評價代碼好壞還有許多其他維度:


  • 貼合程式語言:是否使用了當前程式語言的推薦寫法?語言特性和語法糖,使用程度是否恰到好處?
  • 易於修改:代碼設計是否考慮了未來的需求變更,當變化發生時,代碼是否容易隨之修改?
  • API 設計合理:API 設計是否合理,易於使用?好的 API 在簡單場景下使用方便,在高級場景下又可以隨需求擴展。
  • 性能夠用:代碼性能是否滿足當前業務需求,同時為未來保留了一定提升空間?
  • 避免過度設計:代碼是否存在過度設計、過早優化的毛病?


總而言之,對於任何層級的程式設計師來說,好代碼都不是什麼唾手可得的東西。要寫出好代碼,需要在許多維度上反覆權衡、精心設計,最後再加以持續打磨。


既然如此,假如想儘快掌握寫代碼這門手藝,有捷徑嗎?


寫好代碼的捷徑


在許多層面上,我認為編程和寫作非常相似。二者都是使用文本和符號來表達思想,只是方式略有不同。


談到寫作,我想問一個關於作家的問題:「你聽說過不讀書的作家嗎?你有沒有聽到過某位作家說,他從來不讀其他人的作品,只讀自己的東西?」。我猜答案應該是否定的吧。


如果你去查閱相關資料,你會發現許多職業作家的日常生活,就是閱讀和寫作兩件事在不斷循環。他們每天會花大量時間閱讀各類文字,然後再寫作。


同樣是「文字工作者」,程式設計師們就很少重視閱讀。但要想快速提升編程能力,閱讀正是不可或缺的重要一環。除了日常工作接觸到的項目以外,我們應該更多地閱讀那些經典軟體項目,從中學習 API 設計、模塊架構和代碼編寫的技巧。


不光代碼和技術文檔,最好再定期讀一些計算機方面的專業書,保持閱讀書籍的習慣。在這方面,我認為 Jeff Atwood 在 15 年前寫的文章 "Programmers Don't Read Books -- But You Should(都說程式設計師不讀書——但你應該讀)",如今讀來仍不過時。


提升編程能力的捷徑,就藏在「閱讀 <-> 編程」這個無盡循環里。



「一個好的程式設計師應該做什麼?」


2. 編程的精髓是「創造」


在程式設計師的日常工作中,有很多事情會讓人充滿成就感,甚至情不自禁地感嘆「編程真美好」。比方說,修復了一個極難定位的 Bug,用新算法將代碼性能提升了一倍,等等。但在所有的這類事情當中,沒有任何一件,能和「親手創造出一件東西」相比。


當你在編程時,創造新事物的機會實際上隨處可見。因為並非只有發布一個新軟體,才稱得上是「創造」。寫一個可復用的工具函數、設計一套清晰的數據模型,全都可以歸入「創造」的範疇。


身為程式設計師,保持對「創造」的熱情至關重要。因為它可以幫我們:


  • 更高效地學習:學習一門新技術,最高效的方式就是用它開發一個真實項目,在創造的過程中學習,效果最好。
  • 有機會邂逅了不起的東西: 許多改變世界的開源軟體,最初都是作者純粹出於興趣所創造,比如 Linus Torvalds 和 Linux,Guido van Rossum 和 Python。



1989 年的聖誕假期,荷蘭人 Guido van Rossum 敲下了 Python 語言的最初幾行代碼,Python 最初僅被期望作為 ABC 語言的繼承者,但後來「吞噬」了全世界


雖然「創造」好處多多,程式設計師們也有大把機會去做,但許多人常常缺少一種身為「創造者」的覺悟。就像那個廣為流傳的小故事所說:一位哲學家詢問正在砌磚的工人,有人清楚地知道自己是在建造一座大教堂,有人卻認為自己只是在砌磚。很多程式設計師正是「只見磚塊,不見教堂」。


將自己定位成創造者後,看待事物的方式就會發生天翻地覆的變化。舉個例子,同樣是給 API 增加報錯提示文字,創造者們就能跳出「快速完成需求就好」的思維陷阱,向前一步,追問自己一些更重要的問題:「我想為用戶創造什麼樣的產品體驗?怎樣的報錯文字,更能幫助我達成該目標?」


就像任何一個有用的編程模式一樣,「創造者思維」也能成為你的職業生涯的一道巨大推進力。因此,現在就試著問自己一個問題吧——「我的下一份創造會是什麼?」


3. 打造高效試錯的環境至關重要


我曾參與開發過一個網際網路產品,它設計精美、功能豐富,每天都有大量用戶使用。


但就是這麼一個從市場角度看頗為成功的產品,工程質量卻非常糟糕。如果你打開它的後端項目,把所有目錄翻個底朝天,都找不到任何一行單元測試代碼,其他自動化測試流程也是無從談起。而業務邏輯偏偏又十分複雜,最後,項目代碼間的意料耦合多如牛毛,開發一個新特性很容易把舊功能給搞掛。



「在忙啥呢?」 「試著修復我之前修一個問題時搞出來的問題,那問題是我之前解決另一個問題搞出來的,而那個問題又是我……」


因此,項目每次發布時,開發和產品同學全都得嚴陣以待,氛圍十分緊張。整個發布過程也很刺激,緊急回滾時有發生。一個人在這樣的環境中工作,技術成長拋開不談,心理素質肯定能得到極大鍛鍊。


編程原本是一件充滿樂趣的工作,但為這樣的項目編程,樂趣根本無從談起。究竟是什麼奪走了編程的樂趣?


理想的編程體驗≈「刷題」


LeetCode 是一個著名的編程學習網站,上面提供了許多覆蓋各個難度的編程題,大部分與算法相關。用戶可以選擇自己感興趣的題目,直接在瀏覽器上編寫代碼(支持十幾種程式語言)並執行。如果通過了全部的測試用例,則算作解答成功。



在 LeetCode 上做題


在 LeetCode 刷題很像在玩遊戲,富有挑戰性,同時也很有趣。整個做題過程,實際完美展現了一種理想化的編程體驗:


  • 關注點分離:每道題目都是一個獨立個體,同一時間內,開發者可以完全沉浸在一道題目中;
  • 快速獲得精準反饋:開發者每次調整代碼後,能通過自動化測試快速獲得結果反饋;
  • 零成本試錯:寫出的代碼語法有錯誤、邏輯有問題,沒有任何不良後果,心理負擔小。


不過,屏幕前的你很可能覺得我在說些廢話。


「不然呢?解算法題、寫小腳本,不就是這樣的體驗嗎?有啥特別值得說的?」你很可能會繼續補充道,「你知道我們公司的項目有多複雜嗎?規模超大,模塊巨多,你懂我意思嗎?每天服務 ××× 萬人,光資料庫就好幾套,消息隊列都有三種,開發起來當然要麻煩一點咯!」


確實,全世界的軟體千差萬別,開發起來不可能都像在 LeetCode 上刷題一樣輕鬆愉快。但這並不意味著,我們不應該努力改善自己身處的編程環境,哪怕只有一點點。


要通過改善環境來提升編程體驗,可用的理念和工具包括:


  • 模塊化思想:妥善設計項目中的每一個模塊,降低耦合,提升正交性
  • 設計原則:微觀層面上,應用那些經典的設計原則和模式,比如「SOLID」原則
  • 自動化測試:編寫規範的單元測試,必要時使用 Mock 技術,用自動化測試覆蓋業務關鍵路徑
  • 縮短反饋迴路:切換編譯速度更快的工具,優化單測性能,竭盡全力縮短從「改完代碼」到「獲得反饋」的等待時間
  • 微服務架構:必要時,將大單體拆分為多個職責各異的微服務,分散複雜度
  • ……


關注編程環境,刻意創造出允許高效試錯的「代碼樂園」,讓工作像刷題一樣輕鬆愉快。是經驗豐富的程式設計師能為自身團隊做出的最好貢獻之一。


4. 避開代碼完美主義陷阱


在代碼質量上精益求精是好事,但也要注意別掉進完美主義的陷阱。因為編程不是藝術創作,不鼓勵人們無限度地追求極致。作家大可花上數年打磨一本傳世之作,但程式設計師在代碼上鑽牛角尖就很有問題。


世間沒有完美的代碼。大多數時候,你的代碼只要能滿足當前需求,又為未來擴展留了一些空間就夠了。有那麼幾次,我在簡歷上看到候選人給自己打著「代碼強迫症」標籤。隔著屏幕,我雖能感受到 TA 對代碼質量的那份重視,但在我心底,其實更期望 TA 早已將完美主義陷阱遠遠甩在了後頭。


5. 技術很重要,但「人」也許更重要


在軟體開發領域,「單一職責原則」(全稱為 Single responsibility Principle,後簡稱為 SRP)是一條非常著名的設計原則。它的定義很簡單,一句話就可以概括:「每個軟體模塊應該只有一個被修改的理由」。



單一職責原則:能做到,並不意味著你就該這麼做


要掌握 SRP 原則,關鍵在於搞清楚「被修改的理由」為何物。很顯然,程序是沒有生命的,它自身不能也不需要主動去改變。任何修改程序的理由,都來自與之相關的人,人是導致修改的「罪魁禍首」。


舉個簡單的例子。看看下面這兩個類,其中哪一個違反了 SRP 原則?


  1. 一個字典數據類,支持兩類操作:存數據、取數據;
  2. 一個員工資料類,支持兩類操作:更新個人信息、渲染一張用戶資料卡片圖。


在大多數人眼裡,第一個例子沒問題,但第二個例子卻明顯違反了 SRP 原則。要得出該結論,好像無需任何嚴格的分析和證明,運用一丁點直覺即可。但假如做一些正經分析,第二個例子的可疑之處,在於能為其輕鬆找出兩個不同的修改理由:


  1. 管理員認為資料中的「個人電話」欄位不能有非法號碼,需增加簡單的校驗邏輯;
  2. 某員工認為資料卡片圖上的「名字」部分太小,希望加大字體。


」It is people who request changes. And you don’t want to confuse those people, or yourself, by mixing together the code that many different people care about for different reasons.」 ——「The Single Responsibility Principle」


「是人在要求軟體變更。你絕不想把那些不同人出於不同原因所關心的代碼混在一起,這樣只會把他們和你自己搞糊塗。」——「單一職責原則」


理解 SRP 原則的關鍵,在於先理解人以及人在軟體開發中所扮演的角色。


再舉一個例子。微服務架構是近些年很火的一個技術話題。但許多人在討論它時,往往只關注技術本身,卻忽視了微服務架構與人之間的關係。


將微服務架構風格與其他東西區分開的關鍵,在於將大單體拆分為獨立的微服務後,不同模塊間的邊界可以變得更清晰。跟數百人的團隊一同維護著一個大單體比起來,許多小組織各自維護著獨立的微服務,明顯擁有更高的運作效率。


如果缺少了特定的組織規模(也就是「人」)作為前提,空談微服務的各種技術優勢和那些花活,純屬本末倒置。


技術當然很重要。身為技術人員,那一張張瑰麗的架構圖和獨具匠心的代碼細節,天然吸引著我們的注意力。但是,也請千萬不要對軟體開發里的另一個重要因素「人」視而不見。必要時,轉換一下看事情的角度(從「技術」轉向「人」),那樣對你大有裨益。


6. 求知若渴是好事,但也要注意方法


如今人人都在說「終身學習」,而程式設計師是一個尤其需要終身學習的職業。因為計算機技術的疊代更新非常快,某個三年前流行的框架或程式語言,很可能一個月前已經過時。



一分鐘之內會發生什麼事情?Netflix 觀看時間增長 70,000 小時;Snapchat 上有三百萬視頻被觀看;Google 新增兩百四十萬次搜索;一個 JS 新框架被發明(這條不是真的 )


要在工作中表現得遊刃有餘,程式設計師們需要學習的東西非常多,涵蓋各個層面。拿我比較熟悉的後端領域舉例,一位合格的後端工程師至少需要掌握以下這些:


一種或多種後端程式語言 / MySQL 等關係資料庫 / Redis 等常見存儲組件 / 設計模式 / 用戶體驗 / 軟體工程 / 編譯原理 / 作業系統 / 網絡基礎 / 分布式系統 / …


雖然要學很多,但據我觀察,大部分程式設計師其實都挺愛學習(至少不排斥),因此心態不是問題。不過有的時候,光有「求知若渴」的心態並不夠,學習時,我們尤其需要關注「性價比」。


關注學習性價比


下面這張圖,展示了學習成效和投入之間的關係。



學習成效與投入關係圖,橫軸為學習投入,縱軸為學習成效


從圖中可以看到,在學習的初級階段,投入較少時,所獲得成效增長飛快。但當成效超過某個閾值後,之後再想繼續提升,所需要的學習投入就會呈指數級增長。


正因如此,我建議你在學習任何一項新事物時,先在腦海中想清楚一個問題:「我應該在上圖中的哪個位置停下來?」,而不是悶頭猛學。


知識的海洋浩瀚無邊,有些東西需要我們成年累月的持續學習,不斷精進。也有些東西,蜻蜓點水般學到一些皮毛已綽綽有餘。準確判斷並分配自己有限的學習精力,甚至比努力學習本身更重要。


挑選合適的學習資料


有了學習目標後,下一步就是尋找合適的學習資料。在這方面,我想分享一次自己的失敗經歷。


有段時間,我突然對產品互動設計產生了濃厚的興趣,認為自己應該在這方面有所精進。於是,我精心挑選了一本領域內非常經典的專業書:《About Face 4: 互動設計精髓》,將其買回家中,滿懷信心地認為自己的互動設計能力可以迅速獲得提升。


但事與願違,當我捧著那本經典著作時,發現自己連第一章都無法順利讀完——那句老話說的沒錯:「隔行如隔山」。


從這次失敗中,我總結出了一點經驗。那就是學習某項新東西時,我們最好挑選那些更易讀,更適合「門外漢」的學習資料,不要「眼睛大,嘴巴小」,只知道奔著最經典、最權威的資料而去。


回顧之前的經歷,我覺得以下幾本書非常適合門外漢學習使用,性價比極高:


  • 《寫給大家看的設計書》:設計相關
  • 《點石成金》:Web 用戶體驗相關
  • 《鳥哥的Linux私房菜》:Linux 系統相關


也許每個人的內心,都想成為一個博學的人,無所不知,無所不曉。但可供分配的時間的精力總是有限,我們不能,也不需要在所有領域都成為專家。


7. 越早開始寫單元測試越好


我非常非常喜歡單元測試,我認為寫單測這件事,對我的編程生涯影響極大。誇張點說,如果以「開始寫單元測試」作為分界線,把我的職業生涯分割成兩段,後面那段遠比前面那段精彩得多。


寫單測的好處很多,比如單測可以驅動你改善代碼的設計、可以作為代碼的一種文檔,等等。此外,完善的單元測試還是構建前面提到的「高效犯錯的環境」的關鍵。


我已經寫過幾篇關於單測的文章,比如《有關單元測試的 5 個建議》、《遊戲「蔚藍山」教我的編程道理》。所以在這兒,我不打算再重複一遍。只說一句:如果到目前為止,你從未試過寫單元測試,或從沒重視過測試,我建議你從明天就開始寫起來。



一般情況下我不測試我的代碼,但假如測的話,我在生產環境測


8. 程式設計師最大的敵人是什麼?


在大多數程式設計師段子裡,產品經理經常作為反派角色出現。他們口中的項目需求總是變個不停,一天冒出一個新想法,搞得程式設計師苦不堪言。



客戶每天都在不停修改需求,所以,我們決定在下次發布前,把這些需求「凍結」起來


在這些段子的烘托下,不斷修改需求的產品經理,仿佛真成了程式設計師們最大的仇敵。似乎只要產品不亂改需求,大家的工作環境馬上就會成為烏托邦。


雖然偶爾吐槽一兩句產品經理很有意思,但我還是想一本正經的說一句:產品經理不是敵人。


因為從某種角度來說,軟體生來就是準備被修改的(不然你猜,軟體為什麼叫「軟」件?)。這樣看來,開發軟體和修建房子完全不同。因為沒人會在建好一棟大樓後說:「讓我們把它推倒重建一遍吧!一樣的樓,但是用的鋼筋和水泥比之前少 30%!」


所以,產品經理以及不穩定的需求不是程式設計師的敵人。並且,能否寫出易於修改、適配變化的代碼,是區分普通程式設計師和優秀程式設計師的重要標準之一。


那麼,程式設計師們最大的敵人又是什麼呢?


複雜度是最大的敵人


就像《代碼大全 2》中所說:軟體開發的核心問題是管理複雜度。失控的複雜度就是程式設計師最大的敵人。


來看看那些導致項目複雜度不斷增長的要素:


  • 不斷增加的新功能: 更多的功能等於更多的代碼,更多的代碼通常意味著更高的複雜度
  • 對高可用的需求: 為了實現高可用,消息隊列等額外的技術組件和代碼被引入
  • 對高性能的需求: 為了提升性能,緩存和相關模塊代碼被引入,部分模塊被拆分後,換成高性能語言重寫
  • 一再被推遲的重構:因項目排期過於緊張,迫在眉睫的重構被一再推遲,技術債越積越多
  • 忽視自動化測試: 沒人寫單元測試,也沒人關心測試


終有一天,當項目的複雜度增長到一定程度後,空中會傳來一聲巨響。「咚!」,一個大家不願改、不敢改的「大坑」憑空出現在了所有人的 IDE 中。


猜猜看,究竟是誰挖下了這個坑?



那些在降低複雜度上投入時間的團隊,所負責的軟體項目更容易成功


減緩複雜度增長的過程


雖然複雜度總是會不可避免地持續增長,但有許多實踐可以減緩該過程。如果每個人都能做到以下這些事,複雜度就有可能被長期控制在合理範圍內:


  • 精通當前程式語言與工具,寫整潔的代碼
  • 使用合適的設計模式和編程模式
  • 對重複代碼零容忍,抽象庫和框架
  • 適當運用整潔架構、領域驅動設計思想
  • 編寫詳盡的文檔和注釋
  • 編寫規範有效的單元測試
  • 分離那些變動的與不變的


要求看上去很多,但總結起來,核心其實就是一句話:寫更好的代碼


作者簡介:

朱雷(@piglei),程式設計師,愛好編程、閱讀以及電子遊戲,著有《Python工匠:案例、技巧與工程實踐》一書。


本文轉載來源:

https://www.infoq.cn/article/e084f4hdSIP0chOiYojK

關鍵字: