工程師的首要職責是建構可靠的東西。而可靠的基礎只有一條,也是唯一重要的一條規則。
We do not break userspace!
我們不破壞使用者空間
這句話的意思很簡單:任何會導致現有應用程式、腳本或工具無法運作的改動,都是一個 Bug。
不管理由多麼「充分」,也不管新設計在理論上多麼「成功」。
只要它破壞了現有的功能或服務,它就是錯的。
主要任務是服務使用者,不是給他們製造麻煩。
向後相容性是我們必須履行的契約,如果必須做出破壞性的變更,正確的做法是新增一個新版本,而不是直接修改舊版本。
🔴 破壞向後相容性的改動
// 版本 1.0
class UserService {
createUser(name, email, age) { /* ... */ }
getUser(id) { /* ...回傳 user 物件 */ }
}
// 版本 2.0 - 一個徹底的災難
class UserService {
// 🔴 錯誤 1:無預警變更參數順序。所有舊的呼叫全錯了。
createUser(email, name, age) { /* ... */ }
// 🔴 錯誤 2:改變回傳格式。所有依賴舊格式的程式碼全部崩潰。
getUser(id) {
const user = this.users.get(id);
return user ? { data: user, status: 'success' } : null;
}
}
如果這麼做了,製造的不是「升級」,而是一堆爛攤子。
🟢 務實的做法:擴展,而不是破壞
一個合格的開發者只會做一件事:新增。
舊的 API 必須確保一樣穩定。
// 版本 2.0 - 唯一正確的設計
class UserService {
// 保留舊的介面,它必須永遠能用。
// 你可以在內部標記它為棄用,但不能刪除或修改它的行為。
createUser(name, email, age) {
console.warn('createUser() is deprecated. Use createUserV2().');
return this.createUserV2({ email, name, age });
}
// 🟢 提供新的、更好的版本。
createUserV2(userData) { /* ... 新的實作邏輯 */ }
// 舊的 API 保持原樣。
getUser(id) { /* ... 原本的實作 */ }
// 🟢 為新功能新增一個全新的 API。
getUserWithStatus(id) {
const user = this.users.get(id);
return user ? { data: user, status: 'success' } : null;
}
}
// 🔴 修改現有 API
class Database {
query(sql) {
// 改變了回傳格式
return { data: this.executeQuery(sql), metadata: {} };
}
}
// 🟢 新增 API,保持舊的
class Database {
query(sql) {
// 保持原有行為
return this.executeQuery(sql);
}
queryWithMetadata(sql) {
// 新的 API
return { data: this.executeQuery(sql), metadata: {} };
}
}
2. 預設值處理
如果你非得給函式加個參數,就必須提供一個預設值,讓舊的呼叫程式碼不會出錯。
// 🔴 強制要求新參數
function processData(data, newRequiredParam) {
// 現有程式碼會因為缺少參數而崩潰
}
// 🟢 提供預設值
function processData(data, newParam = null) {
// 現有程式碼繼續工作
}
3. 漸進式棄用
正確的做法是將其標記為「棄用 (deprecated)」,給出警告,並提供替代方案。
給時間去遷移,而不是在半夜讓人去修程式碼。
class LegacyAPI {
constructor() {
this.deprecationWarnings = new Set();
}
// 標記為棄用,但不立即移除
deprecatedMethod() {
if (!this.deprecationWarnings.has('deprecatedMethod')) {
console.warn('deprecatedMethod is deprecated. Use newMethod instead.');
this.deprecationWarnings.add('deprecatedMethod');
}
// 仍然工作,但使用新的實作
return this.newMethod();
}
newMethod() {
// 新的實作
return 'new implementation';
}
}
在進行任何 API 變更前,問自己: