幹了10年軟件工程師,我學到10個教訓

當我作爲一名年輕工程師,我聽到上述建議。我在科技行業和硅谷已經打拼十年。這十年,我一直在高速增長的創業公司工作,經歷了與此相關的所有起起落落。從構建 nextgen 電子郵件客戶端,到在全球範圍內推廣電動汽車,再到網上購物結賬,我學到很多。

當回想過往,我覺得有些錯誤完全可以避免。在本文中,我將分享我在職業生涯中所學到的全棧工程師經驗。從中,我總結了十大經驗教訓。我相信,這些經驗教訓值得起時間考驗,並在未來幾年裏依然適用。

這份列表從前端開始,然後是後端 API 和數據庫,最後是工程最佳實踐 / 流程。

經驗總結

  1. CSS Specificity
  2. 組件層次結構中的設計狀態
  3. 後端編程中的麪條式代碼、千層餅式代碼和餛飩式代碼
  4. 生產中的 Postgres
  5. 欲速則不達
  6. 投資自動化
  7. 掌握你的工具
  8. 最小可行性產品(Minimum Viable Product,MVP)
  9. 研究支持開發
  10. 科學調試

1. CSS Specificity

錯誤:我的 CSS 不適用。我要用 !important

教訓:應該爲特殊情況保留使用 !important,因爲它們破壞了整個 CSS 層次結構,並強制使用特定樣式。所以,你有必要了解 CSS Specificity。

CSS specificity是瀏覽器應用的一組規則,用來確定哪個 CSS 樣式更specific。你可以將其視爲基於點的系統,它決定哪種 CSS 樣式獲得優先權,並最終應用於 DOM 元素。

如果你想知道,爲什麼你的 CSS 沒有被應用,這與 CSS specificity有關。在大型項目中,這是一個很常見的問題,在這類項目中,像 SCSS 這樣的預處理程序與複雜的 CSS 層次結構一起使用。瞭解 CSS specificity將有助於你保留使用 !important,僅在極罕見的覆蓋情況下使用,例如,當你想要覆蓋 CSS 庫或讓 iframe 覆蓋主機站點樣式時。


本質上,優先順序是這樣:ID selectors > Class selectors > Type selectors。!important 和內聯 style 屬性會覆蓋所有的 CSS。

對於應用於元素的每個 CSS,你都可以輕鬆確定哪種樣式將生效。例如,如果你加載上面的 HTML 代碼:

在本例中,ID selector優先級高於type selector。如果衝突的 CSS 選擇器具有相同的優先級,那麼將選擇 CSS 文件中的最後一個。

最後,Chrome DevTools將會顯示如上圖所示的specificity順序。如果你無法使用 CSS,請查看 Chrome 使用的specificity順序,然後添加一個更具體的選擇器(如 ID 選擇器、類選擇器、類型選擇器),讓你的 CSS 更特定,並指示瀏覽器選擇它。

如果你不想這麼做,請看看這個 specificity計算器

2. 組件層次結構的設計狀態

錯誤:我需要添加這個新狀態。我只需要將它放到這個 reducer 中,但不確定爲什麼這個 reducer 還有這麼多其他狀態?

教訓:管理不當的 redux 狀態會在開發人員中造成混亂,並導致 bug。如果用 react 和 redux 來構建前端應用程序,那麼你可以考慮使用這種可視化技術,從用戶界面組件層次結構中構建狀態和 reducer 層次結構。從零到統一的組件狀態層次結構有三個步驟:

  • 在線框圖(wireframe)將用戶界面進行可視化。
  • 將狀態層次結構進行可視化以反映用戶界面。
  • 構建 reducer 層次結構以反映狀態層次結構。

讓我們來看一個例子。在這個例子中,我們構建一個博客網站,它有兩個頁面,一個頁面用於博客列表,另一個頁面用於個人博客:

第一步:在線框圖中將用戶界面進行可視化


主頁和個人博客頁面

第二步:將狀態層次結構進行可視化以反映用戶界面

相應的狀態層次結構圖如下所示:

狀態層次結構

請注意,公共 Header 狀態是如何被拉到根狀態的。類似的,任何共享狀態都可以在層次結構中“冒泡”,因此很明顯,子組件共享該狀態。

第三步:構建 reducer 層次結構以反映狀態層次結構

reducer 層次結構

