77
JAVASCRIPT CODE QUALITY 講者 josephj 關於 JavaScript 品質,我想說的是...

JavaScript Code Quality

Embed Size (px)

DESCRIPTION

「關於 JavaScript 品質,我想說的是...」於 JSDC.tw 2013

Citation preview

Page 1: JavaScript Code Quality

JAVASCRIPT CODE QUALITY

講者 josephj

關於 JavaScript 品質,我想說的是...

Page 2: JavaScript Code Quality

當談到 JavaScript 品質...

大家心裡的重要性就好像左邊那一本 XD

Page 3: JavaScript Code Quality

JavaScript 的興起...讓開發者覺得 JavaScript 就是要複製貼上

Page 4: JavaScript Code Quality

Bad JavaScript = 違章建築

寫程式應該是被維護的,而不是一看就想要拆掉的

6000 ⾏行 JavaScript!

Page 5: JavaScript Code Quality

Agenda

·在 Yahoo! 學到的教訓

·需要建立前端團隊的架構

·在 miiiCasa 學到的教訓

·需要先設計、多抽離

·未來可持續努力的方向

·自動化 Plus

成長 = 從過去的錯誤反省!

Page 6: JavaScript Code Quality

第 1 課 from Yahoo!前端⼯工程師必須成為真正的團隊、⽽而⾮非只是資源

Page 7: JavaScript Code Quality

中央游泳池當時「前端⼯工程師」不屬於任何產品線

專案 1

專案 2

F2E Pool不會有閒置資源

Page 8: JavaScript Code Quality

中央游泳池當時「前端⼯工程師」不屬於任何產品線

專案 1

專案 2

F2E Pool基本上都在專案

任務導向、缺少團隊機制

大家只管自己的眼前的案子

缺少水平的機制去管理品質

Page 9: JavaScript Code Quality

久了必會發生慘案

• 由新人負責、跟其他專案成員關在小房間

(沒有其他 F2E)

• View 全部用 JS 組、後端只給 JSON

• 不支援 NoJS、沒法上一頁

• 重複程式碼非常多,沒人敢大改

• 專案結束沒多久,新人離職

• 2012 年底,此服務關閉

Page 10: JavaScript Code Quality

永遠在我心裡的一句話

“As a team, F2E 的價值究竟在哪裡?”

好不容易培訓⼀一個新⼈人、卻⼜又因制度不健全害了他

Page 11: JavaScript Code Quality

開始去思考「團隊」這件事

• Code Convention 團隊規範

• Code Review 定期 Review 程式碼

• Checklist 上線前需達成的清單

⾮非常重視⼀一致性、維護性

Page 12: JavaScript Code Quality

規範一些最基礎的小事情

一致性、像同一個人寫的

https://github.com/josephj/f2e-styleguide

Page 13: JavaScript Code Quality

Code Review

http://programmers.stackexchange.com/questions/170211/php-data-access-layer

程式碼接受所有⼈人檢驗

Page 14: JavaScript Code Quality

比 JSLint 更傷感情 XDhttps://github.com/miiicasa/youtube-iframe/issues/1

Page 15: JavaScript Code Quality

需要比較正面的態度

每次 Code Review 都能學到很多東⻄西!

Page 16: JavaScript Code Quality

Pair Programming

http://en.wikipedia.org/wiki/File:Pair_programming_1.jpg

⼀一個⼈人寫程式、⼀一個⼈人當觀察員

Page 17: JavaScript Code Quality

導入正確的模式模組化 (Module) 與觀察者 (Observer) 模式

Page 18: JavaScript Code Quality

模組:寫法一致、避免全域變數

welcome/_notification.js

⼀一個模組即有⼀一個 JS 檔

Page 19: JavaScript Code Quality

模組:寫法一致、避免全域變數

YUI.add("welcome/_notification",  function  (Y)  {    var  trans  =  {},            module;

   module  =  new  Y.Module({            selector:  "#notification",            init:  function  ()  {                                },            on:  {                    viewload:  function  (node)  {

                   }            }    });

},  "0.0.1",  {"requires":  [            "module"    ]});

