Skip to content

走一遭『寫程式』以外的軟體開發 - 大型軟體公司的工作日誌 | HWDC 紀錄

Posted on:September 17, 2024

cover

前幾天參加了 HWDC,裡面有不少厲害的講者,其中最讓我印象深刻的就是「走一遭『寫程式』以外的軟體開發 - 大型軟體公司的工作日誌」,講者用詞簡單明瞭、內容清晰好理解、語調不會讓人覺得想睡,讓我覺得這是一場很棒的分享,故我決定寫成一篇文記錄一下其中內容。

這是講者的 Medium: https://medium.com/@johnliutw

這場主要是講者去訪談一些大型公司的工程師,詢問他們的技術文化,得出一些結果。

company logos

摘要

這四個要點主要是基於一本書叫「Google 的軟體工程之道」的面向來延伸的。

講者的 Medium 也有分享這本書,有興趣的可以自己去看一下。

開發

靜態工具 - Style 原則

這意思是像是有些公司在你剛入職時會叫你讀一些文件,有超多的規則,然後要你遵守這些之類的,可能你讀完就想離職了,沒辦法運作。
故我們要簡化這些流程,切中要點。

在實際開發中,我們其實有 9 成以上的時間都是在閱讀別人的程式碼,所以說你寫出來的程式有 9 成的時間是被閱讀的,故讓程式碼易讀是很重要的。

這邊在後面 CR 階段會再特別提到。

例如像是 PHP 8.1 出了一個叫 Enum 的功能,像這種新的東西若使用指引的方式來運作,而不是直接 block 掉,針對實際情況做調整。

靜態工具 - Style Guide

Google style guide

這是在 Google 裡面實際的一個靜態工具的範例,這是在非常大的公司會有的原則,例如 Amazon、TikTok。
而在中小型公司則是依靠自動化或 lint。

靜態工具 - Sonarqube

Google style guide

這是一個開源的工具,但也提供需付費的 SASS 服務,能自動檢查程式碼的工具,包含但不限於:

CR (Code Review) - 看哪裡?

一般在發了一個 PR (Pull Request) 後,你會希望它快點被通過以及被 merge,所以在設計上會注重:

確認程式碼是否能正常運作

確認沒有 security 的問題

效能是大部分講者去訪談的公司的工程師都非常看重的一塊,例如:Shopback、Nvidia、Appier、Tesla,看看是否有一定的效能,使用的演算法是否有問題。

看看這個程式碼是否很容易出錯、是否很敏感,例如一個程式碼接受一個參數,型態是數字 (int),然後你帶了一個字串 (string),這個程式碼就會出錯,你可能會想設計上就是要數字,帶字串進來本來就會錯,不過如果它夠強固的話,能替它想到各種情境、做各種轉換,就能提升程式碼的強固性。
因為在大型公司,你永遠不知道你的程式碼會給什麼人使用,例如你可能在維護一個 N 年前的程式碼,你根本不知道當初那個人在哪了。

如果一段程式碼會讓你停頓一下、讓你感到困惑,可能原因有兩種:

  1. 可能才剛加入團隊,不了解這個 domain、knowhow。
  2. 可能性可以加強,因為你看不懂,如果你看不懂,其他人也很可能看不懂。

所以可讀性是 CR 階段非常看重的一點。

假設有一段程式,可讀性、效能、安全性什麼的都非常好,但一樣的問題,其他團隊都用了 A 設計方式,就這裡使用了 B 設計方式,這樣可能在未來會造成維護上的困惑:「為什麼這種情境要使用 B 的方式?」。

CR - 怎麼運作?

像是在 CR 時發現對方少加了什麼,可以使用「Could you please…」、「不好意思幫我加一下…謝謝」、「幫我確認一下…我也不太確定」。

如果是偏向主觀的議題,以作者 (開發者) 的決策比較好,除非是重要的 issue,就要提出來,設定客觀條件,讓大家都認同。

可以以 200 行為參考基準 (自己決定就好),小的 PR 能加速被通過的機率。
若真的太複雜,可以開一個 PR review 會議,讓開發者介紹他的架構、怎麼運作之類的,但嫌開會太麻煩,那就把 PR 拆小,就不會有這些事出現了。

使用 conventional commit 來陳述變更類別。

