好多個 28 在這個時刻

工作近況

整合

整合產品就麻煩的環境問題,尤其是大型程式開發,現在又同時有併發、平行等需求,其他產品的優化方式非常特別,各自測試都沒有問題,一整合就出事,接著就是一陣暴打如發生以下幾種狀況,不外乎就是連繫到我身上。

  1. 噴了一排錯誤,但第一個警告訊息來自你負責的功能
  2. 許多執行緒的 stackstrace 錯誤,列表第一個的你負責
  3. 負責整體監控系統,註冊了所有錯誤訊號處理,你回報了錯誤
  4. 噴了一個錯誤,stackstrace 貫穿了好幾個函數,而你是最後一個

滿滿地無奈,在這如此大型的程式開發下,沒有多少人能釐清問題所在。分工之細,只能四處找人通靈。

十月過後,工作就滿四年。在這四年間,代碼的提交次數也逼近七千次,登上了第一位。一部分的原因也在於修改的技術面比較通用,只要出現一則鬼故事,就得去調整整體的使用方法,再透過正規表達式去修正所有關聯代碼。但不是那一種單檔修改就一個 commit,數量之多會拖累前測的工作量,再來是 revert 操作可能過於複雜。的確看起來很不軟體工程,無疑是有點特殊狀況。個人佔有如此大的修改比例,很容易引火自焚,瑣碎的小錯誤都只能交付到自己。在緊急狀況下,與其用講的交代下去,不如一肩扛起,工作量便越來越不樂觀。

測試

拖了一年的規劃,總算把 JDI (Java Debug Interface) 掛上了覆蓋率測試,不同於單元測試的覆蓋率。原先的單機框架中,動態分散測試點到不同的 JVM 平行測試,盡可能地達到負載平衡,因此就不知道怎麼搭覆蓋率的套件,最後不得不讚嘆原生的 java agent 和 reflection 機制,偷偷用了一些黑技術才完成。覆蓋率終於由 16% 提升至 36%,以後就更方便知道哪些檔案有測試。

待續

忙了好一陣子的 tcl interface,上層下指令要有些做好 3D Viewer,兩年前規劃的 3D Viewer 恰好派上用場給個交代,那時嘗試做了一個小原型,也未能滿足所有需求。實際做起來問題之多,就像玩了十年的《楓之谷 Maplestory》,突然要求做個 3D 版本一樣,如何操控角色、過場動畫、使用模型、載入模組等問題逐漸浮出,一個人恐怕做不好。跨平台環境的戰爭,肯定在等著我。

生活雜事

Serendipity

交友方式千奇百種,曾經讓朋友在我忙著 coding/論文的時候替我回覆訊息,結果幾年後湊了一對基友。9/15 那日,ptt 上收到來自一個多月前的文章回信,自述「受朋友委託,幫忙在 github 看我曾寫的 source code,感興趣便跑去看部落格文章,追了幾篇文章,其中提到 ptt 文章 … 然後就送信了。」那時我心想是哪一個 source code?卻看到「批改娘」,第一個反應是「學弟是不是用美人計想騙我回實驗室」。

「他們是打算讓我們工作到死啊」《異世界迷宮黑心企業》

某日夜晚

一般來說,在路邊看到漂亮小姊姊能閃則閃,因為十之八九都是推銷,一旦下起雨來,也只能硬著頭皮上了。果真還是被纏上了,甚至跟著撐傘走一大段路

「你們哪間公司的?」正想著甩掉
『O O O O』
「這個我知道,之前有告過你們公司耶」
『那沒事了,謝謝』
朋友普遍表示:「都沒被搭話過,我看起來又窮又宅 … 我不在她的考慮範圍內 …」

「我不在他的考慮範圍內嗎?」《加油吧同期酱 》

某日清晨

朋友普遍可以做到「還沒睡著,天就亮了」、異常的我則是「醒來,天還沒亮」,深刻體會到自己老了。從小就被訓練得很早睡,隨太陽而起。嘗試的那幾次熬夜,隔天都不太像自己。而飛往美國出差的那幾次,調時差的自己就像廢人。

某日正午

高中資訊老師打了通電話聯繫我,這不是第一次,仍然能讓老師們記得,備感榮幸。大學畢業前詢問我要不要回去當老師,那時的我正苦惱無法通過畢業門檻。現在被感覺好像步入正軌,實際上在職場上卻感到力有未逮。這一次,被交付指導小學弟一些程式問題,而那是曾經我出的題目,那時的技術力也不夠成熟,老是被說測試資料不夠好,被指出標準答案的一些漏洞,想起來就是滿滿的黑歷史。

老師隨口一句「現在在哪工作? … 結婚了沒?」不經淚下,在公司合作的老同事,也聊著聊著問道「聽說升了主管,不錯哦。 … 這麼快就吃完飯,在家工作?有結婚?老婆了嗎?」已經回不去那個幼稚的我,也得開始面對人生課題。

二十八

恭喜 morris821028 抵達了 28 歲,老是想小小地抱怨,要是 11 月生,整串就是可以迴文了。海外的小夥伴總是覺得 1982 年生的老傢伙,怎麼這麼活躍。

住址上滿滿的 28 號碼,近期稅單上也正好著 1028,繳費單上也也有不少的 28,就像著「巴德爾邁因霍夫現象 Baader Meinhof Phenomenon」 其中的證實性偏見,巧合般的重複,不斷地在意著。

