over 11 years ago

Bootstrap 是 Twitter 推出的一套 CSS Framework。相當受歡迎的原因是因為讓原本對設計苦手的程式設計師在開發產品早期原型時可以有個可以看的門面先擋著。

又因為 Bootstrap 在 2.0 版以後加入了 configuable 以及 responsive 的設計。所以有些開發團隊,不僅只在 development 階段當臨時門面,在 production 階段,也作為骨架使用。

不過作為 production 使用,就出現了一個不容忽視的課題:如何在一套已經有現成的 styling 的 CSS Framework 上「客製發揮」。

「客製」往往意味著「大幅修改」。不過既然也要「乾淨」,這也表示一個附加條件:「不能破壞 bootstrap 原始架構」。

How can it be possible?

以下是我平常使用 bootstrap 的方式:

利用 Bundler 掛上 Bootstrap 的 rubygems

Bootstrap 的原始版本是使用 LESS 撰寫,不過也有開發者修改成 SCSS 版本。我本身是使用 anjlab 的 bootstrap-rails

透過 Gemfile 把 Bootstrap 掛上來,不直接放進 Rails project 裡面。

Gemfile
gem "bootstra-rails"

利用 Asset Pipeline 以及 SCSS 機制,客製、覆寫

application.css 內容
//= require base
base.scss 內容
@import "bootstrap-config";
@import "bootstrap";
@import "bootstrap-customized";
@import "responsive";
@import "responsive-customized"
解說
  • bootstrap-config.scss 是用來修改「Bootstrap 預設的變數」
$navbarHeight: 50px;
$navbarBackgroundHighlight: white;
$navbarBackground: #F7F7F7;
$navbarSearchBackground: #EAECEF;
$navbarSearchBorder: #EAECEF;
$navbarSearchPlaceholderColor: #565E65;
  • bootstrap 則是 Gemfile 裡面掛上的預設 bootstrap 包。(不修改)

  • bootstrap-customized.scss 則是無法透過修改變數的效果,通通放這裡用 override 的方式覆蓋。

.navbar {
  .navbar-inner {
    background: url(/assets/bg_header.png);
  }
}
  • responsive 是 bootstrap 用來作 responsive 的 css
  • responsive-customized 是你想要針對 bootstrap 的 resposnive 版本做的客製。

小結

這樣你的 application 「理論上」就可以跟著 bootstrap 的小升級而升級,而不會被纏到這個纏那個...

 
over 11 years ago

角色判斷 current_ability

這是一段普通的 ability.rb 權限範例 code。

class Ability
  include CanCan::Ability

  def initialize(user)

    if user.blank?
      # not logged in

      cannot :manage, :all
      basic_read_only
    elsif user.has_role?(:admin)
      # admin

      can :manage, :all
    end

  end

  protected

  def basic_read_only
    can :read,    Topic
    can :list,    Topic
    can :search,  Topic
  end
end

一般開發者最有疑問的是 def initialize(user) 這一段程式碼中的 user 到底是怎麼來的?怎麼會沒頭沒尾的天外飛來一個 user,然後對這個 user 進行角色判斷就可以動了?

這一段要追溯到...lib/controller_additions.rb 中的這一段 current_ability。

cancan 裡面去判斷是否有權限的一直是 current_abibilty,而 current_abibilty initialize 的方式就是塞 current_user 進去。

def current_ability
  @current_ability ||= ::Ability.new(current_user)
end

所以 initialize(user) 裡的 if user.blank? 其實就等於 if current_user.blank?(若沒登入)。

這樣去解讀程式碼,看起來就好理解很多了…

權限類別解說 :manage, :all, ..etc.

cancan 裡面用了一堆自定義縮寫,如 :manage:read:update:all,讓人不是很了解在做什麼。

  • :manage: 是指這個 controller 內所有的 action
  • :read : 指 :index 和 :show
  • :update: 指 :edit 和 :update
  • :destroy: 指 :destroy
  • :create: 指 :new 和 :crate

而 :all 是指所有 object (resource)

當然,不只是 CRUD 的 method 才可以被列上去,如果你有其他非 RESTful 的 method 如 :search,也是可以寫上去..,只是要一條一條列上去,有點麻煩就是了。

組合技:alias_action

cancan 還提供了組合技,要是嫌原先的 :update, :read 這種組合包不夠用。還可以用 alias_action 自己另外再組。例如把 :update 和 :destroy 組成 :modify。

   alias_action :update, :destroy, :to => :modify
   can :modify, Comment
組合技: 自訂 method

要是你嫌每個角色都要一條一條把權限列上去,超麻煩。可以把一些共通的權限包成 method。用疊加 method 上去的方式列舉。比如把基礎權限都包成 basic_read_onlyaccount_manager_only, etc…

  def basic_read_only
    can :read,    Topic
    can :list,    Topic
    can :search,  Topic
  end

針對物件狀態控管

在 User story 中,使用者固然 can :update, Topic,但還是讓人覺得覺得哪裡有點怪怪的?

是的。使用者應該只能編輯和修改屬於自己的文章,can :update, Topic 只有說使用者可以「修改文章」啊(等於可以修改所有文章) XD

所以 ability.rb 就要這樣設計了

  can :update, Topic do |topic|
    (topic.user_id == user.id)
  end
  
  can :destroy, Topic do |topic|
     (topic.user_id == user.id)
  end

可以玩的更加進階:

can :publish, Post do |post|
  ( post.draft? || post.submitted? ) && !post.published?
end

其他

cancan 還有其他進階主題可以繼續探討,讀者可以自行研究:

不過關於「難懂」和「難用」的部分,我想我應該講的差不多了…

小結

在寫這一系列文章時,我發現 cancan 的作者,其實把大部分的文件與範例,都寫在 lib/ 下的 RDOC 裡面了,光看 code comment 其實就可以瞭解大半流程。

不過我覺得 cancan 讓人覺得難讀的最大原因,可能還是官方缺乏一個 example ability.rb,對於被隱藏的自動完成部分也缺乏解釋,所以才造成大家覺得 cancan 是個難用的 magic library。事實上如果你開始搞懂 cancan 怎麼撰寫的話,它是可以幫你把網站的權限 code 處理的非常漂亮又易懂的。

這系列就寫到這邊,如果你對 cancan 還有什麼使用上的問題,歡迎到 Rails Tuesday 來找我討論。

系列連結

 
over 11 years ago

使用Cancan 的限制:RESTful controller (resource)

一般新進開發者會被 cancan 這兩個 API 搞得七葷八素:load_and_authorize_resourceauthorize_resource

這是因為 cancan 並沒有明顯的在 README 上做出說明:cancan 在使用上是有架構的限制

* 必須為 RESTful resource

(cancan 直接假設了你一定使用 RESTful,畢竟這年頭誰還在寫 non-RESTful …?)

* resource 必須與 Controller 同名

(@article 與 ArticlesController)

使用過 cancan 的人,大概都「猜到」規則好像是這樣?

其實不必猜,source code 裡面就寫的很清楚。

load_and_authorize_resource