假設一個 PR 有 20 個 reviewer,但第一個 reviewer 可能有 80% 的貢獻,第二個 5%,第三個剩下 2% 3%,因他們大概率 context 都是差不多的、都是同個 team 的,思考點都差不多。故 reviewer 不是越多越好。
講者說他公司是兩個 reviewer,除非是大型變更。

搭配剛剛提到的 Sonarqube,搭配一些 CI。

CR - code owner

code owner 01

code owner 02

可以使用 Github 本身擁有的 code owner,可以設定哪些檔案是哪些團隊擁有的,當有這些檔案變更時,會明確指示誰該去 review。

文件的核心價值

除了 API 要寫文件外,也要針對 feature 去寫文件,強迫設計者去清楚了解規格,如果設計者無法文字化來表達他要怎麼設計,通常代表他想的不夠清楚,所以可以強迫開法者釐清自己思維。

將文件交給新人,就可以不用手把手帶新人,如果要規模化的話可以做一個文件庫。

例如像是 SOP,當發生問題時,support engineer 該 follow 什麼流程,可能哪天自己的文件會幫助到自己。

例如現在在選資料庫,要 MySQL 還是 PostgreSQL,若你的文件只有介紹用了 MySQL 怎樣怎樣的,但是沒有寫當初為什麼選擇 MySQL,在未來可能會一直被問,例如:「為什麼明明這個語言的生態系最常搭配 PostgreSQL,為什麼這裡要 MySQL?」

文件的難處

第一個難處是文字與事實不符

block diagram

假設要開始一個功能,然後大家叫你去看文件,結果發現文件與事實完全不匹配。
所以文件也有版本問題,所以不是所有東西都要記成文件,不然很容易發生這個問題,盡量針對重要、長期有價值的來記錄。

也要持續培養自己寫作的能力,持續優化寫文件的技巧。

第二個難處是到處都是文件

documentation everywhere

什麼地方都有文件,google drive 有文件,他的電腦桌面有文件,他的抽屜也有文件,到底文件都放在哪?
所以要盡可能的把文件都集中在幾個地方。

演講者的公司有自己的 AI gpt,可以在裡面搜尋問題,因為統一化了,所以可以 training。

哪時候要寫文件?

前面提到的操作手冊。

例如系統有 20 30 幾個服務,他們怎麼交互運作的。
或是這個 E2E 流程超長,該怎麼設計之類的。

新人入職手冊。

在滿多公司中都會規定在啟動一個 feature 前都要寫 feature design,這裡指的不是 API,而是例如:需要設定文件 reviewer、UI 你會用到哪些 shared component、你會跟哪些 API 互動、你的 case 是什麼?
如果有資料的 migration,你的 schema 設計、migration plan 是什麼?
關於測試,你的 QA 有沒有提供測試計畫、他如果要測你的功能,你有沒有什麼特別的 context 要先設計?

所以有非常多的環節要設計,所以寫程式只是非常短的一個環節,大部分時間都在文件討論。
在越複雜的功能上越需要這麼做。

對外開出來的 API 也需要文件,像是 Amazon 有文件委員會,要送到美國總部給 principle engineer 等級以上的人查看,看看狀態如何、有沒有符合規範等等。

講者分享之前的一個慘痛經驗:曾經發生了一個 incident,在修的過程發現一個字拼錯,例如 apple 拼成 appla,而這個 API 也早已開出去給客戶,且已開出去好幾年了,根本不能改,只好繼續這樣不舒服的看著,也造成閱讀上的困擾,所以這種對外的 API 要非常非常嚴格的對待。

品質和自動化

測試的好處

測試的好處有幾點,例如增加基本品質、增加信心,並且可規模化的,你的單元測試可以給很多人跑。

另外單元測試可以督促更嚴格的設計,迫使開發者去思考可測試性。

講者分享公司曾發生一個 P0 的 incident,它是之前發生了一個改變,但這個改變並沒有程式碼 level 的測試,如果有的話就能在很早的階段被 catch 住。後來詢問為何沒有測試,得到的答案是這個不太好加測試。

測試的維度

測試有不同的維度,最小的像是單元測試,中間的像是手動測試,大的像是 E2E 測試。

不理想的測試環境:

testing ice cream cone

