當前位置:
首頁 > 最新 > Python Web入坑指南一

Python Web入坑指南一

小白的踩坑記錄


文檔化

團隊項目開發前的統一三要素:統一需求/開發文檔,統一代碼規範,統一環境(編譯/測試/發布)。 很多程序員是懶得寫文檔的,彷彿牛逼的程序員不需要寫。但是看人家真正牛逼的開源項目比如flask和tornado等,無論是代碼還是文檔都做得相當棒。對於一些項目,有些東西如部署步驟;常用命令等還是可以記錄下來的,可以使用wiki或者readthedoc,gitbooks等文檔工具記錄一下,方便新人上手。如果不知道記錄啥,就把你發現不止一次會用到的東西文檔化。個人認為需求文檔也應該有歷史記錄,方便接手的人可以快速了解業務和需求變更。資料庫欄位的含義也應該及時記錄和更新。

Readme Driven Development:

Explain the system』s pupose. (What is the business reason ? Why are we here?)

Describe the scope. (What defines what the system does and doesn』t do?)

Summarise what it does. (What does it actually do? What is it for?)

只有少數很複雜的系統需要詳細的文檔,架構圖、UML、數據模型、處理流程、業務邏輯等需要整理成文檔。Write the minimum viable system documentation.


做好代碼分之管理,分清楚開發、特性、bugfix等代碼分枝,不要在同一個分之上一下修改太多功能,導致修復問題不好定位。比如經常和同事做一個需求,結果一個人把幾個需求堆到一個分之改了,把不該上的功能也給上了,這種小細節還是需要注意的,否則就會給測試、上線等帶來嚴重麻煩。命名分之的時候注意使用有意義的命名,比如附帶上task的號碼,jira號等等,把分之和你要解決的問題關聯起來。


有經驗的人都知道看別人的代碼是一件很痛苦的事情,尤其是沒有任何注釋的代碼。代碼除了完成需求外,最重要的就是維護和協作,除非你覺得你做的項目活不過仨月(或你自己玩玩的項目隨便你怎麼艹),否則就一定要重視代碼質量,防止代碼腐化(破窗)以至難以協作和維護。有時候比寫注釋更難的是知道何時寫,寫什麼注釋?python里有規範的docstring用來給類和函數進行注釋,除了說明功能外,關於github,stackoverflow鏈接、複雜的傳入傳出參數(比如嵌套字典作為參數這種你都不注釋就很不合適了),類型說明、需求文檔和bug的jira地址等都可以注釋。凡是你回頭看代碼一眼看不出來幹啥的,都應該有適當的注釋,方便自己也方便別人。

當然,最重要的是代碼清晰易讀,好的命名和編寫風格的代碼往往是自解釋的,看代碼大致就可以看出功能。建議就是給所有的模塊、類和函數都加上注釋,除非一眼能看出來這個東西幹啥,否則都應該簡潔注釋下,讓別人不用一行行看你的代碼就大概知道你這個東西是幹啥的。最後注意的就是一旦函數更改及時更新注釋。qiniu的sdk寫得就不錯,可以去github看看。總之,」Explicit is better than implicit.」, 代碼里不要有隱晦的東西,一時偷懶將來可能會付出幾倍的維護代價,請對將來的自己和他人負責。


筆者認為code review是一件非常重要的事情,可以有效防止代碼腐化,同時方便同事了解業務(可以說編碼規範、靜態分析、代碼審查和單元測試是保證代碼質量的幾個重要工具,沒有使用這幾個工具之一將來代碼都可能難以維護)。可以在公司搭建Phabricator(facebook在用)gitlab 類似工具進行代碼review。可惜小公司流程不嚴格,codereview總是堅持不下去,要不就是被同事吐槽總是給他挑刺。實際上如果是新手能夠從code review當中快速學到很多東西,比如編程慣用法,擺脫不良編碼習慣,不良設計和難以維護的代碼等。review的時候對事不對人,代碼如果有明顯缺陷快速記錄個TODO等待review後修正,以一種開放和學習的心態看待review,慢慢整個團隊的實力和代碼質量就會提高。review就是個互相學習進步的過程,正規的團隊都應該嚴格遵守,而不只是走走流程。

