about 1 year ago

double 可以讓我們輕易地閃掉準備「協作者」的痛苦。但是有時候,我們還是可能被「協作者」本身的內部邏輯改變整到。比如說,這裡是一個「建議」Suggestion 表單:

require 'rails_helper'

RSpec.describe SuggestionsController, type: :controller do

  describe "POST create" do 
    context "when suggestion is invalid" do 
      it "render new" do 
        post :create, :suggestion => { :title => "", :description => "" }

        expect(response).to render_template :new
      end
    end
  end
end

class Suggestion < ActiveRecord::Base
   validates :title, presence: true
end

class SuggestionsController 
  def create
    @suggestion = Suggestion.new(suggestion_params)
    if @suggestion.save
      redirect_to root_path
    else
      render :new
    end
  end

  protected

  def suggestion_params
    params.require(:suggestion).permit(:title, :description)
  end

end

如果物件非法的話,要 render new 這個 action。正常來說,這樣的 controller 測試,應該會通過。

但是有時候 controller 程式碼沒有動,但 model 程式碼內部動了,比如說在這個例子,可能多加了新的欄位,或者是新增了其他的驗證方式,導致這個 controller 測試要因為 suggestion 內部本身的邏輯要修改。

這樣真的很討厭。

重寫測試

其實這裡我們根本不應該塞「例子」進去,這樣 suggestion 內部邏輯一旦改變,我們的 controller test 就要改個沒完。我們真正應該要做的是:

  • 準備一個「一定儲存失敗的替身」
  • 然後在 controller 讓 @suggestion.save 鐵定失敗
  • 因為我們要驗證的是 「render :new」

所以我們可以把測試改寫成這樣

require 'rails_helper'

RSpec.describe SuggestionsController, type: :controller do

  describe "POST create" do 
    context "when suggestion is invalid" do 
      it "render new" do 
        invalid_suggestion = double(:save => false) 
        allow(Suggestion).to receive(:new).and_return(invalid_suggestion)

        post :create, :suggestion => { :xxx => "yyy" }

        expect(response).to render_template :new
      end
    end
  end
end

這樣 suggestion 內部驗證再怎麼樣變,都不會影響到這個 controller test。

 
about 1 year ago

Four-Phase Test 是一種常見的測試 Pattern。幾乎也是一般人在寫測試的手法,依序為:

  • Setup (準備測試資料)
  • Exercie (實際執行測試)
  • Verification (驗證測試結果)
  • Teardown (拆解測試)

但是通常我們在一般寫測試或者是 TDD 時,常常會遇到一些狀況,導致 Setup 階段時,資料「難以被準備」。通常是有以下幾種狀況:

對象物件當中的「協作者」還沒被誕生

比如說都還沒寫到那個 class 或者是 method

對象物件當中的「協作者」是「系統外部物件」

如: API 回傳內容

要準備的資料、相依性過於複雜

要生超多物件才能測試

執行這個測試,呼叫到「吃效能很兇」的 method

導致測試運行緩慢

使用替身

但我們的首要要務,其實是要測我們內部的程式的邏輯,不是要測「外部邏輯」的狀況。所以我們可以使用 mock objects,在 RSpec 裡面叫做 double(替身)。

在 Setup 階段使用「替身」的資料,直接進行測試。

舉例來說,我這裡有一個學生計算器

class StudentsCalculator

  def initialize(course)
    @course = course
  end

  def students_count
    @course.students_count
  end
end

我還沒想好 course.students_count 要怎樣設計(這在 TDD 中很常見),但是我知道在 StudentsCalculator 中的邏輯,吃的就是將來 course 提供的 students_count,而且「我現在就想要驗證這件事」。所以我可以假造一個 course 的替身,讓這段測試可以通過。


require 'rails_helper'

RSpec.describe StudentsCalculator do
  describe "#students_count" do 
    it "returns students count" do 
      course = double(:students_count => 5 )
      student_calculator = StudentsCalculator.new(course)
      expect(student_calculator.students_count).to eq(5)
    end
  end
end

 
about 1 year ago

以這個程式來說,我們要把「課程上架」,課程上架以後,系統會送一封 onboarding 信給開課教師。

class Course
  belongs_to :user

  def published?
    published_at.present?
  end

  def publish!
    user.send_welcome_email!(self)
    update_column(:published_at, Time.now)
  end