不少公司的測試組成都如上圖那樣,手動測試佔最大量,其次是自動化,再來是整合測試,最後才是單元測試。
理想上應該要反過來。

單元測試的生態系

因為我們已經做了自動化了,所以要 merge code 就一定要過測試涵蓋率,迫使人去做這件事。

例如一個 channel 每週傳送一個報告,顯示各個團隊測試涵蓋率是多少,促使彼此競爭。

Google 有一個廁所測試,會在上廁所這種這麼短的時間內告訴你怎麼寫好測試。

雖然自動化測試很好,但仍然有些東西無法自動化,例如影片品質、搜尋結果、或複雜的安全漏洞,依然得仰賴人工測試,所以在自動化測試中可以使用 82 法則,80% 的案例使用自動化測試,把精力放在剩餘的 20% 上,這種特別負責、特別需要人類去判斷的測試上。

Agoda 曾分享他們會對單元測試的一些條件做抽換,看看是否一定的安全性。

大型測試

大型測試,例如 E2E testing,因單元測試有許多的 mock,所以可能有模擬度不足的問題。
以及 configuration,在不同環境,你的 config 是否正確。

再來是特殊情境,例如效能和壓力。
看看有沒有什麼非預期的行為或 side effects,因單元測試是取決於開發者對這個功能的理解透不透徹。

最後是真空效應,指的是單元測試或自動化測試都是在嚴格的環境內,但 user 都是很有想像力的,所以還是需要大型測試去 cover 這種亂七八糟的世界。

大型測試類別

例如聘請團隊來測試你的產品,他們不會針對你的測試案例做測試,他會像是探險一樣,對你的產品點點看、打打看之類的。

假設你的產品是非 web 服務的產品,例如 desktop 的 application,會有 binary 檔案。

例如 monkey testing,找一隻猴子來亂點你的產品,看看有沒有什麼意外發生,類似探索性,但更非理性一點。

在 release 前或第一天,讓大家一起來測最新的版本,各自看看有沒有什麼問題,或許可以提早發現一些 bug。

測試 - CI

讓開發者透過自動化測試,盡可能少的 effect 來完成這件事,搭配 lint 或安全性的確認。

有些公司會透過 build,實際跑起來看看。

測試 - CI 的失敗案例

Google 有一個案例叫「Google Takeout」,他們為了做到這個測試,會把所有的 server 串在一起,因所有服務都串在一起,所以 log 會非常多、非常難以追蹤,等於一個小小工程師發了一個 PR 就要測試整個 Google。

測試 - CD

自動部署強調是敏捷、小、並且快,以及要自動化、隔離性、模組化、且可靠\n 假設一個部屬的 script,執行五次有三次會失敗,就不會有人想用了。

再來是 feature toggle,很多大型公司都會使用,因為即使功能經過測試,但可能還是有 edge case 沒 cover 到,造成客戶困擾,故會仰賴 feature toggle 的保護。

測試 - Release Train

不應該為了一些小小的問題而停下,不可能修復完每個 bug,即使有些 feature 仍有 bug,我們還是可以移到下個階段。

deadline 是固定的,不應該在 deadline 後仍有東西被合併,若有東西要修改,需要高階主管的 approval。

系統迭代

依賴的自評清單

為什麼棄用很難?

有一個 Hyrum’s 的定律:使⽤者時常以意外或不可預⾒的⽅式使⽤系統,尤其是使⽤者越多且越古⽼的系統

想像一個系統已經 10 年了,誰知道誰已經用了什麼方式在使用它,只要是寫在程式碼中的邏輯,都非常非常有可能被某個重要的客戶給依賴。

大規模的變更,需要特別講

團隊文化

工程效率

來到最後一個環節,工程效率,分為三點:

主要是個人技術能力、寫程式的能力。

紀錄自己過去一年參與的專案,當做到很重要的 ticket、解了很有價值的 bug 時把它記錄下來,在考核時可以拿出來參考,不要考核時想不起來自己過去一年中做了什麼。

怎麼溝通、怎麼跟其他人合作。

你做的東西做出了什麼貢獻


以上就是這次「走一遭『寫程式』以外的軟體開發 - 大型軟體公司的工作日誌」分享的東西,我覺得很多層面都是過去工作中都遇過的事,但一個不注意就會被你忘記的。所以時常去複習這些事,讓自己成為更好的工程師!