load_and_authorized_resource 做了兩件事:

   def load_and_authorize_resource
      load_resource
      authorize_resource
    end
  • load_resource
  • authorize_resource

load_resource 作什麼呢?: loard_resource => load_resource_instance

def load_resource_instance
  if !parent? && new_actions.include?(@params[:action].to_sym)
    build_resource
  elsif id_param || @options[:singleton]
    find_resource
  end
end

okay,這段的作用等於如果你在 Controller 裡面下了 load_resource,cancan 會自作聰明的幫你 自動 在每一個 action 塞一個 instance 下去

lass ArticlesController < ApplicationController
  load_resource
  
  def new
  end
  
  def show
    # @article is already loaded

  end
end

如果是 new 這個 action,效果會等於

   def new
     @article = Article.new
   end  

如果是 show 這個 action,效果會等於

   def show
     @article = Article.find(params[:id])   
   end

有好處也有壞處,好處是…你不需要自己打一行 code,壞處就是不熟 cancan 的人,找不到 @article 在哪裡會驚慌失措…

load_resource 還有一些其他進階用法,在 controller_additions.rb 裡面有不少說明...

authorize_resource

authorize_resource 就是對 resource 判斷權限(根據 CanCan::Ability 裡的權限表)。

而這個 resource 必定是與同名的 instance。

如果是 ArticlesController 對應的必然是 @article。

但是你會想說這樣慘了?萬一我在 ArticlesController 裡面要用 @post 怎麼辦呢?

你可以在 controller 裡面指定 resource instance 的 name 要用什麼名字: authorize_resource :post

lass ArticlesController < ApplicationController
  authorize_resource :post
  
  def new
    @post = Article.new
  end
  
  def show
    @post = Article.find(params[:id])
  end
end

Ability 裡面要這樣下

  can :read, Post
  can :create, Post
  can :update, Post

resource 規則小結

所以 cancan 裡面的 resource 第一個會去吃 controller 的名稱當成 resource name,如果是 ArticlesController,instance 就會是 @article,而在 ability 裡面就會是 can :read, Article。這是在假設你已經使用同名設計 resource & controller 的情況下。

如果非同名。你可以做出指定:authorize_resource :post,雖然是 ArticlesController,但是這一組的 resource 名稱為 post,所以 instance 就會是 @post,而在 ability 裡面就會是 can :read, Post

一般開發者常會誤會的是

  • ability 會綁到 model,實際上不是
  • controller 名稱要與 @instance 名稱相同,實際上不一定
  • @instance 要與 model 同名,實際上不用
  • ability 吃的應該是 controller name,實際上不一定(吃的是 resource name,且可以被指定)。

Cancan 吃的是 resource,而且自作聰明的假設了大家「應該」都同名,而且 README example 也是使用「同名」,才會造成了這麼多的誤解…

如果你有更多疑問,可以直接看 source code 裡面的 這一支controller_resource.rb,相信會讓你對整個架構更加的清楚...

小結

這一節解釋了開發者認為最難懂的 load_and_authorize_resourceauthorize_resource。下一節我們要來講 ability 要如何設計…

系列連結

 
over 11 years ago

權限存取設計是在開發 Application 中相當讓人棘手的一個題目。

在一個網站開始建設的初期,通常這樣的問題並不會浮現,畢竟一般人的需求大半只會有 user 和 admin 兩種角色。但是隨著網站長大,更多的生意需求浮現,第三種角色的出現,通常就會把原本乾淨的 code 弄得骯髒不堪。

多種角色的權限設計難題

當只有 user 和 admin 的情況下,你可以在 view 裡面單純的做出這樣的設計

<% if user.is_admin ? %>
  <%= link_to("Admin Pannel", admin_panel_path ) %>
<% end %>

並且在 controller 裡面加上權限判斷

class Admin::ArticleController < ApplicationController
  before_filter :require_is_admin
end

但一段時間之後,User Story 被加進了這樣的需求:

  • 使用者可以被設定為「editor」
  • 擁有「editor」角色的使用者,可以進入 admin 後台發表、編輯文章
  • 擁有「edtior」角色的使用者,進入 admin 後台內的活動範圍僅限縮在文章後台內
  • 擁有「edtior」角色的使用者,進入 admin 後台內,不可以看到其他後台選項。

身為開發者的你,要如何在現有後台內加入這樣的設計?

不用實際動手寫也知道,若如以往使用 if / else 的設計,Helper / Controller / View 鐵定變成一團血肉模糊。

抱怨不能解決問題,但世界上是否存在乾淨的解答?

Rule-engine based authorization library: Cancan

答案就是:「Rule Engine」。

「針對多種條件執行多種動作」,此類的使用者需求,無論是使用 if / else,甚至是 case when,架構還是不免會一團混亂。與其承襲舊思路,不如啟用新想法「Rule Engine」實作:預先設計撰寫一套邏輯規則引擎,而後程式針對預設的規則進行邏輯判斷後執行。

而「角色權限」的設計需求上,正特別適合用 Rule Engine 這樣的觀念去建構。Rails 界知名的 authorization library cancan 正是以此作為基礎。

Cancan 可以做到的:介面單純化

cancan 希望做到的是,把權限判定的處理部分從 Helper / Controller / View 裡面,全部移到 app/models/ability.rb 進行判定。也因此可以做到

  • View 只需要判斷是否可以執行動作,而不必問是否有權限
<% if can? :update, @article %>
  <%= link_to "Edit", edit_article_path(@article) %>
<% end %>
  • Controller 不需要手動判斷是否具有權限
class ArticlesController < ApplicationController
  authorize_resource

  def show
    # @article is already authorized

  end
end

但驚人的是 ** view 的權限會是與 controller 的權限判定規則 ** 卻是一致的。(以往「自刻」權限判定,往往加了 view 卻會忘記 controller, 加了 controller 卻會忘記 view )

Cancan 希望做到的:權限中心化管理

而是否有權限存取,則全交給 app/models/ability.rb 去判斷處理。

class Ability
  include CanCan::Ability

  def initialize(user)

    if user.blank?
      # not logged in

      cannot :manage, :all
      basic_read_only
    elsif user.has_role?(:admin)
      # admin

      can :manage, :all
    elsif user.has_role?(:member)
      
      can :create, Topic
      can :update, Topic do |topic|
        (topic.user_id == user.id)
      end
      
      can :destroy, Topic do |topic|
         (topic.user_id == user.id)
      end
      
      basic_read_only
    else
      # banned or unknown situation

      cannot :manage, :all
      basic_read_only
    end


  end
  
  protected

  def basic_read_only
    can :read,    Topic
    can :list,    Topic
    can :search,  Topic    
  end
end

小結

cancan 是一套相當 powerful 的權限管理系統,但是它的文件卻相當不好讀,第一次想使用 cacan的 developer 很難從文件上找到自己想要的範例以及 api,或者了解其原理構造。如果沒有先給一些基礎範例,往往會是寸步難行。

下一篇我會深入頗析 Cancan 更深的設計原理,讓大家更看得懂 cancan 的 API 到底想幹什麼....。

系列連結

 
over 11 years ago

