[Tech] Code of Contact (Programmer)

Jack Huang
A Good Guy
Published in
14 min readNov 22, 2020

釐清開發需求

一般而言在接受一個開發專案的時候,一名 programmer 應該都希望能先理解所有開發需求,只有在理解自己到底要做什麼樣的產品出來後才會開始著手開發。因為就過去的經驗有的時候自己對最終產品的想像與委託開發者不同,會以自身的角度開發一個不符合需求的產品,遇到這種況只能重新規劃再開發一個新的產品。而在確認每項開發需求時必須至少理解其:

目的:理解開發需求的目的有助於勾勒出最終開發目標的模樣。

需求與需求之間的相關性:開發需求有時彼此會存在相關性,釐清開發需求相關性有助於在設計模組或 API 時合併提供重複功能的部份。或者在確認需求的階段時幫助客戶合併不同的需求為 1 種需求。

可能所需要使用的技術或資源:初步分析需求背後所需要的技術支援,思考本身團隊是否具備該技術支援、或者還缺多少程度的資源需要預先處理。在選擇技術上有幾個可以考慮的因素:

1. 舊的技術有什麼缺點?是否一定要使用新的技術?
2. 每個服務之間是透過哪一種 licensing?
3. 如果使用開源 solution,該社群活不活願?
4. vendor 的支援程度如何?

然而有的時候也會遇到有些客戶也會不清楚自己的開發需求,這個時候應與客戶一起討論需求,有的時候會建議使用別的方式滿足客戶想達成得目的。而根據討論的情況也有可能會直接拒絕或放棄這次的專案開發。大部分會拒絕或放棄專案開發的情況都滿足以下幾點因素,而這些因素都會導致我們無法正確理解客戶需求

1. 客戶在討論開發需求階段每次需求說明前後不一致。
2. 客戶無法在時間內提出開發需求。
3. 客戶對於最後開發產品沒有想法,無法自己解釋每一項開發需求。在這邊解釋每一項開發需求並非技術層面的如何實作,而是針對開發需求有具體希望達成的目標。

另外也有可能發生經過討論後開發需求和本身所想像的不太一樣,可能是需要使用從未接觸過的新技術開發,而學習該技術的成本過大,或者開發需求條列出來無法在時間內交付。

Example: 確認需求

目前電信公司中提供網路收銀機網路卡服務,只有當網路收銀機安裝網路卡才可以進行收銀服務。而客戶為一服務業商家,原先就以購買電信公司中的網路收銀機網路卡,然而因為當前網路卡只能透過預先儲值金額才得以使用網路服務,對於服務業商家提出必須定時前往超商儲值的流程很繁瑣,希望能夠改善流程,因此客戶提出了以下需求:

1. 提高儲值金額上限,由原本的 1000 元提升到 5000 元。
2. 每月月底將網路卡中的儲金自動加值到金額上限。
3. 當網路卡中的儲金低於 100 元時自動加值到金額上限。

在客戶提出的三項需求中,可以理解客戶的需求目的為:替代或簡化定時前往超商儲值的流程
接著,我們必須先評估每項需求之間的相關性,而仔細思考需求後會發現當我們提供需求 3 時需求 2 似乎就不一定要去實作也能滿足客戶最終想要達成的目的,但是仍然必須考量到客戶本身營運流程(可能在月底自動儲值計算營業收支時較方便),因此不能少開發需求 2 的功能。

當考慮完需求的相關性的最後便是思考可能所需要技術或資源:

1. 需求 1 相對直覺,可以針對加值操作的 API 設置 upper bound ,或者某些資料庫系統可以針對表格的欄位更新操作時 trigger handler,在 handler 中檢查是否超出最大金額。
2. 需求 2 則需要設置排程程式自動針對特定網路卡進行加值,在規劃整個排程時可能需要考慮不同網路卡是否啟動自動加值的服務(並不是每個客戶都需要自動加值的服務),因此可能需要額外的界面或資料庫的欄位設定是否使用自動加值的服務,因此就需求 2 可以達成。
3. 針對需求 3 上可以考慮在扣除網路卡金額的 API 上下手,多增加判斷該網路卡的金額是否低於 100 元,若為真便自動加值該網路卡並發送通知給網路卡持有者,因此就需求 3 而言可以達成。