這是一個簡單而強大的例子,展示瞭如何構造狀態和 reducer 層次結構來匹配用戶界面。這一過程可以輕鬆擴展到複雜的應用程序和大型團隊。最後,你可以在這個結構上構建操作和表示層。

3. 後端編程中的麪條式代碼、千層餅式代碼和餛飩式代碼

錯誤:這個代碼庫是如何組織的?也許我可以在這裏添加這個文件,就像所有其他倉庫代碼一樣。

教訓:我寫過三種“意大利麪條式代碼”。說實話,我覺得在“餛飩式代碼”裏放上“迷你千層餅式代碼”也許是個好辦法。組織和培訓所有開發人員以這種方式構建代碼庫,可以保持代碼的可維護性、可測試性,最重要的是能保持敏捷性。你能在不影響其他功能的情況下,輕鬆修改特定的“餛飩式代碼”(也就是功能)的實現細節。

“千層餅式代碼”是:

  • 分層架構
  • 架構整潔
  • 外層是 I/O,內層是純數據結構
  • 依賴關係向內注入
  • 內層不依賴於外層
  • 優先考慮組合,而非繼承

“餛飩式代碼”是:

  • Screaming architecture
  • 切片與分層
  • 文件夾和文件的空間位置
  • 可以是微服務形式

將它們放在一起,就能得到一個可擴展、可維護的代碼庫。如果你可以想出一種方法,通過特徵名來組織文件夾,並且在每個特徵中實現整潔結構,這將讓你使用很長時間。

4. 生產中的 Postgres

錯誤:爲什麼這個查詢速度這麼慢?我認爲是因爲 Postgres 很慢的緣故。我需要切片,或者我認爲這是 ORM,或者,我需要一個不同的數據庫,Postgres 並不適合我。

教訓:如果你在生產環境中運行 Postgres,那麼你將會遇到查詢緩慢、表鎖、無限等待遷移和錯誤的問題。如果不是這樣,那麼對你有好處,你又是怎麼做到的?這並不意味着 Postgres 不再是正確工具,而是意味着你需要揭開帷幕,看看下面發生了什麼。

截至目前,我發現的最好工具是 pgbadger。你可以用它解決幾乎所有的 Postgres 問題。

這是一個 Perl 命令行工具,它將 Postgres(如果你使用 AWS 的話,就是 RDS)日誌作爲輸入,並輸出報告。該報告的好壞取決於你在 Postgres 上啓用的日誌。因此,在第一步時你可能需要啓用這些日誌:

log_checkpoints = on
log_connections = on
log_disconnections = on
log_lock_waits = on
log_temp_files = 0
log_autovacuum_min_duration = 0
log_error_verbosity = default
log_min_duration_statement = 1s

此外,你可能還需要啓用 pg_stat_statements 語句來實時分析查詢,並啓用 auto_explain 來自動解釋日誌中運行緩慢的查詢。

運行報告:

pgbadger --prefix '%m %u@%d %p %r %a : ' /pglog/postgresql.log

該報告將彙總數據,並提供有關 Postgres 所做工作的大量信息。你可以找到關於錯誤、最慢查詢、等待最長查詢、獲取的鎖類型、臨時文件是否用於排序、檢查點運行的頻率、真空運行的頻率以及其他類似信息。有了這些數據,你就可以識別和修復運行緩慢的查詢,並通過調優提高 Postgres 的性能。

你可以持續運行此報告(CLI 支持增量模式),從而隨時掌握新問題。

另外,如果你想理解解釋輸出,可以用這款工具

該工具將解釋 JSON 和原始查詢作爲輸入,並將在如下所示的可視化樹形圖中對解釋輸出進行解釋:

正如你所見,節點將有最大、最慢、最貴等標籤。這將幫助你根據 Postgres 的執行方式來優化查詢。

最後,如果在 Postgres 中建立能力不可行,我建議你問問像Percona這樣的數據庫諮詢公司。

5.欲速則不達

錯誤:看起來不錯嘛,讓我們交付吧!

教訓

在這個快速行動、打破常規的時代,這似乎不是最受歡迎的建議,但是“慢慢來”會有回報的。與其快速發佈糟糕的代碼,還不如有條不紊地來,發佈幾乎沒有生產 bug 的代碼。

優秀工程師會考慮到軟件系統的所有問題

他們不僅關心代碼覆蓋率,還關心可能破壞相同代碼路徑的奇怪輸入。通過分層架構,它們可以實現模擬層,並只測試所考慮的層。他們不僅實現單元測試,而且還實現了集成和功能測試。如果你的團隊還有 QA 工程師,就與他們一起測試這些用例。

