Monday, January 22, 2018

Simple Made Easy

話說我長久以來一直有個問題:『看不懂別人寫的 OOP style 程式碼。』學了 Clojure 之後,這個「看不懂」似乎有理由了。

OOP style 的東西,當一組 class/object 被定義生成時,有太多可能的語意 (semantic) ,要猜的可能性太多。

可能是:
1 values object , 在  clojure 會用單純的 hashmap 或是 records 來處理。
2 entity object, 在 clojure 會用 atom 處理。
3 function with state, 在 clojure 會用 lexical closure 來處理。
4 純脆只是 namespace 功能,為封裝而封裝
5 multimethod 的語意, 在 clojure 會用 defmulti 又或是  protocol + reify 來實現。

最近偶然又重看了一遍 Rich Hickey 的 Simple Made Easy 。發現類似的概念,他已經討論過了。在 Simple Made Easy 這個 talk 裡。object 被 Rich 歸類為 complex construct ,難以做出 simple artifact 。而太 complex 的東西會讓人腦的認知能力超載,因為要同時考慮太多元件。

該 talk 中有許多深具啟發的句子和概念:
1. All too often we do what is easy, at the expense of what is simple.
2. The benefits of simplicity are: ease of understanding, ease of change, ease of debugging, flexibility.
3. Reliability tools: testing, refactoring, type systems are good but secondary. They do not enforce simplicity. They are just safe net.
4. Build simple system by choosing construct that generate simple artifacts and by abstracting: design by answering questions related to what, who, how, where, when, and why.

Friday, January 19, 2018

Never build an Application

最近看了一篇談軟體設計的文章,文章是 Never build an Application 。主要的概念大概有幾項:
1. Never build an application. Build a library of reusable functions specific to the problem domain instead.  不要針對「需求」設計一體成形的應用程式做為解決方案。取而代之的是,要對「需求」設計一套可復用的函式庫,透過函式庫來做出解決方案。

2. 這套針對「需求」而產生的函式庫,可以視為是一種 domain specific language 。而它也可以是各種的形式: 比方說,一堆 class 可以提供的服務、一些 meta data 、 XML config 檔等。換言之,並不是一定要有特殊的語法,才可以看成是 DSL 。重點是在於,要做出一個「抽象層」,這個抽象層可以很好地對 problem domain 做很好的塑模。於是 application 就會變得很小,因為 application 是利用抽象層來做出的。

3. UML 在設計的應用上,有一個明顯的問題。它的塑模是針對 solution domain ,而不是針對 problem domain 。然而,DSL 的解法之所以有用的原因,是因為 DSL 是針對 problem domain 的 modeling 。

真的要講的話,其實這篇文章講的核心概念,和 Paul Graham 在 On Lisp 的序言中提到的 programming bottom-up 概念近乎一致。然而,我自己在實際的 clojure 程式設計經驗中,得到的心得啟發則是:

(*) REPL-driven development  是促成 programming bottom-up 的首要條件。

用不同的程式語言來開發,一方面是語言的表達能力不同。但是,更關鍵的一點是:語言對於「單元測試」的表達能力也有差異。使用 OOP 來開發時,單元測試的最小單位,往往就已經是物件,所以最小的抽象層,也往往要用 object 和 interface 來表達。 而使用 Lisp 語言的話,因為最小的測試單位是 REPL-driven development ,所以往往可以做出理論上最小的抽象層組件。

換言之,使用 Lisp 之所以可以導致 elegant design ,主要的原因在於 REPL-driven development  。跟 Lisp 擁有最強大的 macro system 的關系則比較小一些。使用其它的程式語言來開發時,只要貫徹「讓被測試的組件最小化」的原則,也一樣可以做出高品質的抽象層。

Thursday, January 18, 2018

Nodejs 開發環境設置 --- navigation

快速查詢變數 (navigation)
tern_for_vim 可以幫 vim 整合 ternjs 的 javascript 靜態語言分析器。

使用方式:
:TernDef                   查詢變數的定義
:TernRefs                  查詢所有出現的位置 / (會開啟 location list ,需要用 :lcl 指令關閉。)
:TernRename            重新命名變數,適合重構時大量修改使用。

安裝:
(a)  cd ~/.vim/bundle/   && git clone https://github.com/ternjs/tern_for_vim.git
(b)  cd ~/.vim/bundle/tern_for_vim  && npm install
(c)  在要做變數查詢的 project 根目錄,增加一個 .tern-project 檔
{
  "plugins": {
    "node": {},
    "modules": {},
    "es_modules": {},
    "requirejs": {
      "baseURL": "./",
      "paths": {}
    }
  }
}

Sunday, January 14, 2018

vim 開發環境設置

用 vim 做為主要的開發環境、也陸陸續續開發了三、四種不同的語言: javascript, python, clojure, golang 。這邊總整理一下,我覺得最常用的幾種功能。

1. Formatting/Linter
2. Evaluating
3. Navigating
4. Autocomplete

在 golang ,因為語言的特性,無法達成 2 。裝 vim-go 就可以達成 1, 3 。安裝 gocode 可以達成 4 。

在 clojure 的話:
1. Formatting/Linter  -> cljfmt 和 clj-kondo
2. Evaluating -> Conjure
3. Navigating  -> clojure-lsp 可以查找「函數引用」
4. Autocomplete -> Conjure 也有基本的支援

Nodejs 開發環境設置 --- eslint

設置 eslint
1. npm install -g eslint-config-airbnb-base eslint-plugin-import eslint
2. eslint --init

在 vim 安裝 vim-syntastic 
cd ~/.vim/bundle && git clone --depth=1 https://github.com/vim-syntastic/syntastic.git

設置 vim 的設置檔  .vimrc
set statusline+=%#warningmsg#
set statusline+=%{SyntasticStatuslineFlag()}
set statusline+=%*
let g:syntastic_always_populate_loc_list = 1
let g:syntastic_auto_loc_list = 1
let g:syntastic_check_on_open = 0
let g:syntastic_check_on_wq = 1
let g:syntastic_javascript_checkers = ['standard']
let g:syntastic_javascript_standard_generic = 1
let g:syntastic_javascript_checkers = ['eslint']
" let g:syntastic_javascript_eslint_exec = 'eslint'
let g:syntastic_mode_map = { 'mode': 'passive', 'active_filetypes': [],'passive_filetypes': [] }

在 vim 啟動 linter
:SyntasticCheck

在 vim 檢查 linter 是否成功設置
:SyntasticInfo

關閉顯示 lint Error 的 Location List
:lclose

讓本地端資料夾的 eslint 被 syntastic 優先採用
cd ~/.vim/bundle && git clone https://github.com/mtscout6/syntastic-local-eslint.vim.git

在已經有安裝 local eslint 的資料夾下,啟動 eslint 來做 autofix
node_modules/.bin/eslint   --fix   FILENAME

Saturday, January 13, 2018

API v.s. DSL

API 與 DSL 是不同的概念,偏偏有一句電腦科學的諺語: library design is language design.

查一下 stackoverflow ,也查到了還不錯的解釋與比較。

DSL (domain specific language) 是「程式語言」。它們可以是編譯的、直譯的、資料查詢用的 (如 SQL)、標記語言 (如 JSON, XML)。總之,DSL 會是個語言。 DSL 這個詞描述的是它們的本質 (nature),而不是它們的用途 (purpose)。

API (application programming interface) 是「讓軟體元件可以被其它元件使用的溝通介面」。這個詞彙描述的是它們的用途 (purpose) ,而不是它們的本質 (nature) 。所以 API 可以是各種不同的形式: 物件方法、 C 語言的函式等等。

在 Martin Fowler 的 DslBoundary 一文中則是特別討論了算是一種特例的 embedded DSL :
1. API 強調提供新的「功能」
2. DSL 的重點則是在於:以一種『新的語法、編程方式』來提供新的「功能」

Tuesday, January 9, 2018

12 factors 心得

I. Codebase
一個 app 對應一個 git repo ,而不是一個 distributed system 對應一個 git repo 。

II. Dependencies
python 可以用 pip 和 virtualenv 來做依賴聲明 (dependency declaration) 與依賴隔離 (dependency isolation)

V. Build, release, run
應該要使用 release management tools,以便用於管理 release 的版本。必要時可以一個命令 rollback 退版。

VII. Port binding
語言有提供 http server 的 library 是一大優點。app 可以直接做 port binding

IX. Disposability
要讓 app 可以 graceful shutdown ,主要需要考慮兩個問題:
(1) network app 要拒絕所有新的請求,並且繼續執行當前已接收的請求,然後退出。
(2) worker process 終止之前,要把當前的任務退回 Queue 。比方說,對 RabbitMQ 發送 NACK

XII. Admin processes
語言有提供 REPL 是一大優點,可以直接使用該種語言寫成的 script 來做 admin/management process。如此就可以與 app process 使用相同的依賴聲明、依賴隔離。

Saturday, January 6, 2018

[jQuery語法] 找出 DOM 裡的 leaf node

用 JQuery 找出 Dom 裡頭的所有 leaf node ,是做前端時有可能遇到的有趣問題。
整理一下暴力但是好用的解法:

找出所有在 body 裡的 leaf node
$('body *:not(:has(*))')

找出所有本身是 div 的 leaf node
$('div:not(:has(*))')

找出所有最內層的 li 。注意:li 總是存在於 ul 和 ol 裡頭。
$('li:not(:has(ul, ol))')
============================================
Extract value from XML format

仔細想想的話,上述的問題可以歸類於「從 xml 提取值」。
解法大概可以分成兩種:

1. 命令式編程的解法:對 xml 做樹的走訪 (tree walk),邊走邊取值。
    這種設計如果來源資料的 xml 有局部變化,通常就會朝 polymorphism 的方向演化。每一種局部變化會對應獨立的模組 (module)。

2. 宣告式編程的解法:找一個支援 XPath 的 xml parsing library 。用 XPath 做查詢 (query) 取值。
    這種設計如果來源資料的 xml 有局部變化,每一種局部的變化會對應不同的 XPath 設定檔 (config)。

Friday, January 5, 2018

git log analysis --- find out the hot spot

Clojure 寫的 https://codescene.io/
它是一個 on-line 的 data analyzing tool 。分析的目標是 git commit  log 。

它可以回答下列幾個我覺得對於 technical manager 會有意義的問題:
1  How shall we prioritize improvements to our codebase?
2  How can we follow-up on the effects of the improvements we do? Did
we really get an effect out of that design improvement?

它分析 git log 的方式,是透過一種 hot spots 的分析,找出 git repo 裡變動頻率最高、相對的行數也特別多的檔案,這些檔案是 git repo 裡的 hot spots 。

同時,hot spots 往往也是
(1) 最容易產生 bug
(2) 最有可能需要、或是值得重構、
(3) business value 最高、
(4) 最容易造成 delivery delay
(5) 最值得做 code review 的區塊。

hot spots 可以如何使用呢?
  • Developers use hotspots to identify maintenance problems. Complicated code that we have to work with often is no fun. The hotspots give you information on where those parts are. Use that information to prioritize re-designs.
  • Hotspots points to code review candidates. At Empear we’re big fans of code reviews. Code reviews are also an expensive and manual process so we want to make sure it’s time well invested. In this case, use the hotspots map to identify your code review candidates.
  • Hotspots are input to exploratory tests. A Hotspot Map is an excellent way for a skilled tester to identify parts of the codebase that seem unstable with lots of development activity. Use that information to select your starting points and focus areas for exploratory tests.

-------------
針對這個透過 git log 來找出 hot spot 的工具,再三思考後,我覺得它本質上還是有點像 unit test, static type system 這樣子的東西,它基本上還是輔助用的東西,可以提供「提示」卻無法提供「洞察」。

故意舉一個極端的案例:如果我寫 web application 使用了 ORM 。ORM 也許不會要常常修改,要手動改的程式碼也不多。所以 ORM 相關的區塊就不會被 git log analysis 標定為 hot spot 。然而,ORM 的本質卻是極致的 complex ,因為它同時把物件與 SQL 纏繞在一起,絕對是極度複雜的區塊。