開發流程

  一個完整的開發流程應該有這四步:分析->設計->編碼->測試。不少開發團隊每每只有編碼這邊,弱化了其餘步驟,他們拿到需求就開始寫代碼, 寫着寫着發現有問題,要麼是遇到一個難點解決不了,要麼是發現要返回修改之前寫過的代碼, 要麼是發現有大量的重複代碼,又不知道怎麼封裝,只能將錯就錯。作好了分析和設計編碼時就不會有這麼多問題, 作好了測試產品bug就少,產品質量才高。 下面我分別詳細講解一下這四步。php

分析

  分析的時候,咱們要分析需求和難點。   分析需求的方法是作需求陳述處理,前面我提到過, 要區分作什麼和怎麼作,把這兩部分獨立出來,作什麼是固定不變的, 而怎麼作可能會常常變。咱們再熟悉一下舉的那個例子:咱們要作一個成員列表(如圖1-44),產品經理告訴咱們要按姓名拼音排序。 前端

圖1-44 成員列表的例子laravel

  咱們有時候不能直接聽產品經理的,若是真寫死成按姓名拼音排序就沒有可擴展性了,好比某一天產品經理又告訴你須要把VIP會員提早,那麼你只能再去修改排序的程序。這個需求始終不變的是排序,按姓名拼音只是排序的一種方法,咱們在設計數據庫時應該把排序字段設置爲數字而不是拼音,再寫一個拼音轉換爲數字的算法便可,這樣在後面排序規則變化,好比VIP會員要提早,只是修改對應用戶數據庫的排序字段數值便可,不用大改程序。   咱們能夠用xmind作需求分析, 先把看見和聽見的全部需求一條一條的列出來。拿到產品原型的時候,一點一點看,把看見的每一個地方都先列出來 。   而後在每條需求進行分析, 看看可否區分作什麼和怎麼作,若是能區分,在作這條需求後面建兩個分支,把分析結果寫上去,如圖1-45。    git

圖1-45 用xmind作分析程序員

  需求過了一遍後,咱們須要思考程序哪兒可能有難點,再把難點列到xmind中,好比程序列表這個功能,難點可能有:1,如何把中文姓名轉換爲拼音。2,若是把拼音轉換爲數字。   而後再想這兩個難點的解決方案, 在網上搜索發現有中文轉換拼音的開源庫(overtrue/laravel-pinyin),咱們直接用這個庫就行,把解決方案寫在xmind中對應難點的後面。另外,咱們還應該作一個簡單的demo,測試一些是否真的能解決。   程序員的二元性思惟比較多,每每認爲不是「是」就是「非」, 一個難點不能完美的解決感受就不能解決,咱們要有優雅降級的思惟。假設,咱們要作一個特殊的緩存的功能,緩存到服務端是最完美的,但有難度,很差實現。這時候想一想能不能優雅降級,不能緩存到服務端可否緩存到客戶端, 緩存到客戶端方案不完美,好比用戶可能會手動清空緩存,可是有緩存總比沒有好,那麼咱們能不能作呢?   咱們再分析剛纔第二個難點, 拼音轉換爲數字的算法, 咱們可能定義a轉換爲1, b轉換爲2,c爲3。 但有一個問題,咱們對人名的第一個字排序了,要不要對第二個字排序。 好比「張三」和「張飛」兩我的名, 第一張的拼音首字母都是Z ,咱們還須要對「三」和「飛」轉換爲拼音排序不? 要作完美的解決方案的話是要對第二個字進行排序的, 那麼怎麼排序? 假設咱們在這裏卡住了,暫時想不到好的解決方案,不少人就以爲這個功能作不了,不能作,咱們能不能優雅降級一下,先只對第一個字最排序,之後想到其餘解決方案再完善這一塊呢?算法

設計

  設計這一步,要體現出程序怎麼寫,咱們要設計出數據庫的表結構、API接口以及前端頁面等。設計這步也能夠在xmind中完成(如圖1-46)。 數據庫