子曰:“無慾速,無見小利。欲速則不達,見小利則大事不成。”

6. 在自動化上投資

錯誤:我們手動部署到 staging 和沙箱,是臨時的。生產也是手工部署,但每天執行一次。

教訓:擁有一個 CI/CD 系統管理部署意味着更可預測的結果。軟件以促銷策略在pipeline中移動,而臨時部署則被降級到特殊情況下。這可以確保你正交付軟件的穩定性和可靠性,這是工程團隊的主要責任。

投資:

  • 培訓團隊成員學習如何進行代碼審查。你的團隊成員可能有各種各樣的技能,但不是每個人都知道如何進行更好的code review。因此,你應在學習和教授code review的最佳實踐上進行投資。
  • 使用像peril和hound這樣的自動化code review系統。Peril 可以檢查代碼更改,並根據預先配置的設置,標記警告和構建失敗。例如,如果數據庫遷移文件缺少 statement_timeout 或包含不必要的 DEFAULT NULL,則pull request可能失敗。你可以編寫許多這樣的檢查和特定於團隊的規則,並讓 Peril 成爲更改的“看門人”。HoundCI 可以做類似的事情,而且規則是完全可配置的。

  • 使用CircleCI之類的工具,通過自動推廣策略設置 CI/CD pipeline。隨着時間推移,優化構建和部署管道。

7. 掌握你的工具

錯誤:我需要找到這個實現接口,先搜索一下。我記得它以前就在這個文件夾裏,現在卻不見了?在那個文件夾裏找找,我還是問問別人吧!

教訓:不知道如何使用你的工具,這會讓你效率變低。你能想象一個邋遢的裁縫使用縫紉機的樣子嗎?這不僅關係到代碼結果,還關係到構建軟件的效率。

瞭解你的工具,掌握捷徑。代碼編輯器可能是你要掌握的第一個工具。你應該知道如何設置選項卡的排序、打開最後的編輯文件、顯示調用圖等。如果你使用基於文本的編輯器,而不是圖形用戶界面,這也可行。類似Vim這樣的編輯器,有很多實用技巧。

請注意手動執行的常見操作,並學習如何通過快捷鍵來執行這些操作。要做到這點,一個簡單的方法是先記住5 條捷徑,並熟練掌握它們,然後再記住 5 個捷徑。

全棧工程師每天接觸的其他常用工具有終端、docker、tableplus/pgadmin/ 一些其他數據庫客戶端用戶界面、Chrome 開發工具等。

8. 最小可行性產品(MVP)

錯誤:我覺得這個功能會很有用。我要使用分佈式容錯複製高可用數據存儲。我還要構建一個基於插件的架構,使這個軟件具有超強的可擴展性。

教訓:在構建某個東西前,請確保它是你要構建的正確的東西。這就是MVP的用武之地。

理想的最小可行性產品(Minimum Viable Product,MVP)應該儘可能少地觸及所有層面,而不僅僅是一個層。這是降低風險的一種做法。最好是最低限度地構建所有層,而不是完善單個層。最小可行性產品並不意味着技術債、糟糕的代碼或缺乏測試。它不是拋棄型代碼(throw-away code)。

如果最小可行性產品花的時間太長(在某種程度上),那麼它就有可能是錯誤的方案,並且可能有更簡單的解決方案。

奧卡姆剃刀:在其他一切同等的情況下,較簡單的解釋普遍比較複雜的好。

9. 研究支持開發

錯誤:我(工程師)認爲這是我們應該構建的。

教訓:在開發前,應該先進行大量的研究以佐證。與其跟隨你的直覺,不如進行一項用戶研究。要了解用戶需求,可以親自或者通過視頻採訪,進行調查、查看日誌等。這將幫助你更好地瞭解用戶。然後,你可以提出一個假設並進行實驗。當形成一個假設時,請使用反演來駁斥自己的主張。

在一個 A/B 測試框架上投資,可以讓你進行實驗。

時間寶貴,要明智使用。最聰明的工程師會嘗試優化一些不應該存在的東西。儘早提出正確問題非常重要。

10. 科學的調試

錯誤:有一個 bug。我想是因爲代碼改變所致。讓我看看這個文件。沒準也許是內存問題所致。或者這兩個原因都有可能。

