顯示具有 software development 標籤的文章。 顯示所有文章
顯示具有 software development 標籤的文章。 顯示所有文章

2013年1月27日 星期日

整合版本管理系統在 IDE 裡使用 vs. 直接使用版本管理系統

不論是使用命令列或視窗圖形介面的版本管理系統 (svn, hg, git, etc),我都偏好直接使用版本管理系統。這有許多好處:

  • 換開發環境時,不用重新學習一次。熟悉版本管理系統的功能愈多,重新學習的成本愈高。
  • IDE 內的 plugin 有可能是比較舊的版本

最近寫了一陣子 Android 和 iOS 程式後,對這個選擇更有信心。不論現在是用 vim + C++ 寫伺服器端 、用 vim + Python 寫網站、用 XCode 寫 iOS、用 Eclipse 寫 Android,通通都是一樣的方式使用版本管理系統,省了不少力氣適應不同的開發環境。

現今的 IDE 或編輯器都會自動偵測檔案更動時間,在別的地方用版本管理系統更動檔案,切回 IDE 或編輯器後會自動更新,不會編輯錯內容。讓這個選擇更無風險。

2011年10月28日 星期五

交接心得

將之前隨手的筆記稍微整理一下備忘。

主要參考文章: 《How to hand over a project systematically? - Stack Overflow》。Stack Overflow 真是居家必備的好參考資料。其它參考文章滿發散的, 沒有詳記出處, 看些設置專案的 best practice 也有用, 預防勝於治療。

消化後, 我自己覺得重要的事, 依順序如下:

  1. 每個專案要有個 readme 說明如何開始。確保對方知道如何跑 tests 還有跑範例指令得出範例結果, 確認專案沒有問題。建立專案環境包含知道專案相依性, 若有用適當的軟體, 會少很多痛苦, 像 python 的 virtualenv + pip 是不錯的選擇, 可以盡可能地找出最乾淨的相依組合。
  2. 為什麼我們需要這東西, 它的目的為何
  3. System Context 和 Architecture Overview: 方便對整體有個概念, 兩張圖的用途不同, 參考《Architecture Overview》。不過我的交接對像本來就知道部份內容, 雖然有畫這些圖, 卻不知效果如何。
  4. data flow diagram: 個人偏好有個實例表示資料如何在各元件之間轉換, 能自己想通整個資料走向, 對理解整個架構和除錯很有幫助。我自己在理解專案時, 常會自己畫資料流, 藉此弄清楚架構, 找出我不明白的環節。

從上面列的東西, 可看出來重心放在小巧實例, 方便快速上手, 有東西跑, 比較會有感覺。再來是架構 (概念)和設計背後的原因, 這些很難從程式碼看出來的東西。程式註解也該寫這麼做的原因, 而不是它在做什麼。後者看程式就可以懂了, 寫文件的時間也要花在刀口上。此外, 文件本身也會有維護成本, 盡量寫真的必要的部份。

2011年9月12日 星期一

tab vs. space

討論 coding style 時, 這個常會戰到爆。記一下在多人一起開發一陣子軟體後的心得。

我後來覺得 space 比較方便, 因為可確保任何地方看到的畫面一致, 且不需另外設定。支援 tab 的人認為只需要一次設定就可以了, 這樣可保留彈性。困難的地方在於有太多地方可能會看到程式碼: IDE、editor、VCS log、issue tracking、e-mail、其它可能的 web-based 工具 (如 code review)。而有些地方無法提供客制化設定 tab 寬度 (如 e-mail)。

以我自己用 Java 來說, 平時可能用 Eclipse, 特定需求時用 vim, 偶而要用 hgtk 看別人的 commit (一些程式碼的 diff), 或用 hg blame 看修改記錄 (輸出到 console 或用 hgtk 看), 偶而寄 code diff 給別人討論。在這些過程中, 會發覺 tab 實在是不太方便。

至於 space 的缺點: 一但定了就很難改, 實務上不是問題, 各個語言都有標準規範 (大概都是四個空白), 照著公定規範做就是了。若不幸地原本已有一大堆其它長度, 讀個一陣也會習慣, 應該會比在不同地方, 縮排亂掉來得好。

2011年8月21日 星期日

用 Sphinx 寫文件

以下是最近試用 Sphinx 的心得, 有錯還請指正。

我原本很納悶, 專案文件是用 Wiki 寫好, 還是用 Sphinx 寫來得好。為啥要特別做個工具來產生文件呢? 抱著實驗的性質試了 Sphinx, 試下去才明白, 難怪不少專案用 Sphinx 來寫。

