[工作筆記] 問題,不重要不緊急,就永遠不作了嗎?

問題

最近我在公司引導大家一些敏捷方法的實作,
在這過程中討論了 PBI 排序的原則,價值和風險。
那麼問題來了,這樣有一些事情很小,但價值也不高,又沒有什麼風險,那不就永遠作不到了嗎??

回答的心路歷程

這是一個很好的提問,我反思一下常見的四象限模型,即「重要緊急矩陣」中,
我可以把重要對應到價值,緊急對應到風險,然後 A、B、C、D 四象限,
我們知道要先作 A、B 這樣我們 C、D 的事永遠都不作了嗎 ?

我分享一個故事如下:

有一位教授,他拿著大石頭、小石子、沙子和一個玻璃罐,
先放入大石頭,問學生滿了沒? 再來是小石子,再問學生滿了沒?
最後再倒入沙子,問學生滿了沒? 結果還是沒滿,還可以再放入水

你學到了什麼?
—常見的說法是要先處理重要的事情,順序很關鍵。

我們可以有一些反思,首先當然是重要的事先作(大石頭先放),
再來,你總是還有辦法找到一些空隙。
—時間就像乳溝一樣擠一擠就有了。

這是我當時的回答,
—如果你能守住重要的事,剩下的小事,你是有辦法找到空隙塞進去的。

回答後的反思

我要怎麼知道可不可以塞進去??
這就是透明度的重要性,在 Scrum 或看板方法都有提出。
而我們可以在 Scrum 的每日例會使用看板方法(現行主流的作法),
加上每次迭代所行成的 TimeBox,正好可以對應這個故事中玻璃罐。
有趣的是,我沒想過如果玻璃罐不是透明的話,教授的問題還有意義嗎 ?
正是透過每日例會與看板方法,讓我們可以透明的掌握進度(瓶子剩多少空間/你剩多少時間),
所以你可以判斷是不是可以再塞下石頭或沙、水。

TimeBox

它是一種任務和時間管理的方法,將任務分成小塊,每塊都有一個簡短而固定的時間,以確保高優先級的任務能按時完成。

看板方法

它包括將工作可視化、限制同時進行的工作(WIP)、管理流程、明確陳述流程政策、實施回饋循環以及以協作方式改善。
這些原則和工具都教導我們要優先處理最重要的事情,這是成功的關鍵。

看板方法我還會聯想到限制理論(Toc),但就不過度展開了。

(fin)

[生活筆記] 潛水日誌

20230916

一潛(導潛)

1
2
3
4
5
地區: 東北角美灩山
入水時間: 14:08 |潛水時間:41 |出水時間: 14:49
能見度: 10m |平均水深: 4.8m |最大深度: 10.5m |水溫: 26.4
起始壓力: 200 |結束壓力: 70 |SCR: 3.17 Bar/min
天氣: 晴 |浪況: 無浪 |目的: 訓練 |方式: 岸潛

時隔半年再次下水
第一潜稍嫌緊張
首次體驗美灩山
無浪無流,嘗試三角、四角導潜
浮力控制不當,未注意深度
未能找到原位

二潛(放呆)

1
2
3
4
5
地區: 東北角美灩山
入水時間: 16:11 |潛水時間: 57 |出水時間: 17:08
能見度:8m |平均水深:5.1m |最大深度:11.5m |水溫: 26.8
起始壓力:200 |結束壓力:100 |SCR: 1.75 Bar/min
天氣: 晴 |浪況: 無浪 |目的: 訓練 |方式: 岸潛

第二潛是與小夥伴伴潛屬於放呆行程,比較沒有那麼緊張,
算是複習了一下背滾式與跨步式入水,
不過小夥伴的問題有點多,一個是有人被海膽刺到膝蓋,
另兩個小夥伴二級頭跟 BCD 卡住而不自覺,直到教練去救援,
回程有看到黑色相間類似蛇的生物

三潛(導潛)

1
2
3
4
5
地區: 東北角美灩山
入水時間: 17:30 |潛水時間:38 |出水時間: 18:08
能見度: 5m |平均水深: 7.1m |最大深度:11.8m |水溫: 26.5
起始壓力:200 |結束壓力:100 |SCR: 2.63 Bar/min
天氣: 晴 |浪況: 無浪 |目的: 訓練 |方式: 岸潛

這次練習導潛主要是跟著教練,並繪製海圖,
方向感本來就不好的我,加上水深這個緯度,
其實是個蠻大挑戰,我只知道大概的路徑是一個狹長的 U Turn,
試著認地形,入水後右側是壁,左側是斜坡,前進20米左右會出現沙地,
這裡教練會稍作轉向,然後出現一個更深的地方,大約有到 11 米
之後我就有些迷航了,可能是在附近作了一個四角或三角的繞圈
海圖繪製比例失恆,所幸未來還有好幾次的潛水機會

四潛 (夜潛)

1
2
3
4
5
地區: 東北角美灩山
入水時間: 19:03 |潛水時間: 53 |出水時間: 19:56
能見度: 12m |平均水深: 5m |最大深度: 11.9m |水溫: 26.3
起始壓力:200 |結束壓力:50 |SCR: 2.83 Bar/min
天氣: 晴 |浪況: 無浪 |目的: 訓練 |方式: 岸潛

最擔心的夜潛反而是最美的一潛,作好安全工作後再下水其實不會有什麼心理壓力,
除了教練外還有一個陪潛的前輩,看到很多生物但是叫不出名字,
過程中有點小意外,前輩以伏地魔之姿突然從下方出現,害我撞向礁石同時被海膽刺了,
所幸以前有被刺過的經驗,知道不會有生命危險,所以我沒有驚慌,但上岸後手上還是多了 4 個黑點,

20230917

一潛(高氧)

1
2
3
4
5
地區: 東北角龍洞四號
入水時間: 9:51 |潛水時間:35 |出水時間: 10:26
能見度: 10m |平均水深:16.2m |最大深度:25.5 |水溫: 25.6
起始壓力:200 |結束壓力:100 |SCR: 2.85 Bar/min
天氣: 晴 |浪況: 無浪 |目的: 訓練 |方式: 岸潛

突破個人最深的記錄,沒有什麼感覺,下水點的魚群很多,
但深一點的地方就沒有魚了,看到很多三點蟹的空殼,
聽說是漁船上拋下來的,另外撿了一些垃圾

二潛(高氧)

1
2
3
4
5
地區: 東北角龍洞四號
入水時間: 11:07 |潛水時間: 45 |出水時間: 11:52
能見度: 10m |平均水深: 11.8 |最大深度: 25.8 |水溫: 25.7
起始壓力: 200 |結束壓力: 100 |SCR: 2.22 Bar/min
天氣: 晴 |浪況: 無浪 |目的: 訓練 |方式: 岸潛

再次突破個人最深的記錄到 25.8,浮力的控制還是很差,到深處會揚沙,
突中遇到一團揚沙大隊,簡直是我,
回程時有看到小丑魚

三潛(放呆)

1
2
3
4
5
地區: 東北角美灩山
入水時間: 14:47 |潛水時間: 37 |出水時間: 15:24
能見度: 8m |平均水深: 5.1 |最大深度: 8.6 |水溫: 26.1
起始壓力: 200 |結束壓力: 100 |SCR: 2.70 Bar/min
天氣: 晴 |浪況: 無浪 |目的: 訓練 |方式: 岸潛

看小夥伴考試,感覺大家很慌張,不過也都通過了,沒有到 11 米處的沙地
覺得有遇到流,但是教練是很清鬆就踢過去了,有種提早回程的感覺

四潛 (夜潛)

1
2
3
4
5
地區: 東北角美灩山
入水時間: 18:54 |潛水時間: 51 |出水時間: 19:45
能見度: 12m |平均水深: 5.1 |最大深度: 11.2 |水溫: 25.9
起始壓力: 200 |結束壓力: 100 |SCR: 1.96 Bar/min
天氣: 晴 |浪況: 無浪 |目的: 訓練 |方式: 岸潛

看到最多生物的一次,饅頭海星、海蛞蝓跟他的卵、海鰻、河豚、龍蝦
獅子魚、還有鼻子長長的不知什麼魚、黑的紅的、睡覺的、龍蝦等…
微型生物也很多,但是我看不太到,要很靠近峭壁,所以中心浮力控制要很重要。

20230918

一潛(打浮力帶)

1
2
3
4
5
地區: 東北角龍洞四號
入水時間: 09:04 |潛水時間: 53 |出水時間: 09:57
能見度: 8m |平均水深: 6.3 |最大深度: 8.4 |水溫: 25.2
起始壓力: 200 |結束壓力: 50 |SCR: 3.65 Bar/min
天氣: 晴 |浪況: 無浪 |目的: 訓練 |方式: 岸潛

第一次打差點拉不住,後面就好多了,
不過看了紀錄,發現深度起伏很大,
中心浮力沒有控制好,應該至少有打了10次以上

二潛(打浮力帶)

1
2
3
4
5
地區: 東北角龍洞四號
入水時間: 10:18 |潛水時間: 40 |出水時間: 10:58
能見度: 8m |平均水深: 6.4 |最大深度: 12.2 |水溫: 24.9
起始壓力: 200 |結束壓力: 50 |SCR: 3.75 Bar/min
天氣: 晴 |浪況: 無浪 |目的: 訓練 |方式: 岸潛

覺得很難控制趴姿,看了錄影,其實還不差,就是一直會停腳,
中心浮力控制不好外,還有旋轉的問題,所以一直忍不住踢腳,
不僅耗氣,看起來也很好笑,打氣只要二級頭向上就好,不用按壓