命名是 CS 中兩大難題,(ref: 在 DK 週一 Passion Bean 的 Talk 上聽到的 )。

今天在公司批票,剛好稍微教了一下同事如何設計寫出好讀的 Ruby method 名稱,覺得蠻有價值的,就隨手整理了一下貼上來。

場景

手機需要驗證,需要寫一個 Class 包裝一個手機驗證碼的寄送與儲存。原始的寫法是使用 「auth_gsm」欄位儲存。

視狀況使用「能表達情境」的名詞設計欄位 : gsm_authcode

  • auth_gsm 的缺點在於讓人看不出來這個字的主題是 gsm 還是 auth。是 gsm 嗎?看起來也不是,只能從字面知道這是跟 gsm 相關的 auth 行為。

  • auth_gsm 但這個 auth 到底是驗證的「狀態」,還是被驗證的「內容」?從名詞上看不出來。

  • 如果 User Story 是指這是應該傳送給使用者的驗證碼內容。應該被更具體的用名詞設計。

使用過去分詞 + "at" 或者是 "is" + 形容詞設計欄位

  • 被驗證的時間不應該使用 activated_time 而應該使用 activated_at。因為 time 是表示時間(時間是指什麼時候還是花多久時間?),不是「什麼時候被驗證」。

  • 是否已被驗證可以使用「is_activated」配合 boolean (true / false)。

使用 ? 表示這個 method 預期會傳回來的值只會是 true/false。

  • ruby 允許 method 名稱有 ?,若 method 名字內有 ?,開發者會預期回傳值是 true/ false
  • if post.is_hidden?if post.is_hidden 直觀
  • if user.is_activated?is_activated 直觀
  • 加上 ? 更強調了狀態,而非只是驗證欄位是否 true / false
  • Array 也有類似用法 array_a.include?(element_a)

通常會在 class 內再對作欄位作一層包裝

class Post < AR
   def is_hidden?
     is_hidden
   end
end

使用動詞與名詞如 generate_gsm_authcode 表示要做的事情

  • 不要在 controller 裡面直接使用
  user.gsm_authcode == "123456"
  user.save
  • 應該在 User model 內設計一個可以敘述要作什麼事的 method 包裝起來
class User < AR
  def genetate_gsm_authcode
    update_attribute(:gsm_authocode, rand(10))
  end
  • generate_gsm_authcode (動詞 + 名詞)只做事

名詞只是名詞

  • 如果 method 只是名詞,不要偷偷動作,而且預期傳回來的要是純量如 String, Array, Hash, Set, Object
class User < AR
  def full_gsm_number
    "#{area_code}-#{gsm_number}"
  end
end

使用 ! 表示這個 method會改變原先自身的狀態

  • String 的 gsub! 與 gsub 是不同的結果與作用。
  • 在 Ruby 中如果 method 加上! 通常會預期這個 method 會改變該 object 本身的狀態
  • ActiveRecord 的 save 與 save! 會發生的事其實是不同的。
  • save 遇到 validation 不過會儲存失敗,但不會 throw excecption。但是 save! 遇到 validation 會 throw exception。
  • dalayed_job 還是哪一套 background job 的 gem。method 後面若被加上 ! 表示立即執行不進 queue。
class User < AR
  def regenerate_authcode!
    # blah
  end

Library 的名稱應儘量為中性,並且貼近實際的責任。

如果這一支程式是拿來傳簡訊的函式,命名應考慮到這支程式應該要作什麼。

  • TWSMS (twsms.rb) 是比較好的。原因是這隻程式負責把 API 呼叫「包裝」了起來。

  • TWSMSWrapper (twsms_wrapper.rb) 是不好的。因為這隻程式並沒有實際「包裝」了什麼東西。它只是提供了一個介面讓其他人可以呼叫。

  • TWSMSSender (twsms_sender.rb) 是不好的。因為這隻程式並沒有實際自己去「呼叫」了外部程式。如果有 twsms_sender.rb 這支程式,裡面應該是一支負責實際去「呼叫」的 services wrapper。就跟 Mailer 的作用一樣。

module SMSsedner
  def send_to_customer
    # blah
  end
  
  def send_to_vendor
    # blah
  end
end

動作應該是中性的動詞,但不應該是保留字

這是安全的字

  • request
  • assign
  • ….

這是不安全的字

  • send
  • save
  • ….

worker 這個字被視為 background worker 。而不是作事的 dispathcher。

worker 通常會負責被視為是去 background job queue 裡面取出來的人。而非 job dispatcher。

Summary

這是我目前整理出來的一些比較大的方向。如果照著這樣的原則走去寫程式碼,大致上都會蠻乾淨好懂的…

 
over 11 years ago

前幾天貼了這一篇 3 招實用 Asset Pipeline 加速術

順便去 sass-rails 上面這個靠北 sass-rails 的 issue 回了一下 zurb/foundation 主要的問題…

我觀察 foundation 原先慢的原因是:幾乎所有的 library 都 @import "base";,然後 base 又 @import "compass";。所以導致只要 compile foundation 就無敵慢…

不過不知道解掉這個問題可以實際上快多少。今天正打算抓下來 benchmark 時,就發現這篇靠么 zurb 的人應該收到了。他們 patch 掉了,版本從 3.0.4 升到 3.0.5 。

主要變更就是把原先 @import "compass"; 改成 @import "compass/css3";。然後把所有的 @import "base"; 拿掉。

Benchmark

我就開了一個空的 Rails。實際用手上的機器去測 compile 時間。

iMac i5 2010 mid 款 8GB ram
  • Compiled application.css (9516ms) (pid 41169) # 3.0.4
  • Compiled application.css (2300ms) (pid 41483) # 3.0.5
Linode 4096
  • Compiled application.css (12518ms) (pid 12585) # 3.0.4
  • Compiled application.css (3201ms) (pid 12853) # 3.0.5

平均來說速度快了四倍...

結論

  • … 拜託不要偷懶直接用 @import "compass"; 啊 XD
  • 太慢有時候可能是 SCSS Framework 的問題
 
over 11 years ago

Asset Pipeline 最讓人詬病的就是 deploy 時花費速度過久。在社群聚會時發現大家都對這個主題非常不熟。所以把最近累積了的這方面技巧整理出來分享給大家。

1. Capistrano deployment speedup

使用 capistrano 內建 task 執行 assets:precompie

capistrano 內建了 'deploy/assets' 這個 task。只要在 Capfile 裡面

Capfile
load 'deploy/assets'

deploy 就會自動執行 assets precompile 的動作。由 原始檔 可以看到這個 task 實際執行的是

"cd /home/apps/APP_NAME/releases/20120708184757 && bundle exec rake RAILS_ENV=production RAILS_GROUPS=assets assets:precompile"

而執行的時機是

after 'deploy:update_code', 'deploy:assets:precompile'

許多開發者不知道有這一個 task 可以用。手動寫 task 去 compile,造成了兩個問題:

  1. 時機執行錯誤。Compile 時機錯誤會造成站上出現空白 css。
  2. 執行 compile 機器負擔太重。如果是手寫的 task 通常會是 load 整個 production 的環境去 compile。與只 load assets 這個 group 所吃的系統資源「有可能」差得非常多。

如果沒有變更到 assets 時,就不 compile

請把這裡面的內容貼到你的 deploy.rb 檔裡面

這是在 Railsconf 2012 的 Stack Smashing 上學到的一招。

如果你的 assets 檔案沒有變動的話,只要執行 copy 上一版本的 assets 就好了。這段 task 會偵測

  • app/assets
  • lib/assets
  • vendor/assets
  • Gemfile.lock
  • confir/routes.rb

是否有變動。基本上已經含了所有可能 assets 會變動的可能性。有變動才會重新 compile。

整體上會加速 非常非常的多

2. use @import carefully

避免使用 @import "compass"; 這種寫法

compass 是大家很愛用的 SCSS framework。大家寫 gradiant 或者 css spriate 很常直接開下去。

但是你知道

@import "compass";

@import "compass/typography/links/link-colors";

這兩種寫法。

前者 compile 的速度可能比後者慢到 9 倍以上嗎?

會這麼慢的原因,是因為 compass 本身即是懶人包@import "compass"; 會把

  • "compass/utilities";
  • "compass/typography";
  • "compass/css3";

下面的東西 通通 都掛起來(還跑 directory recursive)。

所以自然慢到爆炸。如果要用什麼 helper,請直接掛它單支的 CSS 就好了,不要整包都掛上來。

全掛其慢無比是正常的。

避免使用 partial

我知道 partial 是 SCSS 整理術的大絕招。但是若非必要,也儘量避免一直單檔一路 @import 到底。

common.css.scss
@import "reset";
@import "base";
@import "product";
common.css.scss
//= require "reset"
//= require "base"
//= require "product"

這兩個在 asset pipeline 輸出結果是一樣的。但後者會比前者快。

如果真的需要用到非得使用 partial 的技巧(如需讀變數用 require 讀不到,@import 才讀得到)再使用即可,因為只要一牽涉到directory recursive compile 就會慢…

3. don't require .scss & .coffee for no reason

避免使用 require_tree

使用 generator 產生 controller 時,rails 會自動幫忙產生

  • product.css.scss
  • product.js.coffee

然後 application.css 與 application.js 會利用

application.css
//= require_tree

這種技巧來把這些檔案掛上去。

但是你知道嗎?就算這些檔案裡面只寫了這幾行注解:

# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://jashkenas.github.com/coffee-script/

而且實際執行結果也等於空輸出。compile 一支大概也要 250ms。你可以想想,多 compile 10 支,就是 2.5 秒了。難怪超耗時。

可以使用 .js 或 .css 解決的不要用 .scss 與 .coffee 當結尾

Compiled jquery-ui-1.8.16.custom.css (0ms) (pid 19108)
Compiled jquery.ui.1.8.16.ie.css (0ms) (pid 19108)
Compiled jquery.js (5ms) (pid 19108)
Compiled jquery_ujs.js (0ms) (pid 19108)
Compiled custom.css (14ms) (pid 19108)

其中 custom.css 的檔名是 custom.css.scss

這樣應該知道為什麼不要亂用 scss 當檔名了吧?

小結

為了方便大家調整,我把具體加速 assets precompile 過程的步驟羅列在下面。

1. 換掉 deploy.rb 的 assets precompile tasks
2. 觀察 logs/product.log。
  1. 找出慢的 assets。
  2. 拿掉直接使用 import "comppass"; 的 SCSS,改用功能針對性寫法。
  3. 不需要使用 @import 寫法的改用 require
  4. 拿掉 require_tree,改用 //=require 一行一行掛上去
  5. 刪掉空的 scss 與 coffeescript
  6. 單純只是 CSS 的不要自作聰明幫忙加上 .scss 檔名。

====

如果有什麼問題,歡迎各位在底下留言討論。

也歡迎大家有空來 Rails Tuesday 坐坐。我很樂意幫大家解答問題。

P.S. 如果你是要問 Rails 101 書上的問題,請找小蟹。

 
almost 12 years ago

上一篇。作者提到了 「New-product introduction model」有非常大的機率,會讓一個 Startup 從車站一開始出發,終點就注定是地獄。整理歸納了的九宗罪,多數 Startup 都是因為這九宗罪而死掉的。

1. Assuming “I Know What the Customer Wants”

第一條是創辦人盲目的認為自己:

  • 知道客戶是哪些人
  • 知道客戶要什麼
  • 知道怎麼把產品賣給客戶

任何冷靜的觀察者從 Day 1 就會觀察到一個客觀的事實:一個 Startup 一開始不會有任何客戶。除非這個 Founder 原先就是這個領域的專家。

不然創辦人往往只能用「猜」的去猜測「會有哪些客戶」、「存在哪些問題需要解決」、和「可能的商業模式」。

而使用 「New-product introduction model」會讓創辦人將猜測當作是事實,在跟第一個真正的客戶聊過之前,開始設計產品和花大錢。

想要成功,創辦人需要將假設與猜測設法僅快的轉變成「事實」。唯一的途徑就是走出室外真正的與客戶攀談了解需求。當初的假設是否正確,僅快修正自己錯誤的猜想。

2. The “I Know What Features to Build” Flaw

第二條跟第一條有點相關。

創辦人會自以為懂他的用戶,先行假設出一堆他覺得用戶會需要的功能。「閉門」使用傳統的產品開發流程,精心打造一整套完整功能的產品。

但…等等,這是 Startup 應該做的事嗎?

不。這種方法通常是「Company」在已經有客戶的情況下才可以這樣作。

傳統式瀑布開發法,通常一開始起頭,就需要 1-2 年的時間。進度的衡量方式是在推出前究竟寫了多少行 code 以及製造了多少硬體出來。

但問題是,在開發過程中。不跟用戶進行直接且持續的交流,是很難知道哪一項功能才真正的吸引人。

在產品完工之後才再進行修改,代價往往高昂且耗時。巨大的開發能量被浪費,無數小時的工作成果被當成垃圾。

但諷刺的是,很多 startup 常愛用這種傳統的方式去開發產品。

3. Focus on Launch Date

傳統的開發流程會出現:Engineering、Sales 和 Marketing 的時程綁死在一個不能修改的「上線日」。

Engineering 的開發流程中通常會有 alpha / beta / release 三個階段,確保產品能夠有時間空間能被改善到能 deliver 的程度。

好笑的是,往往上線日,真正上線的產品的品質和進度卻往往都是「剛做完」而已。而不是到公司已經知道怎樣去行銷或者販售這個產品的程度。

但幾乎在每一間 startup,不管到底準備好沒有,不能修改的「上線日」卻往往跟「first customer ship」綁在一起。甚至更慘的是,有些投資者的財務計畫甚至跟這個時間也綁在一起。

投資者往往會說:「Why, of course that's what you do. Getting the product to market is what sales and marketing people do in startups. That's how a startup make money.」

這是 絕對致命的建議 ,千萬別理他們。(這句話是書上講的,不是我講的)

