在GitHub上對(duì)編程語言與軟件質(zhì)量的一個(gè)大規(guī)模研究
編程語言對(duì)軟件質(zhì)量的影響是什么?這個(gè)問題在很長一段時(shí)間內(nèi)成為一個(gè)引起了大量辯論的主題。在這項(xiàng)研究中,我們從 GitHub 上收集了大量的數(shù)據(jù)(728 個(gè)項(xiàng)目,6300 萬行源代碼,29000 位作者,150 萬個(gè)提交,17 種編程語言),嘗試在這個(gè)問題上提供一些實(shí)證。
這個(gè)還算比較大的樣本數(shù)量允許我們?nèi)ナ褂靡粋€(gè)混合的方法,結(jié)合多種可視化的回歸模型和文本分析,去研究語言特性的影響,比如,在軟件質(zhì)量上,靜態(tài)與動(dòng)態(tài)類型和允許混淆與不允許混淆的類型。通過從不同的方法作三角測(cè)量研究(LCTT 譯注:一種測(cè)量研究的方法),并且去控制引起混淆的因素,比如,團(tuán)隊(duì)大小、項(xiàng)目大小和項(xiàng)目歷史,我們的報(bào)告顯示,語言設(shè)計(jì)確實(shí)(對(duì)很多方面)有很大的影響,但是,在軟件質(zhì)量方面,語言的影響是非常有限的。
最明顯的似乎是,不允許混淆的類型比允許混淆的類型要稍微好一些,并且,在函數(shù)式語言中,靜態(tài)類型也比動(dòng)態(tài)類型好一些。值得注意的是,這些由語言設(shè)計(jì)所引起的輕微影響,絕大多數(shù)是由過程因素所主導(dǎo)的,比如,項(xiàng)目大小、團(tuán)隊(duì)大小和提交數(shù)量。但是,我們需要提示讀者,即便是這些不起眼的輕微影響,也是由其它的無形的過程因素所造成的,例如,對(duì)某些函數(shù)類型、以及不允許類型混淆的靜態(tài)語言的偏愛。
1 序言
在給定的編程語言是否是“適合這個(gè)工作的正確工具”的討論期間,緊接著又發(fā)生了多種辯論。雖然一些辯論出現(xiàn)了帶有宗教般狂熱的色彩,但是大部分人都一致認(rèn)為,編程語言的選擇能夠?qū)幋a過程和由此生成的結(jié)果都有影響。
主張強(qiáng)靜態(tài)類型的人,傾向于認(rèn)為靜態(tài)方法能夠在早期捕獲到缺陷;他們認(rèn)為,一點(diǎn)點(diǎn)的預(yù)防勝過大量的矯正。動(dòng)態(tài)類型擁護(hù)者主張,保守的靜態(tài)類型檢查無論怎樣都是非常浪費(fèi)開發(fā)者資源的,并且,最好是依賴強(qiáng)動(dòng)態(tài)類型檢查來捕獲錯(cuò)誤類型。然而,這些辯論,大多數(shù)都是“紙上談兵”,只靠“傳說中”的證據(jù)去支持。
這些“傳說”也許并不是沒有道理的;考慮到影響軟件工程結(jié)果的大量其它因素,獲取這種經(jīng)驗(yàn)性的證據(jù)支持是一項(xiàng)極具挑戰(zhàn)性的任務(wù),比如,代碼質(zhì)量、語言特征,以及應(yīng)用領(lǐng)域。比如軟件質(zhì)量,考慮到它有大量的眾所周知的影響因素,比如,代碼數(shù)量, 6 團(tuán)隊(duì)大小, 2 和年齡/熟練程度。 9
受控實(shí)驗(yàn)是檢驗(yàn)語言選擇在面對(duì)如此令人氣餒的混淆影響時(shí)的一種方法,然而,由于成本的原因,這種研究通常會(huì)引入一種它們自己的混淆,也就是說,限制了范圍。在這種研究中,完整的任務(wù)是必須要受限制的,并且不能去模擬 真實(shí)的世界 中的開發(fā)。這里有幾個(gè)最近的這種大學(xué)本科生使用的研究,或者,通過一個(gè)實(shí)驗(yàn)因素去比較靜態(tài)或動(dòng)態(tài)類型的語言。7,12,15
幸運(yùn)的是,現(xiàn)在我們可以基于大量的真實(shí)世界中的軟件項(xiàng)目去研究這些問題。GitHub 包含了多種語言的大量的項(xiàng)目,并且在大小、年齡、和開發(fā)者數(shù)量上有很大的差別。每個(gè)項(xiàng)目的倉庫都提供一個(gè)詳細(xì)的記錄,包含貢獻(xiàn)歷史、項(xiàng)目大小、作者身份以及缺陷修復(fù)。然后,我們使用多種工具去研究語言特性對(duì)缺陷發(fā)生的影響。對(duì)我們的研究方法的最佳描述應(yīng)該是“混合方法”,或者是三角測(cè)量法; 5 我們使用文本分析、聚簇和可視化去證實(shí)和支持量化回歸研究的結(jié)果。這個(gè)以經(jīng)驗(yàn)為根據(jù)的方法,幫助我們?nèi)チ私饩幊陶Z言對(duì)軟件質(zhì)量的具體影響,因?yàn)?,他們是被開發(fā)者非正式使用的。
2 方法
我們的方法是軟件工程中典型的大范圍觀察研究法。我們首先大量的使用自動(dòng)化方法,從幾種數(shù)據(jù)源采集數(shù)據(jù)。然后使用預(yù)構(gòu)建的統(tǒng)計(jì)分析模型對(duì)數(shù)據(jù)進(jìn)行過濾和清洗。過濾器的選擇是由一系列的因素共同驅(qū)動(dòng)的,這些因素包括我們研究的問題的本質(zhì)、數(shù)據(jù)質(zhì)量和認(rèn)為最適合這項(xiàng)統(tǒng)計(jì)分析研究的數(shù)據(jù)。尤其是,GitHub 包含了由大量的編程語言所寫的非常多的項(xiàng)目。對(duì)于這項(xiàng)研究,我們花費(fèi)大量的精力專注于收集那些用大多數(shù)的主流編程語言寫的流行項(xiàng)目的數(shù)據(jù)。我們選擇合適的方法來評(píng)估計(jì)數(shù)數(shù)據(jù)上的影響因素。
2.1 數(shù)據(jù)收集
我們選擇了 GitHub 上的排名前 19 的編程語言。剔除了 CSS、Shell 腳本、和 Vim 腳本,因?yàn)樗鼈儾皇峭ㄓ玫木幊陶Z言。我們包含了 Typescript,它是 JavaScript 的超集。然后,對(duì)每個(gè)被研究的編程語言,我們檢索出以它為主要編程語言的前 50 個(gè)項(xiàng)目。我們總共分析了 17 種不同的語言,共計(jì) 850 個(gè)項(xiàng)目。
我們的編程語言和項(xiàng)目的數(shù)據(jù)是從 GitHub Archive 中提取的,這是一個(gè)記錄所有活躍的公共 GitHub 項(xiàng)目的數(shù)據(jù)庫。它記錄了 18 種不同的 GitHub 事件,包括新提交、fork 事件、PR(拉取請(qǐng)求)、開發(fā)者信息和以每小時(shí)為基礎(chǔ)的所有開源 GitHub 項(xiàng)目的問題跟蹤。打包后的數(shù)據(jù)上傳到 Google BigQuery 提供的交互式數(shù)據(jù)分析接口上。
識(shí)別編程語言排名榜單
我們基于它們的主要編程語言分類合計(jì)項(xiàng)目。然后,我們選擇大多數(shù)的項(xiàng)目進(jìn)行進(jìn)一步分析,如 表 1 所示。一個(gè)項(xiàng)目可能使用多種編程語言;將它確定成單一的編程語言是很困難的。Github Archive 保存的信息是從 GitHub Linguist 上采集的,它使用項(xiàng)目倉庫中源文件的擴(kuò)展名來確定項(xiàng)目的發(fā)布語言是什么。源文件中使用數(shù)量最多的編程語言被確定為這個(gè)項(xiàng)目的 主要編程語言。
表 1 每個(gè)編程語言排名前三的項(xiàng)目
檢索流行的項(xiàng)目
對(duì)于每個(gè)選定的編程語言,我們先根據(jù)項(xiàng)目所使用的主要編程語言來選出項(xiàng)目,然后根據(jù)每個(gè)項(xiàng)目的相關(guān) 星 的數(shù)量排出項(xiàng)目的流行度。 星 的數(shù)量表示了有多少人主動(dòng)表達(dá)對(duì)這個(gè)項(xiàng)目感興趣,并且它是流行度的一個(gè)合適的代表指標(biāo)。因此,在 C 語言中排名前三的項(xiàng)目是 linux、git、php-src;而對(duì)于 C++,它們則是 node-webkit、phantomjs、mongo ;對(duì)于 Java,它們則是 storm、elasticsearch、ActionBarSherlock 。每個(gè)編程語言,我們各選了 50 個(gè)項(xiàng)目。
為確保每個(gè)項(xiàng)目有足夠長的開發(fā)歷史,我們剔除了少于 28 個(gè)提交的項(xiàng)目(28 是候選項(xiàng)目的第一個(gè)四分位值數(shù))。這樣我們還剩下 728 個(gè)項(xiàng)目。表 1 展示了每個(gè)編程語言的前三個(gè)項(xiàng)目。
檢索項(xiàng)目演進(jìn)歷史
對(duì)于 728 個(gè)項(xiàng)目中的每一個(gè)項(xiàng)目,我們下載了它們的非合并提交、提交記錄、作者數(shù)據(jù)、作者使用 git 的名字。我們從每個(gè)文件的添加和刪除的行數(shù)中計(jì)算代碼改動(dòng)和每個(gè)提交的修改文件數(shù)量。我們以每個(gè)提交中修改的文件的擴(kuò)展名所代表的編程語言,來檢索出所使用的編程語言(一個(gè)提交可能有多個(gè)編程語言標(biāo)簽)。對(duì)于每個(gè)提交,我們通過它的提交日期減去這個(gè)項(xiàng)目的第一個(gè)提交的日期,來計(jì)算它的 提交年齡 。我們也計(jì)算其它的項(xiàng)目相關(guān)的統(tǒng)計(jì)數(shù)據(jù),包括項(xiàng)目的最大提交年齡和開發(fā)者總數(shù),用于我們的回歸分析模型的控制變量,它在第三節(jié)中會(huì)討論到。我們通過在提交記錄中搜索與錯(cuò)誤相關(guān)的關(guān)鍵字,比如,error
、bug
、fix
、issue
、mistake
、incorrect
、fault
、defect
、flaw
,來識(shí)別 bug 修復(fù)提交。這一點(diǎn)與以前的研究類似。18
表 2 匯總了我們的數(shù)據(jù)集。因?yàn)橐粋€(gè)項(xiàng)目可能使用多個(gè)編程語言,表的第二列展示了使用某種編程語言的項(xiàng)目的總數(shù)量。我們進(jìn)一步排除了項(xiàng)目中該編程語言少于 20 個(gè)提交的那些編程語言。因?yàn)?20 是每個(gè)編程語言的每個(gè)項(xiàng)目的提交總數(shù)的第一個(gè)四分位值。例如,我們?cè)?C 語言中共找到 220 項(xiàng)目的提交數(shù)量多于 20 個(gè)。這確保了每個(gè)“編程語言 – 項(xiàng)目”對(duì)有足夠的活躍度。
表 2 研究主題
總而言之,我們研究了最近 18 年以來,用了 17 種編程語言開發(fā)的,總共 728 個(gè)項(xiàng)目??偣舶?29,000 個(gè)不同的開發(fā)者,157 萬個(gè)提交,和 564,625 個(gè) bug 修復(fù)提交。
2.2 語言分類
我們基于影響語言質(zhì)量的幾種編程語言特性定義了語言類別,7,8,12 ,如 表 3 所示。
編程范式 表示項(xiàng)目是以命令方式、腳本方式、還是函數(shù)語言所寫的。在本文的下面部分,我們分別使用 命令 和 腳本 這兩個(gè)術(shù)語去代表命令方式和腳本方式。
表 3. 語言分類的不同類型
類型檢查 代表靜態(tài)或者動(dòng)態(tài)類型。在靜態(tài)類型語言中,在編譯時(shí)進(jìn)行類型檢查,并且變量名是綁定到一個(gè)值和一個(gè)類型的。另外,(包含變量的)表達(dá)式是根據(jù)運(yùn)行時(shí),它們可能產(chǎn)生的值所符合的類型來分類的。在動(dòng)態(tài)類型語言中,類型檢查發(fā)生在運(yùn)行時(shí)。因此,在動(dòng)態(tài)類型語言中,它可能出現(xiàn)在同一個(gè)程序中,一個(gè)變量名可能會(huì)綁定到不同類型的對(duì)象上的情形。
隱式類型轉(zhuǎn)換 允許一個(gè)類型為 T1 的操作數(shù),作為另一個(gè)不同的類型 T2 來訪問,而無需進(jìn)行顯式的類型轉(zhuǎn)換。這樣的隱式類型轉(zhuǎn)換在一些情況下可能會(huì)帶來類型混淆,尤其是當(dāng)它表示一個(gè)明確的 T1 類型的操作數(shù)時(shí),把它再作為另一個(gè)不同的 T2 類型的情況下。因?yàn)?,并不是所有的隱式類型轉(zhuǎn)換都會(huì)立即出現(xiàn)問題,通過我們識(shí)別出的允許進(jìn)行隱式類型轉(zhuǎn)換的所有編程語言中,可能發(fā)生隱式類型轉(zhuǎn)換混淆的例子來展示我們的定義。例如,在像 Perl、 JavaScript、CoffeeScript 這樣的編程語言中,一個(gè)字符和一個(gè)數(shù)字相加是允許的(比如,"5" + 2
結(jié)果是 "52"
)。但是在 Php 中,相同的操作,結(jié)果是 7
。像這種操作在一些編程語言中是不允許的,比如 Java 和 Python,因?yàn)?,它們不允許隱式轉(zhuǎn)換。在強(qiáng)數(shù)據(jù)類型的 C 和 C++ 中,這種操作的結(jié)果是不可預(yù)料的,例如,int x; float y; y=3.5; x=y
;是合法的 C 代碼,并且對(duì)于 x
和 y
其結(jié)果是不同的值,具體是哪一個(gè)值,取決于含義,這可能在后面會(huì)產(chǎn)生問題。a 在 Objective-C
中,數(shù)據(jù)類型 id 是一個(gè)通用對(duì)象指針,它可以被用于任何數(shù)據(jù)類型的對(duì)象,而不管分類是什么。b 像這種通用數(shù)據(jù)類型提供了很好的靈活性,它可能導(dǎo)致隱式的類型轉(zhuǎn)換,并且也會(huì)出現(xiàn)不可預(yù)料的結(jié)果。c 因此,我們根據(jù)它的編譯器是否 允許 或者 不允許 如上所述的隱式類型轉(zhuǎn)換,對(duì)編程語言進(jìn)行分類;而不允許隱式類型轉(zhuǎn)換的編程語言,會(huì)顯式檢測(cè)類型混淆,并報(bào)告類型不匹配的錯(cuò)誤。
不允許隱式類型轉(zhuǎn)換的編程語言,使用一個(gè)類型判斷算法,比如,Hindley 10 和 Milner,17 或者,在運(yùn)行時(shí)上使用一個(gè)動(dòng)態(tài)類型檢查器,可以在一個(gè)編譯器(比如,使用 Java)中判斷靜態(tài)類型的結(jié)果。相比之下,一個(gè)類型混淆可能會(huì)悄無聲息地發(fā)生,因?yàn)?,它可能因?yàn)闆]有檢測(cè)到,也可能是沒有報(bào)告出來。無論是哪種方式,允許隱式類型轉(zhuǎn)換在提供了靈活性的同時(shí),最終也可能會(huì)出現(xiàn)很難確定原因的錯(cuò)誤。為了簡(jiǎn)單起見,我們將用 隱含 代表允許隱式類型轉(zhuǎn)換的編程語言,而不允許隱式類型轉(zhuǎn)換的語言,我們用 明確 代表。
內(nèi)存分類 表示是否要求開發(fā)者去管理內(nèi)存。盡管 Objective-C 遵循了一個(gè)混合模式,我們?nèi)詫⑺旁诜枪芾淼姆诸愔衼韺?duì)待,因?yàn)?,我們?cè)谒拇a庫中觀察到很多的內(nèi)存錯(cuò)誤,在第 3 節(jié)的 RQ4 中會(huì)討論到。
請(qǐng)注意,我們之所以使用這種方式對(duì)編程語言來分類和研究,是因?yàn)椋@種方式在一個(gè)“真實(shí)的世界”中被大量的開發(fā)人員非正式使用。例如,TypeScript 被有意地分到靜態(tài)編程語言的分類中,它不允許隱式類型轉(zhuǎn)換。然而,在實(shí)踐中,我們注意到,開發(fā)者經(jīng)常(有 50% 的變量,并且跨 TypeScript —— 在我們的數(shù)據(jù)集中使用的項(xiàng)目)使用 any
類型,這是一個(gè)籠統(tǒng)的聯(lián)合類型,并且,因此在實(shí)踐中,TypeScript 允許動(dòng)態(tài)地、隱式類型轉(zhuǎn)換。為減少混淆,我們從我們的編程語言分類和相關(guān)的模型中排除了 TypeScript(查看 表 3 和 7)。
2.3 識(shí)別項(xiàng)目領(lǐng)域
我們基于編程語言的特性和功能,使用一個(gè)自動(dòng)加手動(dòng)的混合技術(shù),將研究的項(xiàng)目分類到不同的領(lǐng)域。在 GitHub 上,項(xiàng)目使用 project descriptions
和 README
文件來描述它們的特性。我們使用一種文檔主題生成模型(Latent Dirichlet Allocation,縮寫為:LDA) 3 去分析這些文本。提供一組文檔給它,LDA 將生成不同的關(guān)鍵字,然后來識(shí)別可能的主題。對(duì)于每個(gè)文檔,LDA 也估算每個(gè)主題分配的文檔的概率。
我們檢測(cè)到 30 個(gè)不同的領(lǐng)域(換句話說,就是主題),并且評(píng)估了每個(gè)項(xiàng)目從屬于每個(gè)領(lǐng)域的概率。因?yàn)?,這些自動(dòng)檢測(cè)的領(lǐng)域包含了幾個(gè)具體項(xiàng)目的關(guān)鍵字,例如,facebook,很難去界定它的底層的常用功能。為了給每個(gè)領(lǐng)域分配一個(gè)有意義的名字,我們手動(dòng)檢查了 30 個(gè)與項(xiàng)目名字無關(guān)的用于識(shí)別領(lǐng)域的領(lǐng)域識(shí)別關(guān)鍵字。我們手動(dòng)重命名了所有的 30 個(gè)自動(dòng)檢測(cè)的領(lǐng)域,并且找出了以下六個(gè)領(lǐng)域的大多數(shù)的項(xiàng)目:應(yīng)用程序、數(shù)據(jù)庫、代碼分析、中間件、庫,和框架。我們也找出了不符合以上任何一個(gè)領(lǐng)域的一些項(xiàng)目,因此,我們把這個(gè)領(lǐng)域籠統(tǒng)地標(biāo)記為 其它 。隨后,我們研究組的另一名成員檢查和確認(rèn)了這種項(xiàng)目領(lǐng)域分類的方式。表 4 匯總了這個(gè)過程識(shí)別到的領(lǐng)域結(jié)果。
表 4 領(lǐng)域特征
2.4 bug 分類
盡管修復(fù)軟件 bug 時(shí),開發(fā)者經(jīng)常會(huì)在提交日志中留下關(guān)于這個(gè) bug 的原始的重要信息;例如,為什么會(huì)產(chǎn)生 bug,以及怎么去修復(fù) bug。我們利用很多信息去分類 bug,與 Tan 的 et al 類似。 13,24
首先,我們基于 bug 的 原因 和 影響 進(jìn)行分類。_ 原因 _ 進(jìn)一步分解為不相關(guān)的錯(cuò)誤子類:算法方面的、并發(fā)方面的、內(nèi)存方面的、普通編程錯(cuò)誤,和未知的。bug 的 影響 也分成四個(gè)不相關(guān)的子類:安全、性能、失敗、和其它的未知類。因此,每個(gè) bug 修復(fù)提交也包含原因和影響的類型。表 5 展示了描述的每個(gè) bug 分類。這個(gè)類別分別在兩個(gè)階段中被執(zhí)行:
表 5 bug 分類和在整個(gè)數(shù)據(jù)集中的描述
(1) 關(guān)鍵字搜索 我們隨機(jī)選擇了 10% 的 bug 修復(fù)信息,并且使用一個(gè)基于關(guān)鍵字的搜索技術(shù)去對(duì)它們進(jìn)行自動(dòng)化分類,作為可能的 bug 類型。我們對(duì)這兩種類型(原因和影響)分別使用這個(gè)注釋。我們選擇了一個(gè)限定的關(guān)鍵字和習(xí)慣用語集,如 表 5 所展示的。像這種限定的關(guān)鍵字和習(xí)慣用語集可以幫我們降低誤報(bào)。
(2) 監(jiān)督分類 我們使用前面步驟中的有注釋的 bug 修復(fù)日志作為訓(xùn)練數(shù)據(jù),為監(jiān)督學(xué)習(xí)分類技術(shù),通過測(cè)試數(shù)據(jù)來矯正,去對(duì)剩余的 bug 修復(fù)信息進(jìn)行分類。我們首先轉(zhuǎn)換每個(gè) bug 修復(fù)信息為一個(gè)詞袋(LCTT 譯注:bag-of-words,一種信息檢索模型)。然后,刪除在所有的 bug 修復(fù)信息中僅出現(xiàn)過一次的詞。這樣減少了具體項(xiàng)目的關(guān)鍵字。我們也使用標(biāo)準(zhǔn)的自然語言處理技術(shù)來解決這個(gè)問題。最終,我們使用支持向量機(jī)(LCTT 譯注:Support Vector Machine,縮寫為 SVM,在機(jī)器學(xué)習(xí)領(lǐng)域中,一種有監(jiān)督的學(xué)習(xí)算法)去對(duì)測(cè)試數(shù)據(jù)進(jìn)行分類。
為精確評(píng)估 bug 分類器,我們手動(dòng)注釋了 180 個(gè)隨機(jī)選擇的 bug 修復(fù),平均分布在所有的分類中。然后,我們比較手動(dòng)注釋的數(shù)據(jù)集在自動(dòng)分類器中的結(jié)果。最終處理后的,表現(xiàn)出的精確度是可接受的,性能方面的精確度最低,是 70%,并發(fā)錯(cuò)誤方面的精確度最高,是 100%,平均是 84%。再次運(yùn)行,精確度從低到高是 69% 到 91%,平均精確度還是 84%。
我們的 bug 分類的結(jié)果展示在 表 5 中。大多數(shù)缺陷的原因都與普通編程錯(cuò)誤相關(guān)。這個(gè)結(jié)果并不意外,因?yàn)?,在這個(gè)分類中涉及了大量的編程錯(cuò)誤,比如,類型錯(cuò)誤、輸入錯(cuò)誤、編寫錯(cuò)誤、等等。我們的技術(shù)并不能將在任何(原因或影響)分類中占比為 1.4% 的 bug 修復(fù)信息再次進(jìn)行分類;我們將它歸類為未知。
2.5 統(tǒng)計(jì)方法
我們使用回歸模型對(duì)軟件項(xiàng)目相關(guān)的其它因素中的有缺陷的提交數(shù)量進(jìn)行了建模。所有的模型使用負(fù)二項(xiàng)回歸(縮寫為 NBR)(LCTT 譯注:一種回歸分析模型) 去對(duì)項(xiàng)目屬性計(jì)數(shù)進(jìn)行建模,比如,提交數(shù)量。NBR 是一個(gè)廣義的線性模型,用于對(duì)非負(fù)整數(shù)進(jìn)行響應(yīng)建模。4
在我們的模型中,我們對(duì)每個(gè)項(xiàng)目的編程語言,控制幾個(gè)可能影響最終結(jié)果的因素。因此,在我們的回歸分析中,每個(gè)(語言/項(xiàng)目)對(duì)是一個(gè)行,并且可以視為來自流行的開源項(xiàng)目中的樣本。我們依據(jù)變量計(jì)數(shù)進(jìn)行對(duì)象轉(zhuǎn)換,以使變量保持穩(wěn)定,并且提升了模型的適用度。4 我們通過使用 AIC 和 Vuong 對(duì)非嵌套模型的測(cè)試比較來驗(yàn)證它們。
去檢查那些過度的多重共線性(LCTT 譯注:多重共線性是指,在線性回歸模型中解釋變量之間由于存在精確相關(guān)關(guān)系或高度相關(guān)關(guān)系而使模型估計(jì)失真或難以估計(jì)準(zhǔn)確。)并不是一個(gè)問題,我們?cè)谒械哪P椭惺褂靡粋€(gè)保守的最大值 5,去計(jì)算每個(gè)依賴的變量的膨脹因子的方差。4 我們通過對(duì)每個(gè)模型的殘差和杠桿圖進(jìn)行視覺檢查來移除高杠桿點(diǎn),找出庫克距離(LCTT 譯注:一個(gè)統(tǒng)計(jì)學(xué)術(shù)語,用于診斷回歸分析中是否存在異常數(shù)據(jù))的分離值和最大值。
我們利用 效果 ,或者 差異 ,編碼到我們的研究中,以提高編程語言回歸系數(shù)的表現(xiàn)。4 加權(quán)的效果代碼允許我們將每種編程語言與所有編程語言的效果進(jìn)行比較,同時(shí)彌補(bǔ)了跨項(xiàng)目使用編程語言的不均勻性。23 去測(cè)試兩種變量因素之間的聯(lián)系,我們使用一個(gè)獨(dú)立的卡方檢驗(yàn)(LCTT 譯注:Chi-square,一種統(tǒng)計(jì)學(xué)上的假設(shè)檢驗(yàn)方法)測(cè)試。14 在證實(shí)一個(gè)依賴之后,我們使用 Cramer 的 V,它是與一個(gè) r × c
等價(jià)的正常數(shù)據(jù)的 phi(φ)
系數(shù),去建立一個(gè)效果數(shù)據(jù)。
3 結(jié)果
我們從簡(jiǎn)單明了的問題開始,它非常直接地解決了人們堅(jiān)信的一些核心問題,即:
問題 1:一些編程語言相比其它語言來說更易于出現(xiàn)缺陷嗎?
我們使用了回歸分析模型,去比較每個(gè)編程語言對(duì)所有編程語言缺陷數(shù)量平均值的影響,以及對(duì)缺陷修復(fù)提交的影響(查看 表 6)。
表 6. 一些語言的缺陷要少于其它語言
我們包括了一些變量,作為對(duì)明確影響反應(yīng)的控制因子。項(xiàng)目年齡也包括在內(nèi),因?yàn)?,越老的?xiàng)目生成的缺陷修復(fù)數(shù)量越大。提交數(shù)量也會(huì)對(duì)項(xiàng)目反應(yīng)有輕微的影響。另外,從事該項(xiàng)目的開發(fā)人員的數(shù)量和項(xiàng)目的原始大小,都會(huì)隨著項(xiàng)目的活躍而增長。
上述模型中估算系數(shù)的大小和符號(hào)(LCTT 譯注:指 “+”或者“-”)與結(jié)果的預(yù)測(cè)因子有關(guān)。初始的四種變量是控制變量,并且,我們對(duì)這些變量對(duì)最終結(jié)果的影響不感興趣,只是說它們都是積極的和有意義的。語言變量是指示變量,是每個(gè)項(xiàng)目的變化因子,該因子將每種編程語言與所有項(xiàng)目的編程語言的加權(quán)平均值進(jìn)行比較。編程語言系數(shù)可以大體上分為三類。第一類是,那些在統(tǒng)計(jì)學(xué)上無關(guān)緊要的系數(shù),并且在建模過程中這些系數(shù)不能從 0 中區(qū)分出來。這些編程語言的表現(xiàn)與平均值相似,或者它們也可能有更大的方差。剩余的系數(shù)是非常明顯的,要么是正的,要么是負(fù)的。對(duì)于那些正的系數(shù),我們猜測(cè)可能與這個(gè)編程語言有大量的缺陷修復(fù)相關(guān)。這些語言包括 C、C++、Objective-C、Php,以及 Python。所有的有一個(gè)負(fù)的系數(shù)的編程語言,比如 Clojure、Haskell、Ruby,和 Scala,暗示這些語言的缺陷修復(fù)提交可能小于平均值。
應(yīng)該注意的是,雖然,從統(tǒng)計(jì)學(xué)的角度觀察到編程語言與缺陷之間有明顯的聯(lián)系,但是,大家不要過高估計(jì)編程語言對(duì)于缺陷的影響,因?yàn)?,這種影響效應(yīng)是非常小的。異常分析的結(jié)果顯示,這種影響小于總異常的 1%。
ut1.jpg
我們可以這樣去理解模型的系數(shù),它代表一個(gè)預(yù)測(cè)因子在所有其它預(yù)測(cè)因子保持不變的情況下,這個(gè)預(yù)測(cè)因子一個(gè)單位的變化,所反應(yīng)出的預(yù)期的響應(yīng)的對(duì)數(shù)變化;換句話說,對(duì)于一個(gè)系數(shù) βi ,在 βi 中一個(gè)單位的變化,產(chǎn)生一個(gè)預(yù)期的 eβi 響應(yīng)的變化。對(duì)于可變因子,這個(gè)預(yù)期的變化是與所有編程語言的平均值進(jìn)行比較。因此,如果對(duì)于一定數(shù)量的提交,用一個(gè)處于平均值的編程語言開發(fā)的特定項(xiàng)目有四個(gè)缺陷提交,那么,如果選擇使用 C++ 來開發(fā),意味著我們預(yù)計(jì)應(yīng)該有一個(gè)額外的(LCTT 譯注:相對(duì)于平均值 4,多 1 個(gè))缺陷提交,因?yàn)?e0.18 × 4 = 4.79。對(duì)于相同的項(xiàng)目,如果選擇使用 Haskell 來開發(fā),意味著我們預(yù)計(jì)應(yīng)該少一個(gè)(LCTT 譯注:同上,相對(duì)于平均值 4)缺陷提交。因?yàn)椋?e−0.26 × 4 = 3.08。預(yù)測(cè)的精確度取決于剩余的其它因子都保持不變,除了那些微不足道的項(xiàng)目之外,所有的這些都是一個(gè)極具挑戰(zhàn)性的命題。所有觀察性研究都面臨類似的局限性;我們將在第 5 節(jié)中詳細(xì)解決這些事情。
結(jié)論 1:一些編程語言相比其它編程語言有更高的缺陷相關(guān)度,不過,影響非常小。
在這篇文章的剩余部分,我們會(huì)在基本結(jié)論的基礎(chǔ)上詳細(xì)闡述,通過考慮不同種類的應(yīng)用程序、缺陷、和編程語言,可以進(jìn)一步深入了解編程語言和缺陷傾向之間的關(guān)系。
軟件 bug 通常落進(jìn)兩種寬泛的分類中:
- 特定領(lǐng)域的 bug :特定于項(xiàng)目功能,并且不依賴于底層編程語言。
- 普通 bug :大多數(shù)的普通 bug 是天生的,并且與項(xiàng)目功能無關(guān),比如,輸入錯(cuò)誤,并發(fā)錯(cuò)誤、等等。
因此,在一個(gè)項(xiàng)目中,應(yīng)用程序領(lǐng)域和編程語言相互作用可能會(huì)影響缺陷的數(shù)量,這一結(jié)論被認(rèn)為是合理的。因?yàn)橐恍┚幊陶Z言被認(rèn)為在一些任務(wù)上相比其它語言表現(xiàn)更突出,例如,C 對(duì)于低級(jí)別的(底層)工作,或者,Java 對(duì)于用戶應(yīng)用程序,對(duì)于編程語言的一個(gè)不合適的選擇,可能會(huì)帶來更多的缺陷。為研究這種情況,我們將理想化地忽略領(lǐng)域特定的 bug,因?yàn)?,普?bug 更依賴于編程語言的特性。但是,因?yàn)橐粋€(gè)領(lǐng)域特定的 bug 也可能出現(xiàn)在一個(gè)普通的編程錯(cuò)誤中,這兩者是很難區(qū)分的。一個(gè)可能的變通辦法是在控制領(lǐng)域的同時(shí)去研究編程語言。從統(tǒng)計(jì)的角度來看,雖然,使用 17 種編程語言跨 7 個(gè)領(lǐng)域,在給定的樣本數(shù)量中,理解大量的術(shù)語將是一個(gè)極大的挑戰(zhàn)。
鑒于這種情況,我們首先考慮在一個(gè)項(xiàng)目中測(cè)試領(lǐng)域和編程語言使用之間的依賴關(guān)系,獨(dú)立使用一個(gè)卡方檢驗(yàn)測(cè)試。在 119 個(gè)單元中,是 46 個(gè),也就是說是 39%,它在我們?cè)O(shè)定的保守值 5 以上,它太高了。這個(gè)數(shù)字不能超過 20%,應(yīng)該低于 5。14 我們?cè)谶@里包含了完整有值; d 但是,通過 Cramer 的 V 測(cè)試的值是 0.191,是低相關(guān)度的,表明任何編程語言和領(lǐng)域之間的相關(guān)度是非常小的,并且,在回歸模型中包含領(lǐng)域并不會(huì)產(chǎn)生有意義的結(jié)果。
去解決這種情況的一個(gè)選擇是,去移除編程語言,或者混合領(lǐng)域,但是,我們現(xiàn)有的數(shù)據(jù)沒有進(jìn)行完全挑選?;蛘?,我們混合編程語言;這個(gè)選擇導(dǎo)致一個(gè)相關(guān)但略有不同的問題。
問題 2: 哪些編程語言特性與缺陷相關(guān)?
我們按編程語言類別聚合它們,而不是考慮單獨(dú)的編程語言,正如在第 2.2 節(jié)所描述的那樣,然后去分析與缺陷的關(guān)系??傮w上說,這些屬性中的每一個(gè)都將編程語言按照在上下文中經(jīng)常討論的錯(cuò)誤、用戶辯論驅(qū)動(dòng)、或者按以前工作主題來劃分的。因此,單獨(dú)的屬性是高度相關(guān)的,我們創(chuàng)建了六個(gè)模型因子,將所有的單獨(dú)因子綜合到我們的研究中。然后,我們對(duì)六個(gè)不同的因子對(duì)缺陷數(shù)量的影響進(jìn)行建模,同時(shí)控制我們?cè)? 問題 1 節(jié)中使用的模型中的相同的基本協(xié)變量(LCTT 譯注:協(xié)變量是指在實(shí)驗(yàn)中不能被人為操縱的獨(dú)立變量)。
關(guān)于使用的編程語言(在前面的 表 6中),我們使用跨所有語言類的平均反應(yīng)來比較編程語言 類 。這個(gè)模型在 表 7 中表達(dá)了出來。很明顯,Script-Dynamic-Explicit-Managed
類有最小的量級(jí)系數(shù)。這個(gè)系數(shù)是微不足道的,換句話說,對(duì)這個(gè)系數(shù)的 Z 校驗(yàn)(LCTT 譯注:統(tǒng)計(jì)學(xué)上的一種平均值差異校驗(yàn)的方法) 并不能把它從 0 中區(qū)分出來。鑒于標(biāo)準(zhǔn)錯(cuò)誤的量級(jí),我們可以假設(shè),在這個(gè)類別中的編程語言的行為是非常接近于所有編程語言行為的平均值。我們可以通過使用 Proc-Static-Implicit-Unmanaged
作為基本級(jí)并用于處理,或者使用基本級(jí)來虛假編碼比較每個(gè)語言類,來證明這一點(diǎn)。在這種情況下,Script-Dynamic-Explicit-Managed
是明顯不同于 p = 0.00044 的。注意,雖然我們?cè)谶@是選擇了不同的編碼方法,影響了系數(shù)和 Z 值,這個(gè)方法和所有其它的方面都是一樣的。當(dāng)我們改變了編碼,我們調(diào)整系數(shù)去反應(yīng)我們希望生成的對(duì)比。4 將其它類的編程語言與總體平均數(shù)進(jìn)行比較,Proc-Static-Implicit-Unmanaged
類編程語言更容易引起缺陷。這意味著與其它過程類編程語言相比,隱式類型轉(zhuǎn)換或者管理內(nèi)存會(huì)導(dǎo)致更多的缺陷傾向。
表 7. 函數(shù)式語言與缺陷的關(guān)聯(lián)度和其它類語言相比要低,而過程類語言則大于或接近于平均值。
在腳本類編程語言中,我們觀察到類似于允許與不允許隱式類型轉(zhuǎn)換的編程語言之間的關(guān)系,它們提供的一些證據(jù)表明,隱式類型轉(zhuǎn)換(與顯式類型轉(zhuǎn)換相比)才是導(dǎo)致這種差異的原因,而不是內(nèi)存管理。鑒于各種因素之間的相關(guān)性,我們并不能得出這個(gè)結(jié)論。但是,當(dāng)它們與平均值進(jìn)行比較時(shí),作為一個(gè)組,那些不允許隱式類型轉(zhuǎn)換的編程語言出現(xiàn)錯(cuò)誤的傾向更低一些,而那些出現(xiàn)錯(cuò)誤傾向更高的編程語言,出現(xiàn)錯(cuò)誤的機(jī)率則相對(duì)更高。在函數(shù)式編程語言中靜態(tài)和動(dòng)態(tài)類型之間的差異也很明顯。
函數(shù)式語言作為一個(gè)組展示了與平均值的很明顯的差異。靜態(tài)類型語言的系數(shù)要小很多,但是函數(shù)式語言類都有同樣的標(biāo)準(zhǔn)錯(cuò)誤。函數(shù)式靜態(tài)編程語言出現(xiàn)錯(cuò)誤的傾向要小于函數(shù)式動(dòng)態(tài)編程語言,這是一個(gè)強(qiáng)有力的證據(jù),盡管如此,Z 校驗(yàn)僅僅是檢驗(yàn)系數(shù)是否能從 0 中區(qū)分出來。為了強(qiáng)化這個(gè)推論,我們使用處理編碼,重新編碼了上面的模型,并且觀察到,Functional-Static-Explicit-Managed
編程語言類的錯(cuò)誤傾向是明顯小于 Functional-Dynamic-Explicit-Managed
編程語言類的 p = 0.034。
ut2.jpg
與編程語言和缺陷一樣,編程語言類與缺陷之間關(guān)系的影響是非常小的。解釋類編程語言的這種差異也是相似的,雖然很小,解釋類編程語言的這種差異小于 1%。
我們現(xiàn)在重新回到應(yīng)用領(lǐng)域這個(gè)問題。應(yīng)用領(lǐng)域是否與語言類相互影響?怎么選擇?例如,一個(gè)函數(shù)化編程語言,對(duì)特定的領(lǐng)域有一定的優(yōu)勢(shì)?與上面一樣,對(duì)于這些因素和項(xiàng)目領(lǐng)域之間的關(guān)系做一個(gè)卡方檢驗(yàn),它的值是 99.05, df = 30, p = 2.622e–09,我們拒絕無意義假設(shè),Cramer 的 V 產(chǎn)生的值是 0.133,表示一個(gè)弱關(guān)聯(lián)。因此,雖然領(lǐng)域和編程語言之間有一些關(guān)聯(lián),但在這里應(yīng)用領(lǐng)域和編程語言類之間僅僅是一個(gè)非常弱的關(guān)聯(lián)。
結(jié)論 2:在編程語言類與缺陷之間有一個(gè)很小但是很明顯的關(guān)系。函數(shù)式語言與過程式或者腳本式語言相比,缺陷要少。
這個(gè)結(jié)論有些不太令人滿意的地方,因?yàn)?,我們并沒有一個(gè)強(qiáng)有力的證據(jù)去證明,在一個(gè)項(xiàng)目中編程語言或者語言類和應(yīng)用領(lǐng)域之間的關(guān)聯(lián)性。一個(gè)替代方法是,基于全部的編程語言和應(yīng)用領(lǐng)域,忽略項(xiàng)目和缺陷總數(shù),而去查看相同的數(shù)據(jù)。因?yàn)?,這樣不再產(chǎn)生不相關(guān)的樣本,我們沒有從統(tǒng)計(jì)學(xué)的角度去嘗試分析它,而是使用一個(gè)描述式的、基于可視化的方法。
我們定義了 缺陷傾向 作為 bug 修復(fù)提交與每語言每領(lǐng)域總提交的比率。圖 1 使用了一個(gè)熱力圖展示了應(yīng)用領(lǐng)域與編程語言之間的相互作用,從亮到暗表示缺陷傾向在增加。我們研究了哪些編程語言因素影響了跨多種語言寫的項(xiàng)目的缺陷修復(fù)提交。它引出了下面的研究問題:
圖 1. 編程語言的缺陷傾向與應(yīng)用領(lǐng)域之間的相互作用。對(duì)于一個(gè)給定的領(lǐng)域(列底部),熱力圖中的每個(gè)格子表示了一個(gè)編程語言的缺陷傾向(行頭部)。“整體”列表示一個(gè)編程語言基于所有領(lǐng)域的缺陷傾向。用白色十字線標(biāo)記的格子代表一個(gè) null 值,換句話說,就是在那個(gè)格子里沒有符合的提交。
問題 3: 編程語言的錯(cuò)誤傾向是否取決于應(yīng)用領(lǐng)域?
為了回答這個(gè)問題,我們首先在我們的回歸模型中,以高杠桿點(diǎn)過濾掉認(rèn)為是異常的項(xiàng)目,這種方法在這里是必要的,盡管這是一個(gè)非統(tǒng)計(jì)學(xué)的方法,一些關(guān)系可能影響可視化。例如,我們找到一個(gè)簡(jiǎn)單的項(xiàng)目,Google 的 v8,一個(gè) JavaScript 項(xiàng)目,負(fù)責(zé)中間件中的所有錯(cuò)誤。這對(duì)我們來說是一個(gè)驚喜,因?yàn)?nbsp;JavaScript 通常不用于中間件。這個(gè)模式一直在其它應(yīng)用領(lǐng)域中不停地重復(fù)著,因此,我們過濾出的項(xiàng)目的缺陷度都低于 10% 和高于 90%。這個(gè)結(jié)果在 圖 1 中。
我們看到在這個(gè)熱力圖中僅有一個(gè)很小的差異,正如在問題 1 節(jié)中看到的那樣,這個(gè)結(jié)果僅表示編程語言固有的錯(cuò)誤傾向。為驗(yàn)證這個(gè)推論,我們測(cè)量了編程語言對(duì)每個(gè)應(yīng)用領(lǐng)域和對(duì)全部應(yīng)用領(lǐng)域的缺陷傾向。對(duì)于除了數(shù)據(jù)庫以外的全部領(lǐng)域,關(guān)聯(lián)性都是正向的,并且 p 值是有意義的(<0.01)。因此,關(guān)于缺陷傾向,在每個(gè)領(lǐng)域的語言排序與全部領(lǐng)域的語言排序是基本相同的。
ut3.jpg
結(jié)論 3: 應(yīng)用領(lǐng)域和編程語言缺陷傾向之間總體上沒有聯(lián)系。
我們證明了不同的語言產(chǎn)生了大量的缺陷,并且,這個(gè)關(guān)系不僅與特定的語言相關(guān),也適用于一般的語言類;然而,我們發(fā)現(xiàn),項(xiàng)目類型并不能在一定程度上協(xié)調(diào)這種關(guān)系?,F(xiàn)在,我們轉(zhuǎn)變我們的注意力到反應(yīng)分類上,我想去了解,編程語言與特定種類的缺陷之間有什么聯(lián)系,以及這種關(guān)系怎么與我們觀察到的更普通的關(guān)系去比較。我們將缺陷分為不同的類別,如 表 5 所描述的那樣,然后提出以下的問題:
問題 4:編程語言與 bug 分類之間有什么關(guān)系?
我們使用了一個(gè)類似于問題 3 中所用的方法,去了解編程語言與 bug 分類之間的關(guān)系。首先,我們研究了 bug 分類和編程語言類之間的關(guān)系。一個(gè)熱力圖(圖 2)展示了在編程語言類和 bug 類型之上的總?cè)毕輸?shù)。去理解 bug 分類和語言之間的相互作用,我們對(duì)每個(gè)類別使用一個(gè) NBR 回歸模型。對(duì)于每個(gè)模型,我們使用了與問題 1 中相同的控制因素,以及使用加權(quán)效應(yīng)編碼后的語言,去預(yù)測(cè)缺陷修復(fù)提交。
圖 2. bug 類別與編程語言類之間的關(guān)系。每個(gè)格子表示每語言類(行頭部)每 bug 類別(列底部)的 bug 修復(fù)提交占全部 bug 修復(fù)提交的百分比。這個(gè)值是按列規(guī)范化的。
結(jié)果和編程語言的方差分析值展示在 表 8 中。每個(gè)模型的整體異常是非常小的,并且對(duì)于特定的缺陷類型,通過語言所展示的比例在大多數(shù)類別中的量級(jí)是類似的。我們解釋這種關(guān)系為,編程語言對(duì)于特定的 bug 類別的影響要大于總體的影響。盡管我們結(jié)論概括了全部的類別,但是,在接下來的一節(jié)中,我們對(duì) 表 5 中反應(yīng)出來的 bug 數(shù)較多的 bug 類別做進(jìn)一步研究。
表 8. 雖然編程語言對(duì)缺陷的影響因缺陷類別而不同,但是,編程語言對(duì)特定的類別的影響要大于一般的類別。
編程錯(cuò)誤 普通的編程錯(cuò)誤占所有 bug 修復(fù)提交的 88.53% 左右,并且在所有的編程語言類中都有。因此,回歸分析給出了一個(gè)與問題 1 中類似的結(jié)論(查看 表 6)。所有的編程語言都會(huì)導(dǎo)致這種編程錯(cuò)誤,比如,處理錯(cuò)誤、定義錯(cuò)誤、輸入錯(cuò)誤、等等。
內(nèi)存錯(cuò)誤 內(nèi)存錯(cuò)誤占所有 bug 修復(fù)提交的 5.44%。熱力圖 圖 2 證明了在 Proc-Static-Implicit-Unmanaged
類和內(nèi)存錯(cuò)誤之間存在著非常緊密的聯(lián)系。非管理內(nèi)存的編程語言出現(xiàn)內(nèi)存 bug,這是預(yù)料之中的。表 8 也證明了這一點(diǎn),例如,C、C++、和 Objective-C 引發(fā)了很多的內(nèi)存錯(cuò)誤。在管理內(nèi)存的語言中 Java 引發(fā)了更多的內(nèi)存錯(cuò)誤,盡管它少于非管理內(nèi)存的編程語言。雖然 Java 自己做內(nèi)存回收,但是,它出現(xiàn)內(nèi)存泄露一點(diǎn)也不奇怪,因?yàn)閷?duì)象引用經(jīng)常阻止內(nèi)存回收。11 在我們的數(shù)據(jù)中,Java 的所有內(nèi)存錯(cuò)誤中,28.89% 是內(nèi)存泄漏造成的。就數(shù)量而言,編程語言中內(nèi)存缺陷相比其它類別的 原因 造成的影響要大很多。
并發(fā)錯(cuò)誤 在總的 bug 修復(fù)提交中,并發(fā)錯(cuò)誤相關(guān)的修復(fù)提交占 1.99%。熱力圖顯示,Proc-Static-Implicit-Unmanaged
是主要的錯(cuò)誤類型。在這種錯(cuò)誤中,C 和 C++ 分別占 19.15% 和 7.89%,并且它們分布在各個(gè)項(xiàng)目中。
ut4.jpg
屬于 Static-Strong-Managed
語言類的編程語言都被證實(shí)處于熱力圖中的暗區(qū)中,普通的靜態(tài)語言相比其它語言產(chǎn)生更多的并發(fā)錯(cuò)誤。在動(dòng)態(tài)語言中,僅僅有 Erlang 有更多的并發(fā)錯(cuò)誤傾向,或許與使用這種語言開發(fā)的并發(fā)應(yīng)用程序非常多有關(guān)系。同樣地,在 表 8 中的負(fù)的系數(shù)表明,用諸如 Ruby 和 `Php 這樣的動(dòng)態(tài)編程語言寫的項(xiàng)目,并發(fā)錯(cuò)誤要少一些。請(qǐng)注意,某些語言,如 JavaScript、CoffeeScript 和 TypeScript 是不支持并發(fā)的,在傳統(tǒng)的慣例中,雖然 Php 具有有限的并發(fā)支持(取決于它的實(shí)現(xiàn))。這些語言在我們的數(shù)據(jù)中引入了虛假的 0,因此,在 表 8 中這些語言的并發(fā)模型的系數(shù),不能像其它的語言那樣去解釋。因?yàn)榇嬖谶@些虛假的 0,所以在這個(gè)模型中所有語言的平均數(shù)非常小,它可能影響系數(shù)的大小,因此,她們給 w.r.t. 一個(gè)平均數(shù),但是,這并不影響他們之間的相對(duì)關(guān)系,因?yàn)槲覀冎魂P(guān)注它們的相對(duì)關(guān)系。
基于 bug 修復(fù)消息中高頻詞的文本分析表明,大多數(shù)的并發(fā)錯(cuò)誤發(fā)生在一個(gè)條件爭(zhēng)用、死鎖、或者不正確的同步上,正如上面表中所示。遍歷所有語言,條件爭(zhēng)用是并發(fā)錯(cuò)誤出現(xiàn)最多的原因,例如,在 Go 中占 92%。在 Go 中條件爭(zhēng)用錯(cuò)誤的改進(jìn),或許是因?yàn)槭褂昧艘粋€(gè)爭(zhēng)用檢測(cè)工具幫助開發(fā)者去定位爭(zhēng)用。同步錯(cuò)誤主要與消息傳遞接口(MPI)或者共享內(nèi)存操作(SHM)相關(guān)。Erlang 和 Go 對(duì)線程間通訊使用 MPI e ,這就是為什么這兩種語言沒有發(fā)生任何 SHM 相關(guān)的錯(cuò)誤的原因,比如共享鎖、互斥鎖等等。相比之下,為線程間通訊使用早期的 SHM 技術(shù)的語言寫的項(xiàng)目,就可能存在鎖相關(guān)的錯(cuò)誤。
安全和其它沖突錯(cuò)誤 在所有的 bug 修復(fù)提交中,與沖突錯(cuò)誤相關(guān)的提交占了 7.33% 左右。其中,Erlang、C++、Python 與安全相關(guān)的錯(cuò)誤要高于平均值(表 8)。Clojure 項(xiàng)目相關(guān)的安全錯(cuò)誤較少(圖 2)。從熱力圖上我們也可以看出來,靜態(tài)語言一般更易于發(fā)生失敗和性能錯(cuò)誤,緊隨其后的是 Functional-Dynamic-Explicit-Managed
語言,比如 Erlang。對(duì)異常結(jié)果的分析表明,編程語言與沖突失敗密切相關(guān)。雖然安全錯(cuò)誤在這個(gè)類別中是弱相關(guān)的,與殘差相比,解釋類語言的差異仍然比較大。
結(jié)論 4: 缺陷類型與編程語言強(qiáng)相關(guān);一些缺陷類型比如內(nèi)存錯(cuò)誤和并發(fā)錯(cuò)誤也取決于早期的語言(所使用的技術(shù))。對(duì)于特定類別,編程語言所引起的錯(cuò)誤比整體更多。
4. 相關(guān)工作
在編程語言比較之前做的工作分為以下三類:
(1) 受控實(shí)驗(yàn)
對(duì)于一個(gè)給定的任務(wù),開發(fā)者使用不同的語言進(jìn)行編程時(shí)受到監(jiān)視。研究然后比較結(jié)果,比如,開發(fā)成果和代碼質(zhì)量。Hanenberg 7 通過開發(fā)一個(gè)解析程序,對(duì) 48 位程序員監(jiān)視了 27 小時(shí),去比較了靜態(tài)與動(dòng)態(tài)類型。他發(fā)現(xiàn)這兩者在代碼質(zhì)量方面沒有顯著的差異,但是,基于動(dòng)態(tài)類型的語言花費(fèi)了更短的開發(fā)時(shí)間。他們的研究是在一個(gè)實(shí)驗(yàn)室中,使用本科學(xué)生,設(shè)置了定制的語言和 IDE 來進(jìn)行的。我們的研究正好相反,是一個(gè)實(shí)際的流行軟件應(yīng)用的研究。雖然我們只能夠通過使用回歸模型間接(和 事后 )控制混雜因素,我們的優(yōu)勢(shì)是樣本數(shù)量大,并且更真實(shí)、使用更廣泛的軟件。我們發(fā)現(xiàn)在相同的條件下,靜態(tài)化類型的語言比動(dòng)態(tài)化類型的語言更少出現(xiàn)錯(cuò)誤傾向,并且不允許隱式類型轉(zhuǎn)換的語言要好于允許隱式類型轉(zhuǎn)換的語言,其對(duì)結(jié)果的影響是非常小的。這是合理的,因?yàn)闃颖玖糠浅4?,所以這種非常小的影響在這個(gè)研究中可以看得到。
Harrison et al. 8 比較了 C++ 與 SML,一個(gè)是過程化編程語言,一個(gè)是函數(shù)化編程語言,在總的錯(cuò)誤數(shù)量上沒有找到顯著的差異,不過 SML 相比 C++ 有更高的缺陷密集度。SML 在我們的數(shù)據(jù)中并沒有體現(xiàn)出來,不過,認(rèn)為函數(shù)式編程語言相比過程化編程語言更少出現(xiàn)缺陷。另一個(gè)重點(diǎn)工作是比較跨不同語言的開發(fā)工作。12,20 不過,他們并不分析編程語言的缺陷傾向。
(2) 調(diào)查
Meyerovich 和 Rabkin 16 調(diào)查了開發(fā)者對(duì)編程語言的觀點(diǎn),去研究為什么一些編程語言比其它的語言更流行。他們的報(bào)告指出,非編程語言的因素影響非常大:先前的編程語言技能、可用的開源工具、以及現(xiàn)有的老式系統(tǒng)。我們的研究也證明,可利用的外部工具也影響軟件質(zhì)量;例如,在 Go 中的并發(fā) bug(請(qǐng)查看問題 4 節(jié)內(nèi)容)。
(3) 對(duì)軟件倉庫的挖掘
Bhattacharya 和 Neamtiu 1 研究了用 C 和 C++ 開發(fā)的四個(gè)項(xiàng)目,并且發(fā)現(xiàn)在 C++ 中開發(fā)的組件一般比在 C 中開發(fā)的組件更可靠。我們發(fā)現(xiàn) C 和 C++ 的錯(cuò)誤傾向要高于全部編程語言的平均值。但是,對(duì)于某些 bug 類型,像并發(fā)錯(cuò)誤,C 的缺陷傾向要高于 C++(請(qǐng)查看第 3 節(jié)中的問題 4)。
5. 有效的風(fēng)險(xiǎn)
我們認(rèn)為,我們的報(bào)告的結(jié)論幾乎沒有風(fēng)險(xiǎn)。首先,在識(shí)別 bug 修復(fù)提交方面,我們依賴的關(guān)鍵字是開發(fā)者經(jīng)常用于表示 bug 修復(fù)的關(guān)鍵字。我們的選擇是經(jīng)過認(rèn)真考慮的。在一個(gè)持續(xù)的開發(fā)過程中,我們?nèi)ゲ东@那些開發(fā)者一直面對(duì)的問題,而不是他們報(bào)告的 bug。不過,這種選擇存在過高估計(jì)的風(fēng)險(xiǎn)。我們對(duì)領(lǐng)域分類是為了去解釋缺陷的傾向,而且,我們研究組中另外的成員驗(yàn)證過分類。此外,我們花費(fèi)精力去對(duì) bug 修復(fù)提交進(jìn)行分類,也可能有被最初選擇的關(guān)鍵字所污染的風(fēng)險(xiǎn)。每個(gè)項(xiàng)目在提交日志的描述上也不相同。為了緩解這些風(fēng)險(xiǎn),我們像 2.4 節(jié)中描述的那樣,利用手工注釋評(píng)估了我們的類別。
我們判斷文件所屬的編程語言是基于文件的擴(kuò)展名。如果使用不同的編程語言寫的文件使用了我們研究的通用的編程語言文件的擴(kuò)展名,這種情況下也容易出現(xiàn)錯(cuò)誤傾向。為減少這種錯(cuò)誤,我們使用一個(gè)隨機(jī)樣本文件集手工驗(yàn)證了我們的語言分類。
根據(jù)我們的數(shù)據(jù)集所顯示的情形,2.2 節(jié)中的解釋類編程語言,我們依據(jù)編程語言屬性的主要用途作了一些假設(shè)。例如,我們將 Objective-C 分到非管理內(nèi)存類型中,而不是混合類型。同樣,我們將 Scala 注釋為函數(shù)式編程語言,將 C# 作為過程化的編程語言,雖然,它們?cè)谠O(shè)計(jì)的選擇上兩者都支持。 19,21 在這項(xiàng)研究工作中,我們沒有從過程化編程語言中分離出面向?qū)ο蟮木幊陶Z言(OOP),因?yàn)椋鼈儧]有清晰的區(qū)別,主要差異在于編程類型。我們將 C++ 分到允許隱式類型轉(zhuǎn)換的類別中是因?yàn)?,某些類型的?nèi)存區(qū)域可以通過使用指針操作被進(jìn)行不同的處理, 22 并且我們注意到大多數(shù) C++ 編譯器可以在編譯時(shí)檢測(cè)類型錯(cuò)誤。
最后,我們將缺陷修復(fù)提交關(guān)聯(lián)到編程語言屬性上,它們可以反應(yīng)出報(bào)告的風(fēng)格或者其它開發(fā)者的屬性??捎玫耐獠抗ぞ呋蛘?ruby>庫也可以影響一個(gè)相關(guān)的編程語言的 bug 數(shù)量。
6. 總結(jié)
我們對(duì)編程語言和使用進(jìn)行了大規(guī)模的研究,因?yàn)樗婕暗杰浖|(zhì)量。我們使用的 Github 上的數(shù)據(jù),具有很高的復(fù)雜性和多個(gè)維度上的差異的特性。我們的樣本數(shù)量允許我們對(duì)編程語言效果以及在控制一些混雜因素的情況下,對(duì)編程語言、應(yīng)用領(lǐng)域和缺陷類型之間的相互作用,進(jìn)行一個(gè)混合方法的研究。研究數(shù)據(jù)顯示,函數(shù)式語言是好于過程化語言的;不允許隱式類型轉(zhuǎn)換的語言是好于允許隱式類型轉(zhuǎn)換的語言的;靜態(tài)類型的語言是好于動(dòng)態(tài)類型的語言的;管理內(nèi)存的語言是好于非管理的語言的。進(jìn)一步講,編程語言的缺陷傾向與軟件應(yīng)用領(lǐng)域并沒有關(guān)聯(lián)。另外,每個(gè)編程語言更多是與特定的 bug 類別有關(guān)聯(lián),而不是與全部 bug。
另一方面,即便是很大規(guī)模的數(shù)據(jù)集,它們被多種方法同時(shí)進(jìn)行分割后,也變得很小且不全面。因此,隨著依賴的變量越來越多,很難去回答某個(gè)變量所產(chǎn)生的影響有多大這種問題,尤其是在變量之間相互作用的情況下。因此,我們無法去量化編程語言在使用中的特定的效果。其它的方法,比如調(diào)查,可能對(duì)此有幫助。我們將在以后的工作中來解決這些挑戰(zhàn)。
致謝
這個(gè)材料是在美國國家科學(xué)基金會(huì)(NSF)以及美國空軍科學(xué)研究辦公室(AFOSR)的授權(quán)和支持下完成的。授權(quán)號(hào) 1445079, 1247280, 1414172,1446683,F(xiàn)A955-11-1-0246。
參考資料
- Bhattacharya, P., Neamtiu, I. Assessing programming language impact on development and maintenance: A study on C and C++. In Proceedings of the 33rd International Conference on Software Engineering, ICSE'11 (New York, NY USA, 2011). ACM, 171–180.
- Bird, C., Nagappan, N., Murphy, B., Gall, H., Devanbu, P. Don't touch my code! Examining the effects of ownership on software quality. In Proceedings of the 19th ACM SIGSOFT Symposium and the 13th European Conference on Foundations of Software Engineering (2011). ACM, 4–14.
- Blei, D.M. Probabilistic topic models. Commun. ACM 55 , 4 (2012), 77–84.
- Cohen, J. Applied Multiple Regression/Correlation Analysis for the Behavioral Sciences. Lawrence Erlbaum, 2003.
- Easterbrook, S., Singer, J., Storey, M.-A., Damian, D. Selecting empirical methods for software engineering research. In Guide to Advanced Empirical Software Engineering (2008). Springer, 285–311.
- El Emam, K., Benlarbi, S., Goel, N., Rai, S.N. The confounding effect of class size on the validity of object-oriented metrics. IEEE Trans. Softw. Eng. 27 , 7 (2001), 630–650.
- Hanenberg, S. An experiment about static and dynamic type systems: Doubts about the positive impact of static type systems on development time. In Proceedings of the ACM International Conference on Object Oriented Programming Systems Languages and Applications, OOPSLA'10 (New York, NY, USA, 2010). ACM, 22–35.
- Harrison, R., Smaraweera, L., Dobie, M., Lewis, P. Comparing programming paradigms: An evaluation of functional and object-oriented programs. Softw. Eng. J. 11 , 4 (1996), 247–254.
- Harter, D.E., Krishnan, M.S., Slaughter, S.A. Effects of process maturity on quality, cycle time, and effort in software product development. Manage. Sci. 46 4 (2000), 451–466.
- Hindley, R. The principal type-scheme of an object in combinatory logic. Trans. Am. Math. Soc. (1969), 29–60.
- Jump, M., McKinley, K.S. Cork: Dynamic memory leak detection for garbage-collected languages. In ACM SIGPLAN Notices , Volume 42 (2007). ACM, 31–38.
- Kleinschmager, S., Hanenberg, S., Robbes, R., Tanter, É., Stefik, A. Do static type systems improve the maintainability of software systems? An empirical study. In 2012 IEEE 20th International Conference on Program Comprehension (ICPC) (2012). IEEE, 153–162.
- Li, Z., Tan, L., Wang, X., Lu, S., Zhou, Y., Zhai, C. Have things changed now? An empirical study of bug characteristics in modern open source software. In ASID'06: Proceedings of the 1st Workshop on Architectural and System Support for Improving Software Dependability (October 2006).
- Marques De Sá, J.P. Applied Statistics Using SPSS, Statistica and Matlab , 2003.
- Mayer, C., Hanenberg, S., Robbes, R., Tanter, É., Stefik, A. An empirical study of the influence of static type systems on the usability of undocumented software. In ACM SIGPLAN Notices , Volume 47 (2012). ACM, 683–702.
- Meyerovich, L.A., Rabkin, A.S. Empirical analysis of programming language adoption. In Proceedings of the 2013 ACM SIGPLAN International Conference on Object Oriented Programming Systems Languages & Applications (2013). ACM, 1–18.
- Milner, R. A theory of type polymorphism in programming. J. Comput. Syst. Sci. 17 , 3 (1978), 348–375.
- Mockus, A., Votta, L.G. Identifying reasons for software changes using historic databases. In ICSM'00. Proceedings of the International Conference on Software Maintenance (2000). IEEE Computer Society, 120.
- Odersky, M., Spoon, L., Venners, B. Programming in Scala. Artima Inc, 2008.
- Pankratius, V., Schmidt, F., Garretón, G. Combining functional and imperative programming for multicore software: An empirical study evaluating scala and java. In Proceedings of the 2012 International Conference on Software Engineering (2012). IEEE Press, 123–133.
- Petricek, T., Skeet, J. Real World Functional Programming: With Examples in F# and C#. Manning Publications Co., 2009.
- Pierce, B.C. Types and Programming Languages. MIT Press, 2002.
- Posnett, D., Bird, C., Dévanbu, P. An empirical study on the influence of pattern roles on change-proneness. Emp. Softw. Eng. 16 , 3 (2011), 396–423.
- Tan, L., Liu, C., Li, Z., Wang, X., Zhou, Y., Zhai, C. Bug characteristics in open source software. Emp. Softw. Eng. (2013).
作者
Baishakhi Ray (rayb@virginia.edu), Department of Computer Science, University of Virginia, Charlottesville, VA.
Daryl Posnett (dpposnett@ucdavis.edu), Department of Computer Science, University of California, Davis, CA.
Premkumar Devanbu (devanbu@cs.ucdavis.edu), Department of Computer Science, University of California, Davis, CA.
Vladimir Filkov (filkov@cs.ucdavis.edu), Department of Computer Science, University of California, Davis, CA.
腳注
- a. Wikipedia's article on type conversion, https://en.wikipedia.org/wiki/Type_conversion, has more examples of unintended behavior in C.
- b. This Apple developer article describes the usage of "id" http://tinyurl.com/jkl7cby.
- c. Some examples can be found here http://dobegin.com/objc-id-type/ and here http://tinyurl.com/hxv8kvg.
- d. Chi-squared value of 243.6 with 96 df. and p = 8.394e–15
- e. MPI does not require locking of shared resources.