程式設計師的核心能力

  • “圖片提交之前能不能用 TinyPNG 壓縮一下”

  • “data.py 是數據文件,不需要運行單元測試”

  • “我們另一個 git repo 也想用你的 precommit 腳本,但要把 Python 的單元測試先禁掉”

  • 1), 2)都是小意思,加上就行了。3)讓你有點為難,precommit 函數已經有點長了,要不這個判斷寫在 py_unittest 裡吧。到了4),你忍不住頂了回去“我沒空寫,拷貝一份自己去改吧!”

    這時你開始意識到這樣下去不是辦法,但別人的這些需求又很瑣碎,讓你沒辦法很好地組織代碼。如果要系統化地解決這些問題,該怎麼做呢?你發現所有的需求都可以抽象成同一個套路

    如果文件名符合某個條件,就對該文件執行一個特定函數

    如果讓使用者來提供文件名的條件以及要執行的函數,那麼你只需要構建一個引擎來做條件判斷,併為要執行的函數準備參數就行了。有了這個思路後,你把每個需求抽象成了一條規則,規則包含以下幾個屬性

    • id: 規則的名字

    • include: 適合的文件名

    • exclude: 要剔除的文件名

    • func: 對符合條件的文件,要執行的函數

    • warning_only: 如果設為 True,在 func 執行失敗時,只打印警告。否則,中斷提交。默認為 False

    你把代碼重構成了下面這個樣子

    rules = [ { 'id': 'pylint', 'include': ['*.py'], 'func': pylint, 'warning_only': True }, { 'id': 'py-unittest', 'include': ['*.py'], 'exclude': ['data.py'], 'func': py_unittest }, ...]def precommit(): files = get_commit_files() run_hooks(files, rules)

    run_hooks 是你整個引擎的入口。而那些具體的需求不再是引擎的一部分,而只是外部輸入。更進一步,你把 rules 放在一個單獨的 JSON 或是 YAML 文件中。這樣別的團隊可以定製自己的 rules 配置文件,然後直接使用你的 precommit 引擎,這樣就完美解決了之前的第4個需求。

    這裡不給出 run_hooks 的具體實現,有興趣的同學可以自己寫寫看。

    避免過度設計

    當你有了引擎思維之後,也要小心另一個極端,那就是過度設計。這裡有兩種情況,一種是過早地設計引擎,沒有從實際的需求中去總結抽象,而是憑自己的臆測。對於一個瞭解業務的資深工程師來說,這樣做也許是可行的,但如果你經驗不足,還是別太相信你的預判。另一種過度設計是想讓引擎能滿足所有的需求,這可能讓你的引擎變得複雜而難用。在設計引擎時,我也會使用80/20原則,讓引擎能很好地解決80%的需求,對於剩下的20%,引擎需要有一種 fallback 機制,它只要做到不擋道,能讓使用者自己去解決這些需求就行了。過度設計可以聊的點很多,將來可以單獨發文討論,這裡就此打住。

    培養引擎式思維

    我是在工作幾年之後,才逐漸有引擎式思維的意識。對於每個待解決的問題,開始問自己“它是不是某類問題中的一個個例,這類問題能否被系統化地解決”,而隨著經驗地增長,越來越多的時候我會得出肯定的答案。因為這是一個長時間累積的過程,我鼓勵大家儘早地思考這個問題,對你的技術進步一定會有幫助的。

    從另一個角度,如果你已經是團隊中的技術領導,在與團隊成員的技術討論中,在 Code Review 時,你該教他們些什麼?Coding style, 某種語言的語法糖,一個可以解決他們手頭問題的第三方庫?這些是必要的,但還不夠。初出茅廬的小夥伴們可能為了完成佈置的任務就已經疲於奔命了,或是沉醉在某項新技術的學習中,但他們未必會有意識地去思考如何搭建一個引擎來解決一類問題。在這方面,初期他們需要有好的範例去模仿,中期需要有人提醒指引,日久之後他們才會自發地造引擎,在中前期一個優秀的技術領導若給於他們這方面的幫助,就能大大加速他們核心能力的成長。我覺得這是培養團隊成員的重要一環。


    分享到:


    相關文章: