隨筆

我懶,所以我投機取巧

聽懂脫口秀的「三思」,才讀懂系統架構

#隨筆

某次在YT上刷到的脫口秀裡聽到一個段子(請恕我真想不起出處是哪了):「三思而後行」,在暗諷職場的甩鍋文化。

一思:能不能不做?

再思:能不能晚點做?

三思:能不能讓別人做?

台下笑成一片。

我也笑了,而且笑得有點心虛,因為畢竟誰都想當薪水小偷。

後來經歷的一些事,沉澱反思後,我忽然意識到這是一個最高級的行動準則!

我懶,我投機取巧,我大方承認且理直氣壯,因為我很清楚一件事:時間是最貴的成本,花掉了就拿不回來。

一、笑聲背後的機會成本

脫口秀好笑,是因為它戳中了大家心照不宣的偷懶念頭。但現實是,組織裡那個有求必應、什麼都接的「好人」,才可能是整個團隊效率不彰的老鼠屎。 而且他還很冤的要扛著做到流汗,嫌到流瀾的罵名,甚至主管可能完全沒有意識到這是最可怕的經費黑洞(加班費)。

一起把段子裡的調侃剝開,剩下的是資源分配和邊界管理哲學。

一思:能不能不做:先審需求。 很多臨時起意的事,或是客戶看到新功能的想法發散,有時只要冷靜問一句: 「不做會怎樣?它真的解決了什麼問題?」 多半就自己消失了。不懂得拒絕的人,不是比較負責,而是沒把自己有限的時間當成成本。在他的帳本上,時間是免費的,所以什麼都可以接。可是時間偏偏是最貴的那一項。 工程師的時間,就是寶貴的產能。

再思:能不能晚點做:讓時間幫你過濾。 不少被包裝成十萬火急的需求,其實只是需求方當下的焦慮,或是還沒想清楚的半成品。第一時間衝上去做,很可能陪著對方改了好幾版,最後全部推翻。晚一點回應,讓子彈飛一會兒,是成本最低的過濾方式。 工程師們開發功能很像急診室,講求的是檢傷分類,不是先來後到,或是誰比較大聲(當然在工程師這邊可以用錢插隊....急診不行)

三思:能不能讓別人做:找到最適合的人或工具。 什麼都自己來,產出的上限就是一天的工時。把工作交給更適合的工具、流程或夥伴才能提高團隊效率,或是你要叫它甩鍋也行,我不爭辯。重點是,鍋甩給更適合的人,整體的成本就降下來了。 用對的人與工具,才會是最有效率的辦法。

二、實際經歷:懶出來的架構

從心虛到理直氣壯,我自己就經歷過一次。

我負責的是遊戲後台的報告畫面。需求很簡單:根據後端傳來的資料,用捲動的方式產生列表。每個遊戲是一個產品,各有自己的後端。

一開始走的是最直覺的一對一:一個產品一個前端站,前端直接把後端的位置寫死在程式裡。我意識到這樣做會碰到幾個問題:

第一是跨域,瀏覽器擋下前端打到另一個網域的請求,得處理 CORS。(前端一定都撞過這個從前端是無解的問題)

第二是後端的位置直接寫在前端,誰打開瀏覽器的開發者工具都看得到。(等著被 DDoS)

第三是後端一動,前端就得跟著動(甚至有的時候我根本不知道後端改了,還需要定期去「輪詢」確認後端位置)。維運有時候會改網域,我就得改程式、重新部署。而且一個產品不只一套環境,DEV、SIT、UAT、PROD 各有各的後端位置,改一次就是改好幾份。(工讀生等級的維護...沒啥技術含量,但就是需要花時間做手工藝,還容易出錯)

我是真的懶得處理跨域,因為這是瀏覽器限制,有些後端根本不知道這是啥玩意,所以我根據我不務正業時學到的Nginx知識,請維運幫我做反向代理(關公面前耍大刀...當下很抖)。前端只打同一個網域的路徑,由 NGINX 轉給後端。跨域的問題沒了,後端的位置藏起來了,維運要換網域,也是他改他的設定,前端完全不用動。

本來只是為了偷懶,後來我卻忽然意識到:既然轉發是由反向代理路由決定的,那一個前端,不就可以根據路由接 N 個後端嗎?

於是我換了做法。報告畫面根據網址判斷現在是哪個產品,動態組出對應的 API 路由,反向代理再依路由轉發到該產品的後端。

從此要新增一個產品時:

  • 維運:在反向代理加一行,指到新的後端。連那個後端的位置,都是維運自己配的
  • 前端:不用架新站,也不用重新部署,只要把新產品要用的圖片放上去

在 NGINX 裡,大概就是多這麼一行(示意):

