本文寫(xiě)于2018年3月,距離今年發(fā)布已經(jīng)一年有余读存。
從2016年11月開(kāi)始为流,我接受了自研滴滴內(nèi)置導(dǎo)航的任務(wù),到今天让簿,歷時(shí)一年零四個(gè)月敬察。最初的目標(biāo),是替換當(dāng)時(shí)使用的騰訊SOSO地圖內(nèi)置導(dǎo)航尔当,實(shí)現(xiàn)滴滴地圖整個(gè)產(chǎn)品線的閉環(huán)莲祸。
當(dāng)時(shí)把這個(gè)任務(wù)交給我的時(shí)候,是機(jī)遇,也是挑戰(zhàn)锐帜。說(shuō)實(shí)話之前我是沒(méi)有導(dǎo)航語(yǔ)音播報(bào)的相關(guān)經(jīng)驗(yàn)的田盈,僅有的后臺(tái)服務(wù)的經(jīng)驗(yàn)是在高德做公交引擎。除了缺乏經(jīng)驗(yàn)缴阎,時(shí)間也比較緊允瞧,自上而下都有比較大的壓力(自研導(dǎo)航上線是地圖這邊的一個(gè)關(guān)鍵KPI)。
今天药蜻,回頭看看這款產(chǎn)品的研發(fā)結(jié)果瓷式,還是取得了預(yù)期的效果。內(nèi)置SDK全面替換為自研SDK语泽,司機(jī)口碑上升贸典,NPS上升,內(nèi)置導(dǎo)航使用率上升踱卵。調(diào)用自研的導(dǎo)航誘導(dǎo)服務(wù)廊驼,整個(gè)過(guò)程中沒(méi)有出現(xiàn)大的問(wèn)題。沒(méi)有出現(xiàn)過(guò)導(dǎo)航服務(wù)整體不可用的事故惋砂。在研發(fā)過(guò)程中妒挎,原有司機(jī)端一些隱藏比較深的BUG,都得到了系統(tǒng)的梳理和解決西饵。比如客戶端GPS點(diǎn)延遲酝掩,語(yǔ)音TTS延時(shí)等,在自建導(dǎo)航之前問(wèn)題暴漏的不明顯眷柔,自建后期虾,投入了更多的人力,自然也有更高的要求驯嘱,去發(fā)現(xiàn)問(wèn)題镶苞。
總結(jié)一下自己的幾點(diǎn)心得體會(huì)。
研發(fā)的開(kāi)始階段鞠评,速度優(yōu)先茂蚓,quick and dirty
互聯(lián)網(wǎng)是一個(gè)充分競(jìng)爭(zhēng)的領(lǐng)域,在一個(gè)產(chǎn)品從零到一的階段剃幌,速度就是生命聋涨,趕在競(jìng)爭(zhēng)對(duì)手前推出一款產(chǎn)品,或是在對(duì)手推出一款產(chǎn)品后负乡,迅速的追上牛郑,直接決定了這款產(chǎn)品,甚至是一家創(chuàng)業(yè)團(tuán)隊(duì)是否能夠生存敬鬓。具體速度優(yōu)先的tips包括:
不要求一個(gè)完美實(shí)現(xiàn)的方案淹朋,要求一個(gè)穩(wěn)定實(shí)用的方案笙各。
即使帶有明顯缺陷,也是可以上線的础芍,開(kāi)發(fā)人員或者產(chǎn)品經(jīng)理要有ownership的精神杈抢,不是唯唯諾諾惟上是從,也不是小心翼翼瞻前顧后不敢做決定仑性,而是能夠以產(chǎn)品owner的身份惶楼,為產(chǎn)品負(fù)責(zé),為用戶負(fù)責(zé)诊杆,權(quán)衡利弊歼捐,挺身而出。比如滴滴早期的派單引擎晨汹,采用直線距離做為派單權(quán)值豹储,顯然對(duì)跨河、封閉道路等情形淘这,會(huì)有明顯的bad case剥扣。但是,如果當(dāng)時(shí)的產(chǎn)品技術(shù)人員如果決策把這些bug修復(fù)了再上線铝穷,那市場(chǎng)早就被當(dāng)年的競(jìng)爭(zhēng)對(duì)手占領(lǐng)钠怯,也就沒(méi)有今天的滴滴了。
在滴滴的派單引擎中曙聂,直線距離直到今天仍然在發(fā)揮著作用晦炊,在請(qǐng)求地圖的路線規(guī)劃服務(wù)失敗時(shí),仍然會(huì)兜底采用宁脊。如同吳軍老師在《數(shù)學(xué)之美》這本書(shū)中断国,介紹谷歌AK-47的設(shè)計(jì)者那樣,好的方案朦佩,能夠迅速解決80%的頭部問(wèn)題并思,而且非常易于Debug庐氮。
系統(tǒng)設(shè)計(jì)要有前瞻性
雖然產(chǎn)品實(shí)現(xiàn)上语稠,基于速度的考慮,不可能一開(kāi)始就盡善盡美弄砍,但是架構(gòu)的設(shè)計(jì)在產(chǎn)品從0到1的階段卻是必不可少的仙畦,架構(gòu)設(shè)計(jì)是一款產(chǎn)品的設(shè)計(jì)圖紙,圖紙畫(huà)錯(cuò)了音婶,再精良的施工做出來(lái)的也是廢品慨畸。并且,由于互聯(lián)網(wǎng)的創(chuàng)新本質(zhì)衣式,產(chǎn)品經(jīng)理更傾向于把拿不準(zhǔn)的方案多做幾套寸士,放到線上去做A/B Test檐什,通過(guò)用戶數(shù)據(jù)來(lái)做決策。這樣弱卡,對(duì)工程師的架構(gòu)設(shè)計(jì)更是一種考驗(yàn)乃正。
架構(gòu)設(shè)計(jì)要分層,把最容易變更的策略部分和基礎(chǔ)的數(shù)據(jù)分開(kāi)婶博,金字塔模型瓮具,越往下的模塊越要求穩(wěn)定性,少變更凡人,越往上的部分越靈活名党。
拿導(dǎo)航服務(wù)為例,底層的數(shù)據(jù)模塊挠轴,要求高性能传睹,低變更,內(nèi)存自己管理忠荞,采用了C + STL的方式蒋歌,用linux的系統(tǒng)調(diào)用函數(shù)mmap批量申請(qǐng)內(nèi)存,自己設(shè)計(jì)內(nèi)存分布委煤;中間的導(dǎo)航策略模塊堂油,包含了非常多的播報(bào)策略,如預(yù)告輪碧绞,輪播府框,動(dòng)作輪等等,適合采用設(shè)計(jì)模式這樣的東西提高開(kāi)發(fā)效率和代碼復(fù)用率讥邻,我們采用了C++迫靖,良好的面向?qū)ο笤O(shè)計(jì),以應(yīng)對(duì)產(chǎn)品復(fù)雜多變的需求兴使;最上層接入模塊系宜,針對(duì)PM需求比較頻繁,而且會(huì)有一些實(shí)驗(yàn)性質(zhì)的策略发魄,比較適合采用python,node.js,golang這樣的現(xiàn)代語(yǔ)言開(kāi)發(fā)盹牧,以應(yīng)對(duì)頻繁更改的需求,提高開(kāi)發(fā)效率励幼。
基于數(shù)據(jù)分析的軟件開(kāi)發(fā)
一般認(rèn)為汰寓,產(chǎn)品經(jīng)理PM是產(chǎn)品owner,對(duì)最終的產(chǎn)品質(zhì)量負(fù)責(zé)苹粟,并調(diào)動(dòng)開(kāi)發(fā)(RD)有滑,質(zhì)量測(cè)試(QA),數(shù)據(jù)分析(BI)等資源,推動(dòng)一款產(chǎn)品的上線升級(jí)嵌削。RD在整個(gè)產(chǎn)品發(fā)布流程中的角色是開(kāi)發(fā)毛好,是make things望艺,傳統(tǒng)上不會(huì)對(duì)數(shù)據(jù)分析的事情負(fù)責(zé)。
實(shí)踐中肌访,數(shù)據(jù)分析卻變成了經(jīng)常發(fā)生問(wèn)題的環(huán)境荣茫,埋點(diǎn)數(shù)據(jù)經(jīng)常不可用,導(dǎo)致PM拿到的分析結(jié)果是錯(cuò)誤的场靴。由此導(dǎo)致了一系列問(wèn)題:BI的同學(xué)覺(jué)得自己的工作沒(méi)有成就感啡莉,變成了“提數(shù)工具”,RD同學(xué)覺(jué)得增加了自己額外的工作量旨剥,埋點(diǎn)不是在make things咧欣,而是在為他們的工作增加負(fù)擔(dān),PM同學(xué)忙于兩頭協(xié)調(diào)轨帜,疲于奔命魄咕,不能把精力用于思考產(chǎn)品,而是在處理各種瑣事蚌父。大家都相當(dāng)?shù)牟籬appy哮兰。
解決這個(gè)問(wèn)題的核心在于大家需要提高對(duì)埋點(diǎn)重要性的認(rèn)識(shí),Leader要有這個(gè)意識(shí)苟弛,埋點(diǎn)是和項(xiàng)目開(kāi)發(fā)同樣重要的TODO項(xiàng)喝滞。要從RD身上找突破口,首先膏秫,埋點(diǎn)右遭、記日志不是在給RD找額外工作量,而是工作的一部分$拖鳎現(xiàn)代的互聯(lián)網(wǎng)產(chǎn)品窘哈,在設(shè)計(jì)之初就需要考慮統(tǒng)計(jì)的問(wèn)題,在計(jì)算工作量時(shí)也要把埋點(diǎn)亭敢、打日志的工作量記錄在內(nèi)滚婉。
如何記錄好日志也是一門(mén)學(xué)問(wèn),RD要以“方便統(tǒng)計(jì)”為目的來(lái)記日志帅刀,日志格式輸出標(biāo)準(zhǔn)化让腹,要以能直接進(jìn)入hive庫(kù),給BI查詢?yōu)闃?biāo)準(zhǔn)劝篷。比如哨鸭,實(shí)踐中發(fā)現(xiàn)民宿,RD打錯(cuò)誤日志的時(shí)候娇妓,分為error_code,和error_message兩個(gè)字段,error_message除了詳細(xì)解釋了錯(cuò)誤的原因活鹰,竟然還把錯(cuò)誤case相關(guān)數(shù)據(jù)的ID也打在日志里哈恰,這就會(huì)導(dǎo)致統(tǒng)計(jì)中做group by非常困難只估,是要避免的。
TraceID在微服務(wù)架構(gòu)中的關(guān)鍵作用
另外一個(gè)技巧就是TraceID的應(yīng)用着绷,別小瞧了TraceID蛔钙,很多開(kāi)發(fā)者都忽視,甚至有誤解的地方荠医,Google甚至搞了一套系統(tǒng)吁脱,專(zhuān)門(mén)用于查case。目前互聯(lián)網(wǎng)公司流行的微服務(wù)的架構(gòu)設(shè)計(jì)彬向,主要目的在于隔離頻繁上線變更服務(wù)帶來(lái)的影響兼贡,以及便于快速迭代,應(yīng)對(duì)用戶群指數(shù)增長(zhǎng)的需求娃胆,但由此帶來(lái)的問(wèn)題是遍希,服務(wù)產(chǎn)生的bad case追查流程比較困難,有些bad case甚至不是由于單一的某個(gè)模塊產(chǎn)生的里烦,而是跨幾個(gè)模塊的corner case凿蒜,單個(gè)去看每個(gè)模塊,似乎都沒(méi)有什么錯(cuò)誤胁黑,但是串聯(lián)在一起废封,卻產(chǎn)生了bad case。這就需要在產(chǎn)品上線之初就設(shè)計(jì)一套traceID機(jī)制丧蘸,把客戶端產(chǎn)生的一次session會(huì)話串起來(lái)虱饿。
每個(gè)模塊,從客戶端開(kāi)始触趴,都要需要打印具有相同語(yǔ)義的trace日志氮发,格式如下:
| 字段 |含義 |
| -------- | -------- |
| timestamp | server端收到日志的時(shí)間 |
| parentID | 上游傳入的traceID中的childID|
| childID | 傳給下游的parentID |
| cost | 整個(gè)request的響應(yīng)時(shí)間 |
這樣,相當(dāng)于對(duì)整個(gè)調(diào)用鏈冗懦,像一個(gè)鏈表一樣串了起來(lái)爽冕,還能夠復(fù)雜的表示并行處理這樣的會(huì)話請(qǐng)求,例如
(圖披蕉,網(wǎng)絡(luò)調(diào)用session)
圖片來(lái)源:https://bigbully.github.io/Dapper-translation/
放量后颈畸,精益求精
均值和方差
放量后,需要關(guān)注用戶體驗(yàn)没讲,精益求精眯娱,用老大的話講,要關(guān)注均值爬凑,更要關(guān)注方差徙缴。
通俗的講,關(guān)注均值嘁信,就是關(guān)注某次模型優(yōu)化于样,某次策略升級(jí)帶來(lái)的系統(tǒng)性收益疏叨,比如平均車(chē)速提高了多少,偏航率降低了多少穿剖。關(guān)注方差蚤蔓,是指要關(guān)注極端的bad case,在放量后糊余,這些bad case會(huì)隨著放量的增加秀又,逐步通過(guò)各種渠道匯總到開(kāi)發(fā)團(tuán)隊(duì)。有時(shí)候甚至像潮水一樣猝不及防贬芥。
作為產(chǎn)品方講涮坐,均值的提升是有說(shuō)服力的,大數(shù)據(jù)時(shí)代靠數(shù)據(jù)說(shuō)話誓军,誰(shuí)也無(wú)法否認(rèn)數(shù)據(jù)袱讹。但總是盯著均值,不關(guān)注方差昵时,是錯(cuò)誤的捷雕。對(duì)bad case的分析是非常重要的,一個(gè)反饋給RD的bad case背后壹甥,類(lèi)似的問(wèn)題可能已經(jīng)發(fā)生了千百萬(wàn)次救巷。總之句柠,bad case解了浦译,方差會(huì)降低,均值會(huì)提高溯职,而針對(duì)均值的優(yōu)化精盅,卻不一定解決bad case。
但是需要避免的是陷入解bad case的困境中谜酒,避免鉆死胡同叹俏,要能站在系統(tǒng)的角度看問(wèn)題,確保每次優(yōu)化僻族,每次上線都是朝著系統(tǒng)最優(yōu)的方向前進(jìn)粘驰。
導(dǎo)航code
例如,導(dǎo)航服務(wù)中的一個(gè)難點(diǎn)是識(shí)別路口轉(zhuǎn)向播報(bào)述么,由于真實(shí)世界道路形態(tài)的復(fù)雜性蝌数,用抽象語(yǔ)言去描述真實(shí)的復(fù)雜路況需要考慮非常多的因素,例如實(shí)際工作中度秘,我們表示一個(gè)右轉(zhuǎn)的播報(bào)有13種形態(tài)顶伞。需要考慮道路線型,道路等級(jí),相似道路枝哄,順行道路等諸多方面。一個(gè)常見(jiàn)的問(wèn)題阻荒,就是用規(guī)則描述的路口形態(tài)決策樹(shù)挠锥,很容易出現(xiàn)bad case。
我們的想法是為全國(guó)1.4億個(gè)路口轉(zhuǎn)向建立一個(gè)數(shù)據(jù)庫(kù)侨赡。數(shù)據(jù)庫(kù)為每個(gè)轉(zhuǎn)型code都建立了一個(gè)專(zhuān)屬的模型蓖租,模型有23個(gè)維度。對(duì)整個(gè)路口code的識(shí)別策略羊壹,都會(huì)基于1.4億個(gè)code做自動(dòng)評(píng)價(jià)蓖宦,對(duì)diff的部分進(jìn)行人工抽樣。保障每次改動(dòng)是正向的油猫,人工評(píng)測(cè)的結(jié)果稠茂,會(huì)記錄到數(shù)據(jù)庫(kù)中,用于積累真值樣本情妖。
在解決了code的問(wèn)題后睬关,更復(fù)雜的問(wèn)題出現(xiàn)了,那就是語(yǔ)音播報(bào)的合理性毡证,code是單點(diǎn)的电爹,可窮舉的,播報(bào)卻是和路線相關(guān)的料睛,不可窮舉的丐箩。
導(dǎo)航語(yǔ)音播報(bào)
一個(gè)思路是采用用戶的實(shí)際軌跡,去評(píng)估語(yǔ)言播報(bào)的合理性恤煞。需要系統(tǒng)性的思考一個(gè)問(wèn)題屎勘,用戶為什么沒(méi)有按照我們給的路線走?真實(shí)的情況是怎么樣的居扒?
首先挑秉,和解決code的思路類(lèi)似,要對(duì)復(fù)雜路段進(jìn)行用戶畫(huà)像的工作苔货。比如犀概,實(shí)際工作中,我們發(fā)現(xiàn)在北京的路測(cè)效果比較好夜惭,換了一個(gè)城市姻灶,比如有著“魔幻8D城市”的重慶,針對(duì)北京道路形態(tài)進(jìn)行的播報(bào)規(guī)則優(yōu)化诈茧,就不適用了产喉。
那么,重慶的道路形態(tài)又是怎么樣的呢?
重慶是山城曾沈,立交橋多这嚣,隧道多,歷史悠久塞俱,人口稠密姐帚,小路多,出入口障涯,分歧點(diǎn)距離近罐旗。城市規(guī)劃不像北京那樣規(guī)整。
那么我們?cè)趺磻?yīng)對(duì)呢唯蝶?
路測(cè)九秀,消除不真實(shí)感
看競(jìng)品怎么播報(bào),競(jìng)品比我們提前做了10多年粘我,有歷史積累
看用戶軌跡鼓蜒,但是是有策略的看,篩選的條件是:對(duì)周邊路況不熟悉的用戶征字,新手司機(jī)友酱,甚至是疲勞駕駛的司機(jī)。
對(duì)特殊地段進(jìn)行特殊標(biāo)注柔纵,比如重慶一些特殊的立交橋缔杉,交通量很大,道路設(shè)計(jì)很特別搁料,是值得做專(zhuān)門(mén)優(yōu)化的
寫(xiě)在最后的總結(jié)
做為一款工具產(chǎn)品或详,在滴滴的所有服務(wù)中,“快”一直是最被看重的因素郭计,分單要快霸琴,接駕要快,算路要快昭伸,用的時(shí)候召之即來(lái)梧乘,用完了揮之即去。
唯獨(dú)對(duì)于導(dǎo)航服務(wù)庐杨,對(duì)于司機(jī)而言选调,每天10個(gè)小時(shí)的工作,伴隨始終灵份。對(duì)我們的專(zhuān)職司機(jī)而言仁堪,自駕不是追求自由與便利的娛樂(lè),是他們謀生手段填渠,對(duì)導(dǎo)航可靠性的要求弦聂,遠(yuǎn)超自駕對(duì)導(dǎo)航的期待鸟辅。
即使在偏布立交橋、隧道的重慶莺葫,GPS不再可靠匪凉,我們也仍然要想辦法為司機(jī)提供一個(gè)高可用的導(dǎo)航軟件,就像老板要求的捺檬,沒(méi)有什么不可能再层,對(duì)技術(shù)的追求是無(wú)止境的。這是司機(jī)選擇我們欺冀,對(duì)我們的信任树绩,也是我們義不容辭的使命萨脑。