end
class User < ActiveRecord::Base

  has_many :courses    

  def send_welcome_email!(course)
    UserMailer.send_course_welcome(course)
  end
end

但是下面這個測試,我們在這裡測試的狀況,我們根本不在乎「信是否有沒有被送給開課教師」,我們在乎的是:呼叫 publish! 後,是否課程真的已經會被上架。

所以我們就會用 allow(course.user).to receive(:send_welcome_email!)send_welcome_email! 這件事 stub 掉,讓它 return nil,這樣就不會呼叫 UserMailer 了。

何況,我們可能也還沒時間開發 UserMailer 內的東西。

  describe "#publish!" do 

    let(:user) { FactoryGirl.create(:user) }
    let(:course) { FactoryGirl.create(:course, :user => user ) }

    it "will be publsihed" do 
      allow(course.user).to receive(:send_welcome_email!)
      course.publish!
      expect(course).to be_published
    end
  end
  • 備註: 在 RSpec 2.x 版,你可以直接這樣寫 course.stub(:send_welcome_email!),但這寫法造成一些敘述語法問題,所以 RSpec 3 改成 allow + receive
  • 備註: 在 RSpec 2.x 的 stubmock 語法造成一些很大的語法與觀念問題。RSpec 3 改的清楚不少...
 
about 1 year ago

Stub

一般來說,驗證「回傳值」或驗證「狀態改變時」,經常會使用 Stub 手法。
因為在這兩種狀況中,即便這個 method 有可能去呼叫「外部物件」,坦白來說,我們「不在乎」。


比如說這段程式碼

class Post
  def visit!
    update_visit_cache_to_memory!
    self.increment_counter(:visit_count, 1)
  end

  def current_visit_count
    update_visit_cache_to_memory!
    return visit_count
  end
end

因為我們只在乎「當下這個物件」。所以寫測試時,我們會 stub 掉 update_visit_cache_to_memory!「呼叫 外部 method」這件事。以取得我們要的結果。

Mock

「模擬」與一個「協作者」的互動,設立一個「會收到指定訊息」的期望,去驗證互動是否真的有發生。

 
about 1 year ago

要開始講解 Stub 與 Mock 這兩個概念之前。我想要先來介紹單元測試的種類,這樣才講得清楚後續要繼續寫下去的主題。

一般來說,單元測試分成三種狀況。

1. 驗證回傳值是否符合期待

查詢:

  • 回傳某些東西
  • 不改變狀態

2. 驗證物件狀態的改變是否符合期待

命令:

  • 改變內部狀態

3. 驗證是否有去執行與外部的呼動

呼叫:

  • 是否有去執行與外部的互動

 
about 1 year ago

很多新手在經歷一兩年的開發經驗後,開始覺得寫測試很重要。想要入門學習測試。但是看到這些名詞:

  • stub
  • mock
  • double
  • allow
  • receive
  • expect

就覺得很頭痛。特別是 RSpec 2.1 與 RSpec 3 一些語法升級,又造成了更大的混淆。所以我希望寫一系列的文章,解釋清楚這些名詞。想辦法把測試這個主題講清楚。

這一個系列,我會嘗試以以下的順序講解以下主題:

 
about 1 year ago

有同學最近在問 RSpec 的問題,因為會回答很多次,乾脆寫「一個經典例子」,來介紹 RSpec 的常見 syntax,來介紹一串重要的手法。

例子:Course#publish?

publish? 的判斷方式是 published_at 這個欄位存在與否。

class Course < ActiveRecord::Base
    
  def published?
    published_at.present?
  end
end

觀念 1: 測試要寫 正 / 負場景

在這個例子裡面,因為是測 boolean 值,比較好的方式,是要寫正反例子。

RSpec.describe Course, type: :model do
   describe "#publish?" do 
    it "return true when published_at present?" do 
      course = FactoryGirl.create(:course, published_at: Time.now)
      expect(course.published?).to be_truthy
    end

    it "return false when published_at blank?" do 
      course = FactoryGirl.create(:course)
      expect(course.published?).to be_falsey
    end
  end
  
end

觀念 2 :當 when 與重複語句出現,應抽出變成 context

  • when published_at present?
  • when published_at blank?

這兩句是場景,所以接下來可以改寫成