每一家 startup 或者是 company 當然都想要能夠一開始就順利的販售出新做出來的產品,並且能夠執行極佳的行銷策略。但這個夢只有在公司知道「誰會負責賣」以及「為什麼客戶願意買」的前提下才會達成。

但是多半的情形是,大家一廂情願的只會認為:有著良好的「Engineering Execution」,客戶就會買單…

一次又一次的,只有在上線之後,Startup 才會發現沒有足夠多的用戶會使用他們的網站、轉化成有效的訂單。早期用戶數不夠提升到主流市場。不能解決 high-value 的問題。更或者是配送成本過於昂貴。

發現這些事情已經夠慘了。

更慘的是情況變成騎虎難下的局面,已經花了很多錢卻沒有期待的效益。只好開始找問題在哪裡,看看還有沒有機會修正…

Webvan 的情況是,當初他們更身處在 dot-com 狂燥症熱錢到處是的年代,又加重了這樣的情形。公司在前半年只有 400 人,後半年就補了 500 人進來;在初期只開了一間值 4000 萬美金的配送中心,不久之後,又瞬間開設了 15 間相同等級的配送中心。

你問他們為什麼要這樣作?喔。因為這是 Business Plan 上寫的。不管真實狀況是不是需要。

4.Emphasis on Execution Instead of Hypotheses, Testing, Learning, and Iteration

Startup 的文化通常強調「get it done,and get it done fast」。

所以很自然的:「the heads of engineering, sales and marketing all believe they are hired for what they know to do, not what they can learn 」

這些頭頭們直接假設他們的經驗跟現在的事業有強烈的正相關,而且他們需要做的事就是在這個新事業「重製」他們在前單位所作的事。

問題是,Company 跟 Startup 是不同的。Company 需要的是「執行」business model,它們的客戶、需要解決的問題,和產品需要的功能都處於「已知」。

但 Startup 所需要的運作方式卻是必須開啟「Search」模式,然後測試和證明所有當初的猜想。從每次測試的結果中學習,提煉出猜想,然後再繼續測試一遍。目的就是要找尋出一種可以重製(repeatable)、規模化(scalable)以及能夠獲利(profitable)的 business model。

書上特別 highlight 了一段話,我覺得很棒: 「Relentless execution without knowing what to execute is a crime」

實務上,startup 是由一連串的原始猜想所組成,它們可能最後都是錯的。所以,如果專注在執行和產出一個全由這些原始猜想組成的產品,絕對是一個自殺策略。

而傳統的「product introduction model」的想法通常是直接假設建立一間 startup,是一個 一步一步來、有順序的、執行導向的過程。

每一個階段都可以被 PERT chart (PERT 圖是一個項目管理工具,用於規劃,組織和調整項目內的任務)所描繪。根據里程碑投入相對應的資源。

所以想搞 startup 還用「product introduction model」,難道不是有計畫的自殺嗎!?

5. Traditional Business Plans Presume No Trial and No Errors

傳統的產品開發模式對董事會和創辦人來說有一個很大的好處:它能提供一條看似不模糊的道路和前面還有哪些里程碑需要完成。

在這種模式中,財務進度也是用收入現況、資產負債表和現金流等實際指標來追蹤。

但是在 Startup 真實的狀況中,這些指標沒有一個適合。這些財務指標是用來衡量已有存在客戶、市場的大公司用的。

它們沒有一個可以用來追蹤 Startup 唯一目標的進度:那就是「找到一個可以重複、規模化的 business model」。

6. Confusing Tradition Job Titles with What a Startup Needs to Accomplish

多數的 Startup 會借鏡一般的公司所給的 Job Title。但記得,這些都是從已知生意模式借來的玩意。在這些公司中,所謂的「Sales」指的是重複的銷售一個已知的產品給一些已經理解這個市場運作規則的客戶。

但是 Startup 怎麼可能會有這些「已知」的這些玩意??

因為目標用戶、產品規格和產品介紹極可能可能每日一變,Startup 的早期成員要是能夠非常能夠適應混沌的人。他們要對學習和發現抱持著極大的開放態度、殷切找到「可以重複、規模化」的 business model」。

Webvan 的執行長和 VP 都是從大公司挖來的一幫人。他們對於這種 startup 的混沌都非常不適應。對於混沌,他們的解決手段就是:趕快讓公司急速長大,以為這樣就能解決問題。

7. Sales and Marketing Execute to a Plan

公司真的缺人的時候經應該補人。要補人當然要開出正確的缺補到適合的人。但,你確定你真的補進了正確的人嗎?

補進一個正確職稱的 VP,但是他卻用了錯誤的技能以及錯誤的經驗在作事,這也是對 Startup 的一場災難。

在一般公司裡,往往走的是依循著傳統的 Business Plan 和「product introduction model」。也就是讓董事會和創辦人對即將展開的這個新生意,設出 一個上線日、估算 burn rate、制定獲利計畫和一狗票里程碑。

這對已存在生意模式的公司當然是合理的。但大部分的 Startup 都不適用這樣的情形,

現實生活中 Startup 通常一開始小有成績,接下來就會想補業務拓展團隊。這時候的業務拓展團隊適合的方向往往該是跟 Product Development 部門摸索出可以重製而且可規模化的生意。

但問題是你挖來的 Sales VP 和 Marketing VP 可能往往不管這些,他只懂的做的是接著董事會這些假想計畫,假想一個狀況自顧自的進行舖天蓋地的銷售計畫以及行銷手段。

這是 Startup 所需要的嗎?不,這是大災難...

8. Presumption of Success Leads to Premature Scaling

傳統的 Business Plan 往往將公司發展的每個步驟敘述的完美無暇,天衣無縫。這使得在這樣的模式中,能夠犯錯、從中學習、根據客戶意見回饋修正的空間,被壓得很小。

從沒有人規定說「Stop or slow down hiring until you understand customers」或者是「pause to process customer feedback」。

即便是最有經驗的執行者也會被被迫根據進度一直補人。跟著這就會引起下一場 startup 災難:「premature scaling」

明明網站目前每天只有 5000 訪客。但 Bussiness Plan 上面寫的是,認為下半年每天應該衝到 50 萬訪客。這樣的規模就需要大買機器,大肆雇用人,擴張新 feature,於是就開始花大錢衝刺這些部分。

時間慢慢的過去,這些東西都沒有如預期般的用上,當然也沒達到成績。但是東西、人,都已經到位了。放著閒置也不是辦法,只好再找一些「不是事情」的事情給他們作。或者是拼命假想一些情境製作 feature。賭看看可否衝刺到當初的目標。

聽起來熟悉嗎?

書中很酸的舉了 Google 的 Orkut, Wave, Dodgeball. Microsoft 的 Zune, PocketPC 等等作為例子。這都是用了「on rigid schedules driven by the models and the presumption of success]」搞出來的災難。

雇用人和灑錢應該根據產品銷售狀況和市場反應是否能夠進入「可預測、可重複、可規模化」的狀態,而不是根據「它們應該按照原定計畫被執行」。

9. Management by Crisis Leads to a Death Spiral

通常董事會和創辦人會在事情已經發生了,「該做的都已經做」了卻沒有起色,之後才檢討到底出了什麼問題。