(fin)

[工作筆記] 採蘋果

前情提要

公司有三台 Macbook pro 2017 年款,普遍都有電池的問題,
為此研究了一下在台灣舊機換新機的策略。

折抵

選擇好你的新機,在「加入購物袋」前,找到「加入換購方案」,依指示輸入郵遞區號、選擇製造商 Apple 後
輸入序號後選擇相關的硬體設備。
就會出現一個換購的金額,再繼續完成你的購物袋即可。

企業商店

企業採購的話可以有累積的金額優惠,如果滿足到累積的金額(一年內 5000 美金),就可以以優惠的方式來做訂購,
需要打電話(見文末參考),使用統編建立企業商店。
其它方式與折抵一樣。
訂單成立後可以刷卡或是匯款,刷卡可以立即看到訂單進度,
而匯款後需要再打電話(0800-020-021)確定訂單進度。

折抵的部份會再出貨後 14 天來回收舊機器,應該有足夠的時間轉移資料。

參考

(fin)

[工作筆記] 軟體工程的檢傷分類

前情提要

回想一下你手頭上的工作,你如何決定你的工作順序?
常見的有緊急/重要四象限法。
另外在一些方法論上,利如 Scrum 交由特定角色排序,
而急診室的檢傷分類如下。

1
2
3
4
5
6
7
8
9
第一級(復甦急救,立即處理-病況危急生命或肢體,需立即處理。如:心跳、呼吸停止、肢體及嘴唇發青、發紫,體溫高於41度或低於32度,無意識或意識混亂、持續抽搐且無意識。

  第二級(危急,可能等候時間10分鐘)-潛在性危及生命、肢體及器官功能,需快速控制與處理。如:急性意識狀態改變、持續胸悶、胸痛且冒冷汗、低血糖(<40mg/dl)、外傷造成之大量出血,頭頸軀幹骨盆部位血流不止、高處墜落、車禍(乘客被拋出車外)、頭部撞擊後曾失去意識等。

  第三級(緊急,可能等候時間30分鐘)-病況可能持續惡化,需急診處置;病人可能伴隨明顯不適症狀,影響日常生活。如:無法控制的腹瀉或嘔吐、外傷後肢體腫脹變形疑似骨折/脫臼、高血壓(收縮壓>200mmHg或舒張壓>110mmHg)且沒有任何症狀等。

  第四級(次緊急,可能等候時間60分鐘)-病況可能為慢性病及急性發作,或某些疾病之合併症相關,需在1至2小時做處置避免惡化。如:局部蜂窩性組織炎、急性咳嗽但沒有發燒、發燒但無其他不適、反覆性疼痛或暈眩等。

  第五級(非緊急,可能等候時間120分鐘)-病況非緊急,需做鑑別性診斷或轉介門診。如:慢性噁心、嘔吐或打嗝、輕微擦傷,瘀青,軟組織受傷、螫傷或咬傷,但無發燒或疼痛不適、輕微腹瀉,無脫水現象等。

我們可以參考。以等級與具體案例作為分析。

個人的經驗是以 3+1 個維度分析,
主要為發生風險、發生頻率、影響範圍和可否重現。

判斷流程

  1. 是否為線上問題:線上問題較可能產生價值的耗損
  2. 發生頻率:高頻的問題如果不處理會帶來大量干擾、下一階段可能會麻痺,甚至讓真正的問題從眼前跑走
  3. 影響範圍:誰會感到痛苦?老闆?客戶?PM ? RD ? 營運?怎麼作可以停止或減輕他們的痛苦。
  4. 風險評估:不作會怎麼樣?作了會怎麼樣?

定義

x 的維度是風險的高低,風險高者需安排修正,風險低小者可以再觀察
y 的維度是影響範圍的大小,範圍大者需優先停損縮小範圍,範圍小者就安排修正
z 的維度是發生的頻率,高頻者需優先停損止傷,低頻者需要觀察,如果發生一次就會形成持續性或永久性的傷害即為高頻
加上可否重現分析如下:

可重現

高風險/高範圍的情況下,頻率已經不重要,重要的不能讓發生 0 次的事情發生 1次,
一但發生只能砍掉重練,具體例子如廣達被勒索病毒攻擊

維度一:高風險/高範圍/高頻率:終止營業、暫停營運的等級
維度二:高風險/高範圍/低頻率:終止營業、暫停營運的等級

應急辦法,止血: 先限縮風險、範圍、頻率至少其中之一。

低風險/高範圍的情況,即使發生了仍有轉還的空間,高頻時處理的重點在縮小範圍與暫時的降噪
一但修正,理應停止降噪的設定,低頻時安排修正計劃即可

維度三:低風險/高範圍/高頻率:部份功能停止運作。應暫時的降噪(暫時停止通止,避免有其它警告被淹沒),安排修正計劃
維度四:低風險/高範圍/低頻率:部份功能停止運作。儘速安排修正計劃。

在高風險的情況,如果有小範圍的異常,都應立即處理,重點在於能不能根除問題,或是緩解發生頻次

維度五:高風險/低範圍/高頻率:根除問題,或是緩解發生頻次
維度六:高風險/低範圍/低頻率:根除問題,或是緩解發生頻次

在低風險/低範圍的情況下,要小心避免讓人習慣這類型的錯誤(麻痺),或是產生過多雜訊影響判斷。
偶一為之但是知道原因的話可以不處理

維度七:低風險/低範圍/高頻率:降噪或是設定提示水位
維度八:低風險/低範圍/低頻率:不處理

不可重現

不知道自已不知道是最可怕的,如果發生時是高風險/高範圍大多也沒救了,
就死前能不能有所收獲

維度一:高風險/高範圍/高頻率:終止營業、暫停營運的等級
維度二:高風險/高範圍/低頻率:終止營業、暫停營運的等級

低風險/高範圍的情況,即使發生了仍有轉還的空間,高頻時處理的重點在縮小範圍與暫時的降噪
一但修正,理應停止降噪的設定,低頻時安排修正計劃即可

維度三:低風險/高範圍/高頻率:暫時的降噪(暫時停止通止,避免有其它告警被淹沒),查明原因,安排修正計劃
維度四:低風險/高範圍/低頻率:查明原因,安排修正計劃

在高風險的情況,如果有小範圍的異常,都應立即處理,重點會在能不能根除問題,或是緩解發生頻次

維度五:高風險/低範圍/高頻率:查明原因,根除問題,或是緩解發生頻次
維度六:高風險/低範圍/低頻率:查明原因,根除問題,或是緩解發生頻次

在低風險/低範圍的情況下,要小心避免讓人習慣這類型的錯誤(麻痺),或是產生過多雜訊影響判斷。
偶一為之但是不知道原因的話要特別小心標記,這很可能是別處發生問題的潛兆

維度七:低風險/低範圍/高頻率:查明原因,降噪或是設定提示水位
維度八:低風險/低範圍/低頻率:查明原因

小結

幾個重點,預防勝於治療,
真的發生時儘可能讓風險降低、範圍縮小、頻率減少,
十萬火急時可以先用暫解去處理,減少傷害。
但是回到正常流程就是要找出根本原因,並用正確的方式治療。

我作了個表以供參考

情境 風險 範圍 頻率 行動
可重現情況(Reproducible)
1 立即處理,確保不再發生。
2 立即處理,確保不再發生。
3 暫時降噪並制定修正計劃。
4 制定修正計劃。
5 根除問題或減少頻率。
6 根除問題或減少頻率。
7 降噪或設定提示水位。
8 不處理。
不可重現情況(Non-Reproducible)
9 查明原因,可能無法挽救。
10 查明原因,可能無法挽救。
11 暫時降噪,查明原因,制定修正計劃。
12 查明原因,制定修正計劃。
13 查明原因,根除問題或減少頻率。
14 查明原因,根除問題或減少頻率。
15 查明原因,降噪或設定提示水位。
16 查明原因,特別小心標記可能是其他問題的潛在預兆。

(fin)

[生活筆記] 一些程式轉職相關的問答

前情提要

一直有在作程式相關的助教、導師相關的工作,有些學生的問題很棒,也是我的盲點。
沒有心思好好整理,至少記錄下來,給未來的自已一絲反芻機會。

問題

Express.js在業界的實用性如何? 優點和缺點是什麼? 有需要再學習第二種語言/框架嗎?,建議的選擇是?

算常見的 Nodejs 的 Web 框架,優點就是夠主流,資源好找。
缺點就太簡單。

語言/框架可以多學,自已判斷。不知道就先跟著公司或社群走。

參考文章10 Best Nodejs Frameworks for App Development in 2023

Google 關鍵字: Nodejs Web Framework 2023

思路:不論什麼樣的程式語言,我的本質是 Web 開發,不同的語言與框架有不同的場景,總不缺乏新的挑戰者,
可以觀望、研究、測試、入坑,沒有一定的正確的答案,能夠快速作出判斷與取捨是重要的,但這需要經驗。

2. 如果想要在自有主機上部署Express.js,比較適合搭配的web伺服器軟體是?

這裡的”web伺服器軟體”是指什麼? Nginx 或 Apache 嗎?
選擇適合的場景就好。目前業界主流是 Nginx,思路參考第一題

3. 樣板引擎在業界的使用度高嗎? 有使用的話會是在什麼情況呢? 有比較主流的選擇嗎? 還是大部分被前端框架取代了?

註:學生這裡指的是 express + handlebars 的開發方式
大部份都前後分離端了,維護舊案可以能會碰到。
樣板引擎主要會發生在 Web Form 這類的舊專案,主流可能是 Php 或 Asp.Net 的某些專案。
或是在 2010 很主流的 MVC 開發方式,這段時間各家語言也有自已的解決方案。  

思路:理解為什麼會有前後端分離的發生,某些情況的小專案,樣板引擎的開發方式會比前後端分離有效率。

4. AC一個練習教案的規模,大概有幾十個到百個檔案要維護。實際業界專案的檔案數量應該更多,檔案間的關係複雜,維護人員要如何弄得清楚呢? 有沒有什麼比較好的管理工具?

檔案是指程式碼的部份嗎?  
學會架構分層,基本就三層式架構、MVC、MVVM、MVP、MV* 等,
使用 OOP 語言的話可能也要依 Pattern 分層,現在比較流行 DDD、六角架構等…
再大一點會是微服務的情況。

思路:想一下檔案間的關係複雜,那你會怎麼作?基本上就是分門別類,
好的分類可以讓錯誤發生時可以快速定位、實作功能時可以職責分離。
舉個生活化的例子,就是像是整理你的房間。

5. 在前後端的協作中,如何決定一項功能應由前端製作或後端製作? 舉例來說,要加上分頁的功能,本質上只是資料的整理重排,這樣的功能應該由前端或後端做呢?

Case by Case, 以我來說跟畫面有關的歸前端、其它歸後端。  
分頁通常會由後端處理、但是有些情況前端也要處理。
Ex: 避免太頻繁的 request 到 Server,一次性回傳大量資料,由前端自行製作分頁。

思路:還是以需求為依歸、預設一個簡單的原則。團隊合作的話,最理想是團隊共識,次之由 Leader 決定,不要發生由某端強勢主導的情況。
舉例說明,欄位檢查,前後端都要作,前端的目的是引導使用者正確操作,後端是真正的防護。

6. 求職網站中許多職缺要求會其他程式語言如: C# or PHP,會建議再去學其他語言嗎? 有推薦的學習平台或課程?

我會想學 TypeScript、Python。
程式在 web 開發上有類似的作法,更多領域其實也需要程式人才,
例如:手機 App 開發、IoT 開發、AI 工程師等…
建議找一個自已可以生存的領域,專研打磨裡面的技術會建議
社群活動可以用來搜集關鍵字,
Udemy 的課程很便宜,YT 也有很多大神的頻道可以學習。

思路:唯一的問題是學不完,怎麼整合應用變現,這個時候就不只是求職了,公司只是某一些技術棧整合的結果,
當你學的更多時,或許可以有別的出路?

7. 建議如何練習LeetCode? 才不會好像只是背答案,而不是懂背後的邏輯。有聽說 NeetCode 平台不錯用?不知道助教是否推薦

就是多刷多累績經驗,看題目、看答案。
兩個重點,把題目看懂、把答案看懂,試著把自已的思路轉化成程式
平台很多:NeetCodeLeetcodeHackerrank

思路:助教用 TDD 刷 LeetCode,實務上蠻常遇到面試造火箭、入職擰螺絲的狀態,
但是仍然不知道要學什麼或作什麼樣的 Side Project 時,刷題是一個好的選擇。

8. Junior工程師除了準備作品集,LeetCode 與程式語言的觀念外,通常面試還會考什麼嗎?

我主要會問架構與流程題,如果能把自已開發的架構與流程說明清楚,就可以同時觀察到技術能力與溝通能力。

思路:常常去面試就會知道現在業界常問什麼了。

10. 職場上在開發專案時,後端工程師的實作範圍會是全部一手包辦還是還會細分工作?或者工作架構會是怎麼分配的?

看公司,建議去有分工的公司,一手包辦很可能會很雷,學到的可能也是一堆大泥球的作法

12. 除了現有AC課程之外,助教建議Junior後端工程師還需要提升哪方面的技術

掌握好版控與協作流程/自動化與容器化技術/實用精實的開發實踐

  • Git、Docker、AWS/Azure/GCP、K8S
  • TDD (BDD、ATDD)/ Refactoring /Design Pattern

13. 目前軟體業有受到景氣影響而減少職缺數嗎或停止招聘

國外有一波大裁員,有影響到,但台灣本身還好
至少我還好

14. 請問助教平常寫程式會如何結合AI加速開發,有建議相關課程嗎

Github Copilot+TDD

15. 網站若有多國語言版本,技術上要怎麼實作呢?

關鍵字i18n

1
2
3
4
5
6
7
8
{
"tw":{
"login":"登入"
},
"en":{
"login":"Login"
}
}

16. 手機版和電腦版的網站要怎麼分割呢?

我知道的兩種作法

結語

部份題目涉及專案內容,所以排除掉。
但是有關從無到有開發一個專案,或是與人合作開發專案,是整個職涯中會很常發生的事。
現在不作,以後也會遇到,可以儘量去嚐試。

(fin)

[實作筆記] Websocket 初體驗

前情提要

tl;dr, websocket 至少是一個 10 年以上的技術了,但是筆者一直沒有機會實作它。
大部份的時候是應用場景不符合,或是簡單 Long Polling 已經足夠,甚至就算用 Polling 這個方法也無法把 Server 打掛。
另一個情況是團隊已經很成熟,有專門負責的部門在統一處理這塊邏輯,
而通常這塊邏輯會與主要的核心功能作切分,所以我也沒有機會接觸到。
這次難得有個小型的專案,有機會實作,故稍作記錄一下。

需求

使用者會停留在某些頁面等待系統的資料狀態更新,大約每 10 秒要作一次 Polling,
而平均資料狀態更新需要 3~10 分鐘不等。在這個情況下要改用 websocket,
(註:我不認為這是一個很好的應用場景,但是牽扯更多未揭露的調整故不展開討論,
EX:Heartbeat、Keep-Alive 等等…機制也未討論到)

WebSocket Server 實作

使用的技術:Typescript + Express + ServerSocket

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
// import library
import express, { Request, Response } from "express";
import { Server as ServerSocket } from "ws"; // 引用 Server

if (process.env.NODE_ENV !== "production") {
require("dotenv").config();
}
// form env 指定一個 port
const PORT: number = Number(process.env.WS_PORT);
// 設定斷開時間
const CT: number = 1000 * Number(process.env.CONNECTION_TIMEOUT);
const app = express();
const server = app.listen(PORT, () =>
console.log(
`[Server] Listening on http://localhost:${PORT} , timeout is ${CT} ms`
)
);
const wsServer = new ServerSocket({ server });

// Connection opened
wsServer.on("connection", (ws: WebSocket, req: Request) => {
// Connection 建立時發生的邏輯

// Listen for messages from client
ws.on("message", (data: string) => {
try {
// 發送 Message 時發生的邏輯
} catch (error) {
console.error("Invalid JSON format: ", data);
}
// Get clients who has connected
const clients = wsServer.clients;
// Use loop for sending messages to each client
clients.forEach((client) => {
client.send(JSON.stringify(docStatus));
});
});
// 設定 ping 時間間隔,用來讓連線太久的 Client 斷開
const interval = setInterval(() => {
if (ws.alive) {
ws.terminate();
clearInterval(interval);
return;
}

ws.alive = false;
ws.ping("", false, () => {
console.log("[Timeout]", ws.key);
});
}, CT); // 30 秒

// Connection closed
ws.on("close", () => {
// Connection 關閉時發生的邏輯
console.log("[Close connected]", ws.key);
});
});

// 新增一個路由處理器
app.get("/", (req: Request, res: Response) => {
// health and version check
res.send(`WebSocket Running Version ${process.env.WS_VERSION}`);
});

前端實作,收送雙向

使用的技術:JavaScript
以下內容大多參考於神 Q 超人的文章,
作為參考用,實務上會搭配使用的前端框架作修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
var ws;

// 監聽 click 事件
document.querySelector("#connect")?.addEventListener("click", (e) => {
console.log("[click connect]");
connect();
});

document.querySelector("#disconnect")?.addEventListener("click", (e) => {
console.log("[click disconnect]");
disconnect();
});

document.querySelector("#sendBtn")?.addEventListener("click", (e) => {
const msg = document.querySelector("#sendMsg");
sendMessage(msg?.value);
});

function connect() {
// Create WebSocket connection
ws = new WebSocket("wss://localhost:8088");
// 在開啟連線時執行
ws.onopen = () => {
// Listen for messages from Server
ws.onmessage = (event) => {
// 收到訊息的 Logic
console.log(`[Message from server]:\n %c${event.data}`, "color: yellow");
};
};
}

function disconnect() {
ws.close();
// 在關閉連線時執行
ws.onclose = () => console.log("[close connection]");
}

// 監聽 click 事件
document.querySelector("#sendBtn")?.addEventListener("click", (e) => {
const msg = document.querySelector("#sendMsg");
sendMessage(msg?.value);
});

// Listen for messages from Server
function sendMessage(msg) {
// Send messages to Server
ws.send(msg);
console.log("[send message]", msg);
}

後端實作,只送不收

使用技術: php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
include "./vendor/autoload.php";

$client = new WebSocket\Client("wss://localhost:8088");

$message = json_encode(array(
"your_property" => "Your Data",
"complex_property" => array(
"no" => 1,
"status" => "Hello word"
)
));

$client->text($message);
$client->close();
?>

後記

實務上在股票看盤即時更新、多人聊天室、多人網頁遊戲上或許十分有用,
但實際上在這此的案例上就有些大材小用了,先當作學習了。
在查找資料的過程有一段話很受用,
我稍作總結如下:
當討論到 WebSocket 時,不應用 Http 的標準去審視它,
更應該關注這些 Connection 會持續連接多久? Connection 之間交互的行為是什麼?  
是運算密集的行為還是讀寫密集的行為?…等等,才是你決策的關鍵。

參考

(fin)

[踩雷筆記] Gitlab CI 執行 Command 結果與本地環境不一致

前情提要

我們建立 CI 的一個目標是讓繁鎖的工作流程自動化,
而自動化的前題是標準化,而自動化的未來是可以規模化。
每一次的自動化,就像是在為工作流程添加柴火,讓未來的的每一步可以走的更穩更快。

問題

一直以來我認為只要在本機環境上可以執行的指令(command),就一定可以在 CI 中執行,
沒想到這次不一樣。

下面這個語法是我 CI 流程的一部份,主要的目的是要重啟 pm2 的服務

1
pm2 restart app.js

這段在 Gitlab-runner CI 的寫法如下,主要的目的就在 Gitlab-runner 中將指令送到指定的機器$VM

1
- ssh -i ~/.ssh/id_rsa gitlab-runner@$VM 'pm2 restart app.j'

不過我會收到錯誤訊息

1
bash: pm2: command not found

當我直接在$VM執行時,確又不會有錯誤

根本原因

原來在 Unix-like 系統中,shell 分為 Interactive Shell(互動式)和 Non-Interactive Shell(非互動式)。
兩者環境變數和 PATH 不同,導致在 CI 或遠程機器上執行命令可能與本地不一致

查詢了一下兩者的 PATH 如下

Interactive Shell

/home/gitlab-runner/.nvm/versions/node/v20.4.0/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin

Non-Interactive Shell

/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin

明顯可見的差異在於NVM的路徑設定,這個 PATH 是在安裝 NVM 時被加上去的

解法

我們可以選擇重新 export NVM_DIR 這個作法

1
2
- ssh -i ~/.ssh/id_rsa gitlab-runner@$VM 'export NVM_DIR="$HOME/.nvm" && [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" &&
pm2 restart app.js

另一種作法會多上一層 shell,並再次執行互動式的 bash 我覺得較不易理解
不好維護,故不採用。

1
- ssh -i ~/.ssh/id_rsa gitlab-runner@$VM 'bash -i -l -c "pm2 restart app.js"'

參考

(fin)

[生活筆記] 2023 半年工作回顧

前情提要

這份是 2023 年, 3 至 6 月間,我在工作上獲得的成果,
以理性務實的態度推動各項工作,以下是在這段時間內所取得的重要成果:

敏捷觀念的引進與實踐

我引進了迭代開發要事優先的敏捷觀念,
同時建立了 PBI(產品待辦項目)與工作看板,以確保團隊的工作透明度
我們每日召開立會,及時更新資訊,並建立了文件系統,以便於知識的分享與傳承。

前後端分離的優化

我推動了前後端分離的工作方式,使專業得以適當分工,減少了彼此等待的時間。
這樣的優化使得服務可以被重複使用,避免了重複開發,節省了寶貴的時間與資源。

自動化的部署機制引入(CI/CD)

我們成功建立了自動化的部署機制,每次部署都節省了至少 10~30 分鐘的時間,
同時大幅減少了人工操作可能帶來的錯誤風險
這項優化為團隊帶來了高效率與更穩定的工作環境。
這是持續性的正面效益,假設每周部署 10 人次以上,以換算時薪為 600 計算,
每年每位 RD 約可以省下等值於 8 萬的工作時間。並大幅下降風險。

準時交付 N 系統,完成參展目標

我們順利地在新加坡、法國、台北和高雄四地參展,並提供了展場現場的支援,
這已超出我們本職專業的範疇。
但我們成功地達成目標,贏得了更多的曝光與商機。

帶回自主開發的 I 上鏈系統,省下外包費用

為了節省成本,我們將原本要外包的 I 上鏈系統帶回自主開發,從而節省了公司大筆的費用與時間。
這項舉措不僅讓我們獲得了更多的控制權,還提升了整體效率。
此項目,粗估至少擁有 300 萬以上的產值。

其他突破與調整

除了以上成果,我還做了以下貢獻:

  • 解決了公司 domain 數量達到上限的問題,通過與系統商的溝通,成功解除了限制。
  • 幫助查找了未關閉的 AWS 資源,避免了資源的浪費。
  • 調整了資安需求,提高了系統的安全性。
  • 協助調解了系統與需求之間的衝突,確保了順利推進工作。
  • 未來的發展計劃
  • 為了持續推動工作的進步,我擬定了以下計劃:

下一步

  • 將預計外包的 P 系統帶回自主開發,進一步節省公司費用。粗估其價值為 110~800 萬之間。
  • 積極招募人才,引進新血,提升團隊實力。
  • 推進單雲轉多雲的轉型,增加靈活性與彈性。
  • 針對資安問題進行調整,包括關閉公開 IP 和加密資料等措施。
  • 處理舊系統 L 的大泥球,逐步優化系統結構,提高整體效率。

結論

整體而言,在短短三個月內,接手的專案已為公司撙節了 150 萬 左右的支出,
而在自動化工具,用一個粗略評估的方式,假設每周每個 RD 會部署 5 次,
一年 52 周,一次部署可以省下 10 分鐘的情況下,目前公司 5 位 RD:
5*5*52*10= 13000 分鐘,大約可以省下 1.25 個人月,用來作更有意義的事。
而其它工作在長久看來也會持續帶來收益,整體而言,這三個月的產值已超過 500 萬
希望在未來的日子裡,能夠持續取得進步,為公司的發展做出更大的貢獻。

(fin)

[實作筆記] GitHub Actions:停用 save-state 和 set-output 命令的措施

前情提要

GitHub Actions 是一個強大的自動化工作流程工具,可讓您在 GitHub 存儲庫中執行各種自動化任務。
它允許您根據事件觸發工作流程,例如提交代碼或創建拉取請求。
您可以使用預設的操作或自定義操作來建立工作流程,並將其用於自動化測試、部署、持續集成等開發流程。
GitHub Actions 提供了一個靈活、可擴展和可自訂的方式來增強您的開發工作流程。

而我實務上的情境是用來寫 Blog,並進行自動部署,而我注意到了一個警告訊息。

1
Warning: The `set-output` command is deprecated and will be disabled soon. Please upgrade to using Environment Files. For more information see: https://github.blog/changelog/2022-10-11-github-actions-deprecating-save-state-and-set-output-commands/

問題

在 Github 的官方 Blog 指出,自 CI Runner 版本 2.298.2 起,
如果用戶在使用 save-state 或 set-output 將開始發出警告。
並計劃在 2023 年 5 月 31 日完全禁用它們。
從 2023 年 6 月 1 日開始,使用 save-stateset-output 命令的工作流程將因錯誤而失敗。

補充

實際上根據 Github 觀測數據顯示,這些命令的使用率相當高。
考慮到受影響的客戶數量,而推遲了移除的時間。

我的情況

為什麼我會用到這值呢?看看我的 CD 部署檔

1
2
3
4
5
6
7
8
9
10
11
12
13
## Skip ...
step:
# Deploy hexo blog website.
- name: Deploy
id: deploy
uses: marsen/[email protected]
with:
deploy_key: ${{ secrets.DEPLOY_KEY }}
- name: Get the output
run: |
echo "${{ steps.deploy.outputs.notify }}"

## Skip ...

在我這裡有兩步驟,Deploy  與 Get the output
Deploy 直接用了我的另一專案進行部署,內部的工作簡單的說明如下:

  1. 用 Dockerfile 建立執行環境
  2. 拉取 Hexo Blog
  3. 執行相關指令 ex: hexo g
  4. 部署完成印出成功訊息

問題就出在這個第四步,在 Github Runner 啟動的 image 執行過程的輸出,
並不會顯示在 Action Steps 的 Output 資料之中。
所以在這個專案多作一步 Get the output 來印出這個訊息。

1
2
3
- name: Get the output
run: |
echo "${{ steps.deploy.outputs.notify }}"

可以知道我們的訊息是放在 steps.deploy.outputs.notify 之中,
而原始接出資料的方式如下,這個作法即將被逃汰了(為了避免無意間的資訊外洩)

1
echo ::set-output name=notify::"Deploy complate."

解決方式

如果官方 Blog 所說,你只要將 key-value 用以下的形式設定到環境變數 $GITHUB_OUTPUT 即可,

1
echo "{key}={value}" >> $GITHUB_OUTPUT

舉例來說:

1
2
result="Deploy complate."
echo "notify=$result" >> $GITHUB_OUTPUT

參考

(fin)

[踩雷筆記] 移除 VM 外部 IP 後 GCS 檢查異常的問題

前情提要

我們使用 GCP Compute Engine 建立 VM,並且配置了一個對外的 IP(External IP),
同時設定了一個排程,定期將 VM 上產生的資料轉存到 GCS(Google Cloud Storage),
我們主要使用的工具是 gcloud 並且在 VM 上設定了某個 Service Account,
並且一切運作順利。

某日因為資安考量我們移除了 VM 的 External IP,而搬檔到 GSC 的排程就異常了。
我們簡單使用以下指令作檢查

1
gcloud storage ls

這個指令會列出我們 GCS 上的資源,
當 VM 有 External IP 時一切運作正常,而移除時會無回應。

解決方法

這裡只需要修改 GCP 的 VPC 設定,
選擇 subnet (ex:asia-east1,看你的 VM 在哪個 Zone)
啟用 Private Google access 為 On 然後存檔

參考

(fin)

Please enable JavaScript to view the LikeCoin. :P