建立 review 檢查表,防止不合理、過於複雜、明顯缺陷、可讀性差的代碼。眼睛足夠多,bug 無處藏。越早修復缺陷,成本越低。

建立提交模板,每個提交是需求、bugfix還是啥一目了然,同時貼上需求、jira 等地址,方便追溯。

對事不對人,review 和被 review 的人都要以一種開放和學習的良好心態看待 review,共同進步。新手或者新加入項目的人不要過度吹毛求疵(會有很大心理負擔和反感情緒),共事久了步調和代碼風格慢慢趨同了。

及時複查,防止一次太多的commit。使用 gitlab 等工具可以在代碼 diff 的地方評論,這樣方便對照別人的評論迅速修改代碼里的問題

邏輯是否正確,代碼行為是否符合預期

代碼規範(風格和命名等,動態語言沒類型聲明,很依賴良好的命名推斷變數含義和類型)。同志們學好英語,命名真不是個簡單的問題(尤其是各種中式英文和縮寫)。

是否有單測

是否健壯(安全性、性能、異常捕獲)

必要的文檔和注釋(意圖,外部鏈接需要註上)

可讀性和可維護性(是否有過於複雜的邏輯)

commit 信息(commit信息是否準確,比如附上 jira 或者需求文檔地址,bug 地址等,有跡可循, 目前團隊加上了提交模板,對於 bug fix、新特性、重構等都需要填寫對應的模板信息)

代碼潔癖要適度,如果代碼遵守了規範並能正確解決問題,就不要吹毛求疵。review 過程中出現分歧是很常見的,每個人都有自己的編碼習慣。如果出現難以解決的分歧,可以列出優劣表格,對各自的方式有一個量化的分析(比如從實現難度、可讀性、可擴展性、可維護性等方面打分)。如果無傷大雅,不必吹毛求疵。

檢查內容:

《https://www.kevinlondon.com/2015/05/05/code-review-best-practices.html》

《如何用人類的方式進行 Code Review》


一定要有良好的日誌記錄習慣。良好的日誌對於記錄問題至關重要。python有方便的日誌模塊幫助我們記錄,日誌輸出的代價是比較小的,python的日誌模塊盡量做到對函數功能沒有性能影響,可以在線上和開發環境設置不同的log等級,方便開發調試。注意別再日誌語句里引入了bug或異常。有時候需要判斷什麼時候需要日誌,記錄哪些東西方便我們排查問題,分析數據。 對於異常,一定『不要吞掉任何異常』,常有新手上來就try/except,也不區分非退出異常,也沒有日誌記錄(坑啊......)。請先閱讀python文檔的異常機制,可以使用Sentry等工具記錄異常。同時發生異常時候的時間,調用點,棧調用信息,locals()變數等要注意記錄,給排查錯誤帶來便利。有些錯誤的復現是比較困難的,這時候日誌和異常的作用就凸顯出來了。


調試也是個很重要的問題,不可能保證代碼沒bug,要命的是有時候寫代碼完成功能的時間還沒調試的時間多。注意復現是排錯的第一步,之後通過各種方式確定原因(訪問日誌、郵件報的異常記錄)等,通過走查代碼、斷點調試(二分法等)確定錯誤位置,確定好錯誤原因了就好改了。修復後最好反思下問題的原因、類型等,哪些地方可以改進,爭取下次不犯一樣的錯,慢慢減少錯誤才能越來越高效。

《調試九法》