什麼是「該做的都已經做了」?

就是明明都已經請厲害的 PM、程式設計師打造這個產品了。行銷計畫也聘請了好的公關公司舖天蓋地的宣傳了,當中也請了不少 focus group 來對談。但是就是沒有多少用戶想來使用,更別提留下來變成長期用戶了。

他們往往檢討的原因不會在這段時間到底做了什麼錯事,結論往往會導向:當初的某個 VP 是否適任,他的策略有很大的問題。接著董事會會作一件事,就是再從外面挖來一個高手,換掉這個人,「修正」當初的錯誤。

而這個「高手」,一進來也會直接給一個結論:那就是「前一個人有問題,之前的策略通盤皆錯,於是我們必須這樣那樣。」他會說出這樣的話不意外。

因為這就是他被『雇用來的原因:前人有錯,之前策略有問題』。不然你期待他要說什麼?

但事情本質並不是這樣的。Startup 本來就是對『假設』一連串的『試誤』、『驗證』與『頓悟』。而非是一堆被『Bussiness Plan』和『Milestone』驅動的『怪物』。

後記感想

這一章節只有短短的 10 頁。但是卻讓我讀起來冷汗直流,腦海裡一直衝上不少真實場景、真實例子。

一直以來,我對一些實例百思不得其解。有些企業挾著原先的資源優勢和招募優勢,風風火火的搞了一個偽 Startup。最後卻慘敗收場。但是毫無資源的個人或單純只是優秀的程式設計師,卻赤手空拳自己蓋了一座雄偉堡壘。

你說這世界一定是這樣嗎?也有大企業投入資源最後取得有效的成功(美國、德國的大型山寨集團),而個人陣亡的更是不計其數。

每個創業家都在思考這個問題:『成功』到底跟『資源』有沒有正相關,還是只跟『團隊』與『創辦人』有關?

這些年來我也一直在思考這個問題的答案:只是每當以為自己稍微想通一點脈絡,另一個實例就突然打了我一巴掌。最後我也只好這些例子收起來,因素歸諸成『Luck』與『God』。

這本書,最吸引我的就是作者寫的前言和導讀。作者在這本書一開始的部分就寫,這一本書就是一本 step-by-step,教人怎樣建造一間成功、獲利、可規模化 startup 的指南。他認為這樣的公司不是神話而是可重製的。這本書就是答案。

他也不希望讀者一口氣就讀完這本書(而且他也認為讀者一口氣讀不完)。還寫了長達六頁的指南教大家怎麼讀。

還沒正式閱讀本書時,光看到這幾頁,我心中只有一個想法:「這個作者真狂妄」。這本書再厲害,也不可能有你講的這麼誇張吧?我就是要一次讀 200 頁,不可以嗎?

讀了 30 頁以後,我的想法徹底改變了。我開始認為他說的一切都是真的。而在這本書裡面勸告讀者的話,都是真心的。(竟然好心的寫了六頁教你怎樣讀這本書)

我開始對一些懵懂不解的問題有了答案:

至少我開始理解原來一直以來,大家習慣用的作事的方法,就是製造業一來使用的方法。只適合在有確定用戶,確定市場,確定 bussiness model 的情況下才能使用。也只有在這樣的情況下才有機會成功。

這很大程度了解釋了為什麼:個人、大公司要『新創』一個事業很容易失敗。而一些『大公司』要『山寨』一個服務也有機會取得成功。

Webvan 盛大開場,悲慘結束。也是因為盲目 follow business plan,沒有摸索出 customer 的樣貌,也沒有針對 customer 的 feedback 中調整產品,沒有從構築公司裡面學習並修正。錢花光,於是就破產收場了。

第一章通篇在講的是如果你想要『新創』一個事業,你絕對不能掉進這個傳統製造業的公式裡。而且作者認為,在 21 世紀的現在,網路與生活緊密接軌,新創公司、新創服務可以套用傳統製造業的公式的機會越來越小。大家都是往未知前進。於是你必須改用另外一種模式探索、構築你的 Startup 才行。

而這個模式就是作者頓悟出的另一個模式『Customer Development Model』。

Customer Development Model 分為四個階段:

  1. Customer discovery
  2. Customer validation
  3. Customer creation
  4. Company-building

我會在下一篇讀書心得中,整理這四個階段的內容。

====

(待續…一個禮拜,已經讀完了但是寫出來要花很久的時間)

 
almost 12 years ago

最近終於有時間坐下閱讀這本親自跑到美國帶回來的這本創業指南:The Startup Owner's Manual: The Step-By-Step Guide for Building a Great Company。這本書目前才僅僅看了 30 多頁,就讓我迫不及待的先寫下書的部分讀後整理,因為太過值回票價。

短短的 30 頁,卻花了快 3 個小時的時間閱讀,不是因為艱澀難懂,反而是因為這本書遍地都是許多真知洞見的見解與實例。一邊閱讀,眼前一邊湧上過去幾年的成功和失敗,很多時候我必須激動地停下閱讀,仔細反芻,才能繼續往下一章前進。

很多過去讓我百思不解的疑惑,在這數小時激盪下,都一一撥雲見日。

這本書第一章的標題是 The Path to Disaster: A Startup Is Not a Small Version of a Big Company。作者以一間 2000 年網路泡沫時代創立的大公司 Webvan ,從募集巨額資金/ IPO 不久後瞬間破產倒閉,當中所犯的種種錯誤,來解釋大部分的人在創辦企業時所犯的錯的致命程度。再藉機引出第二章:The Path to Epiphany: The Customer Development Model,作者對 Startup 的建立過程整理了自己的一套見解,他認為多數 Startup 必須要經過一段 Customer Development Model(有四個步驟),才能變成一家真正的 Company。

泡沫時代的 PChome 24hr : Webvan

Webvan 做的生意是 online ordering and same-day door-to-door grocery delivery 生意。若你不清楚這是什麼,把它想成 PC home 24hr 就對了。關於 Webvan 的失敗故事,網路上有不少篇的紀錄文

Webvan 的背景是讓人很羨慕的。在短短的時間內就募集了鉅量的資金,請到了很棒的 CEO。很多客戶也喜愛他們的服務。但是竟然在開業短短不到兩年的時間,就宣告破產倒閉了。到底發生什麼事?

他們的問題不在於低落的執行能力,相反地 Webvan 一開始就打正規戰,使用了最普遍使用的 「New-product introduction model」方式擴展執行,並且徹底的往多數投資者所樂見的方向去:「先行者優勢」、「快速變大」。

那為什麼還會失敗呢?而且還是這麼迅速的倒閉呢?因為 Webvan 略掉了一件相當重要的事?直接為這個公司帶來了死亡。

製造業的模式:New-product introduction model

在二十世紀,每個公司要在市場上推出一個新產品,都會使用一種固定的 product management model。這套模式很常被用在製造業上。

New-product introduction mode

Concept/ Seed => Product Development => Alpha/Beta Test => Launch / 1st Ship

從一個簡單的想法作為出發點,然後進入產品開發階段,接著進行使用者測試,然後再推出市場。

