看了「2017台大杜鵑花節—圖資系宣傳包」的一些感想

看了「2017台大杜鵑花節—圖資系宣傳包」,有些想法不吐不快,只好再次打破自己「不談 LIS」的戒律。

「圖書館」

「圖書館」是個和製漢語,有其時代脈絡,但是 “Library” 的精神概念不應該一直被載體、以及迷戀這種載體的人綁架。

今天我看到太多人堅持 Library 的 typical 樣貌,卻又主張「圖書資訊學」不是只談「圖書館」,這種解釋方式,只會讓外人聽得更一頭霧水。

LIS 這門學問何時可以回歸「資訊」本質、再更上一層樓,就看這個圈子的人何時把「載體」從腦中洗掉。

Library 的中心概念應該是「蒐集、整理、提供資訊的專門機構」,所以我覺得 LIS 有一個比較適合的、不需再為「圖書」、「圖書館」多費唇舌解釋(卻常愈描愈黑)的譯名,是「資訊與資訊機構學」。

「資訊流」與「系所特性」

那份宣傳包的「資訊流」一節,具體而微地把 LIS 的範疇從頭到尾介紹過了,但是看到「系所特性」這節,卻沒有適當的 mapping,反而像在列舉一些酷炫潮夯的技能課程來吸引學子選系,我覺得很可惜。

國家選才、資訊社會、人文關懷的廣度

最後這一段,我本來想以 Aaron Swartz 的例子來痛批這個學界的。不過,最後還是全段刪除。畢竟我已經決定逃離這個圈子,所以有些事情,我不在圈子裡,批判也只會淪為「你這麼會講,怎麼不來考考看?」的泥巴仗而已。

拖稿拖很久的 Packer 簡介文(主要講的是如何建 AWS EC2 AMI)

這篇是很久以前答應 hSATAC 大人說要寫的,由於一直有更有趣的事情可以做所以就拖稿拖到現在,主要講的是如何使用 Packer 來快快樂樂建 AWS EC2 AMI,將這件事情給它 infrastructure as code 這樣。

範例檔案放在 [GitHub] 這裡,這篇舉的例子,是建出一個把 Drupal 8 裝好的 AMI,先看 packer.json:

{
    "variables": {
        "drupal_version": "8.2.5"
    },
    "builders": [
        {
            "type": "amazon-ebs",
            "name": "drupal-8-ami-us-east-1",
            "ami_description": "My Drupal 8 barebone AMI ({{user `drupal_version`}}) (us-east-1)",
            "region": "us-east-1",
            "source_ami": "ami-e13739f6",
            "instance_type": "t2.micro",
            "ssh_username": "ubuntu",
            "ami_name": "drupal-8-ami-{{timestamp}}",
            "tags": {
                "Name": "drupal-8-ami-{{timestamp}}",
                "Distribution": "Ubuntu Server 16.04 LTS (HVM), SSD Volume Type",
                "Drupal": "({{user `drupal_version`}})"
            },
            "ami_block_device_mappings": [
                {
                    "device_name": "/dev/sda1",
                    "volume_size": 8,
                    "volume_type": "gp2",
                    "delete_on_termination": true
                }
            ],
            "launch_block_device_mappings": [
                {
                    "device_name": "/dev/sda1",
                    "volume_size": 8,
                    "volume_type": "gp2",
                    "delete_on_termination": true
                }
            ],
            "vpc_id": "vpc-cc9aeba9",
            "subnet_id": "subnet-940f76ae",
            "associate_public_ip_address": true,
            "security_group_ids": [
                "sg-15a50671"
            ],
            "communicator": "ssh",
            "ssh_pty": true
        },
        {
            "type": "amazon-ebs",
            "name": "drupal-8-ami-us-east-2",
            "ami_description": "My Drupal 8 barebone AMI ({{user `drupal_version`}}) (us-east-2)",
            "region": "us-east-2",
            "source_ami": "ami-d1cb91b4",
            "instance_type": "t2.micro",
            "ssh_username": "ubuntu",
            "ami_name": "drupal-8-ami-{{timestamp}}",
            "tags": {
                "Name": "drupal-8-ami-{{timestamp}}",
                "Distribution": "Ubuntu Server 16.04 LTS (HVM), SSD Volume Type",
                "Drupal": "({{user `drupal_version`}})"
            },
            "ami_block_device_mappings": [
                {
                    "device_name": "/dev/sda1",
                    "volume_size": 8,
                    "volume_type": "gp2",
                    "delete_on_termination": true
                }
            ],
            "launch_block_device_mappings": [
                {
                    "device_name": "/dev/sda1",
                    "volume_size": 8,
                    "volume_type": "gp2",
                    "delete_on_termination": true
                }
            ],
            "vpc_id": "sg-789c2d11",
            "subnet_id": "subnet-60e84409",
            "associate_public_ip_address": true,
            "security_group_ids": [
                "sg-789c2d11"
            ],
            "communicator": "ssh",
            "ssh_pty": true
        }
    ],
    "provisioners": [
        {
            "type": "file",
            "source": "./nginx/default",
            "destination": "/tmp/nginx-default"
        },
        {
            "type": "shell",
            "inline": "wget https://ftp.drupal.org/files/projects/drupal-{{user `drupal_version`}}.tar.gz -O /tmp/drupal.tar.gz"
        },
        {
            "type": "shell",
            "script": "./provision.sh"
        }
    ]
}