用 Sphinx 寫有幾個好處:

  • 文件原始碼可和程式碼放在同一個 repository, 也可藉此順便做版本管理。不過我覺得以版本管理來說, 還是沒 Wiki 方便。
  • 可透過語法載入程式碼內的註解, 這點 Wiki 就沒輒了, 我覺得這是用 Sphinx 的最大優勢。
  • 一堆好用的 plugin, 像是畫圖、寫數學式子、畫 graph等。將 graphviz 原始碼直接寫在文件裡, 還挺方便的, 不過這點 Wiki 也辦得到。
  • 可嵌入 ipython 語法, 顯示 ipython 的結果, 包含自動填入執行結果等功能。這個相當好用, 適合寫執行的範例 (寫實例演練的必備工具), Wiki 就沒有啦。
  • 產生的結果是純 html, 並有提供文章搜尋功能 (實作方式很妙, 建好 index 存成 js 程式碼, 透過 js 執行搜尋)。架網站提供文件時, 不需另裝任合套件 (PHP / CGI / Web framework / etc)。
  • 功能彈性, 若是 Python guy 的話, 缺什麼語法可自己寫 extension 補一下。Sphinx 也有提供巨集功能自定語法。

相較之下, Wiki 最大的好處是編完存檔就看到結果, 省掉 make html 的步驟, 方便大家隨時一起共筆, 提供 lock 避免多人同時編輯相衝, 方便查閱版次之間的差異。Sphinx 得透過 VCS 做, 也不方便提供 html 的版次差異。所以若是多人同時共筆, 偏重於功能說明, Wiki 還是較方便。

這裡列幾個熱門專案選擇的做法:

看完後發覺 ........, 一時好像也看不出個什麼頭緒, 之後再慢慢觀察吧。

Sphinx 入門不難, 以下是幾個相關網站:

  • sampledoc: 一小份入門文件, 說明必要的部份, 快速上手。
  • 線上試語法
  • Read the Docs: Python Software Foundation 提供的網站, 設好後, 會自動 fetch codes, 並重編 Sphinx 文件, 還有支援顯示各個版本文件。有了這個站後, 就不用擔心用 Sphinx 還得自己另外找網站放文件。
  • 官網: 東西多, 自然也較難找, 不過文件滿完整的

用 Sphinx 寫文件後, 我才明白文件包含兩種不同的內容: tutorial 和 library reference。像 Java Doc、Epydoc 這類工具用來抽註解產生 library reference, 避免在兩個地方寫文件。Java Doc 我沒實際試用, Epydoc 雖說產生的結果很炫, 卻不方便用作者自己的觀點組織整個專案的套件, 描述它們的互動, 或是提供常見的使用情境等。

使用者第一時間需要的是一個可立即執行的 tutorial, library reference 則是備查。Sphinx 好用的地方就在於, 它可以自由組織 tutorial, 並提供語法引入 library reference (用 autodoc 或 Epydoc extension )。所以用 Sphinx 可同時滿足這兩種需求。

除 tutorial 和 library reference, 開發者可能會需要 system context 和 architecture overview 了解整個架構。這部份就得自己另外用工具畫, Power Point 之類的軟體滿適合的, 重點是容易上手。最近看了《How to make Awesome Diagrams for your slides》 覺得相當有幫助, 順便列在這備忘。

2011年7月31日 星期日

semantic versioning

WanCW 在 前篇留言 提到 《Semantic Versioning》, 是 github 的創辦人 Tom Preston-Werner 寫的。當初好像就是看到這篇吧。

文中先說明為了減少 dependency hell 的影響, 若大家定版號時共同遵守同樣的規範, 就能比較放心地說明自己的 package 需要那些版本的 packages:

If the dependency specifications are too tight, you are in danger of version lock (the inability to upgrade a package without having to release new versions of every dependent package). If dependencies are specified too loosely, you will inevitably be bitten by version promiscuity (assuming compatibility with more future versions than is reasonable). Dependency hell is where you are when version lock and/or version promiscuity prevent you from easily and safely moving your project forward.

接著提出 semantic version 的定義:

Consider a version format of X.Y.Z (Major.Minor.Patch). Bug fixes not affecting the API increment the patch version, backwards compatible API additions/changes increment the minor version, and backwards incompatible API changes increment the major version.

然後用嚴僅的方式陳列 semantic version 的定義, 言簡意賅。這裡用我的方式描述一下, 完整的定義以原文為準:

  • 版號 X.Y.Z 代表 Major.Minor.Patch
  • X = 0 時, 隨便你搞
  • X > 0 後, 在程式裡或文件註明那些是 public API
  • 沒動到 public API (如修 bug、重構):Z++
  • 動到 public API 相關程式, 但介面不變 (如加新功能):Y++
  • public API 介面有變:X++

FAQ 裡有說明如何處理特殊情況, 像是「若一有 incompatible API changes 就升 major version, 那不是一下就跳到 42.x.x」? 作者回答那表示你沒有認真看待 public API, 要盡可能地減少 incompatible changes。「若不小心破壞規則, 該如何修正」? 作者回答修正回來後, 再發一個新版號, 並在文件中註明沒遵守規則的版號。另外還有一個 issue 在 github 上, 供大家反應不完備的部份, 真是不錯的規範。