當評估需求後認為需求是可以完成的,便可以和客戶進下一步規劃。

設置開發需求備案

在每個專案開發時,所有列出的需求我都會儘量替每個需求考慮另一個備案以預防遇到無法開發出來的窘境,而這些備案大多會具備以下 2 個特點:

1. 大多會是以自身技術必能達成的。
2. 存在第三方插件得以實現。

然而因為並不是每個需求都一定能想出另一個備案,因此當一個開發需求沒有任何備案時應該要提昇其開發優先程度,先滿足該開發需求後在完成沒有備案的開發需求。

最好的情況:固定的開發需求

就如同字面上的意思,一旦開始開發專案後開發需求不能一直被修改,最好的情況是只有到開發結束後再將所有要修改的功能視作新 feature 在未來的更新中釋出

在開發時一般而言我們會針對開發需求訂定一些規則、安排開發進度或討論 API、模組介面,而大部分規則和進度都會依賴於客戶的開發需求,因此當客戶需求在開發階段不斷在改變時,每次的改變都需要重新討論前面所有敘述的事項,會造成整個專案的停滯。

當然,我們無法避免客戶需求的改變,若是強制所有客戶需求完全固定似乎也並不是那麼合理,但在接受需求改變的前提是不能讓客戶認為所有的開發需求改變都是可接受的,當接受需求改變時需要考量的點有:

1. 需求改變會不會影響其他需求的開發?
2. 需求改變會不會影響開發進度的安排?
3. 需求改變後能不能仍然保持原先開發需求的目的?
4. 需求改變需不需要額外學習新技術?

適當使用 plugin/package/tool/framework

plugin/package/tool (下文統稱第三方插件)

在大型專案的開發中我們可能會遇到:

1. 開發進度不如預期。
2. 開發技術不熟悉。
3. 開發需求的調整。

在上面三種情況發生時,因為 programmer 一次能負擔的任務有限,以及一名 programmer 的能力也有限,要在預定的時間內完成一個專案並且沒有額外的時間學習新技術時我們可以搜尋適合的第三方插件輔助開發。而在使用時也必須注意第三方插件的 license,有些 license 不允許商業行為有些 license 規定使用插件後必須整個專案開源等等。

在大型專案中適時的使用第三方插件對於開發是有幫助的,它幫助我們解決一部分技術上的問題,加速專案的開發。然而我們也必須考量的是:第三方插件也可能增加最終產品潛在的 bug,因為使用第三方插件等同於將開發這件事情轉移給了外部其他的開發團隊,我們沒辦法去保證所有第三方插件不存在任何 bug,也沒辦法確認所有第三方插件彼此之間交互影響了專案的什麼,除非在使用每個第三方插件前你都會將其原始程式碼都爬過一遍,完整評估風險以及對於其他程式碼有沒有什麼影響之後才使用該第三方插件,然而這麼做某種程度上便失去了使用第三方插件的好處,因為使用第三方插件便是為了縮短開發時間,但是你卻花額外的時間去爬完整的原始程式碼仔細分析使用後的影響。(同時、爬完分析完照理來說你也等同於擁有實作這個套件的功能)

framework

而採用 framework 則是為了讓整個專案開發能夠跟隨 common convention,framework 可以幫助解決一些常見的議題像是: view rendering, asset generation, security, application configuration 等等,此外在一定的 convection 規範下我們也能依循 convection 思路比較輕易抓出問題點在哪裡,我想這也是大部分大型專案開發時有些團隊會使用 framework 開發專案原因之一。

確保跨領域合作下的任務交付

在大型 software 專案中可能需要跨領域合作,例如需要由 UI/UX designer 設計 prototype/wireframe 以及 software GUI 介面設計、由行銷人員所構想的數位行銷手段等等。在開發一個產品的過程中可能不只牽扯到程式開發,在面對與其他領域合作上最好的情況是所有和程式開發相關的素材我認為應該在開發前都至少準備好一半以上,例如: UI/UX、文件。

在規劃整個產品開發進度的時候,我們不應該只考慮或著重在程式開發方面如何規劃以及完成,跨領域合作時每個領域的專項人員所需要負責的任務也同樣重要,畢竟不同任務之間彼此可能會有相依性,當任務 B 依賴於任務 A 時其他領域負責任務 B 沒能按時交付會造成整個任務進度的延後,更甚者可能會造成整體專案的進度延後。

Refactoring

大部分大型的專案在敏捷開發模式下雖然能快速交付產品,但是可能會產生出低可讀性的程式碼,因此才需要重構程式碼來降低程式碼的複雜性。而當決定專案須被重構時的情況可能是:

1. 對於更改程式碼感到不方便時。
2. 為了便於新增功能。

而在下列情況時我認為就不應該立即進行重構:

1. 遺留的技術債太大,需要花費巨大成本進行重構。
2. 現在專案並未完成。
3. 專案快要到 deadline 時。

重構是對 software 內部結構的一種調整,目的是在不改變 software 行為的前提下,提高 software 程式碼的可讀性,降低其修改/維護的成本。重構對於程式碼的長期維護非常重要,其重要性僅低於程式碼可以 work。對一個專案進行重構背後有很多原因:

重構可以改進軟體設計:

一般而言一名 programmer 大多只為短期的目的進行程式碼修改,或者在沒有完全理解整體架構下就開始修改程式碼,這樣會導致程式碼結構的流失,programmer 與 programmer 之間也越來越難通過閱讀原始碼理解原本的設計,於是整體架構也變得越來越糟糕,經常性的重構有助於維持最初整體架構。

重構可以使軟體更容易被理解:

大部分 programmer 認為程式碼只要能夠運行起來就可以了,不必在意程式碼的質量,但作為一個開發人員,寫出漂亮的程式碼是最基本的素質。而透過不斷修改程式碼的過程中,程式碼的可讀性會慢慢累積起來。

重構可以協助找到 bug:

重構有助於 programmer 理解程式碼以及其行為,在理解程式碼的原理下 programmer 能更快速的鎖定 bug 可能落在專案中的哪個模組。

什麼是 TDD(Test-Driven Deployment)?

TDD 是一種開發流程,許多專案在開發時通常會邊寫程式碼邊寫測試,或是先寫程式碼在寫測試,或是更常見的 — 寫程式碼不寫測試。

而 TDD 則是「先寫測試再寫程式碼」。有些工具可以讓你寫測試時,一邊幫你產生空的類別與方法外(如 Eclipse 撰寫 Java 時就有類似的功能),一般來說也可以直接假設你已經撰寫好了程式,先揣摩如果已經寫好了這個程式該要如何使用。而當我們還沒開始寫程式之前就開始寫測試最大的好處是開發者當寫出測試時,就可以瞭解這一個元件最後會怎麼使用,同時也釐清的程式的介面會如何定義。

而對於 TDD 而言既然存在測試,便存在重構程式碼的議題在:當測試寫完後完成第一個版本的程式碼時會再交由先前所寫好的測試來驗證第一個版本的程式碼是否能通過測試,當如果沒有通過測試則代表程式碼並沒有符合當初的需求,就必須重新修改推出第二個版本進行測試 -> 這便是 TDD 中所提到的重構。

良好的軟體架構

在專案開發時存在良好的軟體架構有助於提昇專案開發速度,而一個良好的軟體架構大多都以「最小化建置和維護需求所需要的人力資源」為目標所設計,而軟體架構的設計品質相對於滿足客戶需求所花的 effort,如果 effort 是少的,並且在整個軟體開發生命週期中都保持少的,那麼這個設計就是好的。

在勾勒出整個軟體架構的時候有幾個必要的面向:

管理非功能面需求

所謂的非功能面需求(non-functional requirements)一般指的就是與開發功能無關的需求,比如說:系統的反應速度、系統容納上限、是否具備 HA (high availability) 或 fault tolerance。在設計軟體架構的第一個面向應該馬上將功能面以外的需求加以管理並確認清楚,不要讓該需求無線擴張,比如說客戶所要求的系統如果不需要 24/7 的營運保證,或許就可以讓他有系統更新與修改的時間與機會。或是可以釐清究竟需要多少的連線速度。

架構評估

針對每個架構進行評估時有兩個面向:

