Type Inference 在實務上碰上的問題

最近在讀 Implementing Domain-Driven Design 這本書,學習一下別人
是怎麼整理好整個大型軟的架構,並且重新檢視一下我們公司的程式碼的問題。

在 DDD 的概念中,一個模組 Domain / Bounded Context 應該是一個獨立的觀念,一個大型程式,應該是由許多 Bounded Context 組合而成,整個程式的架構應該僅量保持這些的純淨性,Bounded Context 的互用,要把上下行的關係定義清楚,程式碼間不應該在一個區塊中引入多個 Bounded Context

在整實作上,如何合檢視一個 class 是否有妥善的處理引用入的 Domain ,最簡單也最好用的方式就是看檔頭 import 的地方

import com.codahale.metrics.Meter

import org.apache.commons.lang.StringUtils
import org.jboss.netty.handler.timeout.TimeoutException
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.yunglinho.common.JsonSerializer

import com.yunglinho.app.core.model.Location
import com.yunglinho.app.feature.SocialProfile
import com.yunglinho.app.service.LocationService

import scala.concurrent.Await
import scala.concurrent.ExecutionContext
import scala.concurrent.Future
import scala.concurrent.duration._

過去我排 import 的慣用法是按 有授權疑慮 的順序去排

  • com.* 的在最上面
  • org, net 排第二
  • 接著是排自己公司的東西,按 common, base, app 的順序去排,common 跟 based 是放 utilities & framework , app 就是放一個專案的東西了
  • 最後scala & java 的東西

這樣排的好處是, code review 一眼看過去,就可以知道用了那些版權有疑慮的東西,而且 import 時,不會把 java & scala 內建的東西蓋過去,同時也知道這支程式碼用了多少其它的東西

然而,這個機制,在 Scala’s Type Inference 的影響下,變的很不可靠。

Type Inference 是初學 Scala 時,覺得很方便的一個功能,能夠在寫程式時,少打很多的字,例如:

   // 在沒有 Type Inference 下, statement 的左右兩邊都要宣告型態
   val location: Location = new Location("Taipei")

   // 左邊的這邊其實是可以省下的
   val location = new Location("Taipei")

   // 左右兩邊都沒有 *Location* 的存在,但是卻是被引入在這邊了 
   val location = locationService.lookup("Taipei")

這看似方便的功能,卻有著非常深遠且負面的影響,在上面的例子中 Location 這類別,雖然在 import 時沒有出現過,但是卻是在 Reviewer 意料之外中,被帶入到了這程式碼中。

當然,在上面的例子中,Location & LocationService 是一套的觀念,所以沒有真的引入意料之外的 Domain 進來,但是如果今天程式碼寫的不好,是有個 ApplicationContextHolder.getCalendarService 的話,那就不一樣了。

在一行行 code review 時,有可能會抓到這問題,但是如果你沒有空一行行的看呢?或者是你是在 refactor 數千行程式碼時,那要怎樣一行行的去看呢?

所以,設計程式語言真的是很困難,一個看似方便的功能,卻在意料之外的地方造成困擾,讓我最近有點想回去改用 Java 。當然 Scala 還是個很好的語言,能夠幫我增加兩三倍的生產力,不過,卻會讓團隊工作時造成困擾。

Metrics

歡迎各位來到首屆的 Java Community Conference ,我是何永琳,目前於飛向科技擔任工頭,所謂的工頭,就是做
大家做的事跟做大家不喜歡做的事,什麼是大家不喜歡做的事呢?那就是管上線機器的大大小小問題。

在我們開始之前,先岔題一下,大家都是工程師,(放someone on the internet is wrong)應該都有些大大小小的睡
眠問題,過去我一直以為我一天有睡到七小時,一直到去年穿戴式配備的興起,我買了個 Jawbone 來戴(放 Jawbone Up的圖片),
才知道我一天只睡六小時左右,中間的睡眠品質不佳。有了 Jawbone Up 天天計錄我的生活起居狀況,我才了解到自己真
正的狀況,才能著手改善問題。

上面的故事,就是今天講題的主軸 測量 / metrics

在軟體開發的過程中,程式的編寫,其實只在軟體生命週期的很小一塊,在經過數個禮拜至數週的開發及測試後,軟體就
被佈屬到線上環境中,在過去,可能是放在公司的機房中或是資料中心,現在則是被放到雲端平台之上,總之,你細心開
發的軟體,將要被真實世界開始摧殘。

在一個軟體的生命週期中,他可能會碰上大量的用戶擁入,也可能跑了數十天沒有重開,也有可能在臨晨三點碰上資料庫
備份,當然也有可能每兩個禮拜碰上其它整個的 REST API 不穩的狀況,在他跑了一兩年後,有可能被換到新的硬體上。