welcome/_notification.js

以 CLI ⾃自動從樣板建⽴立模組⼀一個模組 (Partial) 配⼀一個 JavaScript 檔

好⽤用⽅方法與屬性都圍繞在此 instance

⼀一旦當此模組存在於 DOM 之中

相依模組

此模組的 Selector 或物件參考

Page 20: JavaScript Code Quality

模組:寫法一致、避免全域變數

YUI.add("welcome/_notification",  function  (Y)  {    var  trans  =  {},            module;

   module  =  new  Y.Module({            selector:  "#notification",            init:  function  ()  {                                },            on:  {                    viewload:  function  (node)  {

                   }            }    });

},  "0.0.1",  {"requires":  [            "module"    ]});

                           module.broadcast("greeting",  trans.greeting);                            module.listen("need-­‐love",  function  (e)  {  /*  do  something  */  });                            new  Y.ScrollPagination({node:  node});

welcome/_notification.js

所有的功能都圍繞著模組化開發

多國語系

送廣播

聽廣播

加上其他相依模組

trans.greeting  =  module.getTrans("greeting",  "Hello  World");

,"module-­‐popup",  "module-­‐intl",  "gallery/scroll-­‐pagination"

Page 21: JavaScript Code Quality

模組:寫法一致、避免全域變數

YUI.add("welcome/_notification",  function  (Y)  {    var  trans  =  {},            module;

   module  =  new  Y.Module({            selector:  "#notification",            init:  function  ()  {                                },            on:  {                    viewload:  function  (node)  {

                   }            }    });

},  "0.0.1",  {"requires":  [            "module"    ]});

                           module.alert(trans.greeting);                            module.broadcast("greeting",  trans.greeting);                            module.listen("need-­‐love",  function  (e)  {  /*  do  something  */  });                            new  Y.ScrollPagination({node:  node});

welcome/_notification.js

所有的功能都圍繞著模組化開發

多國語系

共⽤用的 popup

送廣播

聽廣播

加上其他相依模組

trans.greeting  =  module.getTrans("greeting",  "Hello  World");

,"module-­‐popup",  "module-­‐intl",  "gallery/scroll-­‐pagination"

限制團隊成員在沙箱(Sandbox) 中開發

功能擴充也能有一致介面

只專注開發該模組功能、無法污染外界

Page 22: JavaScript Code Quality

不只是 JavaScript Ninja!認識軟體⼯工程與持續集成,事半功倍!

Page 23: JavaScript Code Quality

需從軟體工程的角度出發

• Lint 自動檢查程式碼

• Combine/Compress 自動合併及壓縮 JS/CSS

• Optimize image 圖檔最佳化

• Compass 用 High Level 的語言讓 CSS 簡化

• Skeleton 自動建立樣板檔案

導⼊入⾃自動化、減少「⼿手⼯工」、才能提昇品質

Page 24: JavaScript Code Quality

在 miiiCasa 實踐了這些理念很幸運可以從零開始、擔任前端⼯工程部⾨門主管

這些想法與理念都⼀一⼀一地實踐了

難道就不會有問題了嗎?

http://www.slideshare.net/josephj/ss-7705095

建立前端開發團隊

Page 25: JavaScript Code Quality

第 2 課 from miiiCasa 必須先設計、並導⼊入更好的 JavaScript Pattern

Page 26: JavaScript Code Quality

miiiCasa Space 家庭雲:用單一 URL 存取家中裝置上的資料

• 4 個不同的媒體:照片、音樂、影片、文件

• 2 個不同的類型:首頁列出 Storage、內頁只列目錄

• 都是檔案列表、但差異很大:• View 都各自有差異

• 照片要 Slideshow

• 音樂要播放器

• 影片用 VLC / Flowplayer

• 文件要用 Lightbox 顯示照片、同時支援

影片、音樂、Google Drive Preview...

Page 27: JavaScript Code Quality