RSpec.describe Course, type: :model do
  describe "#publish?" do 

    context "when published_at present?" do 
      it "return true" do 
        course = FactoryGirl.create(:course, published_at: Time.now)
        expect(course.published?).to be_truthy
      end
    end

    context "when published_at blank?" do 
      it "return false" do 
        course = FactoryGirl.create(:course)
        expect(course.published?).to be_falsey
      end
    end

  end
end

觀念 3 : 用 let 抽出 course,it 內只測 expect

將物件抽到 let。

RSpec.describe Course, type: :model do
describe "#publish?" do 

    context "when published_at present?" do 
      let(:course) { FactoryGirl.create(:course, published_at: Time.now) }
      it "return true" do 
        expect(course.published?).to be_truthy
      end
    end

    context "when published_at blank?" do 
      let(:course) { FactoryGirl.create(:course) }
      it "return false" do 
        expect(course.published?).to be_falsey
      end
    end

  end
end

觀念 4 : Minimal Valid Object 與「測試真正的變因」

但是在步驟 3 中,這樣的測試手法,是生成了「兩個不同的物件」去測。但我們寫這個測試的目的是要「測試一個物件,在published_at值不同」的情況下的回傳狀況。

真正的「宿主」是 Coruse 本身。所以我們改用 subject。傳 nil 與 現在的時間,去測試真正要測的行為。

RSpec.describe Course, type: :model do
  describe "#publish?" do 

    subject(:course) { described_class.new :published_at => published_at } 
    context "when published_at present?" do 
      let(:published_at) { Time.now }
      it "return true" do 
        expect(course.published?).to be_truthy
      end
    end
    context "when published_at blank?" do 
      let(:published_at) { nil }
      it "return false" do 
        expect(course.published?).to be_falsey
      end
    end

  end
  
end

觀念 5 : xxxx? 可以使用 be_xxxxx 去測試

  describe "#publish?" do 

    subject(:course) { described_class.new :published_at => published_at } 
    context "when published_at present?" do 
      let(:published_at) { Time.now }
      it "return true" do 
        expect(course).to be_published
      end
    end
    context "when published_at blank?" do 
      let(:published_at) { nil }
      it "return false" do 
        expect(course).not_to be_published
      end
    end

  end

觀念 6 :is_expected

測 return true/ false 看起來像是超多餘的。而且我們是對 subject 測,所以又可以改成 is_expected

RSpec.describe Course, type: :model do
  describe "#publish?" do 

    subject(:course) { described_class.new :published_at => published_at } 

    context "when published_at present?" do 
      let(:published_at) { Time.now }
      it { is_expected.to be_published }
    end
    context "when published_at blank?" do 
      let(:published_at) { nil }
      it { is_expected.not_to be_published }
    end
  end
  
end

最後看起來是不是變得很好維護呢?

 
about 1 year ago

之前不少讀者都問過我一個共有的問題:就是如果它的產品很新,或者它的 TA 很小眾,要怎麼開始做 Growth / Marketing。我的建議都是請他們先「實作 Content Marketing」。

What is Content Marketing ?

現在網路上的用戶,每天都面臨著「廣告轟炸」。基本上如果你的產品很新或 TA 很小眾,下廣告基本上也沒什麼用。使用者通常也會跳過。

減肥粉絲團

更多人的購買決策是,觀看部落格心得、看別人推薦而購買。或者長期訂閱某粉絲團的內容,對該粉絲團產生信賴,進而購買這個粉絲團上面推薦的內容。

電子書下載

又或者我講一件更令你熟悉的場景。有天你逛到某一個電子書免費下載的頁面,輸入 email 就能下載該本電子書,然後你的電子信箱裡面就開始收到對方寄得一些厲害的 Tips,因為也很有趣,所以你沒退掉的意思。某天該公司推薦一個軟體,你覺得還不錯,就買了....

這就是「Content Markerting」做的事。

Content Marketing 的形式

Content Marketing 的技巧與策略有很多種。比較常見的格式是

  • 養粉絲團分享文章
  • 寫部落格(關鍵字)
  • 製作免費電子書
  • 線上研討會

See / Think / Do framework

本篇只是一篇 Content Marketing 的 Intro。我也沒辦法 Cover 太多的詳盡步驟。在這邊我想直接以製作部落格內容為例子,講解「好的」Content Marketing 要怎麼做。