圖1-46 用xmind作設計編程

  設計這部分必定要體現出程序怎麼開發,不能仍是列舉需求,要說明要創建什麼程序文件、要創建什麼類,以及類有哪些方法, 方法的具體處理過程,在xmind中常常會用這些短句: 「當XXX條件時作XXX」,「調用XXX方法」。   xmind在列這些「處理過程」時,就能發現不少重複的邏輯,咱們應該把這些重複的邏輯獨立成模塊(封裝爲類或函數)。這些要封裝的模塊咱們在列xmind的時候就都想好了,而不是邊寫代碼邊想,這樣會不斷的推翻之前的代碼再重寫,更加浪費時間。   作設計這步時,在設計類的時候注意前面說過的「正交設計、類要有專職,善於用委託」。類方法的處理過程要詳細到在實際寫代碼的時候照着xmind實現代碼便可,那個時候不用想程序邏輯了。   對於新手來講,寫代碼以前把全部細節都想到是有難度的,他們要邊寫邊想,寫到具體的地方纔知道有什麼細節。但這樣的習慣很很差,寫着寫着代碼邏輯就會變亂。必定要慢慢養成寫以前想清楚的習慣。從如今開始咱們養成寫代碼以前列xmind作分析和設計的習慣, 寫完代碼後再對比以前列的xmind 看看哪些是以前沒有想到的,要慢慢積累經驗,時間長了分析和設計能作得愈來愈好。   咱們在xmind中設計出有哪些接口和模塊後,還利於團隊的溝通和工做的分工。   xmind列好後開發人員要集體開會,全部人要理解某個模塊怎麼寫程序, 並作好分工,評估每一個模塊的開發時間, 由於xmind列的比較細,某個模塊的時間每每能以分鐘或小時來估計,若有些模塊還必須以天爲單位估時間的話,那證實這個模塊還能細化。   咱們分工好後,能夠在trello看板上作時間排期, 看板的用法後面會詳細講解。後端

編碼

  在編碼階段按照以前設計好的xmind編碼, 編程時還要防衛式的編程,這樣之後系統出問題咱們能及時發現和修復。 下面詳細介紹一下防衛式編程。   有時候咱們對本身寫的代碼很自信,認爲「這毫不會發生...」,好比咱們讀取某條數據,認爲這條數據確定不會爲空,而後沒有加任何判斷代碼。 若是某種以前沒有考慮到的狀況下,這條數據爲空了,你的程序就有bug了。這時候你再一點一點慢慢去找問題,可能要花幾個小時才能找到緣由,而若是以前作好了防衛式編程,數據爲空時有報警,就能讓你立刻解決問題。 防衛式編程就是在你認爲不可能發生的地方加上判斷代碼,即便狀況發生了咱們也知道。好比數組

//讀取一條用戶數據
$user=M('User')->find($id);
//若是你認爲這條用戶數據確定不爲空,那麼作個判斷
if(empty($user)){
 //若是爲空了,發個系統報警,讓開發人員知道。
 warn('讀取用戶信息爲空,用戶uid:'.$id);
}

  作好防衛式編程後,用戶再向咱們反饋bug,咱們的第一反應是去看報警,一看報警就知道了,原來在某個狀況下,讀取用戶信息可能爲空。 上面示例代碼中warn函數爲本身自定義的一個報警函數,咱們能夠把報警信息發到手機短信或郵箱。   咱們在「解決問題的方法」這一節講到過有時候在找程序bug的時候,可能找的報錯信息不是真正bug的緣由,之因此出現這種狀況也是沒有作好防衛式編程,一個變量在賦值時就有問題了,那時沒有作判斷,在使用時程序才報錯,問題緣由不是變量使用時的問題,而是賦值時的問題。這比如飛機遭到恐怖襲擊爆炸了,不必定是飛機的問題,多是安檢的問題。   另外對於程序可能出現的性能問題也要作好判斷。   在產品初期用戶量不大的時候,咱們追加的是簡單快速實現功能,而後上線收集用戶反饋,再完善調整產品。若是一開始就程序設計得很好, 考慮高併發狀況, 極可能上線產品沒有高併發狀況,甚至有由於用戶反饋很差功能要作調整,致使以前寫的代碼做廢。   因此通常產品初期時以簡單快速實現功能爲主, 產品後期才考慮性能問題。 但咱們要對簡單快速實現的代碼加一個判斷,讓咱們知道何時應該重構代碼了。 舉一個例子,好比咱們給系統用戶發郵件,剛開始用戶很少, 咱們能夠foreach循環來發郵件。  當用戶變多了,咱們必定要知道這個地方的代碼須要重構了,這時候須要把foreach循環的代碼改成用隊列發郵件了。     //讀取全部用戶的郵箱
    $users=M('user')->field('email')->select();
    //判斷用戶比較多的時候報警通知開發人員
    if(count($users)>5000){
        warn('用戶數已經大於5000,須要重構發郵件的代碼了');
    }
    //foreach循環發郵件
    foreach($users as $user){
        send_mail($user['email'],'郵件標題','郵件內容');
    }