盡量寫出對自己也對其他人負責的代碼,上邊費了牛勁都是在闡述這個顯而易見但是沒多少人嚴格遵守的東西。用動態語言寫大型項目維護起來要稍麻煩, 很多新手寫代碼不注重可維護性,甚至自己寫的代碼回頭自己看都一臉懵逼,問了一句這代碼TM是幹啥的? 一開始的負責會為以後協作和維護帶來極大便利(當然你想干兩天就走讓其他人擦屁股就當我沒說)。 最後,很多東西我也在摸索,上面的玩意你就當小白的踩坑記錄,隨著理解和經驗的加深我會不定期更新本篇內容。另外我發現網上大部分是教程性的東西,對於python相關的工程性的東西很少,我很疑惑難道大部分公司的python項目都寫得相當規範?沒人吐槽?反正我是踩過坑,希望看到過本章的人能把python代碼質量重視起來。

如何定位和修復 bug:復現和定位。定位需要找到 bug 出現時候的上下文信息,可以用 log,sentry 等查看。確認之後通過走查代碼、斷點調試等方式尋找代碼邏輯錯誤。

第一步是復現,偶爾才復現的代碼是很難排查錯誤的。如果不好復現但是有 sentry 之類的記錄工具也是極好的,sentry 會記錄當前棧信息和變數信息,非常有利於排錯。

走查代碼。使用 pylint 等靜態檢測工具排除低級錯誤(你應該把它集成到開發工具里)。

看日誌,各種日誌(logging, nginx),看 sentry 異常信息

問同事,讓同事幫忙 review 審查代碼。有時候人有思維定勢,你自己看不出來的別人可能一眼就看出來了。小黃鴨調試法

斷點調試。看變數值。二分法排查代碼位置,快速試錯定位。比如一個地方很有隱秘的錯誤,但是又不好快速確定位置,我們就可以用二分加斷點的方式快速定位到具體哪一塊出了問題。

不要死磕,一個法子不行換一個。死磕可能會耗費太長時間並且容易進入死胡同,在一個大型複雜系統中定位 bug 原因是對技術、經驗、毅力、靈感、心理素質的很大考驗,休息一會可能就解決了。

極難排查和復現的 bug 可以無限期擱置。

找到 bug 修復以後增加相應單元測試用例,這樣對回歸測試非常有利,同時避免重複犯一樣的錯誤。tricky 的地方要加上注釋。

留心非代碼因素:比如代碼是否正確部署上線等(比如之前腦殘查一個 bug 無解最後發現是部署到線上沒成功,根本沒起作用)。如果實在沒發現代碼級別錯誤,單測也比較完善,可能就要考慮下非代碼因素。

bug 總結:建立錯誤檢查表(核對清單),哪些可以避免的記錄下來,防止以後再犯。(團隊的知識財富)

大多數 bug 都可以通過設計複審、代碼審查、代碼靜態分析、測試等找出來,我們可以綜合利用以上手段盡量減少代碼缺陷。


不知道你有沒有這種感覺,看那些知名代碼庫flask等,人家寫的代碼水平是比較高的,但是自己的項目確實一團糟。我覺得代碼要經常去重構,想著怎麼寫更優雅,更容易理解和維護。我個人感覺好的代碼就是不斷修改出來的,實現一個需求的時候,適當想想怎麼設計更加優雅易維護,編寫代碼的時候注意想著可讀性。完成需求了如果代碼可以設計更優雅,可以嘗試重構下,慢慢代碼水平就上來了。如果總是直來直去堆砌需求代碼,業務邏輯寫再多依然不會有進步(我個人感覺寫python有時候反而會降低編程能力)。牛人和計算機高手很多,能寫出良好的工程代碼的人卻很少(試想一下讓你維護一個『牛人』的『精巧』代碼)。代碼一次編寫,卻可能被無數次查看、修改和維護,在可讀性和可維護性上的努力長遠來看是值得的,編寫代碼只是整個軟體項目中很小的一部分。寫代碼的時候最好也從維護者的角度思考一下。 Code Quality: Simple, Well-tested, Bug free, Clear, Refactored, Documented, Extensible, Fast.

重構:在不改變代碼功能的情況下優化代碼設計。修改功能和優化代碼不要同時做。優化應該以可讀性為標準。

接手老項目的時候不要盲目大規模重構,但要保證代碼倉庫越來越『乾淨』,不要破罐子破摔。