這裏我想介紹的是一個簡易的 Framewrok 叫做 See / Think / Do。這個框架是一個在 Google 上班的行銷人 Avinash Kashykk 研發的。

See / Think / Do 框架,將所有觀眾分為三種人。也是一個漏斗架構

  • See 群眾,但他們可能沒有購買意願
  • Think 他們可能表達了一點購買意願
  • Do 已經傳達了非常清楚的購買意願

在這裡我舉兩個我喜歡的兩套 SaaS 軟體,來讓各位更了解這是怎麼一回事。

例子 1 : Codeclimate

Codeclimate 是一家用來檢測「軟體品質」的軟體公司,基本上 Ruby on Rails 界的人,人人都知道這間公司。它的功用就是你將程式碼上傳,Codeclimate 會自動檢測你的程式碼裡面的「壞味道」「安全漏洞」,將你的程式碼評級。

它的 See / Think / Do 群眾就是

  • See : 有在寫程式的人
  • Think : 認為把程式寫乾淨有點重要的人
  • Do : 認為團隊的程式「絕對要乾淨」的資深程式設計師,正在找工具讓團隊的程式碼品質更佳的人。

例子 2 : GrooveHQ

Groove 是一家線上客服軟體公司。基本上可以把客戶寄來的 Support 信,自動轉成 ticket,讓公司內客服協作,也提供 FAQ site 功能。我們公司就是用 Groovq 做客服管理。

它的 See / Think / Do 群眾就是

  • See : 有在做生意的人
  • Think : 認為做好客服有點重要的人
  • Do : 認為「客服好」等於「賺大錢」的老闆。正在找工具,讓公司客服回覆速度更快的人。

Why See / Think / Do 框架重要?

很多人做「內容行銷」,花了很多力氣在「Do」這個層面的客群。

甚至認為 Do 才是最正確的客群。

  • Do : 認為團隊的程式「絕對要乾淨」的資深程式設計師,正在找工具讓團隊的程式碼品質更佳的人。
  • Do : 認為「客服好」等於「賺大錢」的老闆。正在找工具,讓公司客服回覆速度更快的人。

所以方向也會建立在:介紹自己產品多好用啦。講自己產品多屌啦。etc.etc.etc...

但事實上,多數成功的 Content Marketing 實作者是怎麼做的?

以 CodeClimate 來說。他們真正成名是這篇 *7 Patterns to Refactor Fat ActiveRecord Models

這篇幾乎所有程式 Refactor 書籍都會提到的經典文章。讓很多不知道怎樣將 code 寫得更好的人,有了大概的基本觀念。也對把 Code 寫得更乾淨這件事有了更高的認知。當他們在尋找「能協助將 Code 寫得更好的工具」時,就會想到 CodeClimate。

而 Groove。我是偶然在 HackerNews 上看到這篇轟動的部落格「把 SaaS 創業公司做到月營業額 10 萬美金」* A SaaS Startup’s Journey to $100,000 a Month 的文章後。開始訂閱他們的部落格。

而他們的部落格也常常會分享「週五創業家心得」、「客服做得更好」的例子。比如說我就很喜歡上個月的這篇文章 10 Tips for Sending Better Customer Service Emails

他們關於使用客服改善服務品質、促進銷售的文章總讓我獲益良多。因此,當我在尋找將客服做得更好的工具時,我第一個想到的就是 Groove。而且就算幾個服務攤開來比較。別家就算做得比較好,我也還是比較傾向使用 Groove。

See / Think 型觀眾才是內容行銷的 TA

所以其實內容行銷的寫作重點與 TA,不在於找「Do」層面的人。而是要找「See / Think」層面的人。

因為這兩個層面的人,不知道「投入這個領域的好處」「如何把這件事做到更好」。

如果你寫作的內容可以把它們的興致「燒起來」,將來他們要做購買決策的時候,第一個想的就是你。

Summary

所以你的內容行銷方向該怎麼做?

  • 不藏私,整理你知道的訣竅告訴大家
  • 把重點落在協助「想進入這個領域」的人,變得更強。

雖然這是很老套的觀點。但是你正在看的這篇文章,其實就是這個策略的示範。

 
about 1 year ago

RedPotion 是我最近在玩的一套 iOS App 開發框架。

