Skip to content

Instantly share code, notes, and snippets.

@liuxd
Last active June 20, 2024 23:09
Show Gist options
  • Save liuxd/34ccd46d6f3b4e982d0734e27c745e33 to your computer and use it in GitHub Desktop.
Save liuxd/34ccd46d6f3b4e982d0734e27c745e33 to your computer and use it in GitHub Desktop.
[#15 網站開發心法:工程篇] #編程之道

網站開發心法:工程篇

初稿日期:2023-03-26

我們不發明輪子,我們只是輪子的使用者。

前言

所謂工程能力,就是將想法實現的能力。

它涵蓋兩種能力:快速學習並現學現賣的能力,以及在資源和需求之間尋求平衡的能力。

結果大於因果,工程能力意味著功利主義。重要的是修復問題、實現功能、按時交付。面對很多問題時,工程師也許不知道根本原因,但是知道如何將它們解決掉。至於深層原因、理論依據之類的玩意兒就無所謂了,實在沒空深究這種大話題。因為老闆不能等,市場不能等,商機不能等,等等。

基本上,就是調用已經發明好的輪子拼裝成產品。至於發明新的、更好的輪子,那是另一個境界了,在此不表。畢竟,用輪子容易造輪子難。

1 環境

上帝說:Hello, World! 於是,世界誕生了?不,在那之前要先配好環境。

程序員就是上帝,他們開發的軟件就是他們創造的世界。但要想說出第一聲“Hello, World!”也不是一件容易的事,因為搭建運行環境往往並不簡單。

1.1 組成

  • 代碼解析。程序員的代碼是給人看的,計算機看不懂。因此,一定要部署好對應的編譯或者解釋的運行環境,用來將程序代碼轉換成機器碼,從而讓機器可以將其識別並運行起來。
  • 數據存儲。所謂編程,就是用代碼來玩數據。代碼需要解析,數據需要存儲。選擇很多,怎麼選是另一個話題,但至少一定要有一個,有時候是一堆。
  • 服務對接。現代軟件開發很難自己做所有事情,免不了要用到一些第三方服務,或者自家的服務。於是環境搭建也必須把對應的服務接進來,不然有些東西沒法用。

1.2 類別

  • 開發環境。程序員自己電腦裡的遊樂場,只要程序能跑起來就行。幾乎不用擔心什麼擴展性、性能表現等問題。
  • 測試環境。給測試人員撒歡搞破壞的場所。比開發環境嚴肅一些,畢竟不只是個人的玩具了。有時候為了性能測試,甚至對硬件有要求。
  • 模擬環境。它是發佈到生產環境前的最後一站。讓新的代碼在生產環境的數據和配置中感受一下真實世界的殘酷,看是否還能保持從容。如果發現任何東西不對勁,還可以反悔,以避免將愚蠢的錯誤暴露到真實用戶面前。
  • 生產環境。最認真的環境。權限緊縮、分布式集群、全方位監控等等。這是直面真實用戶的環境,武裝到牙齒。

1.3 管理

  • 升級:操作系統升級、運行環境升級、硬件配置升級。
  • 配置:操作系統配置、運行環境配置、業務代碼配置。

升級操作和配置文件分發都需要自動化管理,而自動化的腳本連同各級配置文件都需要納入版本管理。

2 開發

造積木,拼起來。

開發,就是把業務需求抽象成數據和邏輯,並用代碼把邏輯表達出來。但是代碼多了就需要秩序,不然亂七八糟的很容易坍塌。那怎麼建立秩序呢?

2.1 封裝代碼塊

塊裝代碼相比散裝代碼好處多多。因為封裝起來後邏輯的影響範圍被清晰的界定了,所以無論是閱讀還是測試都更加安全方便,不用擔心改了一個字,報了十個錯。更重要的是,這段邏輯可以重用了,需要時調用即可,比複製粘貼環保健康。不過,代碼塊不能太大,過大的代碼塊會對開發者帶來認知壓力,理解起來格外困難。大傢伙切成職責清晰的小碎塊,就不容易噎到了。

2.2 放恰當位置

代碼塊有很多種形式,閉包、函數、方法、類、庫、插件、SDK、API、服務等等。名頭超級多,但說到底都是代碼塊。所不同的地方,除了名字以外最重要的就是適用範圍了。這個範圍就很有講究了。太大了不好,容易造成誤會,看似可以用,一用就崩潰;太小了也不好,該調用的地方反而調用不到,看著乾著急。所以,封裝代碼塊容易,如何放到合適的復用層級才是傷腦筋的地方。原則上就是放在剛好所有該調用的地方都可以調用得到的範圍最佳。當然,世異時移,未來的事情誰都說不準,必要時候可以移動。但總體來說,是遵循最小適用原則。

2.3 標準化通信

代碼塊通常有輸入和輸出,一個軟件系統就是一堆代碼塊互相輸入輸出進行通信的集合體。那麼如何通信呢?最好制定標準的通信協議。所謂通信協議就是一個關於結構和含義的約定。最直接的例子就是HTTP協議,有頭部,有主體,有狀態碼以及各碼的含義。有了協議,就有了共識,既方便理解,又方便解析。不同的層級可能有不同的協議,但不管怎樣,得有協議,讓通信標準化,而不是隨心所欲亂傳數據,產生各種莫名其妙的錯誤。

3 調試

反覆踏進同一條河。

3.1 要點

重現問題的上下文環境。相同的參數、相同的步驟、相同的配置、相同的數據。然後沿著邏輯鏈條一步步查看數據變化。每一步變化都有對應的邏輯,如果最終結果錯了,那麼一定是中間某個環節出錯了。

搜索有辨識度的關鍵字。有時候邏輯鏈條不是很清晰,因為某些代碼塊的調用方式非常隱蔽。可以考慮搜索有辨識度的關鍵字,然後在可能的地方都打上標記,看哪裡被觸發到了。哪裡顯示了,哪裡就在你要找的邏輯鏈條上。

至於查看數據的方式,打印、斷點或者日誌都可以,區別不是很大,重要的是看到數據的內容。

3.2 侷限

上下文很難完全模擬。特別是大型系統,生產環境的數據體量巨大無比,根本不可能完全模擬。這讓很多問題的重現非常困難,因為那些問題只有在特定數據組合中才會觸發。

調試只能查邏輯錯誤。如果是系統狀態或者環境變化導致的,那麼調試通常無法重現問題。比如,某台服務器在特定時間點壓力過大,導致某些請求處理過慢而超時,進而導致部分功能無法工作。這就無法通過調試來找原因了,因為這不是邏輯問題,同樣的環境和輸入不能保證同樣的輸出。這類問題需要的是日誌分析,而不是調試。

4 測試

任何事物在通過測試前都是不可靠的。

4.1 單元測試

軟件一直在變化,但是變化頻率並不均勻。表面文章經常變,核心邏輯永流傳。至關重要又不善變的邏輯,就是單元測試的用武之地了。塞入不同參數,模擬不同狀況,檢查結果是否都符合預期。當引入新的變化時,單元測試至少能檢驗已有的核心邏輯是否還能正常工作。

4.2 模擬測試

用爬蟲來模擬真實用戶的行為,構造各種請求,觸發各種操作,看各種淘氣操作之後我們的系統是否還能從容不迫、屹立不倒。模擬測試是自動化的基礎。模擬測試的關節打通了,才能專心構建測試用例,進而系統的進行更多的自動化測試。它需要接受輸入、檢查預期,最後呈現結果,而且都是批量處理的。

4.3 手動測試

總有些東西複雜、麻煩,難以自動化或者自動化成本過高。這樣就只好用活人取代爬蟲來扮演用戶了。

4.4 壓力測試

一個和尚有水喝,一百萬個呢?當業務規模增加幾個數量級後,就發生了質變。最簡單的事情也可能非常有挑戰。壓力測試就是模擬大量數據和請求,看看系統是否仍然淡定。

5 部署

醜媳婦都要見公婆。產品無論美醜,最終都得見用戶。

5.1 常規操作

自動測試、拷貝代碼、更新依賴、清理緩存、服務重啟、切換版本等等,此外可能有一些自定義腳本補上自己私人訂製的操作,比如:啟動某些相關服務,創建相關新文件夾,等等。

5.2 注意事項

數據變化。可能是數據表結構變了,可能需要預設一些數據,可能是某些緩存需要更新,等等。數據變化不是每次都發生的,需要格外小心,否則就是上線事故。輕則部分功能不能用,重則系統徹底崩潰。

配置變化。這通常也不是標準化步驟裡的,往往需要額外操作。如果是新增項目還好,如果是已有項目的變更則要注意操作順序。順序錯了,麻煩大了。

6 監控

老大哥正在看著你!不為侵犯隱私,是怕你有個三長兩短。

6.1 兩類內容

  • 報錯。尤其剛上線新內容之後最可能發生錯誤,那往往是某個沒有考慮到的狀況發生了而程序沒有處理好所致。再者環境問題導致的異常也很要命,需要第一時間知曉。比如:某第三方服務當機了,自家某些功能就會受影響。
  • 指標。CPU,內存,網速,響應時間,等等。這些指標反映了系統的健康狀況,與歷史數據對比過於不吻合那八成就是出問題了。飆升或者暴跌,都是危險的信號。

6.2 兩種角色

  • 用戶。看看他們都用什麼功能,哪些用的多,哪些用的少,是否卡在某個步驟,是否偏愛某些功能。這屬於用戶行為分析,數據挖掘的意味大於監控,但仍然需要保持關注,仍算監控體系的一部分。
  • 黑客。江湖險惡,總有些傢伙出於某種原因想要害朕,不得不防。赤裸裸的攻擊不必多說,那是陽謀,單挑便是;還有些大灰狼偽裝成綿羊假冒普通用戶,這就需要日誌分析了,從訪問模式中嗅出惡意的味道。

6.3 三級反饋

  • 無害數據可視化。各種報表、餅圖、熱點圖,甚至動畫等等,主要用於分析。
  • 錯誤信息發郵件。發送郵件給相關人員進行分級和分類,根據優先級安排資源進行修復。
  • 高危事件發短信。爭分奪秒!火速搶救!沒時間廢話!

結語

過此六步,產品誕生。

這六個環節構成了軟件開發一條龍,貫穿了一個網站系統從無到有的完整過程。會了這些,就足以憑一己之力撐起一家初創公司的技術部分。大多數人工作多年也就止步於此而已。

但這不是結束,而是開始。工程能力只是編程修行之路的第一關,也就是能做出東西而已。至於想把東西做大、做精、做穩、做安全,那就需要更多其他的能力。做大,需要架構能力;做精,需要算法能力;做穩,需要運維能力;做安全,需要攻防能力。

碼海無涯,唯勤而已。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment