第一、保障安全刻不容緩
保障安全刻不容緩,你不能輕描淡寫地說:“今后系統會加強安全性?!?/p>
強大的應用程序安全應該被當作開發過程中的首要問題,應該從第1天開始就需要討論,絕對不能等到第300天。將安全性留到最后一分鐘實際上會增加開發的時間,因為你不得不返工,通過重構實現安全要求。
更糟糕的是,屆時你就沒有那么多時間來解決安全問題了,最終只能發布易受攻擊的代碼。雅虎等公司的遭遇就是前車之鑒。
每個應用程序各不相同,且有各自的需求,根據應用程序的需求選擇,不要屈服于市場流行度。
每個應用程序各不相同,這點無需多說。世上沒有一套適用于所有應用程序的萬全之策。在啟動新應用程序時,應該由應用程序及其體系結構來規定使用的技術或標準化的平臺。
如果在你搞清楚“應用程序需要什么”之前,就決定使用gRPC或Kubernetes系統,那么只會阻礙代碼的編寫工作。這就是為什么Canonical等公司誤入歧途,在物聯網設備上運行Kubernetes。
引用Jeff Goldblum的一句話:“科學家們忙于思考他們能夠干什么,不能夠干什么,以至于他們沒有時間停下來思考是否應該這樣干?!?/p>
第二、你可能并不需要微服務
微服務非常有魅力,我完全理解。即便只是想一想能夠單獨擴展應用程序中的各個小功能,就足以讓人興奮不已,因為你可以感受到滿滿的成就感。
但是,說實話你可能不需要微服務。比如“我希望將功能X的代碼從功能Y的代碼中分離出來”,“防止不良代碼滲透到應用程序的其他部分”,或者“最小化受影響的應用”等到這些理由不是使用微服務的原因,這些不過是糟糕的開發實踐導致的惡果,你需要的是更嚴格的代碼審查標準(不要合并不良代碼),以及更為細致的安全控制。
你是否需要單獨擴展應用程序的各個部分,而且目前的組件(例如登錄流程)是否存在容量的問題?然后再探索那些需要擴展成獨立服務的組件。
你是否需要運行基于虛擬服務器的架構并希望降低成本?如果是,那么就不應該探索微服務。即便采用微服務,充其量也不過是功過相抵。惡劣的情況下,最終只是多了幾個需要單獨啟動的實例。
假設你的整體式架構包含5個服務,而你卻將其分解成了微服務。那么,現在你有5個應用,你所面臨的局面是:
a)你需要逐個啟動專用實例,而占空的空間是最初的5倍;
b)你利用現有的空間,同時承擔額外的運營成本。
第三、標準化的開發環境
在與多個開發人員合作時,標準化整個團隊使用的開發環境可以讓你受益無窮。我并不是說你必須將一些基于容器的虛擬開發環境通過魔法混合在一起。雖然你要這么干我也攔不住,但是你只需使用同一個版本的語言就可以為團隊帶來奇跡。
如果你的同事用Go 1.11編寫代碼,而你卻在Go 1.12上發現了Bug,那么可真是欲哭無淚。協調何時升級版本可能很困難,但一旦協調成功,諸事都會順利。
第四、配置的工作不簡單,請務必做好相應的計劃
雖然有些流行的網站說,配置只不過是“將所有東西都扔進環境變量中”,然而事實遠非如此。
我認為配置應用程序的方法至少有四種:代碼內的默認值、本地配置文件、命令行的標志、環境變量、遠程配置(如使用Hashicorp的Consul)等。我認為遠程配置是可選的,而其他四個都是必要的。
對于開發來說,為了在本地運行應用程序而不得不將27個不同的配置值放入環境變量,這會讓人萬分沮喪。另外,你可能需要更好的自動化和Makefile。你可以利用本地配置源的方法,如application.yaml,并默認設置為“dev”配置。
此外,如果深圳網站建設公司代碼中有默認值,則意味著你需要更改它們的默認值。在通過systemd等初始化系統運行應用程序時,命令行的標志非常實用,因為在查看進程的運行情況時,你可以清楚地看到應用程序的配置方式。在容器內運行應用程序時,環境變量非常實用,但也有一些東西不適合放入環境變量,比如Secret。
你永遠不能將Secret(密碼、身份驗證令牌、證書等你不想泄露給公眾的數據)放入環境變量,因為它們不安全,而且主機上的任何進程都可以讀取環境變量。你應該借助某種Secret管理工具。我個人喜歡使用Vault(來自Hashicorp),但你可以根據應用選擇最合適的工具。
第五、只在必要時導入軟件包
我們都知道left-pad的那個故事吧(https://www.theregister.co.uk/2016/03/23/npm_left_pad_chaos/)。
只因為有人從代碼倉庫中刪掉了一個11行的NPM軟件包,結果幾乎導致整個互聯網癱瘓。
請務必小心。只有當你面臨正當的需求時,才應該導入軟件包,例如某個供應商特定的SDK(比如AWS的SDK),這是一組高度冗長的標準庫抽象(這就是人們喜歡使用Python的請求而不是urllib的原因) ,或廣泛使用的框架,比如Go的Echo HTTP服務器或Python的Flask WSGI服務器。
一些便捷的庫也可以,比如JavaScript的Lodash提供了標準庫中沒有的一些常用功能和附加功能。這些外部的依賴項可以減輕開發的負擔,而且你不需要手動編寫樣板或集成代碼,這些都是包系統的優勢。然而,就像left-pad一樣,你很容易被坑:“這兒有一個庫,我可以使用?!蹦銓氲囊蕾図椕吭黾右粋€,隨之而來的不穩定性和風險也會劇增,可能這些庫根本沒人維護。
新依賴項本身導入的每個包也會增加風險,這稱之為傳遞依賴。如果你導入了一個包,而這個包又導入了5個包,那么恭喜你現在繼承了這五個依賴關系以及隨之而來的所有風險。
我和這個行業的許多其他人都認為軟件包不應該引入傳遞依賴。雖然無法做到萬無一失,但至少應該盡可能減少,如果你需要更多功能,那么就為用戶提供一種明確的擴展方法。
在導入庫時,通常我會遵循一個簡單的規則:如果我可以在10-15分鐘內自己編寫,那么就自己寫;否則再考慮使用外部的庫。
在開發的時候,牢記這條規則可以避免將不必要的內容導入應用程序,但是你不必每次需要提供API時都考慮從頭開始編寫新的HTTP服務器。
第六、沒必要抽象所有代碼
還有一個很大的坑:抽象一切。
有時,你會覺得“稍后我可能會再用到這個功能”,這個想法可能會將你引向一條黑暗又可怕的面向對象之路。
DRY原則(Don’t Repeat Yourself,不要自我重復)徹底征服了我們,盡管這條原則有其充分的理由。
然而,你需要注意不要在抽象上花費太多時間,以至于沒有足夠的時間編寫邏輯。你的工作是寫代碼!等到你發現你需要實現的某個方法或函數之前已經寫過了,那么可以再回過頭來抽象,但是切記量力而行。
我個人遵循的原則是,如果抽象之前只是一個只有3行的函數,那么就重復好了。如果真的只有3行代碼,也許你該想一想是否值得寫成函數。
第七、項目需要像“鳳凰”一樣,
經歷浴火重生的洗禮
這個想法令人不寒而栗。經理們會為此感到緊張,產品所有者會為此變得暴躁,而且開發人員也會因此而感到憤怒,但你必須這么做。
每隔一段時間就從頭開始其實是一件好事。你可以借機刪掉代碼中的冗余,而且無需改造現有的半個代碼庫就可以實現新的想法,同時還可以強制每個人重新評估項目。
你可以把項目想象成一片森林,每一行代碼都是一棵參天大松樹,綿延數里。隨著時間一天天過去,這片森林會布滿灌木叢、飄落的松針、松果、枯枝和許多其他雜物。這些都是你的麻煩,你的技術債務。
這些東西越積累越多,直到受到某種外部力量的影響。對于森林而言,這種外部力量就是野火?;鹧嫠僚斑^的森林,地表寸草不生,只有樹皮足夠厚的樹木才能存活下來,所有未長成的樹木都會被大火燒盡。雖然這對森林來說是滅頂之災,但其中蘊含著一個驚天的秘密:森林渴望大火。多年來,它一直在耐心地等待,等待火焰來凈化一切,因為火焰在樹冠下肆虐過后,下一代的參天大樹才會從松果中發芽。
當火焰橫掃過森林地面時,它會孵化出幼小脆弱的樹苗,讓它們與被大火燒得漆黑的幸存者并肩而立。你的應用程序也需要這樣的洗禮:生命力旺盛、編寫良好的代碼會從清理中存活下來,而新的想法和代碼會從累累白骨中站起來,宛如浴火重生的鳳凰。