測試

  測試的目的是爲了減小程序的bug,本身寫的程序本身必定要測試好了,肯定沒有問題再交付給其餘同事。 有的人工做經驗越久就愈來愈自信,認爲本身寫的代碼必定沒有問題,慢慢養成了寫代碼不測試的習慣。這樣會影響團隊之間協做,在團隊其餘成員那裏印象也很差。   想一想咱們若是是這樣的一個工做方式:  後端告訴前端接口寫好了,前端十分驚訝以爲後端的工做效率好高呀, 本身要加快速度了,前端的程序尚未寫到要調接口的地方。後端此時無所事事清閒的聽着音樂。前端終於程序寫到調用接口的地方了,結果一調用接口發現接口還報程序語法錯誤,前端十分生氣可是仍是壓住火告訴後端接口有問題。 後端回答「哦,是嗎?我看看」 ,後端去查找程序問題時,前端真的沒事幹了,只能處於等待狀態。 過一下子後端告訴前端 「程序好了,能夠了」 ,前端再調用接口沒有語法錯誤了,但發現邏輯走不通, 前端終於怒了,大聲對後端說 「你寫程序能不能本身測試一下」 。 後端義正詞嚴的回答「你反正都要調接口,不就至關於給我作測試了?」   這樣的工做方式效率是極其低的,後端是把本屬於本身的測試工做想讓前端測,後端在修改接口的時候前端只能等待,浪費前端的時間,前端也會很煩這樣的後端,不肯意和他合做。不少時候程序員和產品經理的合做也是這樣,程序員告訴產品經理程序寫好了,而產品經理測試有不少問題,程序員和產品經理的矛盾就此產生了。甚至若是沒有人給程序員把關,直接把產品呈現給了用戶, 用戶操做一兩步發現有問題就直接人走,他纔沒有這麼好心幫你測試,給你反饋問題。   作好測試,減小程序bug有下面四種方式:

  • 1,人工測試   人工測試是最簡單的方法,本身寫的程序必定要本身測試,並且要早測試,程序寫多了再測試,遇到bug找程序問題可能不太好找。   人工測試至少要保證本身寫的程序沒有語法錯誤和邏輯性錯誤。   若是交付的程序都有語法錯誤,那確定是沒有作測試的。   根據產品需求還要驗證邏輯是否是對的,有時候一個邏輯涉及到好幾處功能,這幾處都要連起來測試一下。

  • 2,單元測試   咱們在作人工測試的時候常常須要準備一些測試數據。好比:提交表單時填寫的數據、請求接口時的請求參數。下次再要測試這個功能又要從新填寫這些數據。「自動化」是程序員的生產力。人工測試也是能用測試自動化的, 咱們能夠寫「測試程序」來測試程序的功能,這樣那些每次都要填寫的數據,在測試程序裏面只寫一次能夠重複利用。下次要測試同一個功能,只要運行測試程序便可。不少人認爲寫自動化測試程序很浪費時間,其實咱們每次人工測試填寫測試數據一樣浪費時間,何不一勞永逸呢?   自動化程序測試又分好幾類, 有單元測試,集成測試,黑盒測試,端對端測試等等。   單元測試是人們提得最多的。各個編程語言都有單元測試框架, 如PHP有phpunit 、Java有junit 、 iOS有XCTest,並且iOS在建立項目時就有默認的單元測試代碼,足見蘋果對單元測試的重視。   你們要掌握一種你用的程序語言的單元測試框架。   單元是指一個不可在分的模塊,好比一個函數,一個類。某個功能能夠由不少單元組成,它要調用多個函數或類。 而單元測試是要求咱們從小單元開始測試,寫程序去測試函數或類,判斷函數或類的返回值是否正確。   寫好單元測試能強制讓咱們把程序架構設計好,高耦合的程序是沒法寫單元測試的。咱們必須作到程序的低耦合,一個函數不會和不少函數有關係,纔好作單元測試。   寫好單元測試也能幫助之後重構和修改代碼。咱們每每修改一次代碼可能會影響其餘多處代碼,容易產生bug,靠人工測試很容易漏測, 而若是有單元測試,咱們只要跑一下單元測試程序就知道本身修改的代碼有沒有問題。   人們提倡TDD開發模式不少年了, TDD(Test Driven Development)測試驅動開發,也就是說開發代碼以前先寫測試代碼。但這是要正真作到TDD開發仍是有難度,咱們每每爲了趕項目進度,程序員不肯意寫單元測試。能堅持寫單元測試的團隊很少, 在國內一個開發團隊能寫單元測試那麼他們的開發能力必定是國內領先的。

  • 3,作好報警

      咱們單元測試很難作到把全部地方都測試到,也就是單元測試的覆蓋率很難到達100%。 人工測試也不敢保證把全部地方都測試到了。咱們能夠作好系統報警,這樣即便用戶觸發了我沒有測試到的bug,咱們也能收到系統報警,而後及時修復bug。   當用戶觸發到程序bug的時候,程序每每會有報錯,咱們應該把程序報錯作成系統報警而後通知開發人員。   程序報錯通常分爲兩種,一種是FatalError終止性報錯。一種是warning報錯。   FatalError終止性報錯是當程序出現嚴重錯誤(如語法錯誤)致使程序沒法繼續運行,必須終止程序   warnning報錯 是程序認爲出現了小錯誤了,但程序還能繼續運行,不道德但沒有違法,不會被抓起來。 舉兩個PHP的例子:用file_get_contents 讀取文件,若是文件不存在會報warning錯誤,但不終止程序,會把文件內容看成爲空字符串處理,繼續執行下面的程序; 另外一個例子:在使用不存在的數組下標時,也會報warning錯誤。 有的人認爲warning報錯不重要,反正程序能正常運行,甚至把warning報錯屏蔽掉。但warnning報錯每每能反映程序有bug,好比使用了一個不存在的數組下標,多是由於粗心把下標的單詞寫錯了,但若是屏蔽了warning報錯,這個粗心致使的bug很難被發現。   觸發程序報錯的有可能不是開發人員,而是用戶,用戶看不懂程序報錯,須要把報錯作成報警通知給開發者。 我以PHP爲例,PHP能夠用set_error_handler和register_shutdown_function來接管報錯,如:

