• <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>
  • 我從資深軟件工程師學到的避坑大法

    發表于:2019-09-02來源:Neil Kakkar作者:Neil Kakkar點擊數: 標簽:
    過去一年中,我坐在一位資深的軟件工程師旁邊,可以仔細地觀察他是怎么工作的。我們兩人經常共同編程,使得這項觀察更為容易。此外,在團隊文化中,從背后窺探寫代碼的人并不

    過去一年中,我坐在一位資深的軟件工程師旁邊,可以仔細地觀察他是怎么工作的。我們兩人經常共同編程,使得這項觀察更為容易。此外,在團隊文化中,從背后窺探寫代碼的人并不令人反感。以下是我所學到的:

    編寫代碼

    如何命名

    我首先著手的是 React UI。我們有一個主要組件來放置其他所有的組件。我喜歡在代碼里加點幽默感,因此我想要將它命名為 GodComponent。當進入代碼審查環境的時候,我才明白為什么命名這么難。

    在計算機科學里有兩個難題:內存不足、命名、以及差一(off-by-one)錯誤。——Leon Bambrick

    我每個命名的代碼段都有隱藏含義在里面。GodComponent 是所有我不必費心去尋找合適位置來存放那些垃圾的地方,它可以容納所有東西。如果我早早把它命名為 LayoutComponent,之后的我就會發現它所做的就是分配 layout,沒有狀態。

    我發現命名好的另一個好處是:如果它看起來太長了,就像 LayoutComponent 包含了很多業務邏輯層,我就知道是時候要重構了,因為業務邏輯層并不屬于這里。如果是以 GodComponent 命名,這里的業務邏輯層也不會和其他有所區別。

    命名你的集群?以在服務器上運行的服務名稱來命名更好,直到用它們來運行其他服務為止。我們最終以團隊的名字來命名服務器。

    在函數上也是同樣的道理。doEverything() 是一個糟糕的名字,會有很多難以預料的后果。如果這個函數能夠做所有事情,那么在測試函數某個特定部分時將變得非常困難。因為不管這個函數有多大,你都不會覺得奇怪,畢竟這個函數應該做所有的事情。這時候就需要改名、重構了。

    有意義的命名也有不太好的一面。如果名字的表意太強,結果掩蓋了一些功能上的細微差別怎么辦?例如:當你在 SQLAlchemy 中調用 session.close() 時,這只會關閉會話但不會關閉底層數據庫的連接。

    在這種情況下,可以以 x,y,z 來命名而不是 count(),close(),insertIntoDB(),這樣可防止為其賦予隱性含義并強制開發人員仔細檢查它所執行的操作。

    歷史代碼和下一名開發者

    你曾否看過一些代碼,覺得它們很奇怪?這些代碼為什么這么做呢?它們的實現一點都不合理。

    我曾負責過遺留代碼庫。代碼中有諸如「當 Mohammad 發現情況時取消注釋代碼」這類的注釋。這是在做什么?誰是 Mohammad?

    在這里可以做下角色轉換——想象下一個人來看我的代碼,他們是否會覺得奇怪?

    同行審查可以某種程度上解決代碼注釋這個問題。這讓我想到了上下文的概念:注意我團隊正處的上下文位置。

    如果我忘記了這部分代碼,之后又回到了代碼工作上,沒有注釋的話我不能重新創建上下文,我可能只會想:「為什么他們要這么寫?這沒有任何意義……哦,等等,是我寫的?!?/p>

    這里就是開發文檔和注釋該出現的地方。

    文檔和注釋

    文檔和注釋有助于維護上下文和分享知識。

    正如李在《如何構建好軟件》中所說,「軟件的主要價值不是編寫它的代碼,而是編寫它的人所積累的知識?!?/p>

    比如說,我們有個似乎沒有人用過的、面向隨機客戶端的 API 終端。因為這些原因,我就應該把它刪除嗎?畢竟這是一個技術累贅。

    如果說,在某個特定國家,有 10 名記者會一年一次將他們的報道發送到這個終端,怎么辦?你如何測試它?如果沒有開發文檔(那時就沒有)就不能測試。所以我們沒有測試。我們刪除了那個終端。過了幾個月后,到了一年中發送的時間,因為這個終端已經不存在了,10 名記者也就無法發送這 10 份重要報告。

    雖然熟悉產品的人已經離開了團隊,但是現在代碼中有注釋解釋終端的作用。

    據我所知,文檔是每個團隊都在努力的東西。不僅僅是代碼的文檔,還有關于代碼的流程。

    自信地刪掉垃圾代碼

    我過去很不喜歡刪除垃圾代碼或過時的代碼。我認為過去寫的代碼都是神圣的。我的想法是:「他們寫這些代碼的時候肯定有一些想法?!惯@是傳統和文化與第一性原則之間的碰撞,與刪除一年一次的終端發生的事相同。我在那里學到了詳細的一課。

    我嘗試基于已有代碼進行工作,但是資深工程師會嘗試解決掉它——全部刪除。一個永遠無法到達的 if 聲明?一個不應該調用的函數?是的,都消失了。

    至于我呢?我只會把我的函數寫在最上面。我沒有減少這些技術累贅,反而增加了代碼的復雜程度,以及誤導別人的可能。下一個人將事情拼湊起來會更困難。

    現在我受到的啟發是:有一些代碼你可能不理解,也有一些代碼你知道永遠不會用。刪除那些你永遠都不會用的代碼,小心那些你不理解的代碼。

    代碼審查

    代碼審查對學習來說非常有用。這是你寫代碼和其他人寫代碼時進行的外部反饋循環。

    兩種實現有什么區別呢?一種方法比另一種好嗎?每次代碼審查時我都問自己:「他們為什么這樣做?「。每當我找不到合適的答案時,我就會去和他們談談。

    在第一個月后,我開始在同事的代碼中找到錯誤(就像他們對我代碼做的一樣)。同行審查對我來說變得更有趣了——這是我期待的游戲——一個提高我代碼意識的游戲。

    我的啟發是:在理解代碼如何實現前不要批準它。

    測試

    我非常喜歡測試,以至于如果沒有測試就將代碼寫入代碼庫我會感到非常不舒服。

    如果整個應用程序只做一件事(就像我所有的學校項目),那么手動測試是可以的。但是如果該應用程序可完成 100 種不同的功能,那該怎么辦呢?我不想花半個小時來測試所有的功能,何況有時候還會忘記一些需要測試的地方。

    所以就出現了自動化測試。

    我認為測試是一種文檔,是對代碼假設的文檔。測試會告訴我(或我之前的人)他們預想代碼是如何工作的,以及他們預期哪里會出錯。

    所以,當寫測試時,我會記?。?/p>

    1. 記錄如何使用測試時用到的類/函數/系統。
    2. 記錄我所想到的會出錯的地方。

    在大多數情況下,以上的結論是在我在測試而不是實現的過程中想到的。

    以下是我在 Google 衛生間小休時學到的例子:

    • 我在 #2 中遺漏了一些東西,那里是 bug 出現的地方;
    • 所以每當發現 bug 時,確保修復 bug 的代碼也有相應的測試(稱為回歸測試),用于記錄信息:這里可能出現另一種錯誤。

    僅僅編寫這些測試并不能提高我代碼的質量,而編寫代碼卻可以。但是我從閱讀測試代碼中獲得了寫更好代碼的直覺。

    但是,并不只有這一種測試,這就是為什么有部署環境測試的原因。

    你可以有完美的測試單元,但是如果沒有系統測試,就會出現以下的情況:

    這同樣適用于已經測試好的代碼:如果你機器上沒有你需要的庫,你會崩潰。

    為了測試你需要:

    • 有一臺你用于開發的機器;
    • 有一臺你用于測試的機器;
    • 最后,有一臺你部署的機器(請不要用與開發程序使用同一臺)。

    如果測試和部署機器之間的環境不匹配,你就遇到麻煩了。所以這里就出現了部署環境。

    • 我們先有本地開發環境,在我的機器上是 docker;
    • 然后有服務器上的開發環境,機器上安裝了一系列的庫(和開發工具),我們在安裝了代碼的機器上進行開發。其他相關依賴的測試都可以在這里進行;
    • 接下來是 beta/stage 環境,它與生產環境完全一樣;
    • 最后是生產環境,它是代碼運行和服務于實際客戶的機器上的環境。

    這里的想法是嘗試捕獲單元和系統測試無法捕獲的錯誤。例如,請求系統和響應系統之間的 API 不匹配。個人項目與小公司的情況大不一樣。不是每個人都有資源來搭建自己的設備。然而,這個想法仍適用于像 AWS 和 AZURE 這樣的云供應商。

    你可以為開發和生產設置分開的集群。AWS ECS 使用 docker 鏡像來部署,所以即使跨環境事情也會相對平穩。棘手的一點是其他 AWS 服務之間的集成。你是否可以在正確的環境中調用正確的終端呢?

    你甚至可以更進一步:下載其他 AWS 服務的備用容器鏡像并使用 docker-compose 來配置本地完整的環境。它會加速反饋循環。

    設計

    為什么我要將設計放到寫代碼和測試的后面呢?設計本應該在第一位,但是如果我沒有在環境中寫代碼和測試,我可能會不擅長設計一個遵循環境特性的系統。

    在設計系統時,有很多事情需要考慮:

    • 使用編號是多少?
    • 有多少用戶?預期增長是多少?(即需要使用多少數據行)
    • 未來可能出現的問題是什么?

    我需要把它轉成一個名為「需求收集」的合理清單。這個過程有點與靈活性的原則相悖——在開始系統開發之前,你可以設計多少部分呢?但是這是一種平衡——你需要選擇什么時候做什么。當然僅僅收集需求并不是所有需要考慮的事情。我認為,在設計中包含了開發的過程也是值得去做的。例如:

    • 本地開發如何運作?
    • 怎么打包和部署?
    • 如何進行端對端的測試?
    • 怎么對新的服務進行壓力測試?
    • 怎么管理機密信息?
    • CI/CD 集成?

    我們最近為 BNEF 開發了一個新的搜索系統。做這件事真的很棒。我開始設計本地開發,學習 DPKG(打包和部署)和試圖解決部署機密信息的問題。

    誰會想到對產品中的機密信息進行部署會變得如此棘手呢?

    1. 你不能將這些信息存到代碼中,因為這樣任何人都能看得到。
    2. 把它們作為環境變量?這是一個好主意。但你怎么把它們放在那里?(每次機器啟動時訪問 PROD 機器來填充環境變量是一件痛苦的事情)
    3. 部署為機密文件?文件從哪里來呢?怎么進行填充呢?

    而且我們不想進行手動操作。

    最后我們使用了一個有角色訪問控制的數據庫(只有我們的機器可以與數據庫對話)。我們的代碼在啟動時從這個數據庫中獲取秘密數據。這個能在開發、測試和產品之間很好地復制——在各自的數據庫中都有機密。

    同樣的,對于像 AWS 這樣的云供應商,這可能非常不同。你不必考慮太多機密。獲取你角色賬戶,在用戶界面中輸入機密數據,在需要的時候你的代碼會找到它們。它簡化了很多時間,這非???,而我很高興有經驗領會這種簡易性。

    設計時考慮維護需求

    設計系統是件令人興奮的事。維護系統呢?就沒那么有趣了。

    我在維護過程中遇到了這個問題:系統為什么會降級,以及如何降級?

    有兩個原因可以解答為什么系統也會有降級的時候:

    首先,系統不應當舍棄舊的東西,而是在已有的基礎上增加更多功能。系統更新傾向于增加而不是刪除。

    其次,帶著最終目標來設計。一個進化到做不該做的事情的系統和一個從零來設計做同樣事情的系統一樣,沒有用。這是一種系統的倒退。因此需要對系統進行降級。

    現在我知道至少三種降低降級機率的方法:

    • 將業務邏輯和基礎設施分開:通常是對基礎設施降級——當使用量增加、框架過時、出現零日漏洞等情況下;
    • 圍繞系統維護建立流程。對舊的和新的組件都使用相同的更新。這可以防止組件之間出現差異,保持整個代碼「現代化」;
    • 確保一直修剪你不想要的/舊的東西。

    部署

    將功能進行捆綁部署還是逐個部署呢?如果答案是將功能捆綁在一起,則會出現問題。

    接下來要問的問題是:為什么想要把功能進行捆綁呢?

    • 部署是否花費過多時間?
    • 代碼審查是否容易進行?

    不管是什么原因,這是需要修復的流程瓶頸。

    捆綁功能部署至少有兩個問題

    1. 如果一個功能中有 bug,將妨礙另一個功能執行;
    2. 增加整體出錯的風險。

    然后,無論你選擇什么部署過程,你總是希望你的機器像一頭牛而不是像寵物一樣。它們并不珍貴。你知道每臺機器上運行的是什么,以及如何在死機的情況下重新創建它們。當一臺機器死機時,你不會心煩意亂,你只需要啟動一臺新機器。你像牛一樣放養它們,而不是像寵物一樣養著他們。

    程序出錯的時候

    當事情出錯時,而且一定會有出問題的時候,黃金法則是將對客戶的影響最小化。

    當事情出了差錯,我自然傾向于趕快解決 bug。事實證明,這并不是最理想的解決方案。與其修復哪里錯了,即使只是「修改一行」,所做的第一件事應該是回滾版本?;氐街暗墓ぷ鳡顟B,這是讓客戶恢復工作最快的方法。

    過了這個時候,才應該看看哪里出了問題并修復那些 bug。

    在你的集群中出現一臺「垮掉」的機器也應當是同樣的做法——在試圖找出機器出了什么問題之前,先把它停了,并標記它不可用。

    首先找 bug 這種本能會引導我走上解決 bug 的漫長旅途,反而偏離了讓客戶先恢復工作這一理想的目標狀態。有時候,我覺得它沒有工作的原因是因為寫的代碼有問題,而仔細閱讀每一行代碼后會陷入混亂,像是一種深度優先搜索。

    之后,我的啟發是,首先開始廣度優先搜索,然后再深度優先搜索,去除最頂端的節點。能否用已有的資源確認:

    • 機器啟動了嗎?
    • 是否安裝了正確的代碼?
    • 配置是否正確?
    • <代碼特定配置>,像代碼中的路由是否正確?
    • 模式版本是否正確?
    • 然后進入代碼。

    在某次出錯的問題上,我們以為機器上沒有正確安裝 nginx,但結果是配置被設置為了 false。

    當然,我不需要總是這樣做。有時候錯誤信息已經足以減少需要搜索代碼的區域。而且當我無法解決這個問題時,我嘗試并持續修改代碼以將問題降到最低。修改的次數越少,我就能越快地處理實際問題。

    但是我現在還是會記錄花了 1 個多小時來解決的 bug:遺漏了什么?這通常是一些我忘記檢查的愚蠢錯誤,比如像設置路由、確保模式版本和服務版本匹配等。這是熟悉使用的技術堆棧的另一步,而且只有經驗會告訴我為什么系統無法運行。

    監控

    這是我以前從未想過去做的事。說句公道話,在全職編碼之前,我從沒維護過系統。我只是搭建它們,使用 1 個星期后然后進行下一項工作。

    有兩個系統,一個有良好的監控,另一個并不那么好。我逐漸非常喜歡監控。如果我不知道 bug 在哪我就不能修改錯誤。其中一種最糟糕的感覺是從客戶那里知道有 bug。

    「我做了什么?!我甚至不知道我的系統出了什么問題?」

    我認為監控由 3 個部分組成——日志、衡量標準和警報。

    日志

    以代碼中進行日記記錄就像人寫日志一樣,是一個進化的過程。

    你要找到你可能需要監控的東西,日志記錄下來,運行系統。一段時間后,你會發現你沒有足夠信息來解決的 bug。這是增強日志記錄的好時機——你的代碼少了些什么?

    我想你會憑直覺地知道什么東西很重要需要記錄,但是在我們的服務器中我和資深軟件工程師所記錄的東西有很多不同。我認為只要請求-相應日志就足夠了,但是他會有更多的記錄內容,比如查詢執行時間、代碼進行的一些特定的內部調用,以及何時轉儲日志。一切都已經解決了。

    幾乎不可能在沒有日志的情況下進行調試——如果你不知道系統的狀態,你怎么重新創建它呢?

    衡量標準和驚爆

    衡量標準可以源于日志,也可以獨立于日志(例如向 AWS CloudWatch 和 Grafana 發送時間)。你可以決定你的衡量指標并在代碼運行時發送數字。

    警報是把所有東西整合到一個的強大監控系統的粘合劑。如果一個衡量標準是當前產品中運行的機器數量,當這個數字降到 50% 時,這是一個很好的警報——你知道有什么出錯了。

    失敗計數高于某個閾值時?是的,又一個警報。

    這里暗示了另一個需要養成的習慣。當你修復 bug 時,你不僅僅關注如何修復 bug,而是你為什么不早點發現它呢?是否有布置警報?如何能夠更好地監控來避免類似的問題?

    我還不知道如何監控 UI。即使吧組件測試到位,也還不足以了解出錯的情況。這些錯誤通常是由客戶來告訴我們的——這看起來不太對勁。

    總結

    在過去的一年里,我學到了很多東西。當我對這篇文章進行回顧時,我能夠更好地體會到我的成長。希望你也可以從這里得到一些東西!


    原文轉自:https://neilkakkar.com/things-I-learnt-from-a-senior-dev.html#when-things-go-wrong

    老湿亚洲永久精品ww47香蕉图片_日韩欧美中文字幕北美法律_国产AV永久无码天堂影院_久久婷婷综合色丁香五月

  • <ruby id="5koa6"></ruby>
    <ruby id="5koa6"><option id="5koa6"><thead id="5koa6"></thead></option></ruby>

    <progress id="5koa6"></progress>

  • <strong id="5koa6"></strong>