---
title: "AI 輔助開發 - Copilot 與文件和註解"
date: 2023-08-30T00:00:00+08:00
publishDate: 2023-08-30T00:00:00+08:00
lastmod: 2023-08-27T16:08:27+08:00
tags: ["AI","經驗","Copilot","GitHub"]
toc: true
permalink: "https://blog.aotoki.me/posts/2023/08/30/ai-development-the-copilot-and-comment-document/"
language: "zh-tw"
---


最近因為公司有提供 [GitHub Copilot](https://github.com/features/copilot) 給我們當作工具，我也就順勢將 Copilot 在 Vim 中啟用。這幾個月體驗上跟當初釋出試用版相比，反應速度雖然有提升然而仍然沒有比自己思考的速度還快，但也有改變了開發習慣。

<!--more-->

## 不寫註解{#no-comment}

一直以來我都是「不寫註解」的類型，在 Ruby 的語法特性下，只要願意花時間思考命名，大多能很好的表達語意（Semantic）即使在其他類型的語言也能有不錯的效果。

然而，有一些朋友在 Copilot 的應用方式，則會採取「撰寫註解」作為提示（Prompt）來讓 AI 可以幫助他們生成近似預期的程式碼。就這點而言，我是不太認同的，一方面是殘留跟實作重複的說明，另一方面我們仍該追求容易理解、閱讀的程式碼。

以最近在做的 [OPass Serverless](https://github.com/CCIP-App/CCIP-Serverless/tree/main) 專案作為例子，以下是在沒有註解的狀況下讓 AI 輔助生成的，仍然很容易閱讀。

```typescript
export class AttendeeAccess {
    // ...
  	async getScenarios(token: string): Promise<AttendeeScenario> {
		const attendee = await this.attendeeRepository.findByToken(token)
		if (!attendee) {
			return {}
		}

		const ruleset = await this.rulesetRepository.findByEventId(attendee.eventId, attendee.role)
		if (!ruleset) {
			return {}
		}

		await runRuleset(attendee, ruleset)

		return buildAttendeeScenario(ruleset.visibleScenarios)
	}
}
```

我認為，當我們程式碼中的脈絡（Context）足夠完善時，對 AI 來說是不需要依賴註解去了解意圖，而是能順著工程師的思考分段的推測出所需的內容。

> 因為還是有出錯的可能性，在生成後還是要回頭檢查，這件事情可以讓「測試」這件事情來輔助，因此寫測試也可能是 AI 時代的重要技巧。

## 型別文件{#types-document}

型別議題一直以來在軟體開發中都是一種很難下定論的問題，單從 AI 輔助開發的角度來看，我認為他是一種很好的文件，因此像是 TypeScript、Ruby 的 RBS 這類方式，從這個角度來看是個不錯的設計。

接續上一段的實作，我們要將一個實體（Entity）轉換成 UseCase（使用案例）的回傳，來確保 Presentation Layer（表現層）不會對 Domain Layer（領域層）有過多的認知，假設在沒有型別輔助的狀況下，對 AI 來說要判斷這段程式碼如何產生是會有點吃力的。

```typescript
function buildAttendeeScenario(scenarios: Record<string, Scenario>): Record<string, ScenarioInfo> {
	let result: Record<string, ScenarioInfo> = {}

	for (const scenarioId in scenarios) {
		const scenario = scenarios[scenarioId]
		result[scenarioId] = {
			order: scenario.order,
			availableTime: scenario.availableTime,
			displayText: scenario.displayText,
			usedAt: scenario.usedAt,
			locked: scenario.isLocked,
			lockReason: scenario.lockReason,
			metadata: scenario.metadata,
		}
	}

	return result
}
```

我們希望將 `Scenario` 這個實體中的資訊，拆解出來變成 `ScenarioInfo` 這個 Data Transfer Object（DTO）來回傳，在 TypeScript 中用 `{ ... }` 的情境可能是任意的內容，然而在型別的輔助下，AI 就相對容易猜到要填入 `ScenarioInfo` 的欄位進去，同時能透過語意反推填入的來源是 `Scenario` 身上有的屬性。

那麼，用「文件」的角度去看型別，就可以得到兩種好處。第一種是對人類來說，更容易理解一段程式碼的實作、使用方式，另一種則是對 AI 來推測生成的內容，更容易正確的預測所需的內容。

> Ruby 的 RBS 是在 Matz 認為未來會有更好的輔助工具時，選擇不讓 Ruby 內建型別的情境下產生的，如同寫測試我們可能會預期「都要測試」一樣，對於有 AI 輔助的世界，撰寫 RBS 的意義可能更多在於「描述規格（約定）」這件是情上，也就是一種文件

## 加速開發{#speed-development}

目前來看，Copilot 的價值是幫助工程師省下打字和查閱程式碼的時間，因為我們不需要耗費腦力去翻閱過往的程式實做，只需用精確的關鍵字描述意圖，就能根據以往的物件推導出接近預期的程式碼。這跟今年 ＷebConf 保哥的演講[活用 GitHub Copilot 開發 Web 應用程式](https://hackmd.io/@webconf/BkImQ0Ds3/%2FT5tQ48y8S5GQkAUx9qlgng)的結論基本上是一樣的。

想讓 AI 做到輔助的效果，對工程師的基本功要求還是非常高的。舉例來說，我們用中文跟 ChatGPT 對話時，大多可以得到中文的回應，用英文的話就會得到英文的回應。這就表示，我們用一個比較差的寫法開頭，那麼就會得到訓練資料中比較差的做法。

在 AI 輔助開發的情境中，一個提示的品質好壞還是取決於工程師起頭撰寫的程式碼是怎樣的，剩下的部分則仰賴專案中已存在的部分做為參考，那麼命名、定義等資訊足夠明確的狀況下，產生出來的程式碼品質也會更高、更準確，最後才會讓開發的效率得到提升。

除此之外，有一類型的註解我也會選擇去撰寫，那就是 [RDoc](https://github.com/ruby/rdoc) 或者 [TSDoc](https://tsdoc.org/) 這類基於註解產生的文件，這跟型別文件的好處是類似的，對人類更容易理解，對 AI 來說還因為多了說明和範例，在推導程式碼的效果可能還會更好。

> 目前是還沒有感受到 Copilot 會去掃套件中的註解範例來輔助生成的樣子，也許未來會有這樣的機制，那麼許多「不知道」的使用方式也許也能被涵蓋到，讓我們能更加善用套件來減少重複造輪子的狀況。