<?php
set_error_handler('error_handler');
register_shutdown_function('fatalError');
error_handler($errno, $errstr, $errfile, $errline){
        switch($errno){
            case E_WARNING: $severity = 'E_WARNING'; break;
            case E_NOTICE: $severity = 'E_NOTICE'; break;
            case E_USER_ERROR: $severity = 'E_USER_ERROR'; break;
            case E_USER_WARNING: $severity = 'E_USER_WARNING'; break;
            case E_USER_NOTICE: $severity = 'E_USER_NOTICE'; break;
            case E_STRICT: $severity = 'E_STRICT'; break;
            case E_RECOVERABLE_ERROR: $severity = 'E_RECOVERABLE_ERROR'; break;
            case E_DEPRECATED: $severity = 'E_DEPRECATED'; break;
            case E_USER_DEPRECATED: $severity = 'E_USER_DEPRECATED'; break;
            case E_ERROR: $severity = 'E_ERR'; break;
            case E_PARSE: $severity = 'E_PARSE'; break;
            case E_CORE_ERROR: $severity = 'E_CORE_ERROR'; break;
            case E_COMPILE_ERROR: $severity = 'E_COMPILE_ERROR'; break;
            case E_USER_ERROR: $severity = 'E_USER_ERROR'; break;
            default: $severity= 'E_UNKNOWN_ERROR_'.$errno; break;
        }
        $msg="{$severity}: {$errstr} in {$errfile} on line {$errline}";
        warn($msg);//調用報警函數

    }