不同過往的日子,難得在生日那一日,走出房門,體驗著好多沒想過的事。

Read More +

Company Ghost Story 公司鬼故事 14

Java

Format vs. Concatenation

1
String.format("format " + a + "%s", b);

大部分情況,很少去生成 format string,
混合使用很容易遇到表示錯誤,因為當 a 中出現 % 等特殊字元時,解析上會發生錯誤。最好是統一一種使用規則。

Comparator Signum

1
2
3
4
5
long a = func(x);
long b = func(y);
long dir = mDir;
return (int) a - (int) b; // SHOULD Long.compare(a, b);
return (int) (a - b) * dir; // SHOULD Long.compare(a, b) * Long.signum(dir);

隨意 casting 三不五時就會 arithmetic overflow 搞砸了排序。

What’s Not-Equal

1
2
3
4
5
6
if ((a == null && b != null) ||
(a != null && b == null) ||
!a.equals(b)) {
}
// WAIT, if a == null and b == null ?
// SHOULD Objects.equals(a, b);

有內建方便使用的函數,別再這樣寫了。

Sync-Up

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Record {
boolean merge() {
return a.setParent(b.getParent().copy());
// refactor, a.setParent(b.getParent());
}
}
class Merger {
void process(Record r) {
try {
merge();
} catch (Exception e) {
// nothing here
}
}
}

為什麼偷偷搞了一個 Exception 又不給 log message,一不小心就 refactor 到奇怪的東西,曾經把 NullPointerException 視為一個正常邏輯,修掉來自另一個 call path 的 NPE,卻又踩了另一塊的雷。

Keyboard

1
2
3
4
SHIFT + INSERT = CTRL + V
CTRL + C
CTRL + Z + ENTER
HOME/END

大家耳熟能詳的 CTRL C+V,都不知道 CTRL+INSERT/SHIFT+INSERT,而在 X Window System 上,選取時會自動複製,不用額外按下複製操作。

至於問 INSERT/DELETE/HOME/END 是什麼的小夥伴,還是好好看清楚鍵盤吧。

Read More +

Shiny People 感謝

背景原因

公司有一套 Shiny 機制,可以在特別日期或事件後,來表揚合作不錯的長官同事。有時候表揚可以帶鼓勵金的,當然這筆錢不會由您支付,而是由部門的某一個錢包裡出,理所當然得不可能存在同部門互相洗錢的行為,仍然遵循某一種流程。

詳細信件內容有點不同了,就當作有朝一日會在網路上搜尋到吧。

任職周年慶祝

Software Architect

To Jeffrey Morehouse:

I really appreciate your great art, which facilitates high productivity compared to other products. In these years, give the opportunity to develop my improvement thought in core. During coding experiment, your resource and past experience help me a lots. Although we’re working on different ways these years, I’m looking forward to having technical discussion with you one day. Congratulate to Techholic!

感謝您轉交下來的偉大傑作,讓我們產品相較於其他產品
擁有非常高的生產力、極低的開發週期。同時在這幾年間,
給予我機會去實踐自己的想法。並在開發過程中,給不少
資源與過去經驗參考,這些都讓我學了很多。

雖然這幾年間沒有共事,期待未來可以分享這些技術細節。
為我們的技術狂熱慶祝!

Manager

To Thunder Lay:

I’m glad to be the early employee in your part. The unfailingly patient leader is very important to me in first job. Even though we’re good at different profession, we learned from each other to complish great big tasks these years. That is very good experience, and rare in many kinds of job career.

很高興地成為早期的部門成員,對我第一份工作而言,
您的耐心指導成為很大的助力,雖擅長不同領域的專長,
在這幾年間,取長補短地完成了許多重大任務,這真的
是在其他職業生涯中可未必能見到的非常好的經驗。

結語

寫完英文,看著自己的譯文,赫然發現我的中文死去,找些朋友提前問個意見後,只得到了一致的答覆「看不懂」。在沒有前因後果,不具備相同處理的條件下,看起來可能是誰要離職了吧。

Read More +

Company Ghost Story 公司鬼故事 13

Java

Default Constructor Trap

1
2
3
4
5
6
class Point {
Point() {
throw new UnsupportedOperationException(); // why?
}
Point(int x, int y) {...}
}

一開始不宣告就好了,當有自定義的建構子,預設建構子自然無法被使用,沒必要設一個陷阱讓別人踩。

Feature and Backward Compatibility

1
2
3
4
5
6
class Engine {
private String mFeature = ""; // Why not null ?
}
class DbObj {
private String mFeature = ""; // Why not null ?
}

空與空字串的意義不同,預設為空字串 ("") 的狀況並不多見,為空 (null) 在序列化和反序列化時的差異就很大了。

Fancy Check-and-Cast

1
2
3
Shape shp;
if (shp.getUserName().equals("circle")) // wait, where is your `instanceof`
return ((Circle) shp).getBound();

Call-By-Value

1
2
3
4
5
void readColRow(int col, int row) {
Pair p = readPair();
col = p.first;
row = p.second;
}

Java 沒有 reference type 的這操作。

Slow Feature

1
2
3
void exec() {
for (;;) {} // where is throw new UnsupportedOperationException();
}

我可不想跟客戶說「你再等等,這功能只是比較慢」的謊言。

Read More +