--- tags: SDE-good-read topic: The APEROS Code Review Elements --- # The APEROS Code Review Elements ## Reference [The APEROS Code Review Elements](https://www.linkedin.com/pulse/aperos-code-review-elements-carlos-silvestre?fbclid=IwAR2xw4G7s44iQJ-sfos3BEaSp-pvLF5IWBm6VImXtvtJ_8YHl9BBCtgmwYw) APEROS (Availability, Performance, Extensibility, Resiliency, Observability, Scalability) ## Availability 1. timeout / retry 的平衡 2. **不該**預設上下游的 input/output 都是可預期行為 3. 考慮服務負載 4. 可動態調整的 timeout / retry ### Example a. Horrendous retry policy - multiple instances querying a problematic downstream until starvation 在處理 try-catch 中,應避免不斷做多餘的嘗試 (retry)。 ```java= function readFromDownstream() { var response = null; do { try { response = callServiceA(); } catch (E) { log.err ("Service call failled, retry"); } } while (response != null) } ``` b. Resource exhaustion threat if upstream load is not capped 此範例中當收到 Request 會直接生成新的Task 進行處裡。這可能會讓服務 Loading 太重,應該設定一個上限值以確保系統穩定以及服務可以持續,應使用Service Queue。 ```java= function processRequest() { var future = launchThreadAndAsynchProcessRequest(); if(future.complete) { return 200; } else if(future.err) { return 500; } } ``` ## Performance 效能方面除非實際進行測試,否則無法直接得到結果。然而我們仍然可以以下幾點進行觀測。 1. 使用資源應該在相同效能下使用資源使用量上最少的。 2. Thread 越少越好。 3. 大檔案的 read/write 應該以streaming 方式實施。 4. Mechanical Sympathy 5. 迭代一次可以解決的事,不要做兩次。 6. 應該使用 Multithreading 處理可以平行化的任務 7. 避免多次i/o(system call, Downstream) 8. Operations are being batched if they can ### Example a. Early optimisation 在設計階段就考慮效能最佳化 ```java= function interpolateData() { try { var executionService = new (10); // 10 threads! var runnable = goThroughData(); executionService.execute(runnable); } catch (E) { log.err ("not good"); } finally { executionService.close() } } ``` b. Missed multithreading opportunity - large data sets will most probably benefit from parallel streams if the operation is atomic ```java= function processList() { var veryLargeList.stream().map(Bla::doSomethingAtomic).close(); } ``` c. Immutability candidate - avoid reads and writes if possible, consider whether the data model is right for the use case and whether immutability is not a better option ```java= function updateItem(item) { var existingItem = readFromDB(item.key); if(item.this > existingItem.that) { updateInDB(item); } else { // do nothing } } ``` d. Missed batching opportunity - most of the DBs offer batch operations for a reason ```java= function persistList() { var mediumSizedList.forEach(item -> { myDBClient.persist(item); } ); } ``` ## Extensibility 優雅的程式: Refactor, Clear Code 1. You’re not duplicating code or roles of a component 2. You’re not creating components that have too many responsibilities 3. Existing domains in the code are respected when creating new components 4. No unnecessary coupling between components is created 5. No components are created unless they’re needed ## Resiliency 1. 程式接受錯誤的能力,不能因為一些錯誤導致系統crash。(LOG, Recover) 2. Unit Test 可以排除許多隱藏的錯誤 3. 不需要用到的資源就該釋放 4. Multithreading are used on thread-safe code (delete, free 需要用 safety function) ## Observability LOG 很重要,當然,只需要紀錄重要的資訊。 ## Scalability 1. **The service is stateless.** Stateful services are hard to scale 2. If the application is idempotent, ensure the number of instances will not break the idempotency 3. There are no race conditions when consuming events, generating unique IDs or dealing with data from a data source. Those operations must be atomic and not influenced by the number of instances ### Example a. Stateful - multiple instances will not behave as expected switch case 取代部分攏長 if/else ```java= function iDontScale(var input) { if(input.a.equals(DO_THIS)) { setNextOperation(OPERATION_A); } else if (input.a.equals(DO_THAT)) { setNextOperation(OPERATION_B); } switch(getNextOperation()){ case OPERATION_A: // do this break; case OPERATION_B: // do that break; default: // log I can't scale like that } ``` b. Non-atomic event consumption - multiple instances may read the same event before the event is flagged as read Critical Section 要注意! ```java= function readEventDB(var data) { // read event from DB var event = readEvent() // mark event as read from DB markEventAsRead(event.id) } ```