大家應該了解 用 Ruby 開發的 Rails,在網站開發這一塊,有多強大。那麼白話的形容「redpotion」,你可以想像成它就是 RubyMotion 上的 Rails。

What is RubyMotion?

RubyMotion 是一套在 2012 年誕生的新語言,希望解決的事是「用 Ruby 寫出跨平台的 App」。跨平台的 App 當然包括 iOS App。

這在當時引起了一陣騷動,因為 Ruby 的語法比起 Objective-C 以及 JAVA 起來實在是好寫太多了。如果真的可以寫 Ruby,快速開發出 iOS 與 Android App,那會是一套很棒的事。

Why not RubyMotion?

當然,好日子沒有過的太長。Ruby Motion 更新非常快,母公司技術實力也雄厚。但在推展上卻有一點小阻力。

最大的問題有兩個:

  • Apple 在 2014 年中推出 Swift,把 iOS 的開發門檻大量的往下拉。
  • RubyMotion 的語法還是很囉唆,要寫一個 RubyMotion iOS App,純靠 Ruby 功力鐵定是不行的,你還是要在 Ruby 裡面寫出很多像 Objective-C 的 Code。

所以有人開玩笑說,如果要用 RubyMotion 加速開發,開發者的 Ruby 與 Objective-C 功力都要有一定才可以。否則還不如去學 Swift。

What is RedPotion?

RedPotion 是一家技術狂熱的公司 infinitered 在 2014 年底開始的專案。初衷有點像是 RubyMotion 懶人包,幫開發者準備好上手的 boilerplate (模板),內建許多厲害的 gem。逐漸把 RubyMotion 開發變成是更簡單的事。舉例來說,以下是兩個 app 的 app_delegate.rb ( app 起始檔案)

用 RedPotion 寫出來的程式碼,就比較像人可以維護的程式碼。也像是正常人「希望 RedMotion」可以長成的樣子。

RubyMotion 的 app_delegate.rb

motion_app/app_deligate.rb
class AppDelegate
  def application(application, didFinishLaunchingWithOptions:launchOptions)
    rootViewController = UIViewController.alloc.init
    rootViewController.title = 'my_new_app_2'
    rootViewController.view.backgroundColor = UIColor.whiteColor

    navigationController = UINavigationController.alloc.initWithRootViewController(rootViewController)

    @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
    @window.rootViewController = navigationController
    @window.makeKeyAndVisible

    true
  end
end

RedPotion 的 app_delegate.rb 與 home_screen.rb

potion_app/app_deligate.rb
class AppDelegate < PM::Delegate
  include CDQ # Remove this if you aren't using CDQ


  status_bar true, animation: :fade

  ApplicationStylesheet.new(nil).application_setup

  def on_load(app, options)
    cdq.setup # Remove this if you aren't using CDQ

    open HomeScreen.new(nav_bar: true)
  end

  # Remove this if you are only supporting portrait

  def application(application, willChangeStatusBarOrientation: new_orientation, duration: duration)
    device.orientation = new_orientation
  end
end
app/screens/home_screen.rb
class HomeScreen < PM::Screen
  title "Your title here"
  stylesheet HomeScreenStylesheet

  def on_load
    set_nav_bar_button :left, system_item: :camera, action: :nav_left_button
    set_nav_bar_button :right, title: "Right", action: :nav_right_button

    @hello_world = append!(UILabel, :hello_world)
  end

  def nav_left_button
    mp 'Left button'
  end

  def nav_right_button
    mp 'Right button'
  end

def will_animate_rotate(orientation, duration)
    find.all.reapply_styles
  end
end

RedPotion 內建了什麼 gem 可以讓 RubyMotion 變得這麼好寫?

RedPotion 內建了幾個好用的 gem,讓 RubyMotion 寫起來更加容易,更像 Rails。

RMQ

一套 UI Library。支援

  • events and gestures
  • view factory
  • stylesheets
  • actions
  • position
  • selecting (querying views)
  • templates
  • traversal through view hierarchy (moving around the tree)
  • animations
  • validations
  • tagging
  • colors
  • fonts
  • image utilities
  • app
  • device

白話的說,就是用了 RMQ。可以用 jQuery 的方式 select UI 元件,做出你想要的視覺效果。然後也讓你的 View 可以用類似使用 stylesheets 的方式排版。

ProMotion

把 Screen 變得更好寫。提供了很多排版樣式,常見的內建排版形式都有提供:

  • Screens
  • Navigation Bars
  • Tab Bars
  • Table Screens
  • Grouped Tables
  • Searchable
  • Refreshable
  • SplitScreens
  • Map Screens
  • Web Screens

ProMotion 本身自己也是一套小型框架,所以有一卡車的 addons。比如說 roMotion-XLForm 就還蠻有名的。可以讓開發者很快地就作出一個表單。

比如說,這是我最近在開發登入畫面的程式碼:

sing_in_screen.rb
class SignInScreen < PM::XLFormScreen
  title "Sign In"
  stylesheet SignInScreenStylesheet

  form_options on_cancel: :cancel_form

  def cancel_form
    app.delegate.open_authenticated_root
  end

  def form_data
    [
      {
        cells: [
          {
            title:       'Email',
            name:        :email,
            type:        :email,
            placeholder: 'Enter your email',
            required:    true
          },
          {
            title:       'Password',
            name:        :password,
            type:        :password,
            placeholder: 'Enter your password',
            required:    true
          },
          {
            title: 'Sign In',
            name: :save,
            type: :button,
            on_click: -> (cell) {
              authenticate
            }
          }
        ]
      }
    ]
  end

  def authenticate
    Auth.sign_in(email: values["email"], password: values["password"]) do |response|
      if response.success?
        ApiClient.update_authorization_header(Auth.authorization_header)
        app.delegate.open_authenticated_root
      elsif response.object
        puts response.object
        app.alert "#{response.object['error']['message']} (#{response.object['error']['code']})"
      else
        app.alert "Sorry, there was an error. #{response.error.localizedDescription}"
      end
    end
  end
end

出來成果就會長這樣。

額外搭配使用: MotionKit

如果我要做的版面,超過 ProMotion 可以提供的部份(比如說我要做 Customized Cell),我還會用 MotionKit,直接拉版面。拉的方式很像是寫 HTML ..