function fatalError(){
        if ($e = error_get_last())
        {
                error_handler($e['type'],$e['message'],$e['file'],$e['line']);
        }
}
//報警函數
function warn($msg){
        //得到調用棧
        ob_start();
        debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
        $trace = ob_get_contents();
        ob_end_clean();
        //格式化調用棧
        $trace = preg_replace ('/^#0\s+' . __FUNCTION__ . "[^\n]*\n/", '', $trace, 1);
        $trace = preg_replace ('/^#(\d+)/me', '\'<br />#\' . ($1 - 1)', $trace);
        $msg.=$trace;//報警信息加上調用棧
        send_mail('upfy@qq.com','系統報警',$msg);
}

  上面代碼set_error_handler函數告訴了程序若是有報錯執行error_handler函數 ,  error_handler有四個參數, $errno 爲錯誤碼, $errstr 爲錯誤信息, $errfile 爲錯誤程序文件地址, $errline 錯誤所在文件的行數 。 $errno爲整數可讀性很差,因此代碼中用switch判斷整數的值設置一個可讀性較好的字符串。 另外有debug_print_backtrace 得到了調用棧, 報警信息中有調用棧的話方便咱們分析程序的問題。   由於set_error_handler函數不能接管終止性的錯誤信息,因此程序有用register_shutdown_function  指定程序終止時要執行的函數fatalError。 fatalError中用error_get_last得到錯誤信息並再調用error_handler函數。    上面的示例代碼只是簡單的舉例,正式的報警系統的代碼可能考慮的問題還有更多,簡單說一下下面兩個問題。

  • 1,如何防止重複的報警信息   咱們不能讓報警系統重複的報警信息一直髮給開發者,好比有一個程序bug,但如今同時5千人在訪問這個程序,不能報警5千次吧。   咱們能夠把發過的報警信息緩存一段時間,發報警時查詢一下緩存裏面有沒有同樣的信息,相同信息就不要再發了,緩存能夠用memcache等模塊作, 緩存時間能夠設置爲5分鐘。這樣重複報警信息5分鐘只會發一次。

  • 2,如何跟蹤觸發報警的用戶   不少用戶遇到產品有bug就直接走人,他纔不會好心給你反饋問題。咱們若是還想知道報警是哪一個用戶觸發的,能夠在報警代碼出讀取一個用戶信息一併寫到報警信息中,好比能夠讀一下這個用戶的用戶名,手機號等有用信息。咱們修復bug後還能聯繫以前觸發了bug的用戶讓他回來繼續使用產品。   如今除了郵件、短信等來接收報警信息之外, 還有更好的工具能夠用slack , slack既有PC客戶端也有手機客戶端,這樣發送的報警在電腦上能收到、在手機上也能收到。往slack上發送報警信息只要調用slack的接口便可,使用slack訪問官網:http://slack.com 。但slack是國外軟件,國內使用稍微比較慢,內國可使用仿slack的產品:紛雲(https://lesschat.com )。

  • 4,CodeReview   CodeReview也是減小程序bug的一個手段,CodeReview是指團隊成員之間互相審覈代碼,CodeReview能讓咱們發現程序由於粗心致使的低級問題或者代碼性能的問題。有些問題是作人工測試發現不了的。   好比有人在for循環裏面查詢數據庫,這是性能極其低的寫法,只有新手才這麼幹,這樣會致使每次訪問程序均可能查詢幾十次數據庫, 這幾十次查詢其實能夠合併成一條用in查詢的SQL語句,這樣只一次查詢就能夠。而這種問題只從產品功能來看是看不出問題的,不看代碼是發現不了。   CodeReview還能讓團隊每一個人都瞭解整個程序,避免出現「這程序不是我寫的,我改不了」的狀況。   有的人很難靜下心來看別人的代碼,那證實還處於實現階段尚未進入借鑑階段,能夠用本書前面說的分析代碼的方法去看別人的代碼。   若是咱們開發前作好了分析和設計的xmind,那麼看代碼以前先看看xmind上面列的程序處理邏輯,這樣再去看代碼就比較容易了。   每次CodeReview看新增或修改的代碼便可,之前看過的代碼不用再看,不用每次都從頭看起。能夠用版本控制工具對比功能來作CodeReview ,SVN用命令:svn diff , GIT用命令:git difftool, 具體svn或git的用法你們在網上搜索更多資料。

      以上四種方法能有效的保證項目的質量,咱們要寫好單元測試,但有很難作到單元測試覆蓋率100%,要確保主要功能作了單元測試,有些功能沒法用自動化程序測試的就人工測試,作好CodeReview能夠發現人工測試發現不了的問題,還有作好報警系統,這樣即便用戶觸發了咱們沒有測試到的bug,咱們能及時知道並及時修復。   另外還想提醒你們:好的團隊須要時間。不可強求團隊立刻就能把這些流程和工具用好。

相關文章
相關標籤/搜索