「New-product introduction model」很適合目前已經存在的「Company」所運行的模式,知道消費者長什麼樣子,spec 可以被容易的被列出寫下來,市場也被定義出來了,而且你可能也知道對手長什麼樣子。

問題是,一般的「Startup」很少能符合這樣的標準。但是很多人還是堅持使用這種模式去進行產品開發、客戶尋找,甚至是對銷售計畫、上線、營收計畫制定時間表。然後大多數人都這樣掛掉了。

這套模式到底哪裡有問題?又是怎樣讓 Webvan 爆掉的?

一去不回頭的瀑布模式

「New-product introduction model」的問題在於容易引發瀑布模式:

Requirements => Design => Implementation => Verification => Maintenance

整件事會開始變成這樣:從一個點子變成一本 Bussiness Plan。募到錢後開始招人,人都到位以後,作行銷 (Marketing) 的開始根據 Bussiness Plan 定義市場規模和首批客戶樣貌,舉辦幾場 focus group 對談,然後開始製作 MRD (market requirements document),開始丟給 RD 團隊去作。

Product Development 階段

作行銷 (Marketing) 的繼續準備 sales demo、行銷材料,雇用公關公司。在這個階段,通常公司還會跑去雇用一個業務副總。

同時,RD 團隊會集中火力在制定詳細規格,開發產品。他們的重點會擺在如何在一個定義好的有限集合內,降低工程上的風險。接著就是 18-24 個月的開發期。

在 Webvan 的這個 case 中。就是去蓋自動化倉庫,買各式各樣的輸送設備。開發自己的儲存系統、倉庫、路線管理系統…etc.

而行銷團隊這時候會開始準備圍繞著 Webvan 這個品牌的行銷以及促銷活動,第一批客戶的嘗試體驗,建立客戶忠實度,如何最大化回頭率和單次購買金額。

Alpha/Beta Test 階段

RD 團隊開始測試這套系統運作有沒有問題。行銷團隊忙著制定整套市場溝通策略(建立公司網站、建立業務 sales kit..etc.)。然後公關公司開始聯絡媒體、部落格…

業務團隊開始跟第一批 beta 用戶(當初自願加入嘗試新產品計畫的用戶)簽約。業務主管開始絞盡腦汁的在研究如何達成當初根據 bussiness plan 定的營利計畫。

Launch / 1st Ship 階段

隨著產品開始商轉,公司朝向一個「big-bang」式的花錢模式。舉辦 press event,建花大錢建立全國性的業務組織、業務管道。董事會開始根據銷售執行率來衡量公司績效。

這些都是正規軍作法,但無疑的,都很燒錢。特別是在建立銷售管道和繼續支撐行銷計畫。

花光錢死亡

故事的結局非常不新鮮如同大家當初預料的一樣:

  • 上線之後開始發現當初預先設想的流程不符合實際需求
  • 行銷計畫過於花錢
  • 忙著擴張市場卻一天到晚作賠本生意
  • 客戶群開始逐漸萎縮但公司視若無堵的繼續擴張計畫

最後公司錢花完倒閉了。

The 9 Deadly Sins of New Product Introduction Model

作者從 Webvan 的故事中,整理歸納了九宗罪,點出 「New-product introduction model」 所隱含的致命風險。點出了一般 Startup 常犯的重大缺失。

他點出了很重要的一點,其實大多數的 Startup,特別是網路業,不應該使用「New-product introduction model」去推出自己的產品。而是必須要用 Customer Development Model 去穩固打下基礎,從 Startup 轉型成 Company。

「New-product introduction model」有非常大的機率,會讓一個 Startup 從車站一開始出發,終點就注定是地獄。

這九宗罪是:

  1. Assuming "I Know What the Customer Wants"
  2. The "I Know What Features to Build" Flaw
  3. Focus on Launch Date
  4. Emphasis on Execution Instead of Hypotheses, Testing, Learning, and Iteration
  5. Traditional Business Plans Presume No Trial and No Errors
  6. Confusing Tradition Job Titles with What a Startup Needs to Accomplish
  7. Sales and Marketing Execute to a Plan
  8. Presumption of Success Leads to Premature Scaling
  9. Management by Crisis Leads to a Death Spiral

我會在下一篇讀書心得中,仔細整理這九宗罪的詳細內容。這九宗罪又是如何搞垮一個 Startup 的。

The Startup Owner’s Manual 讀書心得(1): 別再使用製造業思維搞 Startup
The Startup Owner’s Manual 讀書心得(2): New-product Introduction Model 致命的九宗罪

 
almost 12 years ago

這次去美國旅行接近三個禮拜(去參加研討會 + 旅遊)。這算是我第一次的美國自助旅行,機票、保險、飯店,景點的安排幾乎都是自己弄的,
這次玩得非常開心,中間也幾乎沒出什麼大事。

這次旅程橫跨好幾個點,所以就放棄請旅行社代為安排的計畫,因為即便有辦法請旅行社訂,價格也會超過預算。雖然剛開始有點手忙腳亂,
但老實說在這過程中還學到不少東西的,

趁記憶猶新,趕快寫下來,也方便其他人參考。


行程準備

護照、美國簽證

去美國當然要先辦護照和美國簽證。這是有相依關係的:如果沒有護照,就辦不了美國簽證。沒有美國簽證,旅行社不會賣任何美國機票給你。

機票建議是在 conf 開賣之後就訂下去(越晚訂,日期、位子、價格會越來越爛),所以最好那時候就要開始跑護照和美簽的事。因為光搞定護照和簽證最少就要大概花掉一週的時間。

我手上的美簽是一年前在公司出差時申請到的 B1/B2 簽證,一般人大概是申請 B2。簽證攻略在 PTT 的 VISA 版上有很多,這邊我就不細述了。

機票

去小城市就拆開訂

我的計畫原本只有打算到奧斯丁參加 Railsconf,去舊金山多玩兩週的行程是之後才決定的。

比較麻煩的是奧斯丁不是直飛點,所以不管是向旅行社洽詢或者是上網到雄獅、燦星詢價,都得到非常爛的價格(大概都五萬多塊)。價格還是其次,比較大的問題是班次很少時間很爛,而且中間的美國轉機點只有洛杉磯可以選。這就跟預定行程完全衝突。

偶然間跟教母 cjin 靠北到這個問題之後,他建議我採取了另一種策略:「拆開訂」。

於是後來我的機票就變成只跟雄獅訂 「台灣到舊金山」的來回機票。大概台幣 33000 上下。然後再上 Priceline 上訂「舊金山到奧斯丁」的美國國內線來回機票。大概台幣 7-8000 。

拆開訂好處很多,除了價格很明顯的變得非常便宜。最重要的是一把機票拆開訂,這兩段的時間和機位選擇就變得超級多的...機票瞬間就搞定了。

美國國內線機票價格不含行李托運費

這邊要注意的是台北到美國段,我坐國泰的航班,可以免費托運兩件行李各 23.5 kg。但要轉機到奧斯丁的時候,才發現原先我們買的 UA 國內線機票是不含行李托運的。第一件行李要 25 USD,第二件行李是
35 USD,限重也是各 23.5 kg。