CDQ (https://github.com/infinitered/cdq)

CDQ 可以讓開發者操作 Coredata 就像在 Rails 裡面操作 ActiveRecord 一樣。

author = Author.create(name: "Le Guin", publish_count: 150, first_published: 1970)
author.name # => "Le Guin"

author.publish_count # => 150

author.attributes # => { "name" => "Le Guin", "publish_count" => 150, "first_published" => 1970 }

author = Author.first
author.update(name: "Mark Twain", publish_count: 30, first_published: 1865)
cdq.save
author = Author.first
author.destroy
cdq.save

AFMotion(https://github.com/clayallsopp/afmotion)

AFNetoworking 的 Ruby 版。在接 API 時非常 friendly。介面非常像平常寫習慣的 Ruby http-client library。

其他

Gemfile 裡面還有一些選項是 Optional 沒有打開,諸如:

gem "ProMotion-XLForm"
gem "ProMotion-push", "~> 0.2" # Push Notifications
gem "ProMotion-map", "~> 0.3"  # PM::MapScreen
gem "ProMotion-iap" # PM In-app purchases
gem "ProMotion-menu" # PM Side menu
gem "motion-mastr" # Attributed strings: https://github.com/skellock/motion-mastr
gem 'motion-blitz' # Easy HUD with SVProgressHUD
gem "motion-juxtapose", "~> 0.1" # Screenshot acceptance comparison tool
gem "bubble-wrap"

Summary

總之,RedPotion 內建的這幾個核心的元件。就是圍繞著幾個 iOS 內建最常需要被解決的問題

  • Screen 如何拉
  • Layout 如何快速排版
  • 容易的「上色 Styling」
  • 容易的存取 Core Data
  • 很好處理 API 來的資料流

如果你想要享受在 Ruby 上如寫 Rails 般暢快的開發 App 感。歡迎再給 RedPotion 一個機會。

相關資源

 
about 1 year ago

有朋友希望分享一下我過去怎麼管 Team。所以這邊分享一下過去我寫的 General Policy。(有中文版與英文版)

其實我認為這一份已經是正常的 Work Ethics(職業道德與工作完成標準了)

中文版

計畫階段

  • 如果你認為在哪個項目比較在行,但是票卻被指給其他同事。或者是你認為現在手邊哪一些工作,在這週對整體工作進度來說是比較重要的。請先提出來。
  • 團隊重視結果(是否能「幫助公司」或是「幫助同事效率」)高於實作品質或開發速度。
  • 交出成果前,請先找一個比較資深的人幫你看過給意見。

在實際投入工作之前

  • 在打開編輯器實際寫出第一行程式碼前,先問清楚:「為什麼我們要開發這個功能」?
  • 先確定手上這件工作的「死線」以及「重要性」
  • 與你的 stakeholder 時常溝通,取得 feedback 以及修正方向。
  • 如果現有的系統裡面已經有相似的功能時,請先問週遭的同事,是否我們應該針對類似的例子設計一套 pattern 。

在工作的時候

  • 先設計一套能動的東西,讓大家能夠測試。(在進入 Review 以及上線階段前,確定你沒有完全搞錯方向)
  • 在開發的時候,把所有相關的文件以及筆記,記在 Redmine 上,方便同事幫助你時可以找到相關資料。(包括 bug 的 log、資料連結、測試成果)
  • 盡量把 Task 拆細去實作。而不是一次 commit 一大包

完成工作後

  • 如果你感到你完成工作的方式太 Hack。請多開一張 Ticket for Refactoring。然後放在下個 Sprint。
  • 如果你在系統內引入了一套「新的外來技術」(比如說 gem , framework, js plugin 等等)。請在 Redmine 的 wiki 上記載以下相關資訊
    • 這是什麼東西?
    • 基本使用指南
    • 如何讓他跑起來
    • 你認為這套技術 API / 架構中,重要的部分
  • 如果你在系統內開發了一套「新的架構」(如 Service Object, Pusher..等等。請在 Redmine 上記載以下資訊:
    • 為什麼我們需要這套架構,去改良現有的系統
    • 基本的架構運作原理
  • 如果你正在「Refactor」,請記得在票上附上過程。(如 SQL tunning 或演算法變更)、並貼上 bechmark 成果或是截圖。
  • 在將 code merge 之前,請先找一個資深工程師看過,且簽名。

你該記得的基本原則

你蓋的任何東西,應該同時滿足以下幾個條件:

  • 能夠幫助公司
  • 能夠幫助同事把事情做得更好更有效率
  • 幫助自己成長
  • 常問問題並不可恥(別擔心,你不是在浪費同事時間)。沒有什麼比不問問題更糟糕的了。
  • 你蓋的東西,是會被「真的人」使用,而會會影響到「真的使用者」。
  • 為自己的作品自豪

=========

英文版

Genernal Policy

Sprint Planning

  • If you feel you are good at something but it's wrongly assigned to another colleague or you think something is important that you want to finish in this sprint, please let us know
  • We think highly of results (helping the company or helping colleagues ) much more than ticket quantity or development speed
  • Have your architecture reviewed by a senior engineer

Before starting to code

  • Ask "why are we doing this?" before opening your editor
  • Ask about the deadline & priority
  • Communicate with your stakeholder often
  • If there's something similar already in the system, please ask around whether we should we build a pattern/work on the architecture

During Coding

  • Build a prototype first to test drive (Before entering the review phase, ensure you are not building something completely wrong)
  • Document everything related in Redmine (including bugs, links, test results)
  • Break your task into smaller ones

After Coding

  • If you feel your way is too hacky, please create a separate ticket for "Refactoring" then put in next sprint
  • If you introduce a "new" technology in a ticket (gem, framework, js, etc.), write a Redmine wiki about it and inform the team
  • What is this?
  • How to use?
  • Basic Setup
  • The API and architecture you think it's important.
  • If you introduce a "new" design pattern in a ticket (Service Object, Pusher, ..etc), write a Redmine wiki about it and inform the team Why we need this approach?

Basic Structure

  • If you are doing refactor work, like "sql tuning" or "algorithm tuning", please attach benchmark results or screenshot
  • Before committing your code, have it reviewed by a senior engineer

Things to remember

What you must meet

  • Help company
  • Help colleagues do things more efficiently
  • Help yourself grow
  • There's no shame in asking colleagues for advice or seeking help (don't worry, you are not wasting their time). There's more shame in not asking.

The things you are building will affect and be used by "actual human beings". Be proud of your craft.