1. “假設”系統會如預期的運行
2. “證明”系統會如預期的完美運行

不論是功能性需求或是非功能性需求,在評估系統架構上,我們可以透過各種的分別的服務壓力測試來給予我們足夠的信心來評估我們的系統架構,比如說:在決定 Message Queue 系統架構時,可以考量 Kafka, Redis, 或是其他 MQ 給予的壓力測試數據,加上該系統可以裝載的資訊類別來評估是否符合你的系統設計。

所以根據前面的例子,在設計上我們都會先”假設” Kafka 能夠符合你的系統設計需求,再來查看 Kafka 的可以乘載的資料類別與限制,加上他的壓力測試數據.來”證明”系統架構上是可行的。

架構上的合作

一個好的軟體架構往往需要跨組織、跨區域性的合作,很多時候許多系統內部甚至外部參與者的合作才能造就整個系統的穩定與完整,比如說:

1. 外部資料提供者需要足夠的文件說明。
2. 外部服務提供者(或是需求者)能夠了解系統的架構或限制。

系統測試

系統測試是檢查實際結果與預期結果是否匹配,以確保系統交付給客戶後能正常運作。而測試也有助於辨識產品與實際客戶需求是否相符。而一個系統測試的目標有以下幾點:

1. 在當前的開發產品上盡可能地發現錯誤。
2. 驗證產品與其的規格或需求相符。
3. 使用最小的成本驗證產品的 quality。

在建立測試時不論是整合測試、E2E testing、Unit testing 等等功能及非功能測試,不應該設置目的不明確的測試,同時也不該基於程式沒有錯誤為前提進行測試。

然而因為所有的測試都是為了抓出非預期的行為,因此在測試時不只看程式是否做了它該做的事,更要著重在程式是否做了它不該做的事。也就是說,設計測試個案時,不合理、不被預期的輸入狀況,與合理、被預期的輸入狀況同樣重要

自我檢討

大部分進行軟體開發時會採用 MADR 進行所有的決策紀錄,而在專案開發結束後可以透過這份紀錄進行自我檢討,從釐清開發需求、開發過程到最後的 documentation,一步一步檢視每個環節的決策是否正確。每一次的專案開發經過檢討後可以檢視自己在專案開發上所遺漏的地方,將所有檢討的事項寫到一份 report,在未來能夠透過這份 report 重新檢視過去專案開發的癥結點進而避免重蹈覆轍。

Markdown Architectural Decision Records

一個 architecture decision(AD)是一種軟件工程中決策的選擇,這種選擇大多可以解決在架構上很重要的功能或非功能需求。 例如,這可能是技術選擇(例如, Java 與 JavaScript),IDE 的選擇(例如IntelliJ與Eclipse IDE),library 之間的選擇(例如 SLF4J 與java.util.logging) ,或有關軟體應用功能的決定(例如,終身訂閱與月訂閱)。 不要太在意“架構”一詞,也不要太用力地解釋它。 就像範例所描述的,可能以某種方式影響體系結構的任何決策都是 architecture decision。

了解更多

重點整理

1. 必須能詳細釐清開發需求。然而有的時候有些委託開發者也會不清楚自己的開發需求,根據情況會直接拒絕或放棄此次的專案開發。
2. 儘量為每項開發需求設置一個備案以預防遇到無法開發出來的窘境。
3. 開發需求一旦開始開發後便不能再修改,只有到開發結束後所有要修改的 feature 都要被視作新 feature 在未來的更新中釋出。
4. 專案開發時並不是所有程式碼都由我們自己開發,視情況使用對應的 tool 或 framework 加速開發。
5. 能完好的完成規劃好的進度最好,然而也是可以接受晚一點執行。根據不同的任務在有不一樣的可延遲標準。
6. 所有開發相關的素材在開發前都至少準備好一半以上,例如: UI/UX、功能文件。
7. 針對過去專案進行重構,推翻過去的思路,建立全新且正確的程式開發方式。
8. 專案開發結束後要進行自我檢討,從釐清開發需求、開發過程到最後的 documentation,一步一步檢視每個環節的決策是否正確。將所有檢討的事項寫到一份 report,在未來得以重新檢視。

--

--