有時程壓⼒力...該怎麼快速產出?

Page 28: JavaScript Code Quality

『coding』= 複製貼上的工作

http://but.tw/2008/10/programmers_rule/

團隊的每個⼈人都有責任

Page 29: JavaScript Code Quality

用「複製、貼上」達成需求與滿足時程

• 一模組約 1,500 行 JS

• 4 個不同的媒體

• 2 個不同的類型

• 差異很大 = 修修改改

1,500*4*2 = 12,000 行

後續有新功能都幾乎是「複製貼上 x 8」

Page 30: JavaScript Code Quality

Winchester Mystery House

http://www.flickr.com/photos/harshlight/3670192350/

外表富麗堂皇、也確實地達成應有功能與時程

Page 31: JavaScript Code Quality

http://www.flickr.com/photos/dalvenjah/33560263/lightbox/

內部的結構...

http://www.flickr.com/photos/skinnylawyer/5570163978/

疊床架屋是軟體開發最常見的問題

在煙囪上架屋頂? ⾛走向屋頂的樓梯?

Page 32: JavaScript Code Quality

壓縮開發時程之謎

Stoyan Stefanov

The few man-hours spent writing the code initially end up in man-weeks spent reading it.

數⼩小時寫出的程式碼、得花數週或更多的時間維護它

要在 8 個 JavaScript 上:

• 增加新功能

• 修改 Bug

• 改變邏輯

• 看不懂、乾脆重寫

⽼老闆跟專案經理永遠無法理解的事...

Page 33: JavaScript Code Quality

面對問題未來要怎麼預防同樣的問題、或避免讓問題繼續蔓延

Page 34: JavaScript Code Quality

EDD – 設計文件圖表

BranchURLs

檔案結構

Controller

模組詳細說明

預先規劃的假資料

JS 的架構日益複雜

需要跟後端程式一樣先規劃

負責人先仔細思考、撰寫出此文件

所有開發人員一起討論可行性、問題點

把思考不周延之處找出來、了解配合模式

時程預估較為準確、有機會先預防

Page 35: JavaScript Code Quality

Refactoring - 重構

Page 36: JavaScript Code Quality

重構 = 打掉重寫?

應避免 NIH 綜合症(非我所創、Not Invented Here Syndrome)

NO!

技術人員應該用更客觀的心態面對重構

重構 = 持續且小的改進。

Page 37: JavaScript Code Quality

開發 重構 開發 重構

把重構納入產品開發的 Iteration

......

無法⼀一開始寫出完美的程式碼藉由實作、看到問題與需求,再著⼿手修正

逐步達成理想的程式碼品質

Page 38: JavaScript Code Quality

重構案例:Pop-up 視窗

util.showDialog(module,  title,  message,                                  okText,  cancelText);

module.broadcast("dialog-­‐show-­‐request",  {        title:  title,        msg:  message,        buttons:  {                ok  :  {                        label:  okText,                        callback:  okHandler                },                cancel  :  {                        label:  cancelText                }        }});

new  Y.Panel({        headerContent:  title,        bodyContent:  message,        buttons:  []        render:  true,        visible:  true})

過去⾄至少有三種不同的作法

統⼀一作法、將預設⾏行為確認

module.alert(message);

module.confirm({        title      :  title        content  :  message},  okHandler);

module.inform(message);

對程式碼精簡、⾏行為的⼀一致性獲得⼤大幅改善

Page 39: JavaScript Code Quality

重構案例:Pop-up 視窗

util.showDialog(module,  title,  message,                                  okText,  cancelText);

module.broadcast("dialog-­‐show-­‐request",  {        title:  title,        msg:  message,        buttons:  {                ok  :  {                        label:  okText,                        callback:  okHandler                },                cancel  :  {                        label:  cancelText                }        }});

new  Y.Panel({        headerContent:  title,        bodyContent:  message,        buttons:  []        render:  true,        visible:  true})

過去⾄至少有三種不同的作法

統⼀一作法、將預設⾏行為確認