這份 Packer template 裡頭很多地方都可以望文生義,我就不一一逐條解說了。只講一些概念上的、可能會遇到坑的地方。

首先,一份 Packer template 分為好幾個部份,我這個例子裡,就分成了 variables, builders 以及 provisioners 三塊。

其中,在 variables 裡我們可以宣告接下來會用到的變數,在我這個例子裡,我把想裝的 Drupal 版本寫在這裡,之後很多地方都可以用 {{user `drupal_version`}} 來引用。

然後看到 builders 這塊,我分別想在 AWS 兩個美東 region 建立需要的 AMI。這裡有個小 tricky 的地方,就是如果我們不特別寫 name 這個屬性的話,Packer 是不會允許我們建立多個同一 type 的 images 的(這裡就是指 amazon-ebs),如果我們需要在複數個 AWS region 建立 AMI,就要特別寫明 name

還有要注意的一個概念是,Packer template 裡面寫的各種屬性,是在「建立 image 當下」使用的,AMI 建完後,要怎麼使用是你家的事,Packer 並不負責這塊。所以呢,為了 Packer 方便作業(在這裡是透過 SSH 連進一台為了做 AMI 而臨時開出的 EC2 instance),像是 vpc_id, subnet_id, associate_public_ip_address, security_group_ids 這些設定,是 Packer 呼叫 AWS API 開一台 EC2 instance 時使用的,特別是 security_group_ids,注意這些設定要能夠保證 Packer 可以 SSH 登進去機器,不然就等著 timeout 執行失敗。

provisioners 這裡我們可以用很多方式來對 AMI 做 provisioning,Packer 官方提供了很多常見的 provisioners,除了基本的丟 file、跑 shell script 以外,還有一些很潮很夯或不潮不夯但有用的自動組態工具像是 Ansible, Chef, Puppet。搭配這些自動組態工具就有更多花樣可以玩,可能也比 shell script 更好處理一些更需要彈性的東西。

最後提一個小技巧,就是如果我們跑 packer build packer.json 時遇到問題,那麼用 packer build -debug packer.json 進除錯模式,Packer 會把它為了這次 building 動態產生的 SSH key 寫在檔案系統裡,讓您可以手動 SSH 進去這台機器,方便診斷問題所在。

該如何行銷 Rust 的筆戰

先從 [Rust is more than safety] 這篇開始,作者認為到目前為止,Rust 在行銷上並沒有打到「我能為用戶解決什麼問題?」的痛點,光只在語言設計的層面上兜圈子,特別是「安全」這點,然後有了這些回應:

  • [graydon2 | Rust is mostly safety]: 作者認為以一個系統語言來說,Rust 不但追求「安全」還特別致力於滿足 concurrent 下的「安全」,就是 Rust 的最大賣點。
  • [Safety is Rust’s Fireflower]: 作者認為 Rust 主打的「安全」,就是讓涉足系統程式設計的用戶,終於有一個在這方面做得比過去同質產品更好的選擇可用,如同瑪莉歐裡的金花一樣。
  • [Fire Mario, not Fire Flowers]: 原作者被各方吐嘈後寫的回應。引用前面瑪莉歐的譬喻,認為 Rust 的行銷應在是聚焦在「吃了 Rust 這朵『安全』金花,會讓你成為火球瑪莉歐」,而不光是「Rust 是一朵有『安全』功效的金花」。
  • [Fire Flowers and Marios: Marketing Rust – Medium]: 各打五十大板的吐嘈。我覺得這篇前面有點像來亂的,但是後面寫得還頗有道理,對於技術的行銷來說很中肯。
  • [Rust is Software’s Salvation – Redox – Your Next(Gen) OS]: Redox 這個採用 Rust 寫的 OS 專案也來參戰,作者寫出從早期因為 Rust 的幾項特性 (safety, concurrency, performance, documentation) 而開始接觸這語言,算是忠實愛用者,但是覺得 Rust 當前的問題之一就是「太少東西是以 Rust 寫成的」,從這點引申論述 C 語言為何至今還是受到相對多數的用戶採用、C 語言的問題、軟體的品質問題、Rust 或許就是這個問題的救贖。