重量的分配和額外的支出在這裡是要特別注意的。

旅館

大會期間住宿

Railsconf 是在 Hilton, Austin 舉辦的。因為大會有談到還蠻兇的 Discount 價(便宜大概 50+ USD) ,而且聽累就可以直接回房間睡很方便。於是在買票的時候,我就順便訂下去了。不過即便有 discount 還是不便宜,打完稅之後還是要 200 USD 出頭一晚。

舊金山旅館

聽說舊金山的旅館大概是全美數一數二便宜的(我聽朋友講的),還算事實。120 USD 左右一晚就可以訂到不錯的旅館。當初是上 背包客棧 作功課,找了幾家候選,最後
排預算選出來的。我們是住 PowellStratford 這兩間。

這兩家都離 Union Square 很近,靠近各種交通工具的起點,所以我蠻推薦住這一區的。但缺點是 Union Square 晚上超過九點以後,附近會變得比三重還要三重,治安很亂,這是唯一的擔憂。

Powell 能給的房間算比較大,超靠近地鐵,櫃台幾乎什麼票都有在賣,而且櫃台阿伯都很親切,這算優點。缺點是網路 DNS 一天到晚掛掉,很讓人抓狂...

Stratford 的房間比起來就算有點小了。但一樣交通很方便,櫃台也親切。優點是乾淨、整潔、明亮,網路也快。他跟 Powell 差了兩個 block,治安好一點。缺點是靠街的房間晚上很吵...-_-

這兩家旅館我是透過 Hotels.com 訂的,都是信用卡 prepaid。

機場旅館 (過夜班機建議訂)

抵達舊金山的時候大概是晚上 11:00。但是去奧斯丁的班機是隔天早上 8:00 多。剛開始我們傻傻的沒有訂機場旅館,直接睡在機場大廳。沒想到那感覺實在太爛了,不是因為機場不好睡。而是因為連續在機場和飛機上
待了二十幾個小時沒洗澡,全身都快發綠光了。

於是從奧斯丁飛往舊金山時,實在不想再當綠人的我就下決心訂下去了。也是上 Hotels.com 找的,大概 100 美金一晚( two queen beds )。機場旅館會有免費接駁車讓旅客來回機場。神秘的是,機場旅館的網路是我們這趟旅程遇到最快的....orz

機場接駁車

機場到旅館會有很多 option,可以選擇租車、Taxi、Shuttle 或者是鐵路(如果該城市有蓋的話)。奧斯汀段我是在 Priceline 訂國內線機票時,有加錢訂 Shuttle 的選項,就順手訂下去了。到機場時拿著購票證明去 Shuttle 登記處 redeem 就可以了。回程票則是要在 24hr 前打電話去預約回程的時間。

在舊金山段時,到舊金山市區我是搭 BART ( 機場連著 BART )。而離開舊金山時我是請櫃台幫我訂 Shuttle (因為想看回程風景)。Shuttle 一段票大概都是 10 幾塊美金這樣。

如果你是懶人的話我建議訂 Shuttle 會比較好,拖著一個 2-30 kg 重的行李跑來跑去並沒有很好玩 XD

手機門號

有 3G 以上需求請選 AT&T

手機 Sim 卡我是上 Aerobile 買的。這家服務算不錯,很多 plan 可以選。寄送速度也快,隔天到達。你準備要出發時,可以寫信請他幫你開門號(自己開很囉唆)。

主要有兩家門號可以買:AT&T 與 T-mobile。去年我在西雅圖出差時,用的是 AT&T 的 3G,但是印象沒有很好。主要是因為鎖卡(iPhone),自己繞路有點麻煩,加上不是吃到飽。

所以這次就選 T-mobile 的 30 天吃到飽方案。沒想到還是 bad choice。原因是 T-mobile 的頻段 iPhone 只能跑到 edge 而已,速度會讓人很難過。但好處是插上去就 active 了。

又會改建議用 AT&T 是因為美國現在開始都在跑 4G 了,用 iPhone4+ 速度可以跑上去,而且現在 hack 也變得比較容易了。所以綜合來說商務族還是建議使用 AT&T 4G。

500 MB 的 plan 大概就夠一個人用大概 10 天了吧。

電話需求

我平常就有存一些 Skype credit。有時候在國外還是有需求打越洋電話,通常我是直接找有 Wifi 的地方上 Skype 打回去。其實若沒有 Wifi,用 Edge 網路其實勉強也能講一下 skype。省錢很多。建議出國前刷一點點數存進去。

保險

基本上現在用信用卡買機票都有含一些保險在裡面。但是險有很多種,基本上我們這種外行人也看不懂,好險網有整理了旅遊險懶人包,可以讓你看完大概知道這些險的種類。但是我實在也太懶得一個一個去找險來保,最後是在網路上找到美商安達保險這個 旅遊險懶人包
保下去,保了17天,保費大概是 2000 出頭,用信用卡就可以線上申辦了。

旅遊手冊

這次因為是自助旅行的關係,很多訂票訂房訂車的都是我自己在處理。Receipt 超多。所以這次也跟往常不一樣,我特地製作了一本旅遊手冊(一本紙本、iPad 又丟一份 pdf)把這些整理好收進去。裡面主要有放

  • 台灣 <-> 舊金山航班資訊
  • 舊金山 <-> 奧斯丁航班資訊
  • 奧斯丁 Shuttle 接送資訊 (reedem ticket)
  • 奧斯丁 旅館訂房資訊
  • 舊金山 旅館訂房資訊
  • 同行旅伴旅遊資訊
  • 航空公司、旅館、接駁車聯絡資訊
  • 美國友人會面與聯絡資訊
  • Railsconf 及其他周邊會議入場卷
  • Railsconf 議程表
  • 一些旅程注意事項以及 checklist

這次會特別整理這本手冊,是基於過往幾次出差經驗,累積出來的。不得不說,使用經驗實在好極了。因為路上所有遇到的美國海關關員、航空櫃台、旅館櫃台、接駁巴士櫃台,都愛死這本手冊了。因為他們幾乎只要拿著這本手冊照著 key 就可以瞬間完成他們手上的作業。幫他們省不少時間也幫我省時間。

美國海關(拷問程度不下 AIT)

特別要提的是美國海關這一個關卡。之前幾次的出差經驗讓我發現,美國海關是個比 AIT 問話審查還要嚴格的地方。比如說像我就算去出差開會,官員還是會拷問你一大堆,把你當想跳機的犯人。所以過海關時,手上還是要準備一些資料才行。之前去出差時,我就有看到隊伍好一些人,因為英語說不輪轉,身上又沒什麼證明的人。被拒於門外或被請到小房間去...orz。

這次單純只是去參加研討會想玩一玩,結果拿這本入境,老外隨口跟我聊一聊,看看裡面的內容就讓我過關了。

======

這些大致上就是出發前比較特別需要去申請和處理的部分。
晚一點會換貼一些我當初寫在手冊裡面的物品 checklist,不少東西都是幾次出國下來累積出來的經驗...。