以下內容均來自阿里工程師的自我修養,阿里工程的自我修養下載 密碼 6x71
以下總結只是書中的一小部分,希望讓更多的技術人看到這本書,提升自己的思維和眼界
自我總結:
- 不要盲從書本獲取其他地方抄襲,要構建自己的知識樹,習慣性總結。強烈推薦Xmind或者processon
- 多問一下爲什麼”,並一直“刨根問底
- 知識量是爆炸的,但是原理絕對是可控的,原理性的東西比直接的知識有更強的複用度
- 注重自己思考力的提升,考察方向:廣度、深度、速度
- 學習需要系統化,看書或者官方文檔,並非一篇文章
- 視頻可以快速入門,快速開發,視頻推薦Pluralsight
- 關注技術新聞,twitter 上技術大牛 & 參加技術大會
- 做項目,寫 Demo,看源碼
- 基礎知識是決定你能否幹到退休的關鍵因素
- 越是基礎,越是幾十年不變的基礎越是重要;
- 一個人走得快,一羣人走得遠
- 當你不舒服,難受或陷於困境時,應該停下來思考
- 掌握搜索信息的有效方式
程序員成長、發展的常見問題:
-
你自認爲付出了跟別人同樣的努力,但是你的成長確實更慢一些,比如學得比
別人慢,排查問題比別人慢,出方案老是有漏洞等等; -
你覺得你只是在疲於應付需求,自己做的事情完全沒有技術含量(很多人覺得
自己做的業務開發就是沒有技術含量,但我認爲每個領域都有自己的技術含
量,只是你有沒有 get 到); -
你發現總是在犯同樣的錯誤,或者做的事情不斷地在同一個水平循環;
-
每次要晉升的時候,你發現根本講不出來(很多人會認爲是表達能力問題,但
是我認爲不是); -
當你換到一個新的領域,你發現自己的經驗好像用不上;
-
你一直很難搞懂老鳥說的“認知升級”到底是什麼概念?不同級別的技術思維
能力到底有什麼差別?爲什麼晉升的是他,而不是我? -
只要把事情搞定了,成長是自然而然的事情——可能過段時間,你發現之前犯
過的錯誤,後來一個都沒有避免; -
我只要努力,996 甚至 007,我就能夠成長得比別人快——可能你發現你幹
得最多,但是並沒有拿到最好的結果; -
我盡力了,還是比別人慢,應該是我智商確實差一些——恭喜你,其實大家的
智商並不會有太大差別; -
別人表現好,或者晉升了,只不過是比我表達能力更強而已——我可以負責任
地告訴你,這並不是僅僅是表達能力的問題。
“思考力”是程序員需要具備的一種至關重要的素質
先拋一個非常重要的結論:“思考力”是程序員需要具備的一種至關重要的素質。
掌握了思考力,你就掌握了在互聯網領域,這種高度“智力密集型”行業成長的鑰
匙。上面這幾個成長的問題和誤區,跟沒有掌握思考力有着非常重要的關係,而且我
發現所有發展比較順暢的同學,他們的思考和學習能力是非常強悍的
我個人在工作中,一直有意或者無意地鍛鍊自己和團隊同學的思考力,包括哪些
是對我們最重要的思考力,如何去訓練思考力,有一些心得,希望能夠分享給大家
關於思考力
思考力是一門很深的學問,包括認知科學,心理學、教育學、邏輯學,如果要系
統化學習,是需要看很多書的,我推薦以下幾本:
1.《金字塔原理:思考、表達和解決問題的邏輯》-[ 美 ] 芭芭拉·明託,這本書系
統闡述了思考、表達和解決問題的邏輯,也是麥肯錫的思維能力基礎,算是一本比較
標準的思考力教材;
2.《麥肯錫教我的思考武器》- [ 日 ] 安宅和人,作者根據自己在麥肯錫公司工作
時積累的豐富經驗以及腦神經學的專業背景,設計出一套極具邏輯性的問題解決思維
模式;
3.《思維的本質》-[ 美 ] 約翰·杜威 ,這本書是美國著名教育家約翰·杜威的代
表作,闡述了思維訓練的基礎理論和實踐;
有哪些對程序員最重要的思考力
原理性思維:找出知識背後的原理
有的人會說,爲什麼要思考原理,而不是直接掌握知識就可以了?我只需要會用
就行了啊。
我們先來舉一些技術方案設計的案例
什麼訂單創單要先 create,然後 enable ?
這其實是一種採用二階段提交解決分佈式事務的思路,只是從一般的事務框架
延展到交易領域;
- 業務系統中爲什麼要使用消息?
因爲消息使用的是觀察者模式,觀察者模式的好處是可以實現多個消費事務與觸發事務的解耦; - 爲什麼業務系統中會使用 DTS 來做補償?
這本質上是一種最終一致性 BASE 理論解決分佈式事務的一種思路; - 爲什麼更新數據的時候一定要在 sql 中加上版本比對或者狀態比對?
這本質上是一種藉助 DB 實現的樂觀鎖機制。
進一步,你會發現再大到系統架構和頂層設計的案例: - 比如阿里系的技術框架 NBF、TMF、早期的 webx,各類框架設計理念,逃
不脫設計模式,比如開閉原則,模板方法、責任鏈、工廠模式、開閉原則; - 不管是底層中間件,錯綜複雜的業務系統,在設計的時候永遠無法離開核心的
業務建模,比如實體與實體關係的構建;在分析這類系統的設計思想時,你會
發現最好的工具就是 UML !
實際上除了軟件領域的原理,還有商業設計的原理,比如案例:
- 所有的售中退款前必須要先取消履約,所有的履約過程中發生缺貨都需要退
款,爲什麼?因爲交易的基本原則是:“錢貨平衡”,錢和貨的變更必須是最終
同步的(允許短期的不平衡),你掌握了錢貨平衡的基本原理,交易中的很多復
雜的流程設計就很好理解了; - 在設計財務系統、庫存系統時候,業務流程、業務邏輯可能非常複雜,導致你
暈頭轉向,這時候“有借必有貸,借貸必相等”的財務平衡性原理就發揮作用
了,你只要知道這個原理,很快就能看懂各類財務流程、庫存流轉流程,以及
各類數據對賬邏輯; - 在我的領域“高可用線下收銀系統”進行線下系統容災的時候,有各種容災方
案的設計,會員容災、商品容災、交易容災、支付容災……不同的容災手段看
起來讓你眼花繚亂,但是他們有沒有共同遵循的原則呢?有,這就是“讓消費
者最快速度完成交易,但保持最後追溯的能力”。你只要 get 到這個基本原理,
設計各類容災策略就會得心應手了
此外,我們的工作流程、管理手段,同樣也蘊含着深層的原理,非常有意思,大
家可以抽空仔細推敲一下,比如:
- 爲什麼團隊機制要透明?溝通要透明?
- 爲什麼要有 owner 意識,都是在工作,owner 意識會有什麼不同呢?
- 爲什麼管理者不能管得太細,也不能放羊?到底哪些該管,哪些不該管?
所以,掌握了知識背後的原理,帶來的好處是:
- 軟件系統的複雜度越來越高,我們所面對的場景越來越多,掌握原理實際上
可以大幅度降低我們對於知識的記憶量,知識量是爆炸的,但是原理絕對是
可控的! - 原理性的東西比直接的知識有更強的複用度! 記住最核心的原理,當你面對
新的場景時,你會驚喜地發現,你的理解速度大大加快!這個點大家應該有體
會,比如可能之前我們都學習過 dubbo 等底層的 RPC 通信框架的基本原理,
但是你如果僅瞭解了他的基本用法,你會發現對你現在做業務系統沒有什麼幫
助!但是,當你瞭解的是 dubbo 如何尋址,如何做容災,如何做擴展,你再
去做業務系統,發現設計原理是一樣的,並沒有本質區別!這樣你之前研究中
間件的設計思想就可以快速用到業務系統上面。 - 另外探求原理的過程,本身很有樂趣! 這是一個非常有價值的思維訓練過程,
不斷對系統設計思想、業務設計思想、做事情的工作方式,追尋背後的原理,
並找到他們之間的共性,在我看來非常有樂趣,一段時間訓練以後,你會發現
你看透本質的能力越來越強!
好,那麼我們程序員的工作中, 究竟有哪些與原理性知識是需要我們掌握的呢?
按我們團隊的實戰經驗來看:
- java,linux,數據結構和算法,數據庫,網絡通信與分佈式計算的原理,這
幾類是比較重要的基礎知識,我們在做方案設計、編碼、問題排查中會運用
得很多; - 設計模式,UML 這個是對系統架構設計必要要掌握的知識,當你經歷了很多
大規模的軟件系統設計,回到根本上,你會發現逃不出這一塊的理論和工具; - 領域性的基本原則,比如我們上面提到的“錢貨平衡”,“財務平衡公式”,
“線下收銀讓消費者最快速度走人”,這種邏輯需要大家 get 到這些領域性的
設計原理,甚至自己去總結出這種原理; - 關於管理學,人際溝通,心理學的一些基本原理,大家可以按照自己的實際
需求去看一下。
如何在工作中學習和運用這些原理,我覺得有一個最佳實踐:
- 首先,對你可能用到的領域知識,建立一個基本的概念。 看書,看文章,找
行業資深的人去聊,都可以得到。注意,這裏需要有一個基本的概念就可
以,這樣你在有可能 touch 到這些原理的時候,你會有意識,也不至於花很
多時間; - 在實踐中,有個意識是“多問一下爲什麼”,並一直“刨根問底”,最終肯定能
夠追查到背後的最終原理;這裏面還要注意思考一下,爲什麼在這個地方會
運用這個原理,也就是找到“場景” 和“原理” 的關聯關係,這樣你的理解
會更加深刻; - 瞭解了原理以後, 在實踐中運用一下,這樣你對這個原理的理解就會非常深
刻,並且你知道如何去運用這原理; - 如果這是一個非常重要的原理,建議大家如有餘力去結合經典的書籍系統化
學習
結構化思維:構建自己的知識樹
知識樹要解決的問題,我們看一些場景:
- 爲什麼我知道很多東西,但是當場景來的時候老是會記不起來使用;
- 完成一個方案你只能想到一些點狀的手段,還有其他方案被漏掉了;
- 講一件事情的時候邏輯非常混亂,前後沒有邏輯性關聯。
是很有可能你的知識都是知道的,爲什麼會出現這種悲劇?
這個就跟大腦中的知識結構有關,這是知識學習中“索引” 沒有建立,也就是說,
你的知識只有點,沒有線! 大家想一想,把東西亂七八糟地丟在房間中,到用的
時候沒有查找的線索和路徑,怎麼找得到呢?
來看一下我們工作場景的結構化的典型案例,大家體會一下:
項目中測試 MM 提了一個 bug,我總結出來的比較標準的問題定位步驟:
- 確認剛纔是否有過代碼變更和部署,因爲有比較高的概率是剛纔變更的代碼
又搞壞了…… - 追蹤鏈路日誌看鏈路是否有異常;
- 通過 RPC 的控制檯調用看接口輸入輸出是否符合預期;
- 追蹤關鍵方法的入參和出參,看是否有問題;
- 定位到方法細節後,推理邏輯是否有問題;
- 如果無法通過推理,那就最後一招,回放異常流量 debug,這樣肯定能夠找
到原因。
某個鏈路耗時比較長,需要進行性能優化,我的分析步驟是
- 通過實際流量製造一個耗時較高的 trace;
- 進行 trace 分析,看清楚耗時最多的原因,然後按優先級進行排序;
- 針對對原因找解決方案,可能的方案有:
i. 減 少 數 據 訪 問 次 數 或 者 計 算 量, 常 見 手 段 是 增 加 cache: 線 程 內 的
invokeCache;分佈式緩存 tair;頁面緩存……
ii. 增強處理速度,比如多線程加速;
iii. 減少循環調用次數,比如請求合併後再分發;
iv. 減少數據處理範圍,比如減少查詢內容,異步加載分頁;
v. 邏輯簡化,比如邏輯進行優化,或者非核心邏輯異步化等;
vi. …… - 改掉以後,回放同樣的 case,看性能消耗是否滿足預期,不滿足預期繼續
優化;
如何熟悉一個新系統, 我的步驟是:
- 要一個測試賬號,把相關功能走一遍,這樣能非常快地瞭解一個系統的功能;
- 看關鍵的核心表結構,這樣可以快速瞭解系統的領域模型;
- 根據功能步驟找到系統對外的接口列表,瞭解系統的 L0 業務流程;
- 下載系統工程,熟悉整個工程結構和模塊職責;
- 以一個最重要的流程爲入手點,閱讀代碼,看清楚核心的執行邏輯,可以變
看邊畫時序圖; - 製造一個 debug 場景,以 debug 方式走一遍流程,這樣可以實際加深一下
對系統的理解; - 做一個小需求,掌握相關的流程和權限
下單這裏來了一個新的需求,出一個技術方案的步驟
- 看清楚之前的需求,把這個需求所在的場景和鏈路大致閱讀一遍,搞懂;
- 找到需求的變化點;
- 分析變更的方案,涉及的內容可能會有:
i. 數據結構會不會變,如何變;
ii. 交互協議會不會變,如何變,交互協議分爲:端和組件要不要變;和下游
接口要不要變;
iii. 執行邏輯會不會變,如何變,執行邏輯變更的細化考慮點:是否變更域服
務;是否變更流程編排;是否變更主幹邏輯;是否變更擴展點是否變更擴
展點的內部邏輯,變更內部邏輯的時候,又可以進一步拆解:
a. 重構原有的方法,覆蓋之前的邏輯,那就需要進行迴歸;
b. 通過邏輯路由到新的方法,這裏需要增加路由邏輯; - 穩定性方案;
- 發佈方案;
可以看到,面對任何一個場景,不管多大多小,我們所需要掌握的知識或者技能
都可以構建成一個樹結構,同類之間是順序關係,上下之間是父子關係(或者粗細顆
粒度)。
當這個樹在大腦中構建起來以後,你會發現你做什麼事情都是有一個明確的分析
和執行邏輯,不太可能產生遺漏和混亂!
那麼如何訓練出自己的知識樹呢
- 一定要總結出自己的知識樹,而不要盲從書本上的或者別人的,爲什麼呢?
一是因爲人的思維速度和習慣、技能有一定差異,不一定每個人都是一樣
的;二是如果沒有內化別人的知識成爲自己的知識,這棵樹不太能夠很熟練
地運用; - 習慣性總結, 做完任何一個事情,都習慣性地回顧一下,往自己的樹上面掛
新東西,這個是構建知識樹的必備手段,這個總結不需要花很多時間,比如
做完事情後花個幾分鐘回顧一下就可以,但是需要堅持; - 推薦一個很常見的工具: xmind, 把自己的樹記錄下來;
- 訓練自己的思維習慣和做事方式變得結構化, 當你做事情的時候,習慣性用
樹的方式推進,強迫自己按照這個方式來
擴展性思維:舉一反三,拓展思維
擴展性思維的核心目標是提升我們思維的廣度, 也就是讓我們的知識樹變得更加
開闊;
我在工作中總結出來的擴展性思維的兩個關鍵的擴展方向:
(1)舉一反三:解決同類型的 N 個問題
舉一反三的好處是:“我們能否用同樣的知識和手段去解決類似的相關聯的幾個
類似問題”,先舉一些案例:
• 當發現某個系統的 jvm 參數配置存在一個錯誤配置,不是僅僅修復這個系統
的 jvm 配置,而是把負責的幾個系統都檢查一下是否需要統一修改;
• 系統中存在某個 bug 導致產生了髒數據,不是直接訂正已發現的髒數據,而
是根據特徵拉取出所有的髒數據,進行一次性處理;
這種思維方式的特徵是舉一反三,觸類旁通,相當於產生批處理的效果,可以大
大提升解決問題的效率,避免重複處理
尋求更多的可能性:拓展解決問題的不同手段
拓展思維常見的手段是:是否能夠換更多的理解方式,或者更多的解法,舉一些
案例:
• 產生故障的時候,快速止血除了回滾以外,還有哪些方案?如果故障處理經驗
豐富的人一定知道,除了回滾,其實還有系統降級,運營活動降級等多種方案;
• 除了寫更加健壯的代碼,還有哪些手段都可以提升系統的容錯性?還有數據監
控,單據閉環等多種手段;
當解決問題的手段更多了,思維就開闊了
抓重點思維:提升效率,方便記憶和傳遞
當我們發現知識樹構建起來以後,怎麼樣使得記憶和使用的效率變高?而且對外
傳遞的時候更加容易讓人理解?抓重點思維要解決的場景是:
- 如果每件事情都按照知識樹方式做,效率可能不會特別高,有更快的辦法麼?
- 在對外溝通表達的時候,要表達核心思想,否則別人會很難理解你的表達內
容;比如大家再晉升答辯、項目彙報的時候一定會有體會。
解決這兩類困惑, 核心思路是要抓住重點和脈絡。
但是抓住重點和知識結構化之間並不矛盾,而且我認爲是有先後次序的, 一定要
先建立知識結構化,然後才能從裏面篩選出重點, 否則知識的體系是不完整的
反思性思維:思考哪裏可以做得更好
思性思維是提升知識質量和深度的一個關鍵能力。 因爲只有不斷反思才能讓下
一次在上一次基礎上升級, 而不是重複循環。
常見的反思案例:
• 有個問題我查了 2 個小時,師兄只花了 10 分鐘,這是爲什麼呢?是他的業務
比我熟悉?思路比我清晰?還是知道某個我不知道的工具?一定要找到關鍵的差異
點,然後彌補掉這個差距;
• 一個項目項目做完了,從方案設計,研發過程,質量保障上面,哪些地方下次
可以做得更好?找到不足,下次避免;
對於我們技術團隊,哪些內容值得反思,我們團隊的經驗是:
- 這個項目商業價值 OK 嗎?是否取得了預期的效果?
- 項目中我的能力有哪些問題,有哪些做得好的和不好的?
- 系統設計的優勢和不足?
- 項目質量保障是否可以做得更好一些?
- 研發過程和項目管理是否有不足?
反思性思維的實踐,注意有兩個點比較關鍵:
- 反思性思維最重要的意識:做事情的過程總有優化的空間,每次都要有進步;
如果沒有這種心態,那麼很難持續地進行反思; - 反思是一種習慣和潛意識,可以在不經意之間經常進行, 其實不需要很形式
化地花很多時間,有時候做完一個事情,習慣性思考一下就可以。
架構評審需要注意的事項
- 技術選型: 爲什麼選用 A 組件不選用 B、C 組件,A 是開源的,開源協議是
啥?基於什麼語言開發的,出了問題我們自身是否能夠維護?性能方面有沒
有壓測過?這些所有問題作爲技術選型我們都需要考慮清楚,才能做最終決定 - 高性能: 產品對應的 TPS、QPS 和 RT 是多少?設計上會做到的 TPS、
QPS 和 RT 是多少?而實際上我們整體隨着數據量的增大系統性能會不會出
現明顯問題?隨着業務量、數據量的上升,我們的系統的性能如何去進一步
提高?系統哪個環節會是最大的瓶頸?是否有抗突發性能壓力的能力,大概
可以滿足多少的 TPS 和 QPS,怎麼去做來實現高性能,這些問題都需要我
們去思考。 - 高可用: 是否有單點的組件,非單點的組件如何做故障轉移?是否考慮過多活
的方案?是否有數據丟失的可能性?數據丟失如何恢復?出現系統宕機情況,
對業務會造成哪些影響?有無其他補救方案?這些問題需要想清楚,有相應
的解決方案。 - 可擴展性: A 和 B 的業務策略相差無幾,後面會不會繼續衍生出 C 的業務策
略,隨着業務的發展哪些環節可以做擴展,如何做擴展?架構設計上需要考
慮到業務的可擴展性。 - 可伸縮性: 每個環節的服務是不是無狀態的?是否都是可以快速橫向擴展的?
擴容需要怎麼做手動還是自動?擴展後是否可以提高響應速度?這所有的問
題都需要我們去思考清楚,並有對應的解決方案。 - 彈性處理: 消息重複消費、接口重複調用對應的服務是否保證冪等?是否考慮
了服務降級?哪些業務支持降級?支持自動降級還是手工降級?是否考慮了
服務的超時熔斷、異常熔斷、限流熔斷?觸發熔斷後對客戶的影響?服務是
否做了隔離,單一服務故障是否影響全局?這些問題統統需要我們想清楚對
應的解決方案,纔會進一步保證架構設計的合理性。 - 兼容性: 上下游依賴是否梳理過,影響範圍多大?怎麼進行新老系統替換?新
老系統能否來回切換?數據存儲是否兼容老的數據處理?如果對你的上下游
系統有影響,是否通知到上下游業務方?上下游依賴方進行升級的方案成本
如何最小化?這些問題需要有完美的解決方案,稍有不慎會導致故障。 - 安全性: 是否徹底避免 SQL 注入和 XSS ?是否有數據泄露的可能性?是否
做了風控策略?接口服務是否有防刷保護機制?數據、功能權限是否做了控
制?小二後臺系統是否做了日誌審計?數據傳輸是否加密驗籤?應用代碼中
是否有明文的 AK/SK、密碼?這些安全細節問題需要我們統統考慮清楚,安
全問題任何時候都不能輕視。 - 可測性: 測試環境和線上的差異多大?是否可以在線上做壓測?線上壓測怎
麼隔離測試數據?是否有測試白名單功能?是否支持部署多套隔離的測試環
境?測試黑盒白盒工作量的比例是怎麼樣的?新的方案是否非常方便測試,
在一定程度也需要考量。 - 可運維性: 系統是否有初始化或預熱的環節?數據是否指數級別遞增?業務
數據是否需要定期歸檔處理?隨着時間的推移如果壓力保持不變的話系統需
要怎麼來巡檢和維護?業務運維方面的設計也需要充分考慮到。 - 監控與報警: 對外部依賴的接口是否添加了監控與報警?應用層面系統內部
是否有暴露了一些指標作監控和報警?系統層面使用的中間件和存儲是否有
監控報警?只有充分考慮到各個環節的監控、報警,任何問題會第一時間通
知到研發,阻止故障進一步擴散。
代碼評審注意事項
- 確認代碼功能: 代碼實現的功能滿足產品需求,邏輯的嚴謹和合理性是最基本
的要求。同時需要考慮適當的擴展性,在代碼的可擴展性和過度設計做出權
衡,不編寫無用邏輯和一些與代碼功能無關的附加代碼。 - 編碼規範: 以集團開發規約、靜態代碼規約爲前提,是否遵守了編碼規範,遵
循了最佳實踐。除了形式上的要求外,更重要的是命名規範。目標是提高代
碼的可讀性,降低代碼可維護性成本。 - 潛在的 BUG: 可能在最壞情況下出現問題的代碼,包括常見的線程安全、業
務邏輯準確性、系統邊界範圍、參數校驗,以及存在安全漏洞 ( 業務鑑權、灰
產可利用漏洞 ) 的代碼。 - 文檔和註釋: 過少(缺少必要信息)、過多(沒有信息量)、過時的文檔或註釋,
總之文檔和註釋要與時俱進,與最新代碼保持同步。其實很多時候個人覺得
良好的變量、函數命名是最好的註釋,好的代碼勝過註釋。 - 重複代碼: 當一個項目在不斷開發迭代、功能累加的過程中,重複代碼的出現
幾乎是不可避免的,通常可以通過 PMD 工具進行檢測。類型體系之外的重複
代碼處理通常可以封裝到對應的 Util 類或者 Helper 類中,類體系之內的重複
代碼通常可以通過繼承、模板模式等方法來解決。 - 複雜度: 代碼結構太複雜(如圈複雜度高),難以理解、測試和維護。
- 監控與報警: 基於產品的需求邏輯,需要有些指標來證明業務是正常 work
的,如果發生異常需要有監控、報警指標通知研發人員處理,review 業務需
求對應的監控與報警指標也是 Code Review 的重點事項。 - 測試覆蓋率: 編寫單元測試,特別是針對複雜代碼的測試覆蓋是否足夠。
發佈計劃評審
涉及到 10 人日以上的項目,必須有明確的發佈計劃,並組織項目成員統一參加
項目發佈計劃 review,發佈計劃主要包含如下幾點:
1) 明確是否有外部依賴接口,如有請同步協調好業務方;
2) 發佈前配置確認包括配置文件、數據庫配置、中間件配置等各種配置,尤其
各種環境下的差異化配置項;
3) 二方庫發佈順序,是否有依賴;
4) 應用發佈順序;
5) 數據庫是否有數據變更和訂正,以及表結構調整;
6) 回滾計劃,必須要有回滾計劃,發佈出現問題要有緊急回滾策略;
7) 生產環境迴歸測試重點 Case。