可以通過設計(需求)歸檔、代碼規範、靜態檢測工具、單元測試、必要的注釋和文檔、code review(代碼複審)、重構、服務化等手段增加項目的可維護性。

動態語言的重構工具支持不夠完善,重構的時候要注意別改壞了邏輯,要十分謹慎。

《重構 - 讀書筆記(PYTHON示例)》 來自 wklken』s blog


筆者從實習開始做 Python 後端,經歷過一些新項目、老項目,以及和很多 python 工程師(豆瓣、知乎)協作過,大概總結下 python 做 web 業務後端的優缺點吧,盡量客觀, 總得來說就是用動態語言寫項目在追求高生產力的同時要嚴格把控工程質量。先說優點:

多面手。python 可以寫方便地寫爬蟲、網站、數據分析、運維腳本等,都有比較成熟的框架,目前比較火,TIOBE 里腳本語言排第一。

輪子多。python 發布時間比 java 還早,大量現成的輪子可以用。我覺得即使是 web 之王 php 做網站開發體驗和效率上來說並不比 python 強。這可能也是 Instagram 和 Quora 等選擇 Python 的原因。

表達能力強,語法糖多,生產力高。筆者喜歡動態語言的一個原因就是表達能力強,用更少代碼完成功能。(代碼行數越少意味著高生產力和低出錯率,當然不一定對可讀性有幫助)

缺陷:

解釋性語言執行效率低,大部分時間用在 IO 密集場景,比如 web 後端。不過大部分公司不用擔心性能問題,除非真到了一定用戶量級。

開發工具支持不夠完善,不如 java 有那麼完善的 IDE,Pycharm 還不錯,但是依然解決不了濫用動態特性導致的補全和跳轉等問題

易編寫,但難以重構和維護,易出錯,工程上不夠友好,較難寫出 clean code(筆者基本上會強制上 pylint and autopep8, 模仿 gofmt 吧)。基本上重構只能依據字元串匹配,老實說每次重構有稍微大一些的改動都會有點擔心

濫用動態特性導致代碼不好維護。這是個雙刃劍,但是對工程來說還是不要濫用。有時候會利用一些動態特性使用黑魔法來快速完成需求,但是工程上來說這是很不利於維護的。這是很多人抨擊動態語言不適合大型項目的原因,一般需要在編碼規範里明確說明哪些能用,哪些不能用。

沒有類型聲明,看不出一些複雜類型的數據結構(Python、php 都在不遺餘力地加上 type hint),代碼寫糙了維護起來很累(看不出複雜變數的類型和結構,閱讀代碼吃力,我都是給複雜類型加上類型注釋),命名和編碼習慣很重要

缺少一些最佳實踐(技術、小白文章偏多,工程實踐文章比較少),以很多 python 的中小公司在軟體工程上管理不夠,無規範、無文檔、無注釋、無單測、無持續集成的尿性,還是慎用動態語言瞎胡搞,後期維護成本會很高。

python2,3 不兼容,遷移有成本。我個人覺得 python 敢於拋棄當初的設計是值得讚賞的,但是很多企業並沒有足夠的資源來去遷移

招人不好招,這兩年 python 雷聲大,雨點小,而且基本都是在 AI 領域。有經驗的 python 後端遠沒有 Java 好招(往往很多人學得還是半吊子寫代碼很隨意,不重視工程質量),更多是創業公司、中小公司使用

技術選型都是在權衡吧,因為靈活性與工程性、開發效率和運行效率很多時候無法兼得。


認識和熟悉所在團隊中的成員(筆者之前一直做得不夠好,這一條遠比想像中重要,內向性格有時候會比較吃虧),良好的溝通和協調能力能幫助你更快完成(或者委託)任務。

確保正確了解需求,確保熟悉所做的業務;需求分析;適當設計。流程圖或者文檔有時候可以幫助理清楚業務。比如知乎有 rfc 機制,每次做一個稍微大點的需求都需要寫設計文檔。

番茄工作法,勞逸結合(working smart rather than working hard),一次只做一件事(do one thing and do it well)。長時間專註寫代碼是非常消耗精力的。確保編碼期間足夠專註。快速迭代。

邊寫邊測,增量式編程。雖沒有使用 TDD 開發的習慣,但是對於稍複雜的邏輯就要寫單測,以便及時發現錯誤,越早發現越容易修復(修復成本隨時間指數增加)。我習慣用文件變動監控工具(when-changed fswatch等)檢測文件變動,每次保存文件自動跑相關測試(比如 nose pytest 等都可以執行單個文件或類的測試,你可以快速驗證當前代碼是否有問題,及時修改或者重構)。TDD 的好處之一就是改善設計,自頂向下考慮,筆者有時候也會嘗試用 TDD。

注釋先行,意圖導向,表達明確,牢記可讀性可維護性,可追溯(附上需求文檔地址,方便維護者查看)。寫一個模塊、類或者函數之前先想好它的功能,按照功能命名,之後寫簡單的注釋描述其意圖和功能,通常不超過三句話,雖然大部分時間只有一句話(只做一件事) ,但是能快速讓後來的維護者了解你的意圖。別看人代碼最頭疼的就是看不出代碼究竟是要幹啥。

文檔驅動編程(Document Driven Development):比如寫一個腳本的時候,應該在文件頭部註明需求地址 url(保證代碼功能、意圖等是可追溯的),寫下實現方式和目的等。有時候對於很複雜的業務邏輯筆者會用自然語言描述步驟,之後再用代碼實現。對於需要經常維護的代碼,必要的文檔是值得的。

邊開發,邊重構,及時清理技術債。如果有代碼寫糙了(圈複雜度太高、可讀性差、代碼重複等壞味道),應該及時重構不好的代碼,這時的重構成本是最小的。代碼寫得複雜到自己都快看不懂了是個危險的信號。

重視規範。代碼量上去以後沒有規範就是噩夢,也是很多小公司代碼不忍直視的原因。(無文檔、無注釋、無單測、風格混亂、難以維護)

追根溯源,需求歸檔。在代碼、提交信息、文檔中記錄需求文檔地址、引用地址等。方便維護者能夠根據代碼提交尋找代碼意圖,尤其是幾乎沒有任何文檔注釋的代碼。讓人上來就看一段不知所云的代碼無比痛苦。寫代碼有時候和寫文章、論文差不多,可以在 docstring 里附上相關鏈接。commit 信息都應該足夠重視,不要瞎寫,要能體現代碼提交意圖(修復 bug、新 feature、代碼優化等)

結對編程。結對編程和TDD是極限編程中大力提倡的,國內似乎沒有多少公司在實踐,一般幫助新人了解項目或者帶實習生的時候,結對能幫助新人快速上手。有時候兩個人一起邊討論邊寫代碼要比寫完後在 gitlab 一條條評論快很多。

平常可以留心下周圍優秀的同事都有哪些好習慣,我們可以學習並改善下自己的開發流程。

Think more, type less. Aim for minimalism, fewer states, less mutability, and just enough code for the known, relevant parts of the problem.

《The Zen of Python》 - Tim Peters

Beautiful is better than ugly.

Explicit is better than implicit.

Simple is better than complex.

Complex is better than complicated.

Flat is better than nested.

Sparse is better than dense.

Readability counts.

Special cases aren"t special enough to break the rules.

Although practicality beats purity.

Errors should never pass silently.

Unless explicitly silenced.

In the face of ambiguity, refuse the temptation to guess.

There should be one-- and preferably only one --obvious way to do it.

Although that way may not be obvious at first unless you"re Dutch.

Now is better than never.

Although never is often better than *right* now.

If the implementation is hard to explain, it"s a bad idea.

If the implementation is easy to explain, it may be a good idea.

Namespaces are one honking great idea -- let"s do more of those!


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 Python 的精彩文章:

Python以及Django最小白的環境部署
編程資源 Python

TAG:Python |