教訓:作爲一名工程師,無論是作爲事件一部分,還是在本地環境中,你都將調試軟件中的問題。如果不是通過結構化推理來完成的話,調試可能會非常痛苦、緩慢。

我們如何系統找出程序失敗的原因?如果沒有“直覺”、“敏銳思維”等模糊的概念,我們又該怎樣才能做到這一點?我們想要找到一種查找失敗原因的方法——這種方法:

  • 不需要先驗知識
  • 是有系統的
  • 我們可以確定找到根本原因,並隨意複製

將科學方法應用於調試問題,是發展失敗理論的公正方法。科學調試的步驟如下:

  1. 重現錯誤(通常是一些時間、數據、用戶、操作系統、調試器的組合)
  2. 觀察事實(徹底讀取日誌、錯誤跟蹤等)
  3. 在日誌中明確地陳述假設,而不是在心裏做假設
  4. 如果你發現程序中的某部分存在錯誤,使用結構化方法來縮小錯誤範圍,如二分搜索
  5. 測試假設:使用日誌、斷點、斷言
  6. 如果通過驗證,應用修復並確保沒有新錯誤
  7. 如果無效,請重複步驟 3 到步驟 6

對簡單的調試情況來說,這可能看起來有點過頭。但是,對於涉及多個團隊的複雜分佈式系統而言,一個系統科學的調試過程爲消除模糊性提供必要的結構。

額外福利

1. 分享知識,服務他人

優秀的行爲是幫助他人成長。當你需要用別人能理解的方式來解釋某事時,你對事情的理解會更清晰。

每天在 Slack 上分享有思想的鏈接,進行演示、稱讚他人的積極行爲,挑戰不明確的決定,並在你希望與某人或某項決定有着不同的方向時,給予建設性的反饋。你可以使用“感謝 ABC……希望 XYZ”的句式表達你的反饋。

通過這樣做,你可以爲自己打造個人品牌,從而獲得職業資本。研究表明,那些擁有強大個人品牌、網絡形象和幫助他人的記錄的人,會取得成功,更重要的是,還會擁有令人滿意的職業生涯。

2. 塑造自己的世界

你無需接受這一現實世界。你可以通過坐在駕駛座上,塑造你所感知的世界。

這可能意味着在設計討論和 code reviews期間發表意見,或者修復關鍵的不穩定的測試(flaky test)。很多人會告訴你要多發言,提高知名度,以便在公司內部發展,但他們卻從來不告訴你怎樣才能做到這一點。要做到這一點,最好的方法是擁有堅定的觀點和信心,將人們拉向你的方向。不要畏懼組建小型團隊來構建 / 改進事物。不要屈服於你的恐懼。要大聲說出來,只要不是無禮的,你都可以說出來。

負面情緒是改變的巨大動力。如果有問題讓你感到困擾,你要捫心自問,並想出該如何進行改變。如果你將每一天都當作成長的途徑,那麼生活就會成爲一種鍛鍊。

3. 結識朋友

如果你像我一樣,想弄清楚什麼對你真正重要,那就去結識許多朋友吧,尤其是那些讓你感興趣的人。這可能意味着去參加會議、參加在線社區或者在黑客鬆活動和項目上進行合作。這種接觸會幫你弄清楚你想要做什麼。這樣,你就可以對那些無關緊要的事說“No”,並抓住對你來說重要的那些機會。

許多成功人士感到幸運,宣稱只是因爲他們在一個正確的時間和正確的地點做了正確的事情而已,從而知道自己想從什麼開始。這讓他們能隨機應變,抓住機會,減少遺憾。遇到聰明人時,不要輕易拒絕。

通過這樣做,我發現我更喜歡的是廣度而不是深度,更看重創造力和自由,喜歡多樣化和非正式的關係。我不適合從事結構化的重複性工作、例行公事、穩定和安全的事情。

如果你知道你想要什麼,世界會給你所需的信息。

全棧編程充滿樂趣。這是一個不斷髮展的領域,有一片學習衝浪的海洋。不要把自己或錯誤看得太重。分享它們,不斷成長。

作者簡介:

Pranay Suresh,工程師,供職於 Bolt 公司。曾在 Tesla 供職,硅谷創業者。他是佐治亞理工學院(Georgia Tech)計算機科學碩士,同時也是博主、演講者和導師。個人主頁

英文原文:

10 FullStack Engineering Mistakes to Avoid

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章