2010年8月31日 星期二

保持小而頻繁的 commit

一年半前我有個疑問, 究竟要寫成多個小 commit, 還是一個個完整子工作的大 commit。實際運作一陣子後, 我發覺答案很明顯, 小的 commit 才有用處。我現在甚至不明白為啥當時會為這問題困惑...

小的 commit 有以下的好處 (指令用 mercurial 表示):
  • 發覺某個功能掛掉時, 可以用二元搜尋法快速找到改爛的那版, 接著因為 commit 很小, 很快就能找出是那段 (甚至是那行) 程式造成問題。我已用這招在五分鐘內找出兩個數十、數百版前的錯誤。看到原因後發覺, 若不用版本管理系統, 我大概花很久都不見得能找到問題。
  • 當同伴有時間時, 他們可以容易地 code review, 每個修改都很簡單易懂。
  • 當同伴沒時間時, 他們可以容易地先用 log 濾出需要仔細看的 commit, 並只看一小部份和他相關的程式 (hgtk log 超方便的!)。
  • 寫程式難免會被中斷或忽然亂了方寸, 每做完一件小事就 commit, 確保自己一直都能掌握狀況。真的亂掉不知如何除錯時, 可以先將目前更新存起來 (hg shelve 或 hg revert), 再一個個加回去, 釐清目前狀況。這招數度解救我於混亂之中。
  • 隨時能放心地嘗試, 一看 diff 就知道目前做了那些事。commit 前看 diff 也能很快找出漏砍的除錯碼 (hgtk commit 是大家的好朋友!)。
另外, 用 DVCS (如 git、mercurial) 再附帶一些好處:
  • 不用擔心自己頻繁的小 commit 造成 build fail, 讓大家共用的 repository 毀了。做個一陣子確定告一個小段落且不會炸到別人, 再 push 這段時間做的一系列小 commits。當然, 每個 commit 本來就該是完整的工作, 且可以正常編譯和通過所有測試。但考量到效率, 小 commit 可以先用較粗略的測試 (如只跑 smoke test suite, 或目前修改部份的 unit test)。push 前再跑完整測試。
  • 從 DAG 可以輕易看出大家開發的支線。但像 SVN 那樣一直線的記錄, 就無法看出各個人開發的脈絡。可能自己的 commit 變 11、14、16 成一個完整的工作, 中間卻搜了別人的 12、13、14。
  • 任何人都可以輕易地在自己的 repository 上做實驗以熟悉版本管理系統。只要打個 hg init, 立即擁有自己的世界!
不過 DVCS 也不是什麼都好, 像 DVCS 就沒有 lock。若成員眾多, 又會存圖片、音樂這種無法合併 (merge) 的檔案格式, 沒有 lock 就沒辦法避免兩個人在改同一份檔案。
要能完整地發揮小 commit 的功用, 需要一些好工具的協助。我直到有這樣的需求後, 才明白為什麼大家會需要這些指令:
  • hg shelve: 將目前 working directory 的東西存到暫存的 patch 裡。於是 working directory 就和 repository 內的資料一致。自己做到一部份, 和同事討論需要拿他的程式時, 就可以 hg shelve; hg fetch; hg unshelve。就算 unshelve 失敗, 看一下產生的 reject 檔 (也是 diff 格式), 很快就能將它塞回正確位置。頻繁地 fetch / shelve, 可以減少 merge conflict。另外自己也常做到一半發覺得先改另一個東西, 這時就先 hg shelve。改完另一個東西並 commit 後, 再 hg unshelve。若習慣重構再加功能的話, 這招超好用的。
  • hunk selection: 我是用 hgtk 做 hunk selection。一個 hunk 是指一個修改的檔案裡的一段 diff。hunk selection 就是只 commit 目前更新的部份結果。通常寫寫會手賤順手改一些和目前項目無關的小東西, 像是命之前漏改的小錯、加個共用的 helper function。改了都改了, 為了保持小的 commit 還要還原再分多次一一 commit 太累了。這時就能用 hunk selection 將同性質的部份修改 commit, 就能保有小的 commit 又不用改變原本開發流程。
另外為了方便追蹤 log, 以下幾件事務必切成獨立的 commit:
  • 搬函式、類別的位置
  • 修正程式縮排
  • 改名稱
