Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

當談到 JavaScript 品質... 大家心裡的重要性就好像左邊那一本 XD

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

Bad JavaScript = 違章建築 寫程式應該是被維護的,而不是一看就想要拆掉的 6000 ⾏行 JavaScript!

Slide 5

Slide 5 text

Agenda cᄝ:BIPP⇥֥֞࢝⇪ cླေࡹ৫భ؊ↀộ֥ࡏἧ cᄝNJJJ$BTB⇥֥֞࢝⇪ cླေ༵℟ίa؟Ԏ  cໃῲॖӻ⇟୭৯֥ٚཟ cሱọ߄1MVT 成長 = 從過去的錯誤反省!

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

中央游泳池 當時「前端⼯工程師」不屬於任何產品線 專案 1 專案 2 F2E Pool 不會有閒置資源

Slide 8

Slide 8 text

中央游泳池 當時「前端⼯工程師」不屬於任何產品線 專案 1 專案 2 F2E Pool 基本上都在專案 任務導向、缺少團隊機制 大家只管自己的眼前的案子 缺少水平的機制去管理品質

Slide 9

Slide 9 text

久了必會發生慘案 • 由新人負責、跟其他專案成員關在小房間 (沒有其他 F2E) • View 全部用 JS 組、後端只給 JSON • 不支援 NoJS、沒法上一頁 • 重複程式碼非常多,沒人敢大改 • 專案結束沒多久,新人離職 • 2012 年底,此服務關閉

Slide 10

Slide 10 text

永遠在我心裡的一句話 “As a team, F2E 的價值究竟在哪裡?” 好不容易培訓⼀一個新⼈人、卻⼜又因制度不健全害了他

Slide 11

Slide 11 text

開始去思考「團隊」這件事 • Code Convention 團隊規範 • Code Review 定期 Review 程式碼 • Checklist 上線前需達成的清單 ⾮非常重視⼀一致性、維護性

Slide 12

Slide 12 text

規範一些最基礎的小事情 一致性、像同一個人寫的 https://github.com/josephj/f2e-styleguide

Slide 13

Slide 13 text

Code Review http://programmers.stackexchange.com/questions/170211/php-data-access-layer 程式碼接受所有⼈人檢驗

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

需要比較正面的態度 每次 Code Review 都能學到很多東⻄西!

Slide 16

Slide 16 text

Pair Programming http://en.wikipedia.org/wiki/File:Pair_programming_1.jpg ⼀一個⼈人寫程式、⼀一個⼈人當觀察員

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

模組:寫法一致、避免全域變數 welcome/_notification.js ⼀一個模組即有⼀一個 JS 檔

Slide 19

Slide 19 text

模組:寫法一致、避免全域變數 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 或物件參考

Slide 20

Slide 20 text

模組:寫法一致、避免全域變數 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"

Slide 21

Slide 21 text

模組:寫法一致、避免全域變數 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) 中開發 功能擴充也能有一致介面 只專注開發該模組功能、無法污染外界

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

需從軟體工程的角度出發 • Lint 自動檢查程式碼 • Combine/Compress 自動合併及壓縮 JS/CSS • Optimize image 圖檔最佳化 • Compass 用 High Level 的語言讓 CSS 簡化 • Skeleton 自動建立樣板檔案 導⼊入⾃自動化、減少「⼿手⼯工」、才能提昇品質

Slide 24

Slide 24 text

在 miiiCasa 實踐了這些理念 很幸運可以從零開始、擔任前端⼯工程部⾨門主管 這些想法與理念都⼀一⼀一地實踐了 難道就不會有問題了嗎? http://www.slideshare.net/josephj/ss-7705095 建立前端開發團隊

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

miiiCasa Space 家庭雲:用單一 URL 存取家中裝置上的資料 • 4 個不同的媒體: 照片、音樂、影片、文件 • 2 個不同的類型: 首頁列出 Storage、內頁只列目錄 • 都是檔案列表、但差異很大: • View 都各自有差異 • 照片要 Slideshow • 音樂要播放器 • 影片用 VLC / Flowplayer • 文件要用 Lightbox 顯示照片、同時支援 影片、音樂、Google Drive Preview...

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

『coding』= 複製貼上的工作 http://but.tw/2008/10/programmers_rule/ 團隊的每個⼈人都有責任

Slide 29

Slide 29 text

用「複製、貼上」達成需求與滿足時程 • 一模組約 1,500 行 JS • 4 個不同的媒體 • 2 個不同的類型 • 差異很大 = 修修改改 1,500*4*2 = 12,000 行 後續有新功能都幾乎是「複製貼上 x 8」

Slide 30

Slide 30 text

Winchester Mystery House http://www.flickr.com/photos/harshlight/3670192350/ 外表富麗堂皇、也確實地達成應有功能與時程

Slide 31

Slide 31 text

http://www.flickr.com/photos/dalvenjah/33560263/lightbox/ 內部的結構... http://www.flickr.com/photos/skinnylawyer/5570163978/ 疊床架屋是軟體開發最常見的問題 在煙囪上架屋頂? ⾛走向屋頂的樓梯?

Slide 32

Slide 32 text

壓縮開發時程之謎 Stoyan Stefanov The few man-hours spent writing the code initially end up in man-weeks spent reading it. 數⼩小時寫出的程式碼、得花數週或更多的時間維護它 要在 8 個 JavaScript 上: • 增加新功能 • 修改 Bug • 改變邏輯 • 看不懂、乾脆重寫 ⽼老闆跟專案經理永遠無法理解的事...

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

EDD – 設計文件 圖表 Branch URLs 檔案結構 Controller 模組詳細說明 預先規劃的假資料 JS 的架構日益複雜 需要跟後端程式一樣先規劃 負責人先仔細思考、撰寫出此文件 所有開發人員一起討論可行性、問題點 把思考不周延之處找出來、了解配合模式 時程預估較為準確、有機會先預防

Slide 35

Slide 35 text

Refactoring - 重構

Slide 36

Slide 36 text

重構 = 打掉重寫? 應避免 NIH 綜合症(非我所創、Not Invented Here Syndrome) NO! 技術人員應該用更客觀的心態面對重構 重構 = 持續且小的改進。

Slide 37

Slide 37 text

開發 重構 開發 重構 把重構納入產品開發的 Iteration ... ... 無法⼀一開始寫出完美的程式碼 藉由實作、看到問題與需求,再著⼿手修正 逐步達成理想的程式碼品質

Slide 38

Slide 38 text

重構案例: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); 對程式碼精簡、⾏行為的⼀一致性獲得⼤大幅改善

Slide 39

Slide 39 text

重構案例: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 的不一致

Slide 40

Slide 40 text

Decoupling - 抽離 避免把所有程式碼都放在一個 JS 中

Slide 41

Slide 41 text

為什麼會產出「一大包」JS? • 麻煩!JS 載入需考慮「前後順序」 有先後順序,例如載入 jQuery UI 的 Tabview. • JS 本身缺乏「模組」的觀念 直到最近 ECMAScript 才要內建 • 「頁面」眾多、分割不易 不像其他的語言,大多只要 require 就可以使用 • 對 JavaScript 缺乏「軟體工程」的態度 非本科:對程式抽離、正規化概念薄弱。本科:對 JS 不用想這麼多吧!

Slide 42

Slide 42 text

抽離範例:Space Photo • 主要功能:列出 Router USB 裡的照片。 • 其他功能: 提供下載檔案、幻燈秀、縮圖產生機制、 檔案描述、LightBox、分享、重新命名等機制。 這才是此程式中最重要的 每一個都可以另開 Module 處理

Slide 43

Slide 43 text

代碼抽象三原則 • DRY - 不要重複自己 出現重複時就抽象出一個解決方法 • YAGNI - 你不會需要他 快 + 簡單!不要把精力放在抽象化上 • Rule of 3 - 三次原則 當同樣的情況出現三次才進行抽象化 http://www.ruanyifeng.com/blog/2013/01/abstraction_principles.html