location /api/product-c/ { proxy_pass http://<維運配的後端位置>/; }

一開始請維運加設定時,我心裡其實有點過意不去:「明明是我這邊要支援新產品,怎麼變成維運要動手?我是不是把工作推給別人?」

後來我才發現,我根本沒有多推什麼給他,說不定還幫他省了事。

第一,那一行本來就省不掉。 就算維持一對一,為了閃過跨域、藏起後端,每個站本來就需要反向代理,每個前端專案各有一份設定檔,各自寫著自己的後端在哪裡。每多一個產品,維運就要多顧一個站:申請網域、處理憑證、安排部署,一樣都少不了。解耦之後,這些設定集中到同一個專案裡,一個產品一行。網域、憑證、部署全部省掉,要查、要改也只看一個地方。維運的工作不但沒有變多,反而少了一大截,而且省下的都是實實在在的真金白銀與時間。

第二,讓最適合的節點做最適合的事。 「哪個路由轉到哪個後端」本來就是反向代理的強項。放在 NGINX,改一行設定就生效。放在前端,就是改程式、打包、部署,每次都走完整的上線流程。

第三,產品越多,省得越多。 我曾經認真想像過,如果一直維持一對一會變成什麼樣子:假如產品成長到十個(遊戲換個皮就算一個新產品,十個其實很快),每個產品要分 DEV、SIT、UAT、PROD 四套環境,就是四十份要維護的程式碼。維運改一次網域,我就要翻遍四十份,找出該改的地方,再一份一份重新部署。

只要是人做的,就一定會改錯、會漏改。差別只在於,是在測試環境被發現,還是上線之後才被使用者發現。

那是我能預想到的地獄。(我不要)

解耦之後,新增產品的成本被壓到兩件小事:維運在各環境的設定加一行,前端放幾張圖。產品再多,前端始終只有一份程式碼要顧,維運始終只有一份設定要管。

三、Do smart, not hard

英文常說「Work smart, not hard」。但我更喜歡把 work 換成 do:「Do smart, not hard」。work 談的是工作的手段,do 談的是行動的選擇,先決定做什麼、不做什麼,再談怎麼做。

用力過猛的人,做事像一個單體架構:什麼都往自己身上堆,靠意志力硬扛所有流量。遇到問題,第一個念頭是「我要怎麼加班把它寫出來?」。累了,就告訴自己:「我很累,所以我很負責。」花掉的是一去不回的工時和精力。

做得聰明的人,做事像解耦之後的系統:先畫清楚邊界,再把負載分給最適合的節點。遇到問題,第一個念頭是「這件事一定要存在嗎?誰來做成本最低?」。做完了,他想的是:「這套流程跑順了,我隨時可以走開。」他花的力氣,最後變成可以重複使用的工具、架構和自動化。

這種懶,其實很早就有人替它說話。

網路上某個經理人流傳一句話:「我會選一個懶人去做困難的工作,因為他會找到最輕鬆的方法。」出處是誰不重要,這句話能流傳這麼廣,代表很多人都有同感。

軍事史上也有一個常被提起的分類:軍官可以分成聰明、愚蠢、勤奮、懶惰四種特質。又笨又勤快的最危險,因為他會不停製造錯誤。又聰明又勤快的,適合當幕僚。而最適合當最高統帥的,是又聰明又懶惰的人:他討厭窮忙,所以懂得放手,懂得抓重點。(不怕壞人千方百計,就怕蠢人靈機一動)

到了軟體工程,Perl 語言的作者賴瑞‧沃爾乾脆把「懶惰」列為程式設計師的三大美德之一:因為懶得重複做同一件事,所以會寫腳本、做工具、設計架構,讓系統自己跑。

把視野再拉遠一點,諾基亞有句廣告詞:「科技始終來自於人性。」我更願意說:科技始終來自於惰性。

人類的發明史,幾乎就是第三思的歷史:能不能讓別人做?只是這個「別人」,後來變成了機器。懶得走路,有了輪子、汽車、飛機。懶得手算,有了算盤,最後有了電腦。懶得洗衣服、掃地,有了洗衣機和掃地機器人。工程師懶得每次手動部署,於是有了 CI/CD、Docker,還有我前面說的反向代理。

勤奮推動的是眼前的進度,懶推動的是做事方法的改變。如果每個人都甘願靠蠻力硬撐,我們大概到現在還在手抄帳本,反正「加班就抄得完」。

剛入行時,我是用力過猛的那種人(濫好人),總在想:「怎麼用我自己掌控的工具,把需求全部兜出來?」現在我比較常想的是:「這件事放在系統的哪個環節處理,整體的維護成本最低?」

Do smart 就是投機取巧。投機,是看準時機:什麼事現在不必做、什麼事可以等以及什麼事現在不做就會死。取巧,是挑最省力的做法:交給誰、用什麼工具或辦法。這四個字被用成貶義太久了,但拆開來看,講的正是做事最需要的判斷。(事分輕重緩急)

真正該怕的,是「用戰術上的勤奮,掩蓋戰略上的懶惰」:手上很忙,腦袋很懶,懶得想這件事該不該做、該誰來做。真正的推卸責任,是自己沒想清楚,就把混亂丟給下游。成熟的分工,是看清系統的全貌,設計出合理的介面和規範,讓每個環節在最擅長的位置運轉。

再三思

所以,下次再聽到那個段子,不妨認真把三思再想一遍。

一思,能不能不做?砍掉的假需求,就是省下的專注力。

再思,能不能晚點做?讓時間先替你過濾一輪雜訊,手上有沒有更重要的事需要先解決。

三思,能不能讓別人做?讓維運加那一行,讓 NGINX 轉那個請求,讓機器跑那些重複的事。

懶人的三思,一次都不能少:每一思,省下的都是往後無數次的重複勞動(還有時間,時間就是金錢)。

懶就是懶,投機取巧就是投機取巧,不必換個好聽的名字。我只是比較早想通:時間最貴,所以每一分鐘都要花在只有我能做、而且真正值得做的事情上。其他的,能不做就不做,能晚點就晚點,能讓別人做就讓別人做!