結(jié)對(duì)編程的十個(gè)場(chǎng)景
作者 | 廖雪青
前言
文章以真實(shí)項(xiàng)目實(shí)踐總結(jié)為基礎(chǔ),選取了結(jié)對(duì)編程中的典型場(chǎng)景,敘述了在敏捷交付項(xiàng)目中,開發(fā)者在實(shí)踐結(jié)對(duì)編程時(shí)經(jīng)常遇到的挑戰(zhàn)。場(chǎng)景中會(huì)找到你或你身邊同事的影子嗎?
結(jié)對(duì)編程的十個(gè)場(chǎng)景
角色假設(shè)
A:Senior DEV,結(jié)對(duì)編程實(shí)踐者
B:Junior DEV,剛接觸結(jié)對(duì)編程
場(chǎng)景一:結(jié)對(duì)前的約定
A:好,那任務(wù)列表(check list)我們梳理清楚達(dá)成一致了,現(xiàn)在要開始編碼工作了。你們之前是怎么結(jié)對(duì)編程的?
B:嗯~怎么結(jié)對(duì)編程具體指什么?
A:好吧,那不然我們一個(gè)人寫45分鐘,然后休息10分鐘,再交換?
B:好啊,可以的。
開始結(jié)對(duì)之前,做一些約定是較好的開始。協(xié)商結(jié)對(duì)編程過(guò)程中如何交換角色,多久休息一次等。例一:A寫 45分鐘,休息15分鐘,然后B寫45分鐘,休息15分鐘;例二:TDD A寫測(cè)試,B寫實(shí)現(xiàn),A重構(gòu),B寫測(cè)試,A寫實(shí)現(xiàn),編碼40分鐘左右休息10分鐘。
場(chǎng)景二:知識(shí)傳遞
A:休息時(shí)間結(jié)束,那我們開始?
B:好,該我編碼了。那我們接下來(lái)要構(gòu)造接口的返回值了
A:對(duì)的,我們可以新建一個(gè)文件,導(dǎo)出一個(gè)方法,在該方法中構(gòu)建接口返回值
B:嗯,那我們現(xiàn)在去找一個(gè)地方放新建的文件
A:可以在 XX 目錄下建這個(gè)文件...B找到對(duì)應(yīng)的目錄并打開...
B:嗯。咦,這個(gè)目錄已經(jīng)有很多文件了,我看一下其他文件都是干什么的。
A:嗯,其實(shí)這個(gè)庫(kù)有自己的模式,不然我們?nèi)タ匆幌聨?kù)的文件結(jié)構(gòu)說(shuō)明文檔?
B:好呀,那說(shuō)明文檔在哪呢?
A:README文件中有入口,可以從那里點(diǎn)進(jìn)去。
B:哦。...查看結(jié)構(gòu)說(shuō)明文檔...
B:所以我們?cè)诮o上游發(fā)請(qǐng)求前如果要構(gòu)建請(qǐng)求參數(shù)的話,可以在文件夾下面建立XXRequestParams.js文件用來(lái)構(gòu)建參數(shù),從上游接口拿到返回值后,如果要對(duì)值做一些轉(zhuǎn)換的話,可以在文件夾下面建XXTransformer.js文件構(gòu)建接口返回值。是嗎?
A:對(duì)的,這樣的話可以做到把部分功能放到單獨(dú)的文件中,避免文件過(guò)大。還有一個(gè)好處是更好寫測(cè)試。
及時(shí)發(fā)現(xiàn)結(jié)對(duì)編程的伙伴(peer)某方面上下文的欠缺并耐心傳達(dá),幫助新人快速獲得項(xiàng)目知識(shí)。 當(dāng)發(fā)現(xiàn)peer某方面的不足時(shí),我們要意識(shí)到發(fā)展他人的機(jī)會(huì)來(lái)了。在上下文傳遞或知識(shí)點(diǎn)講解的過(guò)程中,我們可能也會(huì)有新的發(fā)現(xiàn)。
場(chǎng)景三:Senior直接上手解決問題
A:我們需要給 state 中增加一個(gè) XX字段
B:對(duì),我們現(xiàn)在要去state文件。這個(gè)文件,好像不是加到這個(gè)state文件呀!這個(gè)文件呢?不對(duì)不對(duì),好像也不是。
A:我們要構(gòu)建的頁(yè)面屬于XX模塊,得把state字段加在XX目錄下的state文件中。
B:嗯嗯,有道理。那這個(gè)文件,嗯~好像也不對(duì)!
A:不然我來(lái)寫吧。
...A拿過(guò)鍵盤,啃哧啃哧的編碼...
B:要遍歷數(shù)組了,用 for each 方法吧!
A:嗯~有其他遍歷數(shù)組的方法嗎?
B:還有 for…of、map 等方法,那我改成 map 吧!
A:其實(shí)這塊用 reducer 方法可能更合適
B:reducer 方法!reducer方法該怎么寫呀?
A:那我來(lái)寫吧。
...A拿過(guò)鍵盤,啃哧啃哧的編碼...
要給Junior足夠的實(shí)踐機(jī)會(huì)和成長(zhǎng)時(shí)間。任何成長(zhǎng)都是需要時(shí)間和實(shí)踐的,每個(gè)人學(xué)通一個(gè)技術(shù)或領(lǐng)域知識(shí)所需花費(fèi)的時(shí)間也不盡相同,我們要關(guān)注peer取得的進(jìn)步,而不是聚焦于peer還沒有掌握的部分,也不要因?yàn)閜eer的速度較慢就自己上手,剝奪他人實(shí)踐和學(xué)習(xí)的機(jī)會(huì)。
敢于暴露自己的弱點(diǎn)。 敢于承認(rèn)自己在某方面的能力比較欠缺,尋找機(jī)會(huì)有針對(duì)性地進(jìn)行提升,不要害怕暴露缺點(diǎn)而不敢發(fā)言。展示弱點(diǎn)并不會(huì)讓我們矮于他人,相反還會(huì)讓大家覺得你比較容易接近。
團(tuán)隊(duì)要?jiǎng)?chuàng)建一個(gè)安全包容的環(huán)境。 團(tuán)隊(duì)有好的氛圍,成員才不會(huì)擔(dān)心說(shuō)錯(cuò),有更大的信心自由地發(fā)表自己的觀點(diǎn)。
場(chǎng)景四:一個(gè)人的獨(dú)角戲
A:任務(wù)列表的這一項(xiàng)太大了,不然我們把它拆小一些?
B:......
A:這塊邏輯很復(fù)雜,還不好寫測(cè)試,不然我們抽一個(gè)純函數(shù)去處理這段邏輯吧?
B:......
A:這塊代碼這樣寫好像不太符合項(xiàng)目的模式,我們?nèi)フ覀€(gè)已有例子參考一下?
B:......
全程無(wú)回應(yīng)(我是誰(shuí)?我在哪?我在做什么?)。
是peer沒聽到嗎?是我聲音太小了嗎?是peer太專注沒心思搭理我嗎?
及時(shí)回應(yīng)。溝通需要至少兩個(gè)人的參與才能正常進(jìn)行,就像討論需要有來(lái)有往才是有效的。如果A講了話卻沒收到來(lái)自B的反饋,有三種可能情況:1. B沒聽到。2.B聽到了但是沒有回答。3.B聽到并回答了,但是回答的方式?jīng)]有被A理解到。對(duì)A來(lái)說(shuō),若不確定B究竟聽到?jīng)],可以加大音量重復(fù)所講的內(nèi)容。如果還是沒有回答的話,可以問A是否有聽到。為了讓結(jié)對(duì)編程更順暢,建議我們聽到他人說(shuō)話后,及時(shí)給出回應(yīng),以示意我們聽到了。這個(gè)回應(yīng)可以是語(yǔ)言、動(dòng)作甚至眼神,所以如果我們正在思考,不能被打斷或不方便給出回應(yīng)時(shí),可以選擇動(dòng)作等非語(yǔ)言的形式給出示意。
及時(shí)反饋。如果我們講話后沒收到對(duì)方的回應(yīng),要及時(shí)反饋。如果該現(xiàn)象發(fā)生了多次,我們可以建立一次反饋會(huì)議,跟peer針對(duì)該問題進(jìn)行正式的溝通,分析原因,明確及時(shí)回應(yīng)的必要,商討對(duì)應(yīng)的解決方法。
場(chǎng)景五:你怎么都不會(huì)
A:我們用 reducer 方法來(lái)對(duì)數(shù)組進(jìn)行處理吧!
B:這數(shù)組遍歷的 reducer 該怎么寫呀?
A:你這都不會(huì)!
...A講解 reducer 的使用方法...balabala… 一頓解釋...
B:測(cè)試要mock外部依賴了
...B開始編碼,嘗試了兩種mock方法,都沒有達(dá)到預(yù)期效果...
A:這塊結(jié)合實(shí)際情況,要這樣mock
...A講解該處mock外部依賴的方法...balabala…
B:對(duì)的,這樣寫測(cè)試結(jié)果符合預(yù)期。
A:你怎么這也不會(huì)!這很簡(jiǎn)單呀。
注意溝通技巧、用詞和語(yǔ)氣。我們?cè)跍贤ǖ倪^(guò)程中,一定要以平和的心態(tài),不帶偏見的、盡可能客觀地表達(dá)我們的意見。有些用詞對(duì)你來(lái)說(shuō)可能不帶任何含義,但是在他人來(lái)說(shuō)就不一定了,所以斟酌用詞也是必要的,盡量使用常見的中性詞。當(dāng)然我們也沒必要對(duì)每一個(gè)用詞語(yǔ)氣都反復(fù)斟酌,否則會(huì)過(guò)猶不及影響正常的溝通,畢竟都是項(xiàng)目的成員,說(shuō)話的人和聽話的人的感受都值得被關(guān)注、被考慮。重要的是在發(fā)生不愉快的時(shí)候,我們都要有去解決的意識(shí),能及時(shí)溝通講清前因后果及雙方的理解,解除不愉快。
正確處理peer的能力低于預(yù)期的情況。peer 的打字速度好慢呀,都不用快捷鍵,方法定位也不會(huì),這么菜嗎?針對(duì)這些情況,要明確:聞道有先后,術(shù)業(yè)有專攻。如果 peer 某方面表現(xiàn)很不好的時(shí)候,不要隨意批判輕視對(duì)方,要知道 peer 在這方面比較 junior,但是在其他方面,我們也可能會(huì)是初學(xué)者,所以我們要換位考慮對(duì)方的感受,同時(shí)還要意識(shí)到發(fā)展他人的機(jī)會(huì)來(lái)了。針對(duì)具體問題也要具體分析,通過(guò)溝通等手段尋找問題根因。是因?yàn)?peer 剛接觸該技術(shù)棧呢?還是 peer 最近壓力比較大,沒休息好,所以有時(shí)會(huì)反應(yīng)比較慢呢?亦或是文件搜索這塊正好是 peer 的短板呢?不管是客觀原因還是主觀原因,都要結(jié)合實(shí)際情況,多方溝通探討可行的解決方法并進(jìn)行追蹤。
場(chǎng)景六:建議被忽略
針對(duì)一個(gè)問題
A:...balabala…(講完自己的思路)
B:...balabala…(提出一種新思路)
A:...coding…(按自己的思路開始編碼)
B:...%#@$%&
...…B很懵,貌似自己的方案更合適吧?怎么就開始編碼了呢?...
關(guān)注并重視自己和peer的感受。結(jié)對(duì)編程實(shí)踐下,一天的大部分時(shí)間我們都在和peer進(jìn)行協(xié)作,如果心有隔閡,那么結(jié)對(duì)工作很難順利進(jìn)行下去,所以在內(nèi)心感到不適的時(shí)候,我們要重視自己的感受,及時(shí)反饋給對(duì)方并進(jìn)行溝通,針對(duì)具體情況協(xié)商解決。
針對(duì)不同解決方案要溝通達(dá)成一致再編碼。如果結(jié)對(duì)的兩個(gè)人針對(duì)同一個(gè)問題分別提出了幾個(gè)方案,這時(shí)要針對(duì)這些方案進(jìn)行溝通,確保雙方都理解了這些方案,并對(duì)方案的優(yōu)缺點(diǎn)、工作量等有一定的理解,雙方都贊同采用某方案后,再開始編碼,避免實(shí)現(xiàn)到一半再出現(xiàn)分歧。這個(gè)過(guò)程中,可以對(duì)每個(gè)方案都從頭到尾梳理一下,能提前識(shí)別一些存在問題的方案并過(guò)濾掉。如果針對(duì)要采用的方案無(wú)法達(dá)成一致,可以考慮暫時(shí)擱置爭(zhēng)論,尋找支持論點(diǎn)的證據(jù)后,再開始新一輪的相互說(shuō)服,也可以考慮引入第三方,TL或在該領(lǐng)域經(jīng)驗(yàn)豐富的組員都是較好的選擇。
及時(shí)反饋。反饋文化是我們所提倡的,可以建立多維度的反饋會(huì)議,比如peer的某種行為讓我感到不適,如果不想打斷工作進(jìn)度且可以接受暫時(shí)不適時(shí),可以選擇一天工作快結(jié)束的時(shí)候再進(jìn)行反饋交流,總結(jié)一天的工作,識(shí)別一些做得好或值得深挖、保持的地方。
場(chǎng)景七:按自己的節(jié)奏走
...A啃哧啃哧編碼...無(wú)講解思路...
B:你在不同的文件之間跳來(lái)跳去的,是在干嘛?
A:我們要修改測(cè)試環(huán)境的配置。
...A繼續(xù)啃哧啃哧的編碼...無(wú)講解思路...
B:這又是在干嘛?我們不是要先去查看一下XX方法,然后看看能不能拿到我們想要的結(jié)果嗎?
A:嗯,對(duì)。
...A繼續(xù)啃哧啃哧的編碼...無(wú)思路講解......
B很懵,不知道A的思路是什么,也不知道A是按怎樣的順序處理問題...
共同貢獻(xiàn),團(tuán)隊(duì)擁有。結(jié)對(duì)編程實(shí)踐下,開發(fā)軟件功能是兩個(gè)人的事情,Senior 不要大包大攬獨(dú)自完成,只按照自己的節(jié)奏,而在沒有講解思路的情況下忽略了peer是否能跟上。Junior 也要為卡負(fù)責(zé),不懂就問,進(jìn)度太快就提出來(lái),不要獨(dú)自承受壓力。
高頻率的溝通。結(jié)對(duì)編程實(shí)踐下,做卡是兩個(gè)人的事情,這需要高頻率的溝通以確保結(jié)對(duì)的兩個(gè)人都清楚理解該卡的上下文、解決思路以及當(dāng)前正在做的事情是什么。在講解實(shí)現(xiàn)思路的時(shí)候,要細(xì)致到每一步(參考美食主播講解菜譜的過(guò)程),畢竟peer的思路可能完全不同。
場(chǎng)景八:表現(xiàn)出被針對(duì)
項(xiàng)目組每周交換一次結(jié)對(duì)伙伴,剛交換結(jié)對(duì)伙伴后,A新加入該功能的開發(fā)。
...B給A同步正在實(shí)現(xiàn)的功能的上下文...
A:停,打斷一下,這個(gè)方法為什么要先過(guò)濾再遍歷數(shù)組呢?
B:因?yàn)?..
...B反應(yīng)激烈,聲音拔高...
...A感覺peer豎起了盾牌...
當(dāng)有新成員加入卡時(shí),同步卡的上下文。需要同步卡的范圍(scope),當(dāng)前進(jìn)度,是否有阻塞(blocker)等。在同步的過(guò)程中,講解者能再次梳理卡的內(nèi)容,有可能發(fā)掘之前忽略的小細(xì)節(jié),接聽者通過(guò)提問等也有可能提出不合理的地方,為卡的質(zhì)量增磚添瓦。
結(jié)對(duì)編程時(shí),大家的建議和爭(zhēng)議都是對(duì)事不對(duì)人的。在結(jié)對(duì)編程的過(guò)程中,意見并不總是一致的。當(dāng)發(fā)生爭(zhēng)議時(shí),作為被挑戰(zhàn)的一方,應(yīng)該客觀地傾聽別人的意見。如果之前沒有充分考慮,要勇于承認(rèn)并接受別人的意見,而不是認(rèn)為別人在攻擊自己,急于辯解并不斷反駁。即使別人提出的建議很有道理,也不要因維護(hù)自己的虛榮心而拒絕采納??赡軋F(tuán)隊(duì)成員缺乏足夠的上下文信息,而挑戰(zhàn)只是發(fā)出需要同步上下文的信號(hào)。要相信別人提出建議是為了讓我們的代碼更加可靠或者需要更多的上下文信息來(lái)理解我們正在做的事情。作為挑戰(zhàn)的一方,我們需要注意用詞和語(yǔ)氣。我們的目的是提出想法,保證代碼質(zhì)量,而不是通過(guò)發(fā)表建議來(lái)展示自己的能力和不凡。我們應(yīng)該保持客觀的態(tài)度,使用平和的語(yǔ)氣。如果感到對(duì)方比較排斥,要及時(shí)澄清情況。
場(chǎng)景九:因?yàn)樗饺耸虑殡x開
...A正在思考著解決方案,邊思考邊講述著自己的思路...
...B突然離開...
...A猜測(cè)B是因?yàn)樗饺耸虑殡x開,感受很不好...
團(tuán)隊(duì)信任很重要。首先,我們不能主觀評(píng)價(jià)peer離開的原因,也不能評(píng)價(jià)事件的重要性。其次,我們需要具備基本的職業(yè)素養(yǎng),盡可能專注于結(jié)對(duì)編程,不在未溝通的情況下無(wú)故離開,除非情況緊急。
在每天開始結(jié)對(duì)編程前,要檢查當(dāng)天的日程并同步可結(jié)對(duì)的時(shí)間段。提前同步可結(jié)對(duì)時(shí)間可以幫助規(guī)劃卡的工作,例如在兩個(gè)人都能結(jié)對(duì)的時(shí)候確定解決方案,或先實(shí)現(xiàn)一些復(fù)雜邏輯。這樣,在一個(gè)人離開后,另一個(gè)人可以無(wú)障礙地繼續(xù)編碼,避免遺留下來(lái)的人無(wú)法確定解決方案或無(wú)從下手處理復(fù)雜邏輯。
如果需要在結(jié)對(duì)編程過(guò)程中離開,盡量使事情透明。結(jié)對(duì)編程需要兩個(gè)人頻繁互動(dòng),因此考慮到peer的感受可以建立良好的關(guān)系,有利于結(jié)對(duì)編程的順利進(jìn)行。如果我們正在專注于結(jié)對(duì)編程,因?yàn)橥话l(fā)情況需要離開,盡量告知peer,方便的話講清離開的原因及預(yù)計(jì)返回時(shí)間,這是對(duì)他人的一種尊重。
相信 peer 是專注于結(jié)對(duì)編程的。在專注于結(jié)對(duì)編程的同時(shí),我們也要相信 peer 也是專注于結(jié)對(duì)編程的。如果peer在沒有通知的情況下離開,我們應(yīng)該相信是因?yàn)榍闆r緊急而沒有來(lái)得及通知,而不是因?yàn)楣ぷ鲬B(tài)度有問題。
離開的人回來(lái)后,留下的人應(yīng)該主動(dòng)同步這期間的代碼改動(dòng)等。代碼改動(dòng)等同步可以讓peer了解卡的最新狀態(tài),方便在下一步的編碼中提出有效的建議。如果留下的人沒有主動(dòng)同步,離開的人也要主動(dòng)詢問改動(dòng)內(nèi)容,以便及時(shí)將注意力集中到當(dāng)前的任務(wù)上。
場(chǎng)景十:peer 分心做其他事
...A在講解一個(gè)解決方案的思路,講著講著發(fā)現(xiàn)B在看手機(jī)...瞬間沒有講解的欲望了,那自己繼續(xù)編碼吧...
...過(guò)了一會(huì)…
B:你剛才說(shuō)什么?
...A偷偷嘆了一口氣,把剛才講的內(nèi)容重復(fù)了一遍...
A:我們這是不是應(yīng)該提取個(gè)方法呢?...
等了20s左右無(wú)回應(yīng),轉(zhuǎn)頭一看,B在瀏覽網(wǎng)頁(yè)...
...過(guò)了一會(huì)…
B:現(xiàn)在到哪了?
A:......
在進(jìn)行結(jié)對(duì)編程時(shí),應(yīng)盡量避免分心。不要進(jìn)行類似閱讀郵件或使用手機(jī)等的活動(dòng),要專注于結(jié)對(duì)編程。如果出現(xiàn)特殊情況需要隨時(shí)查看郵件來(lái)確認(rèn)某件事情,就應(yīng)提前溝通,讓這個(gè)事情透明化,而不是等到出現(xiàn)矛盾時(shí)才采取事后措施。
在規(guī)劃結(jié)對(duì)編程時(shí)間時(shí),要預(yù)留一些時(shí)間。確保每個(gè)人都有足夠的時(shí)間閱讀自己的電子郵件。
總結(jié)展望
結(jié)對(duì)編程是一項(xiàng)需要團(tuán)隊(duì)合作的實(shí)踐,也是一項(xiàng)復(fù)合技能,應(yīng)用場(chǎng)景十分廣泛,不能一一列舉。我們應(yīng)該在結(jié)對(duì)編程中不斷磨練這項(xiàng)技能,提高結(jié)對(duì)編程的體驗(yàn)和價(jià)值。同時(shí),在遇到困難時(shí),也要敢于迎難而上,尋找解決方案。
最重要的是,我們應(yīng)該認(rèn)識(shí)到結(jié)對(duì)編程是一項(xiàng)團(tuán)隊(duì)實(shí)踐,遇到的任何問題都可以從團(tuán)隊(duì)中尋求幫助。