這些修改一次會動到一堆程式。若又混到其它修改, 看的人會很難分辨那些是必須看的修改。千萬不要又縮排又改名字, 或是搬位置順便加幾行新功能。反之, 拆成獨立的 commit, 一看註解 "Refactoring: indent X.", 大概瞄一下覺得沒問題就不用細看了。
以上的討論漏了如何寫好的  commit log。目前我只知道要先寫一行摘要, 若有必要, 再空一行, 接著寫完整描述。這個第一行寫得好, 其他人就能快速地過跟上進度。還在摸索中, 現在覺得像 mercurial 那樣開頭加個 X: 或 (X) 表示和 X 元件相關挺不錯的。
最後附上《Coding Horror: Check In Early, Check In Often》, 這篇幫我解決一些疑惑, 強化一些觀念。

2010年4月23日 星期五

coding style: spaces vs. tab

最近有不少體悟, 很多事沒接觸到那個環境, 看再多資料、想再久也不會明白。但有時候有一點相關經驗後, 反而沒多久就明白了。像大家常說的「換個位置換個腦袋」, 我覺得也是類似的情境。

話說兩年前和 York 聊到這個問題, 當時 York 說他發覺強者都支持用空白縮排, 但看了很多文章, 仍不明白用空白到底好在那。我們小聊了一陣子, 仍沒有定論。

結果出社會工作沒多久, 我忽然明白用空白縮排的好處了 --- 它能確保程式碼在任何地方長得一個樣。

有些人可能會說在自己的 vim / emacs 裡設好 tab 的寬度, 也能做到大家的畫面一致, 又保有日後修改寬度的彈性。然而, 觀看程式碼的介面太多了, 可能的場合遠比我原本想到的還多。像是 vim、emacs、Eclipse (或任何 IDE)、email (還得看用那家 mail client)、VCS (GUI 和 console 版都有)、Issue tracking system、各家 browser, 很難全部都提供彈性的設法, 即使有, 一一設定也太累了。還是爽快點直接用空白縮排比較省事啦。

以我自己為例, 平時用 vim coding, commit 前會用 hg 或 hgtk 看 diff。偶而會用 Redmine 看 changeset。上網找程式讀碼時會用 browser 直接看部份程式, 像看 JavaScript / CSS 是免不了的。附帶一提, 學生時代時, 我只用 vim 和 Eclipse, 自然會天真地認為大家設好 vim 和 Eclipse 就好啦。

2010年4月3日 星期六

在 Ubuntu 8.04 上裝 Redmine 0.9.x

關鍵在於 Redmine 0.9.x 需要 rails 2.3.5, 而 rails 2.3.5 需要 rubygems v1.3.1。但是 Ubuntu 8.04 上的 rubygems 不夠新, 也無法透過 gem 自己昇級 (gem update --system), Ubuntu 會跑出禁止 gem 自己昇級的訊息。之前才讀到《RubyGem is from Mars, AptGet is from Venus》, 想說系統管理和開發者戰得真激烈, 結果馬上就被這場戰爭掃到。

評估了網路上各種解法, 最後決定直接裝 rubygems tarball, 用 gem 裝 rails 2.3.5, 希望之後爛也只會爛 ruby package (我不確定這樣做到底會如何)。

總結安裝流程如下:
  1. 參照這篇裝 Ubuntu 上必要的 package:
    sudo aptitude  install build-essential
    sudo aptitude  install rails rubygems mongrel libmagick9-dev ruby1.8-dev
  2. 直接從原始碼裝 rubygems v1.3.1
  3. 參照 Redmine install 的流程
使用 MySQL 的注意事項:
  • MySQL 資料庫設定檔裡除帳號等資訊外要加一欄 socket:
    production:
      adapter: mysql
      database: redmine
      host: localhost
      username: redmine
      password: my_password
      encoding: utf8
      socket: /var/run/mysqld/mysqld.sock   # Ubuntu's path
    
  • rake migrate 會出現錯誤訊息: "ERROR: Failed to build gem native extension."。參考這篇的解法:
    sudo aptitude install libmysqlclient15-dev
    sudo gem install mysql
和 Mercurial 整合部份參考官方文件和作法如下:
  1. 在 Redmine 的 Repository 設定裡填入 Mercurial repository 的檔案路徑。
  2. 在執行 mongrel 的使用者的 $HOME/~.hgrc 裡加入信任 Mercurial repository 目錄的擁有者, 比方說我用 fcamel 跑 Redmine, 但 repository 的擁有者是 hg, 就要在 ~fcamel/.hgrc 裡加入:
    [trusted]
    users = hg
    
    不然會跑出 "Not trusting file hgrc from untrusted user" 的錯誤訊息。
我懶得設 apache, 就直接跑 mongrel daemon, 用起來還頗順的:
mongrel_rails start -e production -p 3000 -d

在 Fedora 下裝 id-utils

Fedora 似乎因為執行檔撞名,而沒有提供 id-utils 的套件 ,但這是使用 gj 的必要套件,只好自己編。從官網抓好 tarball ,解開來編譯 (./configure && make)就是了。 但編譯後會遇到錯誤: ./stdio.h:10...