我審了 500+ 個 PR:初級開發最容易翻車的十個坑
我花了兩年時間泡在代碼審查里,過去 18 個月單是公司里初級同學的 PR 就看了 500 多個。訓練營來的、科班的、半路轉行的——背景千奇百怪,坑卻驚人一致。
這不是漏分號、寫錯語法的那點小事,而是資深開發真正關心、但教程很少教的那套“工程腦回路”。 這些模式,決定了你是被提拔還是被勸退。不粉飾,不發參與獎。
1.你在解“錯題”:炫技 ≠ 交付
你做了什么:工單寫著“給用戶表加排序”,你上來就手擼一個排序算法,好在 review 里秀肌肉。
// Junior's PR
function bubbleSort(arr) {
// 47 行冒泡,還自豪地寫上“已優化早退出!”
}
users.sort(bubbleSort);為什么錯:JS 早有 .sort()。你這套:
- 比原生更慢
- 沒測試
- 難維護
- 不存在的問題硬要解決
應該這樣:
users.sort((a, b) => a.name.localeCompare(b.name));殘酷真相:資深不在乎你會不會背 CS 課本,他們在乎能否穩定上線。 有標準庫就用,你的工作是交付價值,不是證明你會冒泡。真實后果:我拒了 30+ 次“重復造輪子”。每次作者都爭辯,每次都輸。
2.你的 PR 描述寫成了“修了個 Bug”
你寫的:
- Title: Fixed the bug
- Description: Fixed it
資深需要知道:
- Bug 是什么?怎么造成的?你怎么修的?
- 加了哪些測試?邊界在哪?會不會影響別處?
好示例:
Title: Fix NPE on user profile pageIssue: 無頭像用戶打開個人頁觸發 NPERoot cause: getProfilePicture() 返回 null,但代碼直接 .getUrl()Fix: 訪問前判空;為空展示默認頭像Testing:
- 無頭像用戶 ?
- 有頭像用戶 ?
- 新增空值單測 ?Risk: 無;默認頭像組件已有資源
殘酷真相:兩段話講不清楚,你大概率還沒懂透。半年后線上炸鍋,沒人看得懂你當時在想啥。真實后果:描述好的 PR 審批快 3 倍;糟糕描述會被掛在那里、被私聊追問“這到底干嘛的”。
3.你像按行數拿工資:嵌套越多,越沒人想看
你寫的:
public class UserValidator {
public boolean validateUser(User user) {
if (user != null) {
if (user.getEmail() != null) {
if (!user.getEmail().isEmpty()) {
if (user.getEmail().contains("@")) {
if (user.getName() != null) {
if (!user.getName().isEmpty()) {
if (user.getAge() >= 18) {
return true;
} }}}}}}
return false;
}
}資深會怎么寫:
public class UserValidator {
public boolean validateUser(User user) {
if (user == null) return false;
if (user.getEmail() == null || user.getEmail().isEmpty()) return false;
if (!user.getEmail().contains("@")) return false;
if (user.getName() == null || user.getName().isEmpty()) return false;
return user.getAge() >= 18;
}
}殘酷真相:每一層嵌套都是認知負擔。要我腦補 7 層 if 才讀懂?我寧可自己重寫不留你名字。守護式寫法(Guard Clauses)不是裝飾,是常識。真實后果:我退回過 40+ 個 PR,只寫一句:減少嵌套。能領會的在成長;爭辯“邏輯一樣”的,還在原地。
4.你不敢刪代碼:注釋掉 ≠ 安全
你做的:發現廢函數,不刪;注釋并寫:// might need later
// OLD VERSION - DON'T USE
// function calculateDiscount(price) { return price * 0.1; }
// NEW VERSION - USE THIS ONE
function calculateDiscount(price, userTier) {
return userTier === 'premium' ? price * 0.2 : price * 0.1;
}結果:閱讀成本翻倍;三個月后總有人把舊的解注釋,然后線崩。正確做法:刪。歷史在 Git 里,需要再找。殘酷真相:被注釋的代碼就是壞味道,暴露你對版本控制不信任/不理解。真實后果:我拒過只因“注釋代碼”的 PR。敢刪的同學晉升更快——說明理解系統。
5.命名垃圾:你寫給編譯器的,不是寫給人看的
你寫的:
def process(d):
r = []
for i in d:
x = i['val']
if x > 0:
r.append(x * 2)
return r應該這樣:
def double_positive_values(data_points):
doubled_values = []
for data_point in data_points:
value = data_point['value']
if value > 0:
doubled_values.append(value * 2)
return doubled_values殘酷真相:省幾個字母是在優化打字,不是在優化協作。代碼寫一次,要被讀幾百次。例外:循環計數 i/j/k、e/req/res 這類約定俗成可以;其余請用真名。真實后果:爛命名讓 review 時間翻倍;很多資深直接回一句“improve naming”就關標簽走人。
6.只測“幸福路徑”:線上可不講情面
你測的:
test('should return user profile', async () => {
const profile = await getUserProfile(123);
expect(profile.name).toBe('John');
});你沒測的:
- 用戶不存在?ID 為 null?DB 超時?API 格式錯?
資深會測:
test('handles non-existent user', async () => {
expect(await getUserProfile(999999)).toBeNull();
});
test('handles null id', async () => {
await expect(getUserProfile(null)).rejects.toThrow();
});
test('handles db timeout', async () => {
mockDb.timeout();
await expect(getUserProfile(123)).rejects.toThrow('Timeout');
});殘酷真相:只測順路 = 等于沒測。線上的故障都卡在邊界。真實后果:我見過因為未測失敗路徑導致事故而被 PIP 的——兩次。
7.你“設計模式貨運崇拜”:沒問題也硬上模式
你做的:讀了工廠模式,從此萬物皆工廠。
public class UserFactory {
public User createUser(String name, String email) {
return new User(name, email);
}
}
// Usage
User user = userFactory.createUser("John", "john@example.com");恭喜,你多寫 3 行,做了 new User(name, email) 的事。殘酷真相:模式用來解問題,不是用來秀學識。工廠適用:構造復雜;運行時挑子類;你在寫庫/框架。不適用:包一層 new;想在 review 顯聰明;讀完一本書要滿場用。真實后果:我見過代碼庫被“學了模式就遍地撒”的行為毀掉。業務第一,不是“書本體操”。
8.你無視 Code Review:最快自斷前程的方式
我留了 5 條評論:
- 這里要判空
- 這段抽函數
- 給邊界加測試
- 注釋里有拼寫錯
- 刪除調試日志
你做的:只改 #4,然后把其余標為“已解決”。為什么致命:Review 不是討價還價。我是在防 Bug、提可維護、帶你升級。你無視反饋,就是在說:
- 你不尊重我的時間
- 你不在乎代碼質量
- 你缺乏可教性
殘酷真相:忽視反饋 = 直接封頂。批你 PR 的人,常常也決定你能否晉升。正確做法:逐條回應;不同意就拿技術理由;不懂就發問;修改了就說明。真實后果:持續無視反饋的人,PIP 跑不了。沒人愿意和不會被指導的人協作。
9.你復制粘貼但不理解:這是職業自殺
你做的:Stack Overflow 找到答案,整個復制粘貼,完事。
# From Stack Overflow - works!
def convert_to_datetime(date_str):
try:
return datetime.strptime(date_str, '%Y-%m-%d')
except:
try:
return datetime.strptime(date_str, '%d/%m/%Y')
except:
try:
return datetime.strptime(date_str, '%m-%d-%Y')
except:
return None我問:“為啥要試三種格式?” 你答不上來。殘酷真相:復制代碼沒問題;不懂就用,遲早炸你。出事你修不了,因為你不懂它如何工作。正確做法:讀懂它做什么/為什么能做;按你的場景改;給不明顯的地方加注釋;知道怎么調試。真實后果:我一眼能看出“堆棧味”。解釋不清?拒絕合并。
10.“在我機器上能跑” ≠ 完成
你說:本地測過,OK!線上現實:
- 環境變量不同
- DB 版本不同
- 網絡慢/抖動
- 并發壓力高
- 各種邊界亂飛
殘酷真相:“works on my machine” 不是勛章,是警示燈。該自查:真實數據量/慢網/并發/慢 DB/邊界處理……真實后果:我回滾過“本地好好的”代碼。這種 PR 的作者,那年沒升職。
沒人說破的分水嶺
不成熟的想法:
- “我能跑,就發”
- “Review 就是挑刺”
- “報了再修”
- “資深就是難伺候”
成熟的想法:
- “解決了業務問題嗎?”
- “下一個人能看懂嗎?”
- “線上可能會怎么壞掉?”
- “反饋里有我能升級的點嗎?”
區別不在天賦,不在你刷了多少 LeetCode。在于你是否真的在乎。
給你的行動清單(下一次 PR 就用)
- 寫像樣的描述(What / Why / How / Tested)
- 邊界測試要有,不只“順路”
- 用守護式替代深嵌套
- 刪死代碼,別注釋
- 像給人讀一樣命名
- 請求他人之前,先自審一遍
- 逐條回應 review 評論
職業升級版:
- 別再證明你聰明,證明你可靠
- 別為代碼辯護,從反饋學習
- 少搞花招,多做可維護
殘酷現實:沒人關心你的學位、訓練營、刷題分。 大家只關心:你能否交付穩定的代碼、好好接反饋、別把人半夜叫醒。 這就是工作。
最后一刀
我審過 500+ PR。如今已經是資深的那些人?犯過一次,聽了反饋,不再重犯。 三年還在原地的人?同一個錯誤復讀 50 次,永遠怪別人。你選。
如果這話讓你不爽——很好。說明你看見了自己。 訂閱吧。這里只講不舒服的真相。
PS:我當年也犯過這些。唯一的不同是——有人指出來時,我學了。






























