項目延期半年,我被軟件外包坑慘了!
????轉(zhuǎn)自:infoQ????英文作者:Rajiv Prabhakar,翻譯:平川?多年前,年輕且天真的我決定與他人一起創(chuàng)業(yè),但同時還要兼顧我們的全職工作。我負責技術開發(fā),另一個創(chuàng)始人負責業(yè)務。我們的 MVP 計劃是發(fā)布 iOS 和 Android App。我在后端上有開發(fā)經(jīng)驗,但從未開發(fā)過 App。為此,我沒有選擇從頭開始學習,而是決定雇傭外部軟件開發(fā)人員來構建 App,而我則負責所有服務器端開發(fā)、P/SaaS 集成和基礎設施。
合作始末
這不是我第一次創(chuàng)業(yè)。想起來,因為有過這種經(jīng)歷,所以我過度自信了。以前的創(chuàng)業(yè)中,我曾經(jīng)雇傭過一位年輕的兼職自由職業(yè)者,他來自另一個國家。他是熟人推薦的。他不僅很好完成了工作,而且每小時收費不到 10 美元。當時,我并沒有意識到這一點,但考慮到他的才華和責任心,他的報價非常便宜。現(xiàn)在,他在舊金山掙六位數(shù)的工資。有點遺憾的是,他現(xiàn)在無法參與進來。我的聯(lián)合創(chuàng)始人也想請一個更知名的組織來管理我們的前端開發(fā)。因此,我們決定尋找開發(fā)工作室,而非自由職業(yè)者。當然,先要做一番調(diào)查。我們看了獨立的第三方評審報告,詢問了評審依據(jù),并與開發(fā)工作室之前的客戶進行了交談,最終從多個工作室中選擇了讓我們最有信心的一家。該工作室收費標準約為每小時 25 美元——明顯高于同類的自由職業(yè)者。不過,我們認為,雇傭的是一個有經(jīng)驗的專業(yè)組織,而非隨便一個人,這對我們來說更合適。我的聯(lián)合創(chuàng)始人是一名律師,與他們簽訂合同時,務求詳盡。眾所周知,軟件項目非常容易超支,所以我們協(xié)商簽訂了一份固定價格的合同,并對所有出現(xiàn)的 bug 都“保修”?;撕荛L一段時間后,我們才敲定合同細節(jié),并在合同里詳細描述他們應該構建的每個功能。然后,我們支付了第一筆款項并啟動項目。這里,我們犯下了致命錯誤!根據(jù)合同協(xié)議,這個項目分為三個部分。在完成任何工作之前,我們就要預付 40% 的費用。然后每一部分開發(fā)完成時分別再付 30%,但是在我們收到剛完成部分的交付成果之前。因為我們是個初創(chuàng)公司,并提前支付了 5 位數(shù)的費用,而且在完成下一筆付款之前沒有獲得任何可交付的內(nèi)容,所以我們在整個項目期間都被鎖定了。雖然知道會發(fā)生這種事,但我們覺得沒問題。因為他們有來自獨立第三方機構的良好評價,優(yōu)秀的客戶口碑,而我們沒有發(fā)現(xiàn)什么危險信號。此外,我們知道,在一個由別人創(chuàng)建的項目中增加一名新的開發(fā)人員并不不容易——所以我們計劃在整個項目中與他們并肩作戰(zhàn)。事后看來,那是我們做出的最糟糕的技術決定,給了我們的初創(chuàng)公司一個沉重的打擊。
技術挑戰(zhàn)
按照我們的想法,這款 App 需要具備的一個關鍵功能是實時聊天。在合同談判時,他們提出一些 SaaS 方面的建議來簡化實時聊天功能的構建——其中之一是 Twilio Chat。在研究了他們提出的各種不同建議后,我們覺得 Twilio 似乎是最好的選擇,于是,我倆就同意將其應用于我們的聊天功能。遺憾的是,在開始構建時,他們遇到難題。他們不知道如何在 React Native 中使用 Twilio Chat,盡管是他們最先推薦使用 Twilio Chat 和 React Native。更糟糕的是,他們并沒有坦白地告訴我們,他們陷入了困境,而只是簡單地告訴我們“Twilio Chat 不適用于 React Native”?,F(xiàn)在,他們想讓我們切換到一個完全不同的聊天服務提供商(由一個我們從未聽說過的公司提供),然后重新開始,而我們需要為此支付額外的費用(即使這本是一個固定價格的項目)。最糟糕的是,他們從開始說的話就不是真的。Twilio Chat 用在 React Native 中完全沒有問題——他們只是不知道怎么做。最終,作為一名沒有任何 React Native 開發(fā)經(jīng)驗的開發(fā)者,我花了很多時間去研究解決方案,并教他們應該怎么做。即使在我向他們做了演示之后,他們?nèi)匀恍枰医o他們提供文檔鏈接,并向他們解釋如何使用 Twilio API。如果我沒有和他們在一起,或者沒有替他們想出辦法完成這項工作,那么我們可能就會采納他們的建議。我們可能會完全拋棄 Twilio,轉(zhuǎn)向一個完全不同的、低標準的服務。這個決定可能會讓項目推遲好幾個月,并多花一大筆錢。在安全上馬馬虎虎
我希望關于 Twilio 的問題就此結束,但這還沒完。所有 Twilio 聊天信息都屬于一個通道,而通道可以標記為“私有”或“公共”。顧名思義,私有通道屬于通道中的特定用戶,而公共通道可以“被非會員看到和加入。此外,公共通道及其成員和消息對于給定服務中的每個客戶端端點都是可見的?!?/p>顯而易見,所有的非公開消息都應該使用私有通道來實現(xiàn)。但令人驚訝的是,他們都是用的公共通道——這是我在瀏覽 Twilio 控制臺時看到的。如果我們已經(jīng)上線了他們的實現(xiàn),只要是有一點點開發(fā)經(jīng)驗的人,就能夠竊聽每一個 App 用戶的私人談話。如果我自己沒有發(fā)現(xiàn)這個問題,開發(fā)公司肯定不會安排任何滲透測試人員來發(fā)現(xiàn)這些安全問題。這樣的錯誤令人無法容忍。更令人震驚的是,他們非但沒有為自己的嚴重疏忽而道歉,還不愿意更改。顯然,使用公共通道實現(xiàn)聊天功能更簡單,因此,他們更愿意保持這種方式。只有在我們多次抱怨后,他們才最終同意改變實現(xiàn)方式。Bug 無處不在
我們之所以愿意雇傭開發(fā)工作室,而不是個人自由職業(yè)者,是因為他們承諾給我們的其他支持。特別是 QA 團隊,他們會在向我們展示應用前進行詳盡的測試。任何軟件項目都會遇到 Bug,這是不可避免的,所以我們理解他們不能做出任何承諾。但我們相信了他們的話,他們說我們應該只會發(fā)現(xiàn)一些極端情況下的 Bug。后來我們發(fā)現(xiàn),這完全是一派胡言。我們從他們那里收到的所有交付滿是 Bug。甚至最基本的功能都不能工作——我甚至懷疑,即使他們測試過,他們也不是用真正的手機測試的。在整整一周的時間里,我和我的聯(lián)合創(chuàng)始人每天都要花上幾個小時,煞費苦心地測試,并記錄所有出現(xiàn)的 Bug。程序只求可運行
舉例來說,我們發(fā)現(xiàn)的一個 Bug 是,如果用戶的聯(lián)系人超過 50 個,就只有前 50 個會在 App 中顯示,其他的都無法訪問。事實是,我們的一個 SaaS 集成被分頁了,開發(fā)人員只實現(xiàn)了獲取第一頁結果的代碼。因為這個 Bug 只有在一個用戶有 51 個聯(lián)系人時才會被觸發(fā),而且我們尚處于私人測試階段,所以我們過了一段時間才發(fā)現(xiàn)這個 Bug。之后,我們向他們做了反饋,問題很快就得到了修復。我們測試了他們的修復結果,似乎一切正常。但在審查他們的代碼變更時,我發(fā)現(xiàn),他們的修復方式是多么的旁門左道。他們沒有用一個 while 循環(huán)來獲取所有的結果頁,而只是簡單地添加了一個 if 條件來獲取第二頁的內(nèi)容。一旦用戶的聯(lián)系人數(shù)量超過 100,我們就會再次遇到完全相同的錯誤。我可以原諒第一個 Bug,把它看成是無意的。但第二個 Bug 就是故意失職了。他們一定知道,我們要過很長時間才能觸發(fā)第二頁查詢結果,要過更長的時間才能觸發(fā)第三頁。他們清楚地知道自己在做什么,知道“修復”的局限性,但他們還是那樣做了。如果沒有人仔細檢查他們的代碼,這個 Bug 就會進入生產(chǎn)環(huán)境。沒有版本歷史
作為一名開發(fā)人員,我親身體會到版本控制歷史是多么有用。它可以幫助未來的開發(fā)人員了解為什么要做出某些設計決策,特定的功能是如何構建的,以及如何構建其他類似的特性。出于這個原因,在合同談判中,我特別堅持最后的交付物應該是一個 Git 存儲庫。他們欣然同意,并說他們內(nèi)部也普遍使用 Git。遺憾的是,在交付源代碼的時候,他們只給我們發(fā)送了一個壓縮文件,其中包含所有源代碼和生成的文件。我提醒他們,根據(jù)合同,他們應該給我們一個 Git 存儲庫。事實上,在他們發(fā)送的壓縮文件中,我甚至看到了一個“.git”目錄——表明他們在開發(fā)時確實在用 Git。第二天,他們很快就給我們發(fā)送了一個 Git 存儲庫,其中只有一次提交,而里面的文件與前一天發(fā)送給我們的 zip 文件完全相同。我抑制著自己的挫敗感告訴他們,我們想要整個版本歷史,而不只是一次提交,而且還是提交的同一個 zip 文件。他們回答說,他們的 Git 存儲庫中有一些“敏感信息”,不方便向外人提供。因此,他們不能分享給我們?!昂贤灰?guī)定交付 Git 存儲庫。但并沒有說存儲庫中應該包含所有的開發(fā)提交和歷史“。隨意改變規(guī)則
在談判過程中,我們多次提到服務器端 API 還沒有完全實現(xiàn),我們希望后端開發(fā)和前端開發(fā)同時進行。在項目開始時,我會把所有 API 端點提供給他們,其中一些會完全實現(xiàn)。這樣,他們就可以使用這幾個端點立即開始開發(fā)比較簡單的特性。當他們完成這些功能時,用于下一批特性的 API 也就完成了。我們的目標是避免延期,同時開展這兩項工作,可以更快地推出我們的 App。這是我們預先明確并反復申明的內(nèi)容。我們總是被告知,沒有問題。遺憾的是,付完錢之后,我們開始聽到一些完全不同的聲音。他們直截了當?shù)鼐芙^開始任何工作,直到整個項目中每一個特性用到的后端都 100% 完成開發(fā)并最終確定。所幸,我們在合同談判和設計工作上花費了大量時間,我?guī)缀跻呀?jīng)完成了后端開發(fā)。所以這并沒有成為一個問題。但令人震驚和痛心的是,他們沒有履行銷售人員早些時候做出的承諾。唯我獨尊
當我們把他們當作潛在客戶來交談的時候,他們?yōu)槲覀冧侀_了紅地毯。但一談到實質(zhì)問題,他們就堅持要按他們的方式來做。例如,在研究了各種選項之后,我決定用 Swagger 來記錄所有 API 端點、它們的輸入、模式、描述和行為。這樣,文檔就嵌入到了代碼中,能夠自動生成,并保持更新。Swagger GUI 還提供了一種非常友好的方式讓我們可以瀏覽所有 API 文檔,甚至可以直接從 GUI 進行 API 調(diào)用來做測試。遺憾的是,這不是他們的做事方式。因此,他們拒絕使用 Swagger 作為文檔源。取而代之,他們堅持讓我們用電子郵件給他們發(fā)送一個 Word 文檔,包含所有在 Swagger 中能找到的內(nèi)容,但要按照他們指定的格式填寫。我們花了好幾天討論這個問題,最后他們讓步了。但在整個開發(fā)過程中,他們的態(tài)度一直沒有改變。我們雇傭他們,是為了讓他們使用我們的后端 API 來創(chuàng)建移動應用。但他們卻對 API 的實現(xiàn)方式提出要求。每當在 API 設計上出現(xiàn)意見分歧時,我們就不得不花好幾天討論,還要忍受他們的抱怨。這種爭論可能是源于他們對 API 最佳實踐的熱情,但我懷疑,他們主要是想讓自己的工作盡可能簡單。而且,他們經(jīng)常弄不清楚如何利用現(xiàn)有的 API 實現(xiàn)所需的功能。缺少直接溝通
項目開始后還有一個很大的意外,就是缺乏溝通。在我以前所有的工程項目中,在跨團隊合作時,為了更好地了解和解決出現(xiàn)的問題,我們都會直接與工程師交談。令我驚訝的是,這是他們明確禁止的事情。按照他們的規(guī)定,我們只能與一個非技術的項目經(jīng)理單點聯(lián)系。盡管我們提了要求,但他們拒絕讓我們與實際從事項目開發(fā)工作的開發(fā)人員聯(lián)系。此外,他們的項目經(jīng)理也拒絕通過實時聊天工具交流。他們堅持一切都通過電子郵件進行。隨著時間的推移,這帶來了很大的溝通問題。每當開發(fā)人員遇到問題,或者有什么想不明白,他們就會把問題發(fā)給項目經(jīng)理。然后,她會把所有的問題匯總起來,在一天工作結束時給我發(fā)一封大郵件。即使我很快回復了郵件,他們還是要到下一個工作日才能看到。因此,即使是一個簡單的問答也需要 24 個小時的時間,比較復雜的討論則需要好幾天,而不是聊 30 分鐘,然后問題就解決了。值得慶幸的是,在項目后期,他們終于意識到這個過程是多么低效,并允許我們與他們的開發(fā)人員直接聯(lián)系。遺憾的是,到那時候,一切都太晚了。在項目剛開始時,我們就知道這會成為一個大問題。但他們向我們保證,這不成問題??梢钥隙ǖ氖牵覀兊膿鷳n變成了現(xiàn)實。事實證明,當你每天只能通過一封電子郵件進行溝通時,很難做到敏捷。嚴重延期
很遺憾,上述所有問題體現(xiàn)到了項目時間表上。原本應該是一個為期 2 個月的項目,最后卻用了 7 個月。對我們來說,這是一個重大挫折,因為我們錯過了許多潛在的用戶,他們決定不再等我們的 App 發(fā)布。現(xiàn)在回想起來,這些延誤一點也不奇怪,因為他們?nèi)鄙偌夹g專家,堅持采用瀑布式方法,并拒絕通過聊天或電話直接溝通。但我懷疑,這還不是問題的全部。我懷疑,在不同時段,他們有其他覺得更有利可圖的項目,并因此降低了我們項目的開發(fā)優(yōu)先級。這也是其開發(fā)團隊在項目中途出現(xiàn)重大人事變動的原因。推卸責任
在他們所有的失敗中,要說有什么東西不變的話,那就是他們完全拒絕為任何事情負責。在執(zhí)行任何任務之前,他們都會對自己的能力表現(xiàn)出百分之百的信心,并承諾結果不會有任何差錯。而當他們沒能兌現(xiàn)自己的承諾時,總是把責任推給其他人。- 你們搞不清楚如何使用 twilio SDK?
- 在 React Native 中無法使用 Twilio 聊天軟件
- 你們的聊天實現(xiàn)會暴露所有的私人對話?
- 替代方案太復雜了
- 為什么某個屏幕要花 30 秒來加載?
- 我們必須進行 5 次 API 調(diào)用,這使它變慢了。
經(jīng)驗教訓
面對上面出現(xiàn)的所有問題,我很想說:"離岸開發(fā)者很糟糕。"但是,這樣的結論既狹隘也過于局限。我與來自其他國家的優(yōu)秀工程師合作過,認為優(yōu)秀的開發(fā)人員只存在于美國,是非常愚蠢的。我也很想說,永遠不要把開發(fā)工作外包。如果你的公司像谷歌一樣成熟,或者是由風險投資公司資助的初創(chuàng)公司,那么一切都要自己構建,并且使用工資六位數(shù)的開發(fā)人員。但是,對于一個創(chuàng)始團隊規(guī)模不大的自給自足的初創(chuàng)公司來說,使用一些便宜的雇傭兵來幫助你完成 MVP 是有意義的。這是一個可以成功應用于其他場合的方法。我們不禁會想,既然看到了上面出現(xiàn)的所有問題,那么應該可以通過談判達成具體的合同條款來預防。這種做法注定要失敗。有太多的未知因素和太多的主觀性,不可能把所有東西都囊括在一個法律文件中。更不用說通過訴訟依法執(zhí)行合同,這本身就是一個巨大的工程。歸根結底,當你雇用自由職業(yè)者或開發(fā)工作室時,重要事的只有一件,那就是:如果他們做得不好,你有能力離開他們。我們遇到的所有問題,都是因為我們?nèi)鄙僦坪馐侄?。因為我們預付了很多錢,所以我們沒有能力離開并雇傭其他人,即使事情變得非常糟糕。一種更好的方法
合同一結束,我們就與他們斷了聯(lián)系,并大大地松了一口氣。我真得感到卸下了一個大包袱。從此之后,我們從根本上改變了與外部開發(fā)人員的合作方式:- 針對我們想要構建的功能,擬定一個順序列表。
- 找?guī)酌_發(fā)人員,最好是獨立的自由職業(yè)者,但如果同意以下流程,開發(fā)工作室也沒問題。
- 對于每名開發(fā)人員,挑選列表中最重要的功能,與他們討論功能需求、預算和成本。讓他們實現(xiàn)那個特性并測試。
- 讓一名內(nèi)部人員審核他們的 PR,測試升級后的 App,并標出有問題的地方。
- 符合要求后,合并并部署該特性,這樣,所有創(chuàng)始人 / 用戶就可以繼續(xù)審核該 App,并提供反饋或者根據(jù)需要調(diào)整。
- 如果我們對他們所做的工作感到滿意,就挑選下一個我們希望他們實現(xiàn)的功能,然后再次重復這個過程。
- 如果我們對他們的工作不滿意,就解雇他們,并尋找替代者。
- 客戶合作勝于合同談判
- 個體和互動勝于流程
- 可運行的軟件勝于詳細的文檔
- 響應變化勝于遵循計劃