Slide 44

Slide 44 text

功能抽離範例 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",                //  ...        ] }); 只增加兩行

Slide 45

Slide 45 text

採用模組化 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 模組 任何⼀一個網站都應該要⽤用!

Slide 46

Slide 46 text

應加入的規範 • 一個 JS 檔案不應超過 500 行 • 一行不應超過 100 個字元 維護一個大雜匯的 JavaScript 有何意義? 會超過 500 行應代表你該抽離功能了

Slide 47

Slide 47 text

未來還能做些什麼

Slide 48

Slide 48 text

給自己未來的一些題目 大多是更多工具的整合、讓自動化來提升品質 ⼯工具 ⼯工程師 • 無聊 • 重複性的 • 浪費時間的 藉由⼯工具持續回饋

Slide 49

Slide 49 text

自動偵測重複的原始碼 需要把「避免重複原始碼」變成⼀一個習慣

Slide 50

Slide 50 text

是很重要的一件事! 大家所熟悉的 JSLint, JSHint 卻沒有 Orz... 都只有「單一檔案內」的語法檢查

Slide 51

Slide 51 text

PMD - 原始碼分析器 其中 CPD 工具可以分析 JS 的重複 /bin/run.sh  cpd  -­‐-­‐minimum-­‐tokens  50                                  -­‐-­‐files  ~/project/foo/js                                  -­‐-­‐language  ecmascript

Slide 52

Slide 52 text

跑出來的結果 重複的行數 重複的檔案列表 非常需要納入 Commit 流程或 Auto Build 檢查

Slide 53

Slide 53 text

Error Log 管理 JavaScript 錯誤應該被即時記錄並回報 瀏覽器錯誤 window.onerror try-catch 錯誤發生 ӱൔ㢯ק൞ڎေສഈ❟ ӱൔ㢯ק൞ڎေສഈ❟ 把錯誤丟到 Server 做記錄、回報 就不用辛苦地翻程式碼找問題 控管好這兩個時間點

Slide 54

Slide 54 text

       //  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 才有價值

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

Esprima http://esprima.org/ 最近只要聊到 JavaScript Code 品質,幾乎都會聽到這個工具

Slide 57

Slide 57 text

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"                          } 將程式碼轉成結構⼀一致的 AST 格式,很容易做後續處理 經過 Esprima Parse

Slide 58

Slide 58 text

Esprima 範例 – CodePainter 問題:每個⼈人寫的空格、引號習慣都不⼀一樣,能否統⼀一? 跑⼀一次⽤用 Esprima 實作的 CodePainter ⼯工具,問題全搞定 https://github.com/fawek/codepainter

Slide 59

Slide 59 text

Esprima 的可能應用 • JSHint: 將會改用 Esprima • 語法高亮 • 塞 Log,協助偵錯: • 在每個 Method 都放: console.log("someMethod()  is  executed.") http://www.slideshare.net/ariyahidayat/javascript-parser-infrastructure-for-code-quality-analysis

Slide 60

Slide 60 text

Continuous Integration 持續集成 「所有」⾃自動化檢查,都應該與 CI 整合 中央 Git 個⼈人 Git git  push post-­‐receive trigger git  clone Build JSHint Code Duplication Unit Test Functional Test Code Coverage 通知 成功或失敗 Performance 讓檢查像喝⽔水⼀一樣簡單、確保品質 CI 伺服器

Slide 61

Slide 61 text

推薦服務 CodeShip 懶得⾃自⼰己架⼀一台 CI 伺服器? 市⾯面上有好幾家 SaaS 的服務可⽤用 若你的原始碼是放在 GitHub CodeShip 與 Travis 都只要勾選就可以使⽤用

Slide 62

Slide 62 text

把團隊更緊密的綁在一起 把所有訊息 (Commit, Wiki, Issue) 都倒進聊天室 不用交談也知道彼此的做了什麼、點選可看詳細內容

Slide 63

Slide 63 text

文學編程 Literate Programming 寫程式的理想型態

Slide 64

Slide 64 text

Donald Knuth 現代電腦科學的鼻祖 A style of programming that maximize our ability to perceive the structure of a complex piece of software. 文學編程的理念 w൞ℭީਔđ⃸໡ⁿ֥ӱൔ۷ݺĆx ໓⇥ṇӱ൞၂⊕⇔ӱൔ֥৘མྙⅢ ⃸໡ⁿỚᧄℂⅴ֥ૄἠ♶≗ὔᾳ׻ି ԉٳֹ৘ࢳ

Slide 65

Slide 65 text

更有組織維護自己的文件 Leo Editor - 依自己的思路撰寫文件、組織代碼 可以是任何的內外部⽂文件、 節點可以任意地被複製搬移 ⾃自由地組織、編排節點順序 產出的程式碼可與其他⼈人 Merge、Leo 會幫你追蹤 ⽀支援 Textile、Web 投影⽚片的輸出

Slide 66

Slide 66 text

更有組織維護自己的文件 Leo Editor - 依自己的思路撰寫文件、組織代碼 可以是任何的內外部⽂文件、 節點可以任意地被複製搬移 ⾃自由地組織、編排節點順序 產出的程式碼可與其他⼈人 Merge、Leo 會幫你追蹤 ⽀支援 Textile、Web 投影⽚片的輸出

Slide 67

Slide 67 text

Leo Editor 很強大、也不針對特定語言,但是... 每個人都有自己愛用的編輯器(Vim 或 Sublime) Leo 在程式碼編輯功能上似乎不怎麼樣... 在一堆編輯器中切換也是件很愚蠢的事 以喜歡的編輯器、⽤用⽂文學編程寫 JavaScript Leo 的缺點

Slide 68

Slide 68 text

https://github.com/jostylr/literate-programming 以 JavaScript 實作的文學編程 編譯 Markdown 產出 JavaScript James Taylor

Slide 69

Slide 69 text

https://gist.github.com/josephj/5580918 Demo >   File  count.js  saved literate-­‐programming  ⽂文學編程.md                                                                                                                                                                                                                                                                     寫程式就像寫部落格⼀一樣! 可重新組合章節 讓別⼈人⾮非常容易理解

Slide 70

Slide 70 text

Literate CoffeeScript CoffeeScript 的作者 = 文學編程的大力推廣者 Jeremy Ashkenas Journo 部落格系統 Docco API 文件產生器 以⽂文學咖啡寫成: 缺點:得依照原始碼的順序寫

Slide 71

Slide 71 text

Journo 的 README.md 就是部落格系統的完整原始碼 (journo.litcoffee) $  coffee  -­‐c  journo.litcoffee  #output  journo.js

Slide 72

Slide 72 text

文學編程過去不是主流、未來也不會 文學編程小結 但身為一個開發者,應秉持文學編程的精神: 「程式碼是給人閱讀的」 以上兩種作法,似乎也可以稱為「部落格編程」 是我們現今最容易理解技術的一種 Material

Slide 73

Slide 73 text

Code Review 設計⽂文件 產出 API ⽂文件 模組化 模組架構 代碼規範 Pair Programming 檢查清單 ⾃自動合併與最⼩小化 Lint 單元測試 物件導向 重構 代碼重複檢查 JS 語法分析、重置 持續整合 品質的 TODO LIST 抽離原則 ⽂文學編程 團隊活動 觀念及架構 ⾃自動化 整合訊息 看團隊需求來做導入或整合 Prototyping

Slide 74

Slide 74 text

寫程式是個良心的事業 過去的評價會⼀一直跟著你,我們應該盡⼒力寫好

Slide 75

Slide 75 text

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

Slide 76

Slide 76 text

2b || !2b that is a question.

Slide 77

Slide 77 text

Thank you! • GitHub - josephj • Facebook - 蔣定宇 • Slideshare - josephj • Linkedin - josephj6802 聯繫我