代碼審查的殘酷真相:為什么高級開發者從不糾結命名和格式?
代碼審查的殘酷真相:為什么高級開發者從不糾結命名和格式?
看完這篇,你會發現自己過去的Code Review可能都是在浪費時間。
開篇:一個扎心的場景
你花了兩天時間精心實現一個功能,提交PR后滿懷期待等待Review。結果收到20條評論:
- "這個變量名建議改成userData"
- "這里用三元運算符更簡潔"
- "建議把這段提取成獨立函數"
你逐條修改,再次提交。然后代碼上線了,生產環境炸了——因為并發場景下的race condition沒人發現。
這就是大部分團隊Code Review的現狀:把時間花在不重要的細節上,真正致命的問題卻視而不見。
作為一個在前端摸爬滾打多年的老兵,我見過太多這樣的場景。今天我想聊聊,真正的高級開發者是怎么做Code Review的——以及為什么他們的方式和你想的完全不同。
第一個顛覆認知:先問"該不該存在",別管"寫得好不好"
大部分人的審查順序是錯的
打開一個PR,99%的人第一反應是看代碼實現:
- 變量命名規范嗎?
- 函數拆分合理嗎?
- 有沒有遵循最佳實踐?
但高級開發者的第一個問題是:這段代碼應該存在嗎?
我見過一個經典案例。某同學提交了一個完美的虛擬滾動實現,處理了邊界情況,寫了完整測試,代碼優雅得讓人想點贊:
// PR標題: 為表格添加虛擬滾動優化性能
function VirtualizedTable({ data, rowHeight = 50 }) {
const [scrollTop, setScrollTop] = useState(0);
const containerHeight = 600;
// 計算可見區域
const visibleStart = Math.floor(scrollTop / rowHeight);
const visibleEnd = Math.ceil((scrollTop + containerHeight) / rowHeight);
const visibleData = data.slice(visibleStart, visibleEnd);
return (
<div
style={{ height: containerHeight, overflow: 'auto' }}
onScroll={e => setScrollTop(e.target.scrollTop)}
>
<div style={{ height: data.length * rowHeight }}>
<div style={{ transform: `translateY(${visibleStart * rowHeight}px)` }}>
{visibleData.map(row => <TableRow key={row.id} data={row} />)}
</div>
</div>
</div>
);
}代碼挑不出毛病。但問題在于:為什么要一次性加載10000條數據?后端API明明支持分頁,為什么不用?
虛擬滾動解決的是"如何高效渲染大量DOM"的問題。但真正的問題是"為什么要在前端渲染這么多數據"。這就像你家著火了,你選擇買個更好的滅火器,而不是找出起火原因。
真正的Code Review應該質疑解決方案本身,而不是解決方案的實現質量。
這需要跳出代碼看問題:
- 理解業務背景和技術架構
- 思考有沒有更簡單的方案
- 判斷這個PR是在解決癥狀還是病根
這種思維方式,才是高級開發者和普通開發者的分水嶺。
第二個顛覆認知:懂得什么不重要,比懂得什么重要更重要
注意力是有限資源,別浪費在無關緊要的地方
我有個controversial的觀點:大部分代碼細節根本不重要。
- 變量叫
userData還是user? 無所謂 - 用
if-else還是三元運算符? 無所謂 - 10個元素用
.map()還是for循環? 也無所謂
看到這里可能有人要跳起來:"代碼質量很重要!細節決定成敗!"
我沒說代碼質量不重要。我是說:注意力是稀缺資源,應該花在刀刃上。
這里有個建筑學的概念很有啟發——承重墻和非承重墻。承重墻支撐整個結構,拆了房子就塌;非承重墻只是分隔空間,改了無傷大雅。
代碼也一樣。有些決策是"承重"的:
- 影響性能的算法選擇
- 關系到安全的權限設計
- 決定可維護性的架構方案
這些值得死磕。但更多的是"非承重"的偏好:
// 這三種寫法值得你留10條評論嗎?
// 寫法1
const isValid = data.name && data.email;
// 寫法2
const isValid = Boolean(data.name && data.email);
// 寫法3
const hasName = !!data.name;
const hasEmail = !!data.email;
const isValid = hasName && hasEmail;寫法3可讀性可能好一點點。但值得讓作者改代碼、推新commit、延遲功能上線嗎?
高級開發者知道什么時候該閉嘴。 不是因為妥協代碼質量,而是因為他們清楚:在無關緊要的問題上消耗團隊精力,就沒時間關注真正重要的事了。
第三個核心技能:預見什么會炸
線上事故都是可以預防的,如果你知道該看哪里
經歷過幾次生產事故后,你就會形成一種直覺——知道哪些代碼模式容易出問題。
最容易被忽視的就是錯誤處理。 不是"有沒有try-catch",而是"出錯了會怎樣"?
async function getUserProfile(userId) {
const response = await fetch(`/api/users/${userId}`);
const data = await response.json();
return data;
}這代碼開發環境跑得好好的。但上線后:
- API返回500怎么辦?
- 響應是HTML錯誤頁而不是JSON怎么辦?
- 網絡超時了怎么辦?
- 用戶看到的是什么?白屏?報錯?還是無限loading?
初級Review可能會說:"加個try-catch吧"。
高級Review會問:"用戶體驗應該是什么?顯示錯誤提示?重試?用緩存數據?哪種失敗模式對用戶最友好?"
這才是關鍵差異——不是檢查代碼在理想情況下能不能跑,而是思考在最糟糕的情況下用戶會遇到什么。
所有系統都會失敗。API會掛,網絡會斷,數據庫會超時,第三方服務會返回垃圾數據,用戶會做你意想不到的操作。真正的代碼審查,是在代碼進入生產前,就預見到這些失敗場景。
第四個維度:關注PR里沒有的東西
最隱蔽的bug往往藏在"沒寫的代碼"里
優秀的Code Review不只看寫了什么,更要問:沒寫什么?
function useUserData(userId) {
const [data, setData] = useState(null);
useEffect(() => {
fetchUser(userId).then(setData);
}, [userId]);
return data;
}這代碼看起來沒問題。但高級開發者會問:
"如果userId變化了,但上一個請求還沒返回呢?組件會先設置舊數據,再被新數據覆蓋嗎?"
這就是經典的race condition。用戶快速切換tab,頁面顯示錯亂,沒人知道為什么。
修復其實很簡單,但前提是你要想到去找這個問題:
function useUserData(userId) {
const [data, setData] = useState(null);
useEffect(() => {
let cancelled = false;
fetchUser(userId).then(user => {
if (!cancelled) setData(user);
});
return() => { cancelled = true };
}, [userId]);
return data;
}這類問題需要你在腦子里"運行"代碼:
- 組件卸載時會發生什么?
- 函數同時執行多次會怎樣?
- 異步操作完成前用戶跳轉了呢?
線性閱讀代碼是不夠的,你需要在多個維度上模擬執行。 這是經驗積累的結果,也是高級開發者最值錢的技能之一。
第五個判斷標準:知道什么時候該Block
不是所有問題都值得阻止合并
這可能是Code Review里最微妙的技能:判斷什么問題必須現在解決,什么可以后續優化。
必須Block的:
- ? 會導致數據丟失的問題
- ? 有安全風險的代碼
- ? 影響用戶體驗的性能問題
- ? 會造成內存泄漏的邏輯
- ? 有race condition的并發處理
- ? 未來難以修改的架構決策
可以放行的:
- ? 可讀性改進
- ? 不常執行路徑的小性能問題
- ? 非核心功能的邊界測試
- ? 代碼風格偏好
對比這兩段代碼:
// 這個必須Block - 會留下一堆臟數據
asyncfunction deleteUser(userId) {
await db.users.delete(userId);
// 等等,用戶的帖子、評論、關系怎么辦?
// 刪不干凈會導致數據一致性問題
}
// 這個可以放行 - 只是風格問題
function getUserDisplayName(user) {
return user.firstName + ' ' + user.lastName;
// 可以用模板字符串,但這樣也能用
}第一個會在生產環境造成數據混亂,第二個只是寫法偏好。
判斷標準很簡單:這會導致生產事故嗎?會讓代碼庫顯著難維護嗎? 如果答案是否,建議改進但別阻止合并。
第六個溝通技巧:解釋"為什么",而不只是"怎么做"
好的Review是在教育,而不只是審查
對比這兩條Review評論:
弱評論: "這里應該用useMemo"
強評論: "這個計算每次渲染都會跑,處理1000+項目。用useMemo包裹,依賴項設為[items],可以避免其他狀態變化時重復計算。我本地profiling顯示渲染時間從80ms降到5ms。"
差異在哪?
第二條評論不只說了做什么,還解釋了:
- 問題是什么: 每次渲染都重復計算
- 為什么重要: 處理大量數據,影響性能
- 怎么解決: 用useMemo緩存結果
- 效果如何: 實際性能提升數據
當你解釋"為什么"時,你不是在給指令,而是在傳授思維方式。 開發者學到的不是規則,而是原則。下次遇到類似場景,他們就能自己判斷該怎么做。
這在反對某種方案時尤其重要:
? "這個方法不對"? "這個方法在下個sprint加入多用戶功能時會出問題,因為它假設只有一個活躍用戶。我們需要重構成支持多用戶的結構,現在改比上線后改容易得多。"
第二種方式提供了上下文,讓開發者理解更大的圖景。這樣的反饋引導理解,而不只是強制服從。
第七個長遠視角:為未來的維護者寫代碼
今天寫的代碼,明天就會變成別人的噩夢
代碼的生命周期遠比你想象的長。今天你花2小時寫的功能,可能半年后需要改動。那時候:
- 你可能已經忘了細節
- 可能是新同事在改
- 可能是凌晨3點線上出bug你在查
高級開發者審查代碼時,想的是:"這段代碼能不能脫離作者獨立存在?"
// 缺少上下文 - 半年后沒人知道0.88是什么
function calculatePrice(item) {
return item.basePrice * 0.88;
}
// 有清晰上下文 - 未來維護者能理解
function calculatePrice(item) {
// 扣除12%平臺手續費 (0.88 = 1 - 0.12)
// 詳見定價文檔: https://docs.company.com/pricing
return item.basePrice * 0.88;
}魔法數字0.88讓人一頭霧水。注釋解釋了它的含義和出處,未來的開發者不用到處找文檔或問人。
同樣重要的是識別"技術上正確但讓人困惑"的代碼:
// 能跑,但看著累
const isValid = !!data && !!data.name && data.name.length > 0;
// 同樣邏輯,意圖清晰
const hasValidName = data?.name && data.name.length > 0;第二種寫法更容易理解。有人快速瀏覽代碼時,不用解析布爾邏輯就能明白在檢查什么。
可維護性不是奢侈品,而是必需品。 每次Review都是在為未來投資,減少技術債務,讓代碼庫保持健康。
第八個判斷力:知道什么時候該當面聊
有些問題靠評論解決不了
當一個PR方向根本就錯了,你寫再多評論也沒用:
- 架構選擇有根本問題
- 對需求理解有偏差
- 技術方案從起點就跑偏
這時候別寫長篇大論的評論,直接拉個會聊。
"嘿,我覺得咱們應該聊一下這個PR。我對整體方案有些擔憂,實時討論會比來回留言快。"
當面討論的好處:
- 作者避免走彎路浪費時間
- 審查者不用打字打到手抽筋
- 雙方能快速達成共識
- 可以即時澄清誤解
Code Review評論適合處理具體的、局部的修改建議。對于需要重新思考整體方案的情況,它就是錯誤的溝通方式。
知道什么時候切換溝通渠道,這是高級開發者的關鍵軟技能。
第九個平衡術:信任與教導之間的微妙關系
Code Review最難的不是技術,是人
做Code Review最難的部分不是技術判斷,而是社交平衡。
你需要:
- 信任作者經過了思考,有他們的理由
- 懷疑他們可能沒看到某些坑
- 教導他們不知道的模式和風險
- 尊重他們的決策,即使和你的不同
這種平衡很難把握。太嚴格會打擊積極性,太寬松會放過問題。
一個技巧是從好奇開始,而不是糾正:
? "你應該用方案Y而不是X"? "我好奇為什么選擇方案X而不是Y? 我之前遇到過Y在這種場景下更合適的情況"
第二種說法:
- 假設作者有理由(也許他們試過Y不行)
- 表達了你的經驗,但不強加觀點
- 開啟了對話而不是命令
- 也給你機會學習(也許他們知道你不知道的事)
同時,該教的時候別藏著掖著。初級開發者不知道自己不知道什么。你發現的模式和坑,如果不解釋清楚,他們下次還會犯。
這種社交技能——在信任、教導和共情之間找平衡——才是區分高級審查者和一般審查者的關鍵。
最后的底線:完美不是目標,出貨才是
Code Review不是追求完美,而是在保證質量的前提下讓代碼盡快上線。
每條評論都有成本:
- 作者要花時間修改
- 功能上線被延遲
- 可能產生團隊摩擦
所以問題不是"什么地方可以更好?"——因為代碼永遠可以更好。
真正的問題是:"什么必須在上線前改變?"
這個列表要短得多:
- ? 會造成生產事故的問題
- ? 會讓代碼庫難以維護的決策
- ? 有明顯安全隱患的實現
其他的:
- ? 作為下次的改進建議
- ? 寫在文檔里給未來參考
- ? 作為個人偏好就別提了
最好的Code Review是那些發現了微妙的race condition或架構問題,避免了幾周的痛苦的Review。 不是那些糾結變量命名和格式的Review。
知道區別。審查真正重要的東西。
寫在最后
如果你讀到這里,我希望你能重新思考Code Review的目的。
它不是展示你知道多少規則的舞臺,不是刷存在感的機會,更不是追求完美代碼的工具。
Code Review的真正價值在于:在代碼進入生產前,用經驗和判斷力篩選出真正重要的問題。
下次做Review時,問問自己:
- 這個問題會導致生產事故嗎?
- 這個建議會顯著提升代碼質量嗎?
- 這條評論值得占用團隊的時間嗎?
如果答案是否,就讓它過。把精力留給真正重要的戰斗。

























