從入職到現在已經過了一年,然而博客幾乎沒有更新。本來畢業時和過年前後是打算寫點總結什麼的,寫着寫着就沒有繼續。
但不寫點回顧的話,心裏總感覺難受。以後要是想回憶一下當初都做了啥,結果沒想起什麼,感覺都是在浪費時間,這就不好了。
這幾天剛好是我們這批新人入職滿一年,腦中“寫點什麼回顧一下吧”的想法頻繁浮現。索性就把週末時間拿來寫這篇博客了。
公司、部門
在同班同學的安利下,來到現在就職的這家公司面試。現在我們在同一個小組。
當初是衝着說不用加班來的(然而後來卻經常加班,雖然是自找的)。此外,待遇好也是非常吸引我的地方~
同事之前的氛圍很好。大家人都挺好的。此外我們老大(我們都叫他大大~)經常會請我們吃下午茶,有時候會組織部門聚餐,上週六還一起去看了《我不是藥神》。小組的聚餐也組織過幾次。前面說過,這幾天我們這批新人入職滿一年,大家會買下午茶請部門的同事。於是我們就這樣吃了一週下午茶,哈哈哈。在這樣的部門裏面,感覺很幸福~
也有不好的地方。我所在的小組就編程而言,我的水平是最高的了,其他技術方面的也差不多。這也就意味着在這個小組裏面,沒有大腿可以抱,只能通過自學來提高自己。自學的進步速度比不上有人帶的速度。這也是我經常感到壓力的原因——進步速度太慢了。
所幸的是,從去年十二月開始,我們部門有同事組織起了知識分享會,成立了學習小組。每週五下午會抽出兩個小時的時間給知識分享會。期初是針對特定議題,大家各自學習,並在會上針對議題講述所學內容或發表看法。後來則是由每個人自定主題,然後上臺演講。主題的範圍很廣,非技術相關的例如:時間管理、如何做好演講、團隊合作等;技術相關的例如:代理服務、分佈式、存儲組件等;不知道怎麼分類的例如:人工神經網絡介紹(我講的),三維地圖重建,密碼學入門等。
總的來說,能學到東西,但還是不夠。通常只有講師學到的東西最多,其他人能學到多少,還是要看講師講得如何。像我講的人工神經網絡,雖然全程都是有畫圖講解的(完全沒有PPT),但是到了後半部分往深講的時候,還是有一半同事懵圈了。
項目
一年間接觸的項目總共有三個,姑且稱作項目 A B C。用的語言都是 PHP。
- 主要參與的項目是 A ,是一個用 PHP 寫的至少有十年曆史的任務系統(是不是有點可怕?)。
- 項目 B 是參與了其中一個模塊的重構,但重構得不徹底。因爲這次重構的目的主要是加個流程的流轉引擎進去。
- 項目 C 是個聊天機器人,用到了人工神經網絡。然而我並沒有負責核心部分(淚)。
項目 A 放後面講,另外兩個內容比較少。
項目 B
項目用的是 Zend Framework 框架,1.10.8 版本,2010 年 8 月發佈的。現在最新版本是 3。
舊代碼很少用到框架已經寫好的東西,還有不少東西是框架已經寫好,但還是自己寫的。應該只是爲了用上 MVC 和部分功能吧。
和其他模塊的重構是把原來的代碼拷一份,然後在上面修修補補的方式不同。我基本重寫了所有代碼。把業務相關的東西抽出來,簡化複雜的邏輯,把大塊的代碼拆解成幾個部分。然而這是有代價的,我爲此加班了好多天。不過最後還是按期交付了。如果不重寫的話,我可以不加班也能完成同樣的功能,但是過不了自己這一關。自己經手過的代碼,讓它們保持以前那種糟糕的狀態,實在不能接受。
不過我也知道自己這樣做的風險。從公司的角度講,自然是項目越快完成越好。萬一要是因爲我想把代碼寫好一些,導致項目進度變慢,那問題就大了。雖然在不少地方我剋制了衝動,但還是經常忍不住去重構代碼。幸運的是,項目的進度要求都沒有緊張到沒有時間來做優化。這也是受到公司氛圍的影響吧~
項目 C
一開始衝着機器學習來的,興沖沖地加入到項目裏面,想着學點有深度的東西。但果然還是太年輕了。自身並沒有相關的技術積累,想通過加入項目來提高技術的想法,是不是太幼稚了呢?
在這個項目組裏面,到目前爲止我都是負責後臺的簡單工作。例如提供知識庫的錄入界面等各種界面及其配套的後臺邏輯代碼,大部分工作都是前端相關。後端框架用的 EasySwoole ,因爲涉及到通信,需要用 swoole 擴展。不過我這一部分的工作跟這個框架其實沒什麼關係。前後端接口用的 Restful API 標準,但是 GET 查詢的支持不完善。
其他同事有的負責模型訓練,有的是負責通訊模塊。雖然原理我懂不少,但沒有實踐。後面我們會交換工作內容,我到時爭取換到模型訓練去。
說到我在這個項目所做的事情,可以引出一個話題:
前端 VS 後端
其實我指向好好地做一個後端開發人員。嗯!前端是要了解一點,但是也不必要這麼早吧。等這次做完,以後再有其他前端的事情,我都推掉。這次沒有早點推掉,把自己給坑了。
待我成爲後端大佬,需要拓展技術面的時候再去多瞭解和實踐前端。
項目 A
由於是主要參與的項目,所以可以說的東西就比較多了。下面我就叫它流水系統吧。事先說明,這是個內部系統,用戶量不會大到哪去,但使用頻率很高,而且跟絕大部分提供給用戶的線上的機器有關。
前面說過,這個項目的歷史有十年以上。想象一下十年前的開發吧,現在流行的幾個框架中,最早的是在 2007 年左右發佈第一個版本。沒錯,也就是說這個項目沒有用到任何框架。最常見的 MVC 也是不存在的,基本所有東西都寫在一起,面向過程。據說這個項目的開發人員基本都是從運維轉到開發,包括目前組內的幾個成員都是從運維轉到開發的。
不過神奇的是,我參與的這部分(也是整個系統最有業務價值的部分),可以說是隔離的還不錯。前輩們已經把前端相關的東西封裝好了,只需要到配置界面配置一下,就能組合成一個簡單的表單界面。剩下的就是寫後端部分,你可以把它看成是一個接近純 API 形式的系統,最終只要 return 一個 json 字符串就行了。這點實在是令人佩服。這也爲我接下來的改造提供了便利,因爲在整個請求中,會有一個地方成爲主入口,只要在這個地方做個攔截,把部分請求重定向到另一個位置,最後返回的時候是一個符合規定的 json 字符串就行。
流水系統的開發面臨幾個問題:
- 代碼管理方式落後
- 本地環境搭建複雜
- 單個代碼文件過大
- 難以測試
- 維護成本高
- 沒有文檔
據說在我來之前的一年,已經有說要重構項目了,但一直沒有行動。因爲維護本身就要花去很多時間,再加上需求一直做不完。這周才又開始提起這件事,因爲組內目前有幾個同事,他們負責的部分沒有需求了。而且我在最近的半年間也做了不少事情,對他們也會有幫助。
接下來說說我怎麼逐個解決上面那幾個問題吧。
1. 代碼管理方式落後
原本可以說是沒有什麼管理方式。事實上我們改代碼基本都是直接到線上用 vim 改(導致我現在 vim 幾個快捷鍵用得 66 的)。一旦保存,就會自動生成一份備份文件到指定的備份目錄。然後我們會有代碼審覈,要調用 linux 的 diff 比較兩個文件的不同,然後截圖發到討論組讓其他人審覈。有時候改的地方多,要截圖好幾屏幕。
連 svn 都沒有,更不用說 git 了。
今年三月,我開始推動代碼管理方式的變化,改成用 git 管理代碼。當然,老大是支持滴。我做了幾件事:
- 準備工作
- 找其他組瞭解到我們這邊內部有搭了一個 Gitlab,於是到上面建了個倉庫。
- 線上代碼整份備份,拷貝一份到我本地。把線上代碼目錄下,一些沒用的東西都移動到一個備份文件夾。例如文件名爲:!或者`這一類奇奇怪怪的文件;還有一些開發者個人的備份文件、測試文件等等,統統移出去。這些在移動之前都會把列表發出來,讓其他人確認過才移動。
- 找到程序運行時產生的文件夾,這些是要放到 .gitignore 裏面的。
- 原本超過 100G 的項目文件夾,只有大約 80 MB 的有用文件。大部分都是 log。這 80 MB 的文件裏面,還有可以排除掉的,但可能有危險,就算了。這樣以後要搭建新環境的時候,只要下載 80 MB 的文件就行了。之後把這些文件提交到 Gitlab 倉庫上。
- 代碼同步
如果使用 Gitlab ,那麼要怎麼把代碼同步到線上機器呢?最簡單的就是在線上目錄下直接用 git pull 了,只是要記得把 .git 目錄屏蔽掉,不然別人直接把你 .git 下載過去,就能得到你整套代碼了。當然,也可以在另外一個目錄 pull ,然後用 rsync 同步到線上目錄。後一種方式會比較安全,不過沒有采用,可能是因爲要支持線上修改代碼?
線上修改代碼是爲了在上線代碼後出現問題時能夠立即在線上修改代碼,然後不能影響到後續代碼上線。要補充一點,這個項目沒有定期發佈版本的概念,一旦完成需求,就立即上線。既然是這樣,就不能直接 git pull 。於是我寫了個腳本專門用來同步。當線上代碼和要更新的代碼有衝突時,自動處理衝突——其實就是自動用衝突部分的線上代碼覆蓋上線的代碼。 - 培訓
上面準備工作做完了,就得向組內其他人推廣了。組內只有我那同學有接觸過git,但也是那種 push 和 pull 的程度。這次用的是 GitHub Flow 的方式,就是 Pull Request + 審覈,沒有人可以直接 push 代碼上去。
我總共組織了三次培訓,每次半個小時到一個小時。把 git 的基本使用和 GitHub Flow 的方式都介紹了。然後還寫了篇教程。之後大家就開始用起來。
前後大概一個月的時間,前期準備花去大概三週(畢竟還有需求要做)。培訓前後跨度一週。培訓完的下一週開始使用。算起來應該要從四月初開始算起,到目前爲止合併了 760 個 Pull Request。
我們組四月份的時候來了個新人。當後來談起 git 的時候,我說了在三月底才逐漸開始使用 git 以及以前是怎樣管理代碼的時候,她都驚呆了。
2. 測試機
讓我們回到還沒使用 git 的時候。
上面說了修改代碼時通常要在線上修改,爲什麼不在測試機上修改呢?
- 測試機代碼跟線上代碼有很多不同的地方。數據庫也有很多不同。我那個同學就曾經栽在數據庫表字段不統一上。
- 我們的系統依賴於其他系統提供的數據,在測試機上難以獲取所需數據,雖然他們也有提供測試機,但不好找到合適的數據。
不過我也有一段時間是在測試機上修改,然後複製到線上。但是 vim 編輯雖然挺熟悉的,但還是不舒服。後來就下載 PHPStorm ,在本地電腦上修改,然後通過它內置的 ftp 傳到測試機上測試。
爲啥不在本地搭建個環境呢?這其實也挺困難的。
- 代碼有很多依賴,甚至於有些代碼寫的是用 linux 的路徑。你在 windows 下根本沒法跑。
- 把系統跑起來的數據庫表要先建好,數據要先準備好,但不好找到需要哪些表。
第一點還好解決,畢竟有虛擬機呀。第二點就比較麻煩,全部數據都同步的話,要幾十個G吧。
於是本地搭建環境這點就先擱置了。要等到接下來說的這件事做完纔有繼續的可能。
3. 調試
先說之前的開發是如何調試代碼的。首先肯定是在線上調試,調試的手段是 echo var_dump print_r 這些,直接把數據打印到界面上。是的,你沒看錯。用戶都能看到他們打印出來的信息。
大量的數據暴露給用戶,而且我之前還看到把一個接口請求的 key 暴露出來。更慘的是,這是已經調試完,沒有刪掉的。也就是說存在相當長的一段時間。事實上代碼裏面有大量的這種輸出信息沒有刪除。所幸直接用戶都是公司內部員工,這要是外部人員使用,早炸了。
對於開發者而言,如果是自己寫的代碼,echo 看一下可能很快就找到問題。但如果是別人寫的代碼,可能要 echo 一大堆才知道問題在哪。更可怕的是,這如果是系統基礎層面的問題,根本不知道在哪找 bug,那代碼一坨坨(來自同事的描述)的。
有個比較好的地方是,這個系統有備機。我在備機上裝了個 Xdebug,這才使得斷點調試成爲可能。當然,爲了支持多人能同時調試,還去裝了個 dbgp。這些在我之前的博客中有寫了:php+xdebug+dbgp遠程調試(多人)。當時發到博客園首頁,還被管理員踢掉哈哈哈。
此後我都是使用 IDE 來斷點調試,這才能知道系統到底是怎麼運行的。
4. 本地環境
能夠斷點調試意味着我能夠知道它連接了哪些數據庫,查了哪些表。這樣就可以開始做配置了。
爲了方便以後新搭建系統,我還寫了個半自動初始化的腳本。把要修改的配置組織好,然後轉化成原系統可用的數據。還提示了要把系統跑起來,需要同步哪幾個表格。但是碰到路徑依賴的代碼時,仍然要再去手動修改代碼,然後重試。
這種方式持續很長一段時間,直到我最近接觸 Docker 纔得到解決。我在 Windows 10 上面裝了 Docker。然後對 PHP 鏡像做了定製,其實就是額外裝了 xdebug 、 composer(PHP 的包管理工具)、一些項目會用到的 php 插件。
經過一些配置的調整,總算是跑起來了。效果還不錯~不過還有幾個地方需要調整和添加的,後續再繼續處理。
5. 單個文件太大
看以下幾個數字:
19264 17622 13223 12160 6621 6067 4655
這些是幾個主要文件的行數,最大一個將近兩萬行了。用 PHPStorm 打開後有點吃力,解析要好一會兒。
裏面其實是很多個 if-elseif-else 組合成的,每個分支完成一件事情。其實可以把它們看爲一個個 function,事實上把它們抽取爲 function 也能正常執行。最大的一個文件由大概 200 個分支組成,平均每個分支 100 行代碼。但是也有不少分支有 5~6 百行代碼,3 百行的 foreach 循環瞭解一下?
後來我做了個改變。前面有提到,它在某一個地方會有一個統一的入口,然後也有一個統一的出口。我另外建了個文件夾,然後建了一個文件作爲新的入口,把滿足條件的請求轉發到這個入口。就像是進入一個子系統。
這個子系統可以直接使用 PHP 的一些特性,例如命名空間。在裏面寫代碼直接用面向對象的方式來,只要最後返回的結果是 json 就行了。這樣可以爲以後的重構(其實是重寫)做準備,提前寫好一些工具類,積累一些需要注意的點。
在實現上面,沒有直接使用開源框架。一個是編碼問題,我們項目是 GBK 編碼(其實也不是很難解決);另一個是以後項目重寫的成本問題。目前不用框架,把骨架搭建起來還是蠻快的。在一些實現上面會去參考開源框架,像 Yii2 和 Laravel ,也會往 PSR 上面靠。
這樣單個文件的大小就變小了,變成多個小文件。很多可以重用的代碼也可以封裝起來,放到類裏面。前面那麼多年,沒有留下什麼有價值的代碼,實在是可惜。如果以前有封裝一些業務相關的代碼,現在要了解業務也比較方便些。
新的代碼會寫在這裏面。如果有舊代碼需要維護,且感覺可以重構的時候,也會把代碼重構到子系統裏面。
這個是在 git 之後。因爲做這種大規模添加和變更的時候,有 git 會比較好處理。
6. 測試
這項目的測試基本都是在測試機上直接進入網頁測試,沒有什麼單元測試的概念。有時候測試機沒辦法測試,只能到線上測。Emmm...是挺危險的。
嗯對,沒有專門的測試人員。
現在 git 有了、IDE 有了、面向對象也有了,可以瞭解一下單元測試。正好公司有這方面的績效要求。
以前那些代碼很難測,我先處理的是面向對象這部分的單元測試,引入 PHPUnit。先把工具類的單元測試搞定了。
接着想着怎麼測試以前的代碼。還是從單一入口那裏入手,一些全局變量都事先聲明好,還有一些文件都導入進來,然後調用。當然還要處理其他問題,例如原先的代碼裏面,會直接 echo 出東西,這些要用 ob_start() 這種攔截下來。不過 PHPUnit 也有對這方面的支持。
7. 文檔
這項目沒有什麼文檔,他們的理由是業務經常變化,文檔要經常改,麻煩。不過連基礎的業務無關的開發文檔都沒有,確實麻煩。新人來了還得找導師問,因爲並沒有相關的培訓課程。
後來我寫了篇開發文檔,填補了這個空白。(據說寫得不錯)
自從引入了子系統,也補充了篇開發文檔。不過因爲結構挺清晰了,不用文檔也能開發。
至於業務上的文檔還沒有寫。之前公司的機器在做集羣架構調整,目前我們這邊還在做相應改變。調整完成後,我會逐漸把業務文檔補充上去。
8. 重構
上面提到這個項目要重構(重寫)。這個其實才是我最感興趣的地方,因爲能學到不少東西。到時要向大佬們多請教。如果到時有大腿來指導就好了。
結束
你可能會問,哪來時間做這些事情?不用做需求嗎?我說一個數據。我們加班是沒有加班費的,但是有一個誤餐補貼。每天加班到八點半後就有 20 塊錢,而我上個季度的補貼是 1k+。
這一年間做的事情看起來不是很多,也不是很少,唯一能確定的是學到的東西不多。這是最讓我憂慮的地方。這一點之前我們部門的一個大佬跟我聊的時候有說過,孤軍奮戰進步很慢。其實部門裏面的高手也是有的,但我不善於使用已有資源這一點是硬傷,要想辦法有所突破。
希望接下去能有更多進步的機會~我也會去多爭取這樣的機會。