問題在於,你有沒有辦法知道這些狀況會不會出現,如果出現了,你能不能知道發生了什麼事;而且在這些狀況下,你的
應用程式會出什麼怪手?是全面性的慢下來、還是不可預期的某些 Thread 才慢些來,或者是,你的應用程式可以輕鬆
應付這些狀況,不用你擔心。

如果上線的系統不能應付這些狀況,你要做出什麼相對應的調整呢??

Metrics By Codahale

Metrics 一套是由 Codahale 在 2011 公布出來的 Opensource 專案,自此之後,因為他的 API 設計精良又容
易跟其它的外部程式整合,所以開始被業界所採用。

http://codahale.com/codeconf-2011-04-09-metrics-metrics-everywhere.pdf

為什麼我們需要測量的原因

手動調整

Iterative tuning -> 建立假設 -> 重建問題 -> 提出對應 -> 修理 -> 驗證

談台灣年輕人低薪問題

台灣這幾年商界、政界、很喜歡對年輕人指指點點,說只要努力就可以脫離 22K ,年輕人收入不好的問題根源是在年輕人上,所以在這邊寫一下我的看法。

我在美國念書畢業一年後,在 26 歲時,底薪收入就有八萬美金,後來我又幹了四年,回台灣又幹了四年,但我的收入連八年前的一半都沒有,所以說,是我能力退步了嗎?不對啊,我現在寫程式碼比以前快不知道多少倍,還帶一個團隊,怎麼可以說我退步了呢?

一個人所能領的薪水,是相對於他所能創造的產值,而一個人能創造的產值,則是基於他所在的團隊以及社會之上,我過去還是一兩年經驗的新人能領高薪,是因為我所處在的團隊、社會可以創造高價值,因此我所做的邊邊角角的工作,依附在團隊上仍是有價值的,公司就我當下及未來可能的貢獻,給我高的報酬。

所以說穿了,年輕人能領的薪水,很大的決定因素是在於這個社會能夠如何運用他們,這個社會的掌權者、這個社會的中監份子,能為年輕人創造怎樣的舞台,如果掌權者創造的社會有價值,能夠利用年輕人創造更多的價值,那麼年輕人自然能夠領高薪。

如過這個社會的掌權者,只在乎金錢遊戲,忽視產業,那麼年輕人沒有舞台,自然只能領低薪

TDD

總算是把 TDD 的戰文看完了,看完後我對 DHH 的評估仍是沒變。

DHH 在 Rework 中自述到,他偏好帶領一個三十人的團隊自在的工作,而不要把組織擴大到上百上千人把自己搞的焦頭濫額。 DHH 在組織架構跟應用範圍的偏好偏食,讓我在讀 DHH 文章時都會先考慮過他的出發點跟局限性。

以程式設計師的角色來說, DHH 的精兵政策當然看起來很爽,我自己也做過類似的選擇,捨棄 Java 而就 Scala ,追求更高的個人產值。

然而若是以一個架構師的角度來說,我的目標是成為像 Martin Fowler 的角色,相較於只有 36 個員工的 37 signals ,ThoughtWorks 是個有超過 2500 個員工的專業代工廠,在 ThoughtWorks 掛頭牌的 Martin Fowler 在技術的選用上,自然的考量要更全面,顧慮到不同應用領域的開發需求、以及不同程度的程式設計師該怎麼管理。

DHH 對 TDD 的批評我多數同意,尤其是 Testing / DI 對 Design 的入侵也是一直困擾我的問題,但是我是把 Testing 當成一個需求來看,所以是多一個需求造成的額外成本在困擾我,而不是降低可讀性等問題在困擾我。
在目前看不到較好的替代品之前,我還是會繼續用 TDD (w/o test first) 來做設計上的工法。

我目前對程式的想法是從資料做起,我做的系統,多數就是把輸入的資料,透過一串串的轉換,Map, Filter, Count, Sum, Aggregation 後,變成最後要的樣子,在A -> B -> C -> D 中間,可能會有一些中介型態的出現。

在我的設計中就會變成,在每一次的轉換的過程中,讓輸入輸出是可以被測試的,在設計階段就預先考量到要怎麼測試這些轉換。

至於會不會做 Unit Test, Test First, 我是看有多少時間做多少事的,但至少模組內的一串流程的測試是會去做的。

另外我會把 DAO & Data Transformer(Service) 分開來,資料轉換的運算,是可以不用靠 DB 來的資料直接做測試,這樣跑起來才會快。

許多的商業邏輯,我會在 DAO -> Service 上再多架一層 Controller ,這個 Controller 就會很醜,因為只有三(四)層,所以一個 Controller 可能換串個十來個 Service ,但是醜的就是在這裡面而以。

測試的時候,至少是可以分層把底層容易測的都測好是穩定不容易出錯的,到時後上線有問題時,就可以知道是在 Controller 把東西串起來時出錯了。

沒有 Test First 沒有 Unit Test ,但是會做 regression test, integration test 會在設計時預先留下容易稽核的點。這是不是 TDD??