2016 年回顧

2016 年回顧:

  1. 硬著頭皮、厚著臉皮,報名碼天狗週刊的策展人,鞭策自己看 Erlang, Elixir 的東西。在這種大家都還沒凝聚出死板的操作定義、最佳實踐、把生態圈風氣變得人云亦云、按表操課、造神拜神的時候,很多東西可以隨心所欲地摸索,我覺得就是最好的時候。
  2. 悟出「購物車」這種系統,其實可以採取簿記原理設計。
  3. 在工作中採用 Scrumban 方法,以及實踐心目中的「真.DevOps」工作文化,覺得學到很多、成長很多。也比較快樂。
  4. 「用戶想的與你不一樣」,其實沒有倒站,卻在用戶的感受下(所謂的 UX)認定我們就是倒站, 雖然覺得冤枉,但是習得這樣的教訓並改進相關的 UI 後,覺得如果自己是用戶,這樣的改良用起來也會比較爽。也是一次寶貴的經驗。
  5. 從「月薪嬌妻」中警惕自己對於「責任」與「義務」的區別,不能因為因循苟且而麻木、失去判斷力。追求婚姻的品質,這是根本。

買了幾片 Arduino Leonardo

想做兩個應用:

  • 鄰居的二手菸偵測器
  • 依空間亮度、時段的芳香噴霧機啟動開關

之前其實我對 Arduino 抱著一種鄙夷的態度,認為男子漢既然要做這種單晶片應用,就該直接去用單晶片,這樣才硬派。

但是直到最近想做上列的兩個應用,光是製作一塊電路板,就讓平時已經忙得不可開交的我卻步。而 Arduino 這種把介面(無論是指軟體或硬體)、控制語言的基礎先弄得具體而微的快速原型製作工具,其實幫人省下了不少時間。

而且做出來的東西,就算不是量產等級的,但也不只是不中用的「玩具」,而是確確實實能派上用場的成品。

就是這樣,我對 Arduino 的觀感開始轉為覺得它可親可愛。

雜論 SNS 與入口網站的資訊架構設計 (2)

一開始接著要寫這第二篇的時候,我的腦袋又陷入了之前寫學位論文時的窠臼,或者說是心魔,心想要有一套論述、理論,來支持我最後想帶出來的結論,於是愈寫愈覺得處處碰壁,深怕哪邊沒防禦好、又要被吐嘈。

唉,網路何時變得這麼可怕?(掩面嚇哭)

不過這系列廢文就只是「雜論」罷了,我真的給自己太多壓力了。且其實資訊架構有其適合的人事時地物(不過也存在著因循苟且、習慣成自然的那種「沒有架構的架構」啊…),並沒有絕對的是非好壞。所以我只要照我的腦子想到哪、就記到哪、歸納到哪,這樣就好了吧?

巴哈洽特

因為 Chat 被起了個渾名叫「洽特」,所以我就自己把巴哈姆特 BBS 的 Chat 看板戲稱為「巴哈洽特」。

在很久很久以前,巴哈姆特 BBS 還算百花齊放的時候,Chat 只是一個「不分類雜談區」,但是現在 Chat 可能是巴哈姆特 BBS 之所以還維持資訊流量的、一枝獨秀的看板。在我忘記鳥窩 BBS 密碼、且鳥窩關站後,Chat 就是我溫度最接近的同溫層了。

為什麼要特別講這個呢?是因為我覺得巴哈姆特的「主題串接模式」很難用,又或者說目前這些還活著的 Telnet BBS 站的同質功能都很難用,在訊息量大的 Chat 尤其常讓我覺得心煩意亂,追某主題的新文章,得一直在「一般文章列表」、「主題串接模式」間切換。

但是這當中的含金量、哏量又每每讓我覺得心滿意足。

Twitter 在訊息列表整個攤平 (flatten) 這點上有些相似。Twitter 把資訊單位化簡到「某個人,在某個時間,講了什麼話」的「訊息」,然後在「時間軸」上鋪排開來,這樣的資訊架構。要追個前後文,來來回回點個好幾層是常有的事。

不太一樣的地方是,Twitter 通常是我先看到我 follow 的帳號用戶發的推文,或這些帳號轉他人的推文,但是 BBS 比較沒有這樣的 filter bubble 效應。

現在很多 Web 式的論壇系統,也稱自己為 BBS,但在討論串 (threaded) 呈現這方面,做得更直觀、好用些,甚至還有 semi-threaded 與 full-threaded 的結構。

我當然是希望我的「這個 BBS」能夠把討論串的呈現弄得更好爬文。而我設想的結構甚至不是樹狀,而是圖 (Graph)。

雜論 SNS 與入口網站的資訊架構設計 (1)

寫這篇文的時候,我正在設計一套 BBS。我希望這套 BBS 除了可以找回過去「只要有心(加上做了一些功課),人人都可以架一個 BBS 站」的樂趣,以及期許這樣的樂趣會喚醒人們對 SNS(社群網絡服務)其實可以不必那麼中心化 (centralized) 的認知,還有一點點練功的成份在。

呃,好吧,我承認,也許不只是一點點而已,我希望透過從做中學,瞭解一個基於 Web 技術的 SNS 該如何設計、實作以及營運管理。

因為這樣的動機,促使我整理對於「這個 BBS」應該如何鑑往知來的想法。所以本文會以時間為主軸,在這條線上看 SNS 與入口網站的資訊架構設計。

1995: BBS 以及絕大部分很靜態的 Web

1995 年,在電腦史上,或者不要那麼浮誇,至少在我的個人電腦利用史上,我覺得是很重要的一年。這年,當然地,Microsoft 的 Windows 95 在 PC 市場上取得了成功,積極意義上就是說,PC 市場上有了一個普及的 32-bit 作業系統,這使得 PC 用戶的電腦終於有一個能夠充分發揮硬體性能、且不會因為單一程式影響就動輒當機死透的多工、圖形化、多媒體使用介面。

為什麼這個需要在開頭大書特書?因為有了這麼一台具備圖形、多媒體處理能力(且足夠穩定了)的設備,可說是後來我們之所以可以體驗 Web 的一種基礎建設。

(這裡不免要再大膽不怕羞地提一次,當時我是 Windows 95 競爭對手、IBM OS/2 Warp 3.0 的狂熱愛用者,以現在的流行語來說,就是個腦殘粉。)

約莫同時,Internet 開始普及到民間,在這之前,數據機 (Modem) 的功能多是玩家用在架設與連接 Dial-up BBS,這種 BBS 跟現在大家用的 Telnet 式 BBS 不太一樣,信件、討論文章為了要節省撥接的電話費,所以通常是用「快信軟體」下載到自己電腦上後再閱讀,同時將您的回應上傳,形成一種「信包交換網路」。

而因為「時間就是金錢(電話費)」、「空間也是金錢(那時主流規格硬碟容量約莫是 540MB~1GB 之間)」的緣故,Dial-up BBS 很注重「言之有物」,不歡迎「灌水」、不需要的「引言」需要刪除。這樣的文化,其實也影響了早期的 Telnet BBS。

回到正題。也就是說,在 1995 年,如果您用約二~四千元台幣買了一台當時主流規格 14.4K 或 28.8K Bps 的數據機,再跟 ISP 業者租用他們的上網服務,或是極其有幸,透過學校電腦教室、宿舍提供的網路連線,就可以「上網」。

為什麼要提這件事呢?這件事的隱含意義是:無論用數據機、網路卡還是什麼我這個鄉下人沒見過的黑科技,在當時終於可以讓一台 PC(當時咸認最具代表性的「資訊設備」)不再只是一座孤島,讓用戶有了線上連結、在上頭可以資訊交流、發展社交,如此的可能性。

而當用戶連上網,或撥進 BBS 後,呈現在其面前的,是由「信區」、「新聞群組」、「版面(看板)」為單位,組織而成的一種資訊架構,透過這樣分門別類、按圖索驥的方式,用戶被動去適應這樣的主題分類,或是積極一點去申請一個主題區,但無論如何,用戶是圍繞在「主題→主題分類」這樣的框架下進行資訊交流。

同時,在 Web 這邊,我們回顧以前的蕃薯藤或是 Yahoo! 的分類目錄設計,一樣也是讓服務提供者決定了主題、分類體系,來引導我們尋得或整理相關的資訊。

那麼,如果您的興趣是所謂的「次文化」、「惡趣味」、不見得在這個分類法下找的到一個歸屬的呢?又或者,您根本不想「被分類」,上網只是想要有個地方裸奔、自言自語呢?BBS 裡可能有些站台可以申請「個人版面」或您乾脆架個「個人站」,Web 也可以有業者提供的免費空間,您就自己架個獨特的網站吧!

大致上說來,當時的網路,是很強調「主題分類」這個框架的。我覺得箇中原因之一是,當時還缺乏一個夠強大、很會爬的搜尋引擎,好讓用戶跳脫被設定的分類體系、自行決定「要找什麼」;另外一個原因,就是適應人類其實很喜歡翻目錄、型錄的天性。

解決在 Vim NERDTree window 裡誤切換 buffer 的問題

這篇是承自之前提過的問題,今天終於花了一點時間,看了一下 NERDTree 的 source code 找出解法。

在自己的 .vimrc 裡新增這麼一段自訂 function:

function! LeaveNERDTree()
  if winnr() == g:NERDTree.GetWinNum()
    wincmd p
  endif
endfunction

大概的邏輯是這樣:

  1. 如果目前所在視窗的號碼等於 NERDTree 的視窗號碼
  2. 不管怎樣,跳開就對了,cursor 就是不要在 NERDTree 裡面

然後就可以套用到自己切換 buffer 的快速鍵上:

nnoremap <silent> <F3> :call LeaveNERDTree()<CR>:bprev<CR>
nnoremap <silent> <F4> :call LeaveNERDTree()<CR>:bnext<CR>

短短幾行,解決了長期以來的、常常為了自己操作錯誤而惹得很毛的問題,怨自己當初為什麼沒仔細去看 source code?

Game & Watch EG-26: EGG

昨晚,幫老婆找出童年時玩過的掌上型電玩「接雞蛋」,前因是我跟強者我同事 hSATAC 借 PS Vita 玩,被她看到又再問了一遍:「有『接雞蛋』嗎?」這是她不曉得第幾次提到「接雞蛋」了。心想平常不碰電玩、最多只玩手機休閒遊戲的她,看到別人在玩掌上型電玩就會聯想到「接雞蛋」,就像提到電視遊樂器就有很多人聯想到「超級馬力歐」那般懷念的經典?

用 “Game and Watch Egg” 去搜尋,因為這類液晶電玩,任天堂的 Game & Watch 當然是最知名系列產品,其次應該是 CASIO。

先問是不是就是影片中的這款:

老婆猛點頭說對,看來沒有比我也很懷念的 Game & Watch MW-56: MARIO BROS. 難找。

於是再往 Google Play 上找看看有沒有 clone 這款的遊戲,果然有,而且還不少人做!而 MW-56 這款搬貨工馬力歐兄弟卻沒人 clone,我難過。

看了一下維基百科才明白,原因是 EGG 這款當年在蘇聯有仿製品,所以可能是「國際」(←又是大叔的雙關語冷笑點)知名度最高的一款 Game & Watch,也解釋了為何 Google Play 裡找到的仿製遊戲面板是俄文。

頭一次見到老婆玩遊戲玩得這麼開心,比上次「打飛機」還開心。其實不管是「接雞蛋」還是「搬貨工」遊戲規則都很簡單,變化只在速度與偶爾的例外攪局,就能帶給人樂趣,在這個凡是遊戲都要 3A 級才上得了市面、看得上眼的時代,其實回頭想想,我們在追求無止盡的聲光效果進化時,是不是先問問「我這樣玩得開心嗎」?

之前在巴哈姆特裡不時有針對「任飯」「你們說任天堂作品的『遊戲性』較高?什麼叫『遊戲性』?你們自己都說不上來!」這樣的攻擊。我現在想想,「遊戲性」其實就是一種 UX(用戶體驗),或許 UI 有 guideline 可以遵循,但是光跟著 guideline 做,不代表你就能營造出讓人覺得 “Awesome!”, “Incredible”, “Wonderful” 的 UX。