Overreacted

React 團隊的技術準則

2019年12月25日 • ☕️ 6 min read

Translated by readers into: Español繁體中文

Read the originalImprove this translationView all translated posts

我很幸運地在 React 團隊的這段時間內,能看見 JordanSebastianSophie 和其他團隊成員如何解決問題。在這篇文章中,我會把從他們身上所學到的,濃縮為一些較高層級的技術準則。這些準則未必詳盡,它們只是我個人對 React 團隊運作的觀察和整理——其他團隊成員或許會有不同的觀點。

UI 優先於 API

當我們將抽象概念大規模應用於現實中時,難免會有古怪之處。這些地方該如何在 UI 上清楚呈現?你能看出一個應用程式中蘊含的特定抽象概念嗎?

抽象概念對使用者體驗有直接的影響——它能創造好的、延續性的體驗或限制某些操作。這使得我們設計 API 時,並不會從抽象概念開始發想。我們會從想呈現的使用者體驗來著手,之後再回歸抽象概念。

有時當我們回到了抽象概念,會發現必須砍掉重練,才能打造正確的使用者體驗。如果我們從 API 下手,我們就無法察覺到這點。所以我們先考慮 UI,再考慮 API。

吸收複雜性

簡化 React 內部程式碼的並不是我們的目標。為了讓產品開發者可以使用 React 寫出更平易近人、易於改動的程式碼,我們樂於複雜化 React 內部的實作。

我們想讓產品開發流程能夠更加分散權責、合作無間,很多時候,這代表我們必須把繁瑣的部分封裝在 React 內部。React 不能被切分成小規模、鬆散耦合的模組,因為這樣便無法做到調節和整合的工作,而 React 的使命是成為協調的角色。

透過提升抽象層級,使得產品開發者更有力,受益於可預測的 React 完備系統。這意味著我們推出的每個(第 N + 1 個)新功能都必須相容於(N 個)現存的功能,設計和實作 React 的新功能並不容易,因此我們的核心功能沒有收到太多開源的貢獻。

我們吸收了複雜的部分,防止它們去污染產品的程式碼。

從 Hacks 到 Idioms

每個 API 都不免有一些侷限性。有時,這些限制會妨礙我們打造良好的使用者體驗。為此,我們提供一些後路(escape hatches)以供需要時使用。

非正規化的實作(hacks)不是長久之計,因為它很脆弱。產品開發者必須決定他們是否維護、支援這些 hacks,或者是移除它們而犧牲使用者體驗。通常大多數人會犧牲使用者體驗,不然這些 hacks 也有可能會阻礙使用者體驗的優化。

我們必須讓產品開發者使用這些後路,並觀察他們都如何實作。提供這類實作一個慣用的解決方法(idiomatic solution)是我們的最終任務,目的是達成更好的使用者體驗,這部分有時會花上數年的時間。我們傾向於使用有彈性的 hack 來確立一個還不完整的慣用法(a poor idiom)。

實現局部開發

你無法在程式編輯器裡做太多事情。你可以增加幾行、移除幾行,或複製貼上,但許多抽象概念讓這些基本操作變得困難。

舉例來說,MVC 架構讓刪除一些 render 的結果變得不可靠。這是因為即使你移除了 children 的 method,parents 仍然有可能執行它。相較之下,React 的優勢在於:你通常能安全地刪除 render tree 內的某些程式碼。

在設計 API 時,我們會假設使用它的人只熟悉他們會用到的局部程式碼的相關知識。如果預期產生的影響只會發生在這局部的程式碼,我們將會避免意料之外的結果。意思是通常會假設增加程式碼是安全的;移除和修改程式碼時,應該清楚指出這些改動會連帶影響、應該被考慮到的部分。不應該假設改動單一檔案的人,擁有整個 codebase 的知識。

如果一個改動不安全,我們希望開發者能夠儘早發現這個改動所帶來的影響。雖然可以使用警告、型別檢查和開發者工具來幫忙,但它們也都受限於 API 的設計。如果 API 不夠局部性,局部開發就不可能實現。舉例來說,findDOMNode() 就不是一個好的 API,因為它需要全面的瞭解。

漸進的複雜度

有些框架會選擇在開發的路上分出叉路,提供兩種路線:簡單的方法或強大、完整的方法。簡單的方法容易學習,但你終究會走到它的極限,這時你必須砍掉重練,重新使用另一個方法來實作。

我們認為實作一個複雜的東西,和實作一個簡單的東西,在結構上是沒有差太多的。我們並不會為簡單的狀況提供不同的寫法,因為這樣做會使開發中出現叉路。如果我們認為開發者在開發的過程中會逐漸想要完整的開發工具,我們願意犧牲低門檻來達成這件事。

有時「簡單」和「強大、完整」代表兩種不同的框架,那你仍需要換框架重寫,最好能避免這種事。以 React 為例,伺服器端的 render 這類的優化會需要付出額外的努力,但你不需要完全地重寫。

損害控制

由上而下的解決方式很重要,例如編列程式碼預算。然而長時間下來,我們的標準會下降,功能會在死線前被完成,也有可能不繼續維護產品。我們無法期待所有人都遵守遊戲規則,身為協調者的 React 必須控制損害。

如果有些 UI 相關的程式碼很慢,或達不到預期效能,我們必須使出渾身解數避免讓它拖累載入時間,避免它和其他 UI 互動時產生負面影響。最理想的狀況是:開發者只會為了他們使用到的功能付出開發成本,而產品使用者只需要載入他們會使用到的 UI。Concurrent Mode 便能達成這種理想狀況,可以使用 Time Slicing 和 Selective Hydration 等不同方式來實現。

Library 本身佔用的效能相對穩定,而應用程式的程式碼沒有底限。因此我們傾向於在應用程式之中控制損害,而不是去修正 library 內的程式碼。

相信理論

有時我們會知道某些做法是死路一條,它現在可以運作,但已經可以想像它的侷限:本質上無法靠它來實現想要的使用者體驗。一旦有機會我們會立刻從這類死胡同抽身。

我們不想卡在這裡,如果有某種做法在理論上更站得住腳,就算會花上好幾年,我們也樂於投注心力在其之上。在達成目標的過程中會遇到許多障礙,有時必須因現實考量而妥協,但我們相信若持續地克服這些困難,理論終究會獲勝。

你們團隊的準則是什麼?

以上是我觀察到的 React 團隊在工作時的基本原則,但我可能漏了很多。我也還沒提到 React 如何推出 API、團隊如何溝通未來的改動方向等等。或許下次可以來談談這些。

你們團隊也有一系列的工作準則嗎?我洗耳恭聽。

原始文章發表在這裡