module.alert(message);

module.confirm({        title      :  title        content  :  message},  okHandler);

module.inform(message);

對程式碼精簡、⾏行為的⼀一致性獲得⼤大幅改善

重構的價值

減少程式碼中光怪陸離的現象

未來開發更容易

減少 UI 的不一致

Page 40: JavaScript Code Quality

Decoupling - 抽離

避免把所有程式碼都放在一個 JS 中

Page 41: JavaScript Code Quality

為什麼會產出「一大包」JS?

• 麻煩!JS 載入需考慮「前後順序」<script/> 有先後順序,例如載入 jQuery UI 的 Tabview.

• JS 本身缺乏「模組」的觀念直到最近 ECMAScript 才要內建

• 「頁面」眾多、分割不易不像其他的語言,大多只要 require 就可以使用

• 對 JavaScript 缺乏「軟體工程」的態度非本科:對程式抽離、正規化概念薄弱。本科:對 JS 不用想這麼多吧!

Page 42: JavaScript Code Quality

抽離範例:Space Photo

• 主要功能:列出 Router USB 裡的照片。

• 其他功能:

提供下載檔案、幻燈秀、縮圖產生機制、

檔案描述、LightBox、分享、重新命名等機制。

這才是此程式中最重要的

每一個都可以另開 Module 處理

Page 43: JavaScript Code Quality

代碼抽象三原則

• DRY - 不要重複自己出現重複時就抽象出一個解決方法

• YAGNI - 你不會需要他快 + 簡單!不要把精力放在抽象化上

• Rule of 3 - 三次原則當同樣的情況出現三次才進行抽象化

http://www.ruanyifeng.com/blog/2013/01/abstraction_principles.html

Page 44: JavaScript Code Quality

功能抽離範例

space-centralize.js (共 103 行)

/**  *  @module  space-­‐centralize  */YUI.add("space-­‐centralize",  function  (Y)  {

       //  Implementation  here...

       Y.Space.bindCentralize  =  bindCentralize;

},  "0.0.1",  {        "requires":  [                "space",  "node-­‐base",  "event-­‐resize"        ]});

需要讓圖⽚片隨螢幕寬度置中對⿑齊,瀏覽較舒適

原本負擔很重、1500+ ⾏行的 JS 檔

/**  *  @module  space/photos/_photos_main  */YUI.add("space/photos/_photos_main",  function  (Y)  {

       //  ...        Y.Space.bindCentralize();        //  ...

},},  "0.0.1",  {        "requires":  [                //  ...                "space-­‐centralize",                //  ...        ]});

只增加兩行

Page 45: JavaScript Code Quality

採用模組化 Library解決 HTML 中 JavaScript 缺乏模組機制的問題

define(“editor”, [‘a’,’b’,’c’], function () {    function Editor { /* Constructor */ }    return Editor;});

require(["editor"], function (Editor) {    new Editor();});

editor.js – 定義 editor 模組

demo.js – 使⽤用 editor 模組

任何⼀一個網站都應該要⽤用!

Page 46: JavaScript Code Quality

應加入的規範

• 一個 JS 檔案不應超過 500 行

• 一行不應超過 100 個字元

維護一個大雜匯的 JavaScript 有何意義?

會超過 500 行應代表你該抽離功能了

Page 47: JavaScript Code Quality

未來還能做些什麼

Page 48: JavaScript Code Quality

給自己未來的一些題目

大多是更多工具的整合、讓自動化來提升品質

⼯工具 ⼯工程師

•無聊•重複性的•浪費時間的 藉由⼯工具持續回饋

Page 49: JavaScript Code Quality

自動偵測重複的原始碼

需要把「避免重複原始碼」變成⼀一個習慣

Page 50: JavaScript Code Quality

是很重要的一件事!

大家所熟悉的 JSLint, JSHint 卻沒有 Orz...

都只有「單一檔案內」的語法檢查

Page 51: JavaScript Code Quality

PMD - 原始碼分析器

其中 CPD 工具可以分析 JS 的重複

/bin/run.sh  cpd  -­‐-­‐minimum-­‐tokens  50                                  -­‐-­‐files  ~/project/foo/js                                  -­‐-­‐language  ecmascript

Page 52: JavaScript Code Quality

跑出來的結果重複的行數 重複的檔案列表

非常需要納入 Commit 流程或 Auto Build 檢查

Page 53: JavaScript Code Quality

Error Log 管理JavaScript 錯誤應該被即時記錄並回報

瀏覽器錯誤

window.onerror

try-catch

錯誤發生

程式決定是否要往上丟

程式決定是否要往上丟 把錯誤丟到 Server 做記錄、回報

就不用辛苦地翻程式碼找問題

控管好這兩個時間點

Page 54: JavaScript Code Quality

       //  Log  to  server.        window.onerror  =  function  (message,  url,  line)  {                var  queryString,                        el;                queryString  =  location.search.slice(1);                el  =  document.createElement("img");                el.src  =  LOG_URL  +  "?"  +  [                        "hostname="  +  encodeURIComponent(location.host),                        "message="  +  encodeURIComponent(message),                        "line="  +  encodeURIComponent(line),                        "url="  +  encodeURIComponent(url),                        "query="  +  encodeURIComponent(queryString)                ].join("&");                return  true;  //  Avoid  browser  error.                                                                                                                                                                  };

把錯誤回報給 Server常見作法:用假圖片 Request 達成、可跨網域

但是!錯誤需要被 Grouping 才有價值

Page 55: JavaScript Code Quality

推薦工具 - Sentry接收各種程式語言錯誤、分組、寄通知信

Page 56: JavaScript Code Quality

Esprima

http://esprima.org/

最近只要聊到 JavaScript Code 品質,幾乎都會聽到這個工具

Page 57: JavaScript Code Quality

Esprima 運行原理

程式碼 抽象語法樹Source Code Abstract Syntax Tree

var  answer  =  6  *  7;

{    "type":  "Program",    "body":  [        {              "type":  "VariableDeclaration",              "declarations":  [                  {                      "type":  "VariableDeclarator",                      "id":  {                          "type":  "Identifier",                          "name":  "answer"                      },                      "init":  {                          "type":  "BinaryExpression",                          "operator":  "*",                          "left":  {                              "type":  "Literal",                              "value":  6,                              "raw":  "6"                          },                          "right":  {                              "type":  "Literal",                              "value":  7,                              "raw":  "7"                          }                      }                  }              ],              "kind":  "var"        }    ]}

將程式碼轉成結構⼀一致的 AST 格式,很容易做後續處理

經過 Esprima Parse

Page 58: JavaScript Code Quality

Esprima 範例 – CodePainter問題:每個⼈人寫的空格、引號習慣都不⼀一樣,能否統⼀一?

跑⼀一次⽤用 Esprima 實作的 CodePainter ⼯工具,問題全搞定

https://github.com/fawek/codepainter

Page 59: JavaScript Code Quality

Esprima 的可能應用

• JSHint: 將會改用 Esprima

• 語法高亮

• 塞 Log,協助偵錯:

• 在每個 Method 都放:console.log("someMethod()  is  executed.")

http://www.slideshare.net/ariyahidayat/javascript-parser-infrastructure-for-code-quality-analysis

Page 60: JavaScript Code Quality

Continuous Integration 持續集成「所有」⾃自動化檢查,都應該與 CI 整合

中央 Git

個⼈人 Git

git  push

post-­‐receive

trigger

git  clone

Build

JSHint

Code Duplication

Unit Test

Functional Test

Code Coverage

通知

成功或失敗

Performance

讓檢查像喝⽔水⼀一樣簡單、確保品質

CI 伺服器

Page 61: JavaScript Code Quality

推薦服務

CodeShip

懶得⾃自⼰己架⼀一台 CI 伺服器?市⾯面上有好幾家 SaaS 的服務可⽤用

若你的原始碼是放在 GitHubCodeShip 與 Travis 都只要勾選就可以使⽤用

Page 62: JavaScript Code Quality

把團隊更緊密的綁在一起把所有訊息 (Commit, Wiki, Issue) 都倒進聊天室

不用交談也知道彼此的做了什麼、點選可看詳細內容

Page 63: JavaScript Code Quality

文學編程Literate Programming

寫程式的理想型態

Page 64: JavaScript Code Quality

Donald Knuth現代電腦科學的鼻祖

A style of programming that maximize our ability to perceive the structure of a complex piece of software.

文學編程的理念

「是時候了,讓我們的程式碼更好!」

文學編程是一種寫程式的理想形態 :

讓我們對於軟體的每個複雜環節都能

充分地理解

Page 65: JavaScript Code Quality

更有組織維護自己的文件Leo Editor - 依自己的思路撰寫文件、組織代碼

可以是任何的內外部⽂文件、節點可以任意地被複製搬移

⾃自由地組織、編排節點順序

產出的程式碼可與其他⼈人 Merge、Leo 會幫你追蹤⽀支援 Textile、Web 投影⽚片的輸出

Page 66: JavaScript Code Quality

更有組織維護自己的文件Leo Editor - 依自己的思路撰寫文件、組織代碼

可以是任何的內外部⽂文件、節點可以任意地被複製搬移

⾃自由地組織、編排節點順序

產出的程式碼可與其他⼈人 Merge、Leo 會幫你追蹤⽀支援 Textile、Web 投影⽚片的輸出

Page 67: JavaScript Code Quality

Leo Editor 很強大、也不針對特定語言,但是...

每個人都有自己愛用的編輯器(Vim 或 Sublime)

Leo 在程式碼編輯功能上似乎不怎麼樣...

在一堆編輯器中切換也是件很愚蠢的事

以喜歡的編輯器、⽤用⽂文學編程寫 JavaScript

Leo 的缺點

Page 68: JavaScript Code Quality

https://github.com/jostylr/literate-programming

以 JavaScript 實作的文學編程

編譯 Markdown

產出 JavaScript

James Taylor

Page 69: JavaScript Code Quality

https://gist.github.com/josephj/5580918

Demo

>  File  count.js  savedliterate-­‐programming  ⽂文學編程.md                                                                                                                                                                                                                                                                    

寫程式就像寫部落格⼀一樣!

可重新組合章節讓別⼈人⾮非常容易理解

Page 70: JavaScript Code Quality

Literate CoffeeScriptCoffeeScript 的作者 = 文學編程的大力推廣者

Jeremy Ashkenas

Journo 部落格系統

Docco API 文件產生器

以⽂文學咖啡寫成:

缺點:得依照原始碼的順序寫

Page 71: JavaScript Code Quality

Journo 的 README.md就是部落格系統的完整原始碼 ( journo.litcoffee)

$  coffee  -­‐c  journo.litcoffee  #output  journo.js

Page 72: JavaScript Code Quality

文學編程過去不是主流、未來也不會

文學編程小結

但身為一個開發者,應秉持文學編程的精神:

「程式碼是給人閱讀的」

以上兩種作法,似乎也可以稱為「部落格編程」

是我們現今最容易理解技術的一種 Material

Page 73: JavaScript Code Quality

Code Review

設計⽂文件 產出 API ⽂文件

模組化

模組架構代碼規範Pair Programming

檢查清單⾃自動合併與最⼩小化

Lint

單元測試物件導向

重構

代碼重複檢查

JS 語法分析、重置

持續整合

品質的 TODO LIST

抽離原則⽂文學編程

團隊活動 觀念及架構 ⾃自動化

整合訊息

看團隊需求來做導入或整合

Prototyping

Page 74: JavaScript Code Quality

寫程式是個良心的事業

過去的評價會⼀一直跟著你,我們應該盡⼒力寫好

Page 75: JavaScript Code Quality

程式碼品質,大師們都會強調再強調

Page 76: JavaScript Code Quality

2b || !2bthat is a question.