本文是 Uber 的客戶端工程師團隊是如何開發最新版本司機端的系列文章中的第二篇,代號 Carbon ,是咱們拼車業務的一個核心組件。除了其餘新功能以外,司機端 APP 還爲超過 300萬 司機提供收入,引導他們掙錢。2017年咱們結合司機的反饋開始對司機端進行從新設計,並在2018年9月份啓動了該項目。java
從新編寫一個APP會引起關於新架構和新設計相關的許多問題。迄今爲止,大多數開發者只關注應用程序如何正常工做。雖然以用戶爲中心的設計理念已經成爲軟件開發中的一些主流,可是辨別真實有效的用戶需求並不容易。react
一旦決定 重構Uber司機端, 咱們必須以普遍多樣的用戶羣裏爲基準,尋找如何設計更適用的工做流和保留最適用的產品功能。 咱們收集全世界各個城市的Uber司機的反饋,將這些反饋做爲咱們用戶體驗的重要初衷。android
與此同時,咱們也必須考慮到,成百數千的工程師們,他們須要重構APP的同時也要功能迭代。一個通過深思熟慮的APP架構須要幫助工程師,在保證可靠性的同時高效、快速的工做。ios
幸運的是,Carbon 在知足這些服務時並無發生衝突,這些服務包括更實用的應用流程,改善司機體驗功能,爲開發者提供靈活穩定的架構,事實證實,它們結合的很是好。git
本篇文章,闡述了咱們是爲了適應新司機端(代碼Carbon)如何提出核心需求,而且討論使用RIBs架構和插件設計,來知足司機端應用邏輯。github
自2013年發版以來,Uber司機端積累了須要功能。在將來的四年以後Uber司機端將成爲司機的核心工具。隨着應用程序的日益複雜,Uber確實作到組織化。上百個功能由橫跨公司40多個不一樣子團隊開發和維護。截止2017年1月,Android司機端已經擁有由近200個工程師編寫出428,685行代碼。iOS司機端由200多工程師貢獻720,273行代碼。更爲重要的是,咱們的APP安裝在超過300萬的設備中,天天超過100個國家中的100萬個司機使用。swift
對於Carbon的成功,咱們知道咱們須要及時更新全部現有功能(已經一些新功能)的同時,也須要並行更新架構。設計模式
產品開發須要時刻記住用戶。爲此,咱們想和Uber司機一塊兒建立構建咱們的應用。在開發的最初階段,咱們在用戶調研上投入大量資金。在11個國家,12個城市中採訪了500位司機。網絡
這些訪談對象幫咱們設計新司機端的用戶體驗,肯定最重要的功能。可是,除非司機在在真實條件下在實際道路中使用APP,不然沒法獲取完整的用戶體驗。咱們須要一種方法,它能夠允收集用戶反饋,快遞迭代並每週發佈新版本。架構
圖一:在測試期間,爲APP使用最初設計。
最難而且最重要的挑戰是,在實際測試中的穩定性。當接觸一個新APP,在Alpha和Beta測試階段能夠接受錯誤和問題。Uber司機端在Beta階段,真實的司機已經能夠在實際道路中可使用它掙錢了。實現可靠性是Carbon的關鍵目標。因此,進入首次測試階段時,咱們必須確保Carbon像現有程序同樣可靠。
闡述瞭如今工程的限制後,咱們採用了分段性方法,將項目分爲四個階段。每一個階段的目標是開啓下個階段的開發。
咱們擁有一個通用模板,來描述咱們的APP必須包含哪些因素。這個模板包括網絡庫,存儲庫,ReactiveX,分析追蹤,崩潰上報和咱們的自研的應用架構Ribs。利用這個模板,咱們構建了具有初始化架構的程序,包括存儲能力,網絡,崩潰上報和基礎組件。然兒,在這個階段。程序缺乏司機業務功能,這僅僅是咱們構建特性的框架。
使用RIBs框架的一個好處是,它如何將業務邏輯做爲應用程序體系結構的核心。在Carbon階段,一個好的起點是爲司機定義高標準的用戶狀態。這將致使咱們爲RIBs定義一些基礎:
咱們利用RIBs樹圖,以下圖2所示,說明應用架構。這個簡單的樹形圖展現了,如何使用RIBs組件相互關聯。
圖二:RIBs樹圖向咱們展現了組件關聯的可視化方法。
在關注用戶階段,咱們能夠將UI分離出來。這個方法能夠促使從司機中吸收持續反饋建議,並將建議吸納到設計中。同時促使咱們維護用戶程序的基本。
在一些基礎上,咱們重點轉移到協做中。在第三階段,咱們的目標是擴大規模,使大約40個團隊在同一個APP中可靠無縫的並行工做。基於用戶的反饋和設計、產品團隊的協做,咱們定義出更詳細的RIBs組件。
在合併了框架以後,RIBS樹結構變大了,以下面的圖三所示。
圖三: 按照咱們的定義在RIBs樹中增長層級和新的功能框架。
在咱們細分以前, 咱們清晰下在使用的RIBs架構中的一些概念。
這是一個對象,它直接關聯到RIBs生命週期,具備啓動/中止生命週期功能方法。換句話說,添加到RIBs的工做者從RIB鏈接時啓動,RIB分離時中止。工做者確保交互(一個RIB的業務邏輯組件單元)不會太大,而且容許更好的分離關注點。(在咱們的倉庫中能夠找到 Android 和 iOS)
插件是一種設計模式,插件容許咱們用靈活的方式進行特徵標記。(進一步瞭解Uber若是利用插件 上一篇文章)。首先咱們爲集成的核心代碼定義一個公用的API,而後開發者能夠實現本身的API實例。這是由於他們瞭解代碼由遵照這個架構的特徵標記隱形保護。把每一個插件點想象成微服務體系中一個服務,而後插件工廠做爲該服務的消費者,這樣將使得插件和API或者他們之間的契約類似。
結合RIBs,插件和工做者,能夠定義出架構中的核心和非核心部分。核心組件是必須的組件,而且不能被標記成禁用。另外一方面,若是非核心組件引入了重大問題或者致使迴歸,能夠被禁用。在上面的圖四種,Map和MyHub是非核心組件,他們能夠在不關閉應用程序功能的基礎上被禁用。
進一步查看圖 4 中 RIBs 樹圖的細節功能,咱們就能看到:如何使用核心 RIBs,Plugins,Workers, 以 worker/plugin 模式去實現 Agenda 的所有功能。Agenda 功能暴露了兩個不一樣的插件點(plugin points): Agenda Worker 和 Agenda Section。 使用 Worker plugin point 集成非用戶體驗功能,Section plugin point 去擴展用戶體驗功能。
圖四:仔細觀察RIBs樹的一部分,能夠發現集成的多個領域,它容許在靈活添加特性,也容許並行開發。
使用這個設計模式,咱們能夠理解許多領域APP。例如一些工程師構建登陸和註冊界面,其餘工程師重點開發非核心RIBs的地圖架構。
第四階段,其餘團隊加入咱們開放了Carbon的開發。因爲想要確保每一個功能彼此獨立,咱們建立到了插件框架,所以特性合併是一個相對順暢的過程。若有必要,一些小RIBs上升爲核心組件,可是咱們大部分代碼仍然包含在插件中,因此咱們的架構中一些部分是可選性的(咱們計劃在後續章節中討論一些使人興奮的新特性)。
圖五: 如動畫所示,RIBs生命週期與他們的各自單元相關
軟件工程的架構是一個一般是在犧牲別的資源條件下去優化團隊更爲重要的一些指標。雖然咱們的方法容許咱們優化可靠性、可擴展性和模塊化,可是其餘方面咱們必須妥協。在一個嚴謹的流程中,咱們只容許少許的組件是核心組件。爲了維護核心組件的質量,咱們擁有一個內部審查團隊,他們審查修改核心代碼中每一個代碼。這個流程要求一些工程師投入一些時間去作核心代碼審查,這也減慢了其餘工程師的提交頻率。
大多數應用開發者熟悉MVP或MVC模型。和他們相比,RIBs好像更冗長。RIBs有更多的組件,須要更多的前期規劃。較小的軟件開發團隊也許不須要採用相似的流程。以前咱們已經作完一個重構重寫咱們的乘客端APP,所以我對如何構建Carbon更清晰,咱們將咱們的學習總結以下:
按規模工做: 在Uber, 規模 既是最高限制也是最有價值資源. 300萬司機在使用咱們的APP,因此咱們不能讓他們失望。 同時, Uber做爲一個技術組件公司,咱們已經發展到數百萬工程師一塊兒構建應用程序。規模 是咱們從規劃到推廣決策的關鍵。
RIBs to the rescue: Carbon經過使用RIBs和插件,咱們在如下方面得到成績:
共同構建: 若是沒有司機的幫助,以及他們在用戶調研和測試階段給咱們的建議,咱們不可能作到這一點。