Upload
joseph-chiang
View
6.256
Download
3
Embed Size (px)
DESCRIPTION
「關於 JavaScript 品質,我想說的是...」於 JSDC.tw 2013
Citation preview
JAVASCRIPT CODE QUALITY
講者 josephj
關於 JavaScript 品質,我想說的是...
當談到 JavaScript 品質...
大家心裡的重要性就好像左邊那一本 XD
JavaScript 的興起...讓開發者覺得 JavaScript 就是要複製貼上
Bad JavaScript = 違章建築
寫程式應該是被維護的,而不是一看就想要拆掉的
6000 ⾏行 JavaScript!
Agenda
·在 Yahoo! 學到的教訓
·需要建立前端團隊的架構
·在 miiiCasa 學到的教訓
·需要先設計、多抽離
·未來可持續努力的方向
·自動化 Plus
成長 = 從過去的錯誤反省!
第 1 課 from Yahoo!前端⼯工程師必須成為真正的團隊、⽽而⾮非只是資源
中央游泳池當時「前端⼯工程師」不屬於任何產品線
專案 1
專案 2
F2E Pool不會有閒置資源
中央游泳池當時「前端⼯工程師」不屬於任何產品線
專案 1
專案 2
F2E Pool基本上都在專案
任務導向、缺少團隊機制
大家只管自己的眼前的案子
缺少水平的機制去管理品質
久了必會發生慘案
• 由新人負責、跟其他專案成員關在小房間
(沒有其他 F2E)
• View 全部用 JS 組、後端只給 JSON
• 不支援 NoJS、沒法上一頁
• 重複程式碼非常多,沒人敢大改
• 專案結束沒多久,新人離職
• 2012 年底,此服務關閉
永遠在我心裡的一句話
“As a team, F2E 的價值究竟在哪裡?”
好不容易培訓⼀一個新⼈人、卻⼜又因制度不健全害了他
開始去思考「團隊」這件事
• Code Convention 團隊規範
• Code Review 定期 Review 程式碼
• Checklist 上線前需達成的清單
⾮非常重視⼀一致性、維護性
規範一些最基礎的小事情
一致性、像同一個人寫的
https://github.com/josephj/f2e-styleguide
Code Review
http://programmers.stackexchange.com/questions/170211/php-data-access-layer
程式碼接受所有⼈人檢驗
比 JSLint 更傷感情 XDhttps://github.com/miiicasa/youtube-iframe/issues/1
需要比較正面的態度
每次 Code Review 都能學到很多東⻄西!
Pair Programming
http://en.wikipedia.org/wiki/File:Pair_programming_1.jpg
⼀一個⼈人寫程式、⼀一個⼈人當觀察員
導入正確的模式模組化 (Module) 與觀察者 (Observer) 模式
模組:寫法一致、避免全域變數
welcome/_notification.js
⼀一個模組即有⼀一個 JS 檔
模組:寫法一致、避免全域變數
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 或物件參考
模組:寫法一致、避免全域變數
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"
模組:寫法一致、避免全域變數
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) 中開發
功能擴充也能有一致介面
只專注開發該模組功能、無法污染外界
不只是 JavaScript Ninja!認識軟體⼯工程與持續集成,事半功倍!
需從軟體工程的角度出發
• Lint 自動檢查程式碼
• Combine/Compress 自動合併及壓縮 JS/CSS
• Optimize image 圖檔最佳化
• Compass 用 High Level 的語言讓 CSS 簡化
• Skeleton 自動建立樣板檔案
導⼊入⾃自動化、減少「⼿手⼯工」、才能提昇品質
在 miiiCasa 實踐了這些理念很幸運可以從零開始、擔任前端⼯工程部⾨門主管
這些想法與理念都⼀一⼀一地實踐了
難道就不會有問題了嗎?
http://www.slideshare.net/josephj/ss-7705095
建立前端開發團隊
第 2 課 from miiiCasa 必須先設計、並導⼊入更好的 JavaScript Pattern
miiiCasa Space 家庭雲:用單一 URL 存取家中裝置上的資料
• 4 個不同的媒體:照片、音樂、影片、文件
• 2 個不同的類型:首頁列出 Storage、內頁只列目錄
• 都是檔案列表、但差異很大:• View 都各自有差異
• 照片要 Slideshow
• 音樂要播放器
• 影片用 VLC / Flowplayer
• 文件要用 Lightbox 顯示照片、同時支援
影片、音樂、Google Drive Preview...
有時程壓⼒力...該怎麼快速產出?
『coding』= 複製貼上的工作
http://but.tw/2008/10/programmers_rule/
團隊的每個⼈人都有責任
用「複製、貼上」達成需求與滿足時程
• 一模組約 1,500 行 JS
• 4 個不同的媒體
• 2 個不同的類型
• 差異很大 = 修修改改
1,500*4*2 = 12,000 行
後續有新功能都幾乎是「複製貼上 x 8」
Winchester Mystery House
http://www.flickr.com/photos/harshlight/3670192350/
外表富麗堂皇、也確實地達成應有功能與時程
http://www.flickr.com/photos/dalvenjah/33560263/lightbox/
內部的結構...
http://www.flickr.com/photos/skinnylawyer/5570163978/
疊床架屋是軟體開發最常見的問題
在煙囪上架屋頂? ⾛走向屋頂的樓梯?
壓縮開發時程之謎
Stoyan Stefanov
The few man-hours spent writing the code initially end up in man-weeks spent reading it.
數⼩小時寫出的程式碼、得花數週或更多的時間維護它
要在 8 個 JavaScript 上:
• 增加新功能
• 修改 Bug
• 改變邏輯
• 看不懂、乾脆重寫
⽼老闆跟專案經理永遠無法理解的事...
面對問題未來要怎麼預防同樣的問題、或避免讓問題繼續蔓延
EDD – 設計文件圖表
BranchURLs
檔案結構
Controller
模組詳細說明
預先規劃的假資料
JS 的架構日益複雜
需要跟後端程式一樣先規劃
負責人先仔細思考、撰寫出此文件
所有開發人員一起討論可行性、問題點
把思考不周延之處找出來、了解配合模式
時程預估較為準確、有機會先預防
Refactoring - 重構
重構 = 打掉重寫?
應避免 NIH 綜合症(非我所創、Not Invented Here Syndrome)
NO!
技術人員應該用更客觀的心態面對重構
重構 = 持續且小的改進。
開發 重構 開發 重構
把重構納入產品開發的 Iteration
......
無法⼀一開始寫出完美的程式碼藉由實作、看到問題與需求,再著⼿手修正
逐步達成理想的程式碼品質
重構案例: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);
對程式碼精簡、⾏行為的⼀一致性獲得⼤大幅改善
重構案例: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 的不一致
Decoupling - 抽離
避免把所有程式碼都放在一個 JS 中
為什麼會產出「一大包」JS?
• 麻煩!JS 載入需考慮「前後順序」<script/> 有先後順序,例如載入 jQuery UI 的 Tabview.
• JS 本身缺乏「模組」的觀念直到最近 ECMAScript 才要內建
• 「頁面」眾多、分割不易不像其他的語言,大多只要 require 就可以使用
• 對 JavaScript 缺乏「軟體工程」的態度非本科:對程式抽離、正規化概念薄弱。本科:對 JS 不用想這麼多吧!
抽離範例:Space Photo
• 主要功能:列出 Router USB 裡的照片。
• 其他功能:
提供下載檔案、幻燈秀、縮圖產生機制、
檔案描述、LightBox、分享、重新命名等機制。
這才是此程式中最重要的
每一個都可以另開 Module 處理
代碼抽象三原則
• DRY - 不要重複自己出現重複時就抽象出一個解決方法
• YAGNI - 你不會需要他快 + 簡單!不要把精力放在抽象化上
• Rule of 3 - 三次原則當同樣的情況出現三次才進行抽象化
http://www.ruanyifeng.com/blog/2013/01/abstraction_principles.html
功能抽離範例
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", // ... ]});
只增加兩行
採用模組化 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 模組
任何⼀一個網站都應該要⽤用!
應加入的規範
• 一個 JS 檔案不應超過 500 行
• 一行不應超過 100 個字元
維護一個大雜匯的 JavaScript 有何意義?
會超過 500 行應代表你該抽離功能了
未來還能做些什麼
給自己未來的一些題目
大多是更多工具的整合、讓自動化來提升品質
⼯工具 ⼯工程師
•無聊•重複性的•浪費時間的 藉由⼯工具持續回饋
自動偵測重複的原始碼
需要把「避免重複原始碼」變成⼀一個習慣
是很重要的一件事!
大家所熟悉的 JSLint, JSHint 卻沒有 Orz...
都只有「單一檔案內」的語法檢查
PMD - 原始碼分析器
其中 CPD 工具可以分析 JS 的重複
/bin/run.sh cpd -‐-‐minimum-‐tokens 50 -‐-‐files ~/project/foo/js -‐-‐language ecmascript
跑出來的結果重複的行數 重複的檔案列表
非常需要納入 Commit 流程或 Auto Build 檢查
Error Log 管理JavaScript 錯誤應該被即時記錄並回報
瀏覽器錯誤
window.onerror
try-catch
錯誤發生
程式決定是否要往上丟
程式決定是否要往上丟 把錯誤丟到 Server 做記錄、回報
就不用辛苦地翻程式碼找問題
控管好這兩個時間點
// 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 才有價值
推薦工具 - Sentry接收各種程式語言錯誤、分組、寄通知信
Esprima
http://esprima.org/
最近只要聊到 JavaScript Code 品質,幾乎都會聽到這個工具
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
Esprima 範例 – CodePainter問題:每個⼈人寫的空格、引號習慣都不⼀一樣,能否統⼀一?
跑⼀一次⽤用 Esprima 實作的 CodePainter ⼯工具,問題全搞定
https://github.com/fawek/codepainter
Esprima 的可能應用
• JSHint: 將會改用 Esprima
• 語法高亮
• 塞 Log,協助偵錯:
• 在每個 Method 都放:console.log("someMethod() is executed.")
http://www.slideshare.net/ariyahidayat/javascript-parser-infrastructure-for-code-quality-analysis
Continuous Integration 持續集成「所有」⾃自動化檢查,都應該與 CI 整合
中央 Git
個⼈人 Git
git push
post-‐receive
trigger
git clone
Build
JSHint
Code Duplication
Unit Test
Functional Test
Code Coverage
通知
成功或失敗
Performance
讓檢查像喝⽔水⼀一樣簡單、確保品質
CI 伺服器
推薦服務
CodeShip
懶得⾃自⼰己架⼀一台 CI 伺服器?市⾯面上有好幾家 SaaS 的服務可⽤用
若你的原始碼是放在 GitHubCodeShip 與 Travis 都只要勾選就可以使⽤用
把團隊更緊密的綁在一起把所有訊息 (Commit, Wiki, Issue) 都倒進聊天室
不用交談也知道彼此的做了什麼、點選可看詳細內容
文學編程Literate Programming
寫程式的理想型態
Donald Knuth現代電腦科學的鼻祖
A style of programming that maximize our ability to perceive the structure of a complex piece of software.
文學編程的理念
「是時候了,讓我們的程式碼更好!」
文學編程是一種寫程式的理想形態 :
讓我們對於軟體的每個複雜環節都能
充分地理解
更有組織維護自己的文件Leo Editor - 依自己的思路撰寫文件、組織代碼
可以是任何的內外部⽂文件、節點可以任意地被複製搬移
⾃自由地組織、編排節點順序
產出的程式碼可與其他⼈人 Merge、Leo 會幫你追蹤⽀支援 Textile、Web 投影⽚片的輸出
更有組織維護自己的文件Leo Editor - 依自己的思路撰寫文件、組織代碼
可以是任何的內外部⽂文件、節點可以任意地被複製搬移
⾃自由地組織、編排節點順序
產出的程式碼可與其他⼈人 Merge、Leo 會幫你追蹤⽀支援 Textile、Web 投影⽚片的輸出
Leo Editor 很強大、也不針對特定語言,但是...
每個人都有自己愛用的編輯器(Vim 或 Sublime)
Leo 在程式碼編輯功能上似乎不怎麼樣...
在一堆編輯器中切換也是件很愚蠢的事
以喜歡的編輯器、⽤用⽂文學編程寫 JavaScript
Leo 的缺點
https://github.com/jostylr/literate-programming
以 JavaScript 實作的文學編程
編譯 Markdown
產出 JavaScript
James Taylor
https://gist.github.com/josephj/5580918
Demo
> File count.js savedliterate-‐programming ⽂文學編程.md
寫程式就像寫部落格⼀一樣!
可重新組合章節讓別⼈人⾮非常容易理解
Literate CoffeeScriptCoffeeScript 的作者 = 文學編程的大力推廣者
Jeremy Ashkenas
Journo 部落格系統
Docco API 文件產生器
以⽂文學咖啡寫成:
缺點:得依照原始碼的順序寫
Journo 的 README.md就是部落格系統的完整原始碼 ( journo.litcoffee)
$ coffee -‐c journo.litcoffee #output journo.js
文學編程過去不是主流、未來也不會
文學編程小結
但身為一個開發者,應秉持文學編程的精神:
「程式碼是給人閱讀的」
以上兩種作法,似乎也可以稱為「部落格編程」
是我們現今最容易理解技術的一種 Material
Code Review
設計⽂文件 產出 API ⽂文件
模組化
模組架構代碼規範Pair Programming
檢查清單⾃自動合併與最⼩小化
Lint
單元測試物件導向
重構
代碼重複檢查
JS 語法分析、重置
持續整合
品質的 TODO LIST
抽離原則⽂文學編程
團隊活動 觀念及架構 ⾃自動化
整合訊息
看團隊需求來做導入或整合
Prototyping
寫程式是個良心的事業
過去的評價會⼀一直跟著你,我們應該盡⼒力寫好
程式碼品質,大師們都會強調再強調
2b || !2bthat is a question.
Thank you!
• GitHub - josephj
• Facebook - 蔣定宇
• Slideshare - josephj
• Linkedin - josephj6802
聯繫我