A Comprehensive Study of Bloated Dependencies in the Maven Ecosystem

Build automation tools and package managers have a profound influence on software development. They facilitate the reuse of third-party libraries, support a clear separation between the application's code and its external dependencies, and automate several software development tasks. However, the wide adoption of these tools introduces new challenges related to dependency management. In this paper, we propose an original study of one such challenge: the emergence of bloated dependencies. Bloated dependencies are libraries that the build tool packages with the application's compiled code but that are actually not necessary to build and run the application. This phenomenon artificially grows the size of the built binary and increases maintenance effort. We propose a tool, called DepClean, to analyze the presence of bloated dependencies in Maven artifacts. We analyze 9,639 Java artifacts hosted on Maven Central, which include a total of 723,444 dependency relationships. Our key result is that 75.1% of the analyzed dependency relationships are bloated. In other words, it is feasible to reduce the number of dependencies of Maven artifacts up to 1/4 of its current count. We also perform a qualitative study with 30 notable open-source projects. Our results indicate that developers pay attention to their dependencies and are willing to remove bloated dependencies: 18/21 answered pull requests were accepted and merged by developers, removing 131 dependencies in total.


Introduction
Software reuse, a long time advocated software engineering practice [28,18], has boomed in the last years thanks to the widespread adoption of build automation and package managers [9]. Package managers provide both a large pool of reusable packages, a.k.a. libraries, as well as systematic ways to declare what are the packages on which an application depends. Examples of such package management systems include Maven for Java, npm for Node.js, or Cargo for Rust. Build tools automatically fetch all the packages that are needed to compile, test, and deploy an application.
Package managers boost software reuse by creating a clear separation between the application and its third-party dependencies. Meanwhile, they introduce new challenges for the developers of software applications, who now need to manage those third-party dependencies [9] to avoid entering into the so-called "dependency hell". These challenges relate to ensuring high quality dependencies [35], maintaining the dependencies up-to-date [3], or making sure that heterogeneous licences are compatible [42].
Our work focuses on one specific challenge of dependency management: the existence of bloated dependencies. This refers to packages that are declared as dependencies for an application, but that are actually not necessary to build or run it. The major issue with bloated dependencies is that the final deployed binary file includes more code than necessary: an artificially large binary is an issue when the application is sent over the network (e.g., web applications) or it is deployed on small devices (e.g., embedded systems). Bloated dependencies could also embed vulnerable code that can be exploited, while being actually useless for the application [12]. Overall, bloated dependencies needlessly increase the difficulty of managing and evolving software applications.
We propose a novel, unique, and large scale analysis about bloated dependencies. So far, research on bloated dependencies has been only touched with a study of copy-pasted dependency declarations by McIntosh and colleagues [25]. Our previous work gives preliminary results on this topic in the context of Java [13]. To our knowledge, there is no systematic analysis of the presence of bloated dependencies nor about the importance of this problem for developers.
Our work focuses on Maven, the most popular package manager and automatic build system for Java and languages that compile to the JVM. In Maven, developers declare dependencies in a specific file, called the POM file. In order to analyze thousands on artifacts on Maven Central, the largest repository of Java artifacts, manual analysis is not a feasible solution. To overcome this problem, we have designed and implemented a tool called DepClean, that performs an automatic analysis of dependencies usage in Maven projects. Given an application and its POM file, DepClean collects the complete dependency tree (the list of dependencies declared in the POM, as well as the transitive dependencies) and analyzes the bytecode of the artifact and all its dependencies to determine the presence of bloated dependencies. Finally, De-pClean generates a variant of the POM in which bloated dependencies are removed.
Armed with DepClean, we structure our analysis of bloated dependencies in two parts. First, we automatically analyse 9, 639 artifacts and their 723, 444 dependencies. We found that 75.1% of these dependencies are bloated, mostly due to transitive dependencies and the complexities of dependency management in multi-module projects. Second, we perform a user study involving 30 artifacts, for which the code is available as open-source on GitHub and which are actively maintained. For each project, we use DepClean to generate a POM file without bloated dependencies and submitted the changes as a pull request to the project. Notably, our work yielded 18 merged pull requests by open-source developers and 131 bloated dependencies were removed.
To summarize, this paper makes the following contributions: -A comprehensive study of bloated dependencies in the context of the Maven package manager. We are the first to quantify the magnitude of bloat on a large scale (9,  The remainder of this paper is structured as follows. Section 2 introduces the key concepts about dependency management with Maven and presents a illustrative example. Section 3 introduces the new terminology and describes the implementation of DepClean. Section 4 presents the research questions that drive our study, as well as the methodology followed. Section 5 covers our experimental results for each research question. Sections 6 and 7 provide a comprehensive discussion of the results obtained and present the threats to the validity of our study. Section 8 concludes this paper and provides future research directions.

Background
In this section, we provide an overview of the Maven package management system and present the essential terminology for this work. Then, we illustrate these concepts with a concrete example.

Maven Dependency Management Terminology
Maven is a popular package manager and build automation tool for Java projects and other languages that compile to the JVM (e.g., Scala, Kotlin, Groovy, Clojure, or JRuby). Maven supports developers in the management of the software build, from compilation to deployment [40]. Maven relies on a specific configuration file in XML format, known as the POM (acronym for "Project Object Model"), that allows customizing all the build lifecycle. This POM file contains information about the project and all the configuration details utilized by Maven during its different building phases (e.g., compile, test, package, install, and deploy). As in object-oriented programming, POM files can inherit from a base POM, known as the Maven parent POM.
Maven Project. A Maven project is a set of source code files and configuration files. There are two ways of designing a Maven project: as a singlemodule, or as a multi-module project. The first has a single POM file, which may include all the configurations needed to package the project as a single artifact (JAR file). The latter allows to separately build multiple artifacts in a certain specific order through a so-called aggregator POM. In multi-module projects, developers can define in a common parent POM the dependencies used by all the modules, this facilitates the update of dependency versions at once. The Maven reactor algorithm 1 ensures a deterministic build order in multi-module projects.
Maven Artifact. In this paper, we refer to artifacts as compiled Maven projects that have been deployed to some external binary code repository for later reuse. In Maven, artifacts are typically packaged as JAR files, which contain Java bytecode, incl. public API members. Each artifact is uniquely identified with a triplet (G:A:V ), where G is the groupId that identifies the organization that develops the artifact, A is the artifactId that identifies the name of the library (or the module in the case of multi-module projects), and V corresponds to its version, which follows the Maven versioning scheme 2 . Currently, the most popular public repository to host Maven artifacts is the Maven Central repository. Artifacts in Maven Central are immutable, i.e., once deployed, they cannot be modified nor updated. Accordingly, all the versions of a given library remain permanently available in the repository [39].
Dependency Resolution. The dependency resolution mechanism is a core feature of Maven. It works in two steps: (1) it determines the set of dependencies required to build an individual artifact, and (2) it fetches dependencies that are not present locally from external repositories such as Maven Central. Maven constructs a dependency tree, which represents the dependency relationships between the POMs of the resolved dependencies. One can distinguish between three types of Maven dependencies: direct, inherited, and transitive. Direct dependencies are those explicitly declared in the POM file of the project or of the module. Inherited dependencies are those declared in the parent POM. Transitive dependencies are those dependencies obtained from the transitive closure of direct and inherited dependencies. Maven relies on the dependency mediation algorithm 3 to determine what version of an artifact is chosen when multiple versions of the same dependency are present in the dependency tree.

A Brief Journey in the Dependencies of the Jxls Library
In this section, we illustrate the concepts introduced previously with one concrete example: Jxls 4 , an open source Java library for generating Excel reports. Jxls uses a special XLS template markup to define the output formatting and data layout for the reports. It is implemented as a multi-module Maven project with a parent POM: jxls-project, composed of three modules: jxls, jxls-examples, and jxls-poi.
Listing 1 shows an excerpt of the POM file of the jxls-poi module, version 1.0.15. It declares jxls-project as its parent POM in lines 1 − 5 and a direct dependency towards the poi Apache library in lines 10 − 14. Figure 1 depicts an excerpt of its Maven dependency tree (we do not show testing dependencies here, such as JUnit, to make the figure more readable). Nodes in blue, pink, and yellow represent direct, inherited, and transitive dependencies, respectively, for the jxls-poi artifact (as reported by the dependency:tree Maven plugin). As we observe, it explicitly declares three direct dependencies: commons-jexl, jxls, and poi, while artifacts jcl-over-slf4j and slf4j-api are inherited dependencies of jxls-poi because they are declared in its parent POM. Since the artifact poi depends on Apache commons-codec and Apache commons-collections4, these artifacts are automatically added to the classpath of jxls-poi as transitive dependencies.
The library jcl-over-slf4j declares a dependency towards slf4j-api, version 1.7.12, which is omitted by Maven since it is already added from the jxls-project parent POM. On the other hand, Jxls declares dependencies to version 1.7.26 of jcl-over-slf4j and slf4j-api, but the lower version 1.7.12 was chosen over it since it is nearer to the root in the dependency tree and, by default, Maven resolves version conflicts with a nearest-wins strategy. Once Maven finishes the dependency resolution, the classpath of jxls-poi includes the following artifacts: poi, commons-codec, commons-collections4, commons-jexl, commons-logging, jxls, commons-jex13, commons-beanutils, commons-collections, logback-core, jclover-slf4j, and slf4j-api. The goal of our work is to determine if all the artifacts in the classpath of Maven projects such as jxls-poi are actually needed to build and run those projects.

Bloated Dependencies
In this section, we introduce the key concept presented and studied in the rest of this paper: bloated dependencies. Then we describe our methodology to study bloated dependencies, as well as our tooling to automatically detect and remove them from Maven projects.

Novel Concepts
Dependencies among Maven artifacts form a graph, according to the information declared in their POMs. The Maven Dependency Graph (MDG) is defined as follows: Definition 1 (Maven Dependency Graph) The MDG is a vertex-labelled graph, where vertices are Maven artifacts (uniquely identified by their G:A:V coordinates), and edges represent dependency relationships among them. Formally, the MDG is defined as G = (V, E), where: -V is the set of artifacts in the Maven Central repository -E ⊆ V × V represent the set of directed edges that determine dependency relationships between each artifact v ∈ V and its dependencies Each artifact in the MDG has its own Maven Dependency Tree (MDT), which is the transitive closure of all the dependencies needed to build the artifact, as resolved by Maven.
Definition 2 (Maven Dependency Tree) The MDT of an artifact v ∈ V is a directed acyclic graph of artifacts, with v as the root node, and a set of edges E representing dependency relationships between them.
In this work, we introduce the novel concept of bloated dependency as follows: Definition 3 (Bloated Dependency) An artifact p is said to have a bloated dependency relationship ε b ∈ E if there is a path in its MDT, between p and any dependency d of p, such as none of the elements in the API of d are used, directly or indirectly, by p.
In order to reason about bloated dependencies, we extend the MDT to model the dependency trees of Maven artifact depending on usage relationships. To do so, we introduce edge labels to represent two types of dependency edge statuses: used or bloated. Consequently, we define the Dependency Usage Tree (DUT) as follows: Definition 4 (Dependency Usage Tree) The DUT, defined as G = (V, E, ∇), is an extension of the MDG that implements a dependency usage labelling function, which assigns one of the following six dependency usage types to the relationship between Maven artifacts and their dependencies in the MDT: ∇ : E → {ud, ui, ut, bd, bi, bt} such that: ud, if d is used and it is directly declared by p ui, if d is used and it is inherited from a parent of p ut, if d is used and it is resolved transitively by p bd, if d is bloated and it is directly declared by p bi, if d is bloated and it is inherited from a parent of p bt, if d is bloated and it is resolved transitively by p Figure 2 illustrates how the dependency usage function creates a DUT for the example presented in Figure 1. If we analyze the bytecode contained in jxls-poi, no references to any API member in the direct dependencies commons-jexl (explicitly declared in the POM) and sl4j (inherited from its parent POM) can be found. Therefore, according to Definition 4, they are labelled as bloated-direct (bd) and bloated-inherited (bi) dependencies, respectively. According to the bytecode usage analysis, the direct dependency commons-jexl can be safely removed directly from the POM, while the inherited dependency sl4j can be safely removed in the parent. Indeed, after analyzing the Git history of Jxls, we found that these changes were made in the POM by the authors of the project in a later released version of this library 5 .   Figure 1. Edges are labelled according to Definition 4 to reflect the usage status between jxls-poi and each one of its dependencies. Now let us consider dependencies obtained transitively through the Maven dependency resolution mechanism. commons-codec and commonscollections are included by transitive closure from jxls-poi, but they are never used, directly in its bytecode or indirectly through a used API member of its dependencies. Therefore, these bloated-transitive (bt) dependencies are not necessary to build jxls-poi.

Example
To sum up, the set of dependencies of a given Maven artifact can be partitioned into six dependency usage types, according to two criteria: (1) the dependency usage status (used or bloated), and (2) the dependency type (direct, inherited, or transitive). Table 1 summarizes this partition for jxls-poi. For our study, we design and implement a specific tool called DepClean. An overview of DepClean is shown in Figure 3, it works as follows. It receives as inputs a built Maven project and a repository of artifacts, then it extracts the dependency tree of the projects and constructs a DUT to identify the set of dependencies that are actually used by the project. DepClean has two outputs: (1) it returns a report with the usage status of all types of dependencies, and (2) it produces an alternative version of the POM file (POM d ) with all the bloated dependencies removed (i.e., the XML node of the bloated dependency is removed). Algorithm 1 details the main procedure of DepClean. The algorithm receives as input a Maven artifact p that includes a set of dependencies in its dependency tree, denoted as DT , and returns a report of the usage status of its dependencies and a debloated version of its POM. Notice that DepClean computes two transitive closures: over the Maven dependency tree (line 2) and over the call graph of API members (line 3).
The algorithm first copies the POM file of p, resolving all its direct and transitive dependencies locally, and obtaining the dependency tree (lines 1 and 2). If p is a module of a multi-module project, then all the dependencies declared in its parent POM are included as direct dependencies of p. Then, the algorithm proceeds to construct a set with the dependencies that are actually used by p (line 3).
Algorithm 2 explains the bytecode analysis. The detection component statically analyzes the bytecode of p and all its dependencies to check which API members are being referenced by the artifact, either directly or indirectly. Notice that it behaves differently if the included artifact is a direct, inherited, or a transitive dependency. If none of the API members of a dependency d ∈ DT are called, even indirectly via transitive dependencies, then d is considered to be bloated and we proceed to remove it.
In Maven, we remove bloated dependencies in two different ways: (1) if the bloated dependency is explicitly declared in the POM, then we remove  Fig. 3 Overview of the tool DepClean to detect and remove bloated dependencies in Maven projects. Rounded squares represent artifacts, circles inside the artifacts are API members, arrows between API members represents bytecode calls between artifacts, arrows between artifacts represent dependency relationships between them.
Algorithm 1: Main procedure to detect and remove bloated dependencies.
Input: A Maven artifact p.
Output: A report of the bloated dependencies of p and a clean POM file f of p without bloated dependencies. its declaration clause directly (line 9 in Algorithm 1), or (2) if the bloated dependency is inherited from a parent POM or induced transitively, then we excluded it in the POM (line 11 in Algorithm 1). This exclusion consists in adding an <exclusion> clause inside a direct dependency declaration, with the groupId and artifactId of the transitive dependency to be excluded. Excluded dependencies are not added to the classpath of the artifact by way of the dependency in which the exclusion was declared.
DepClean is implemented in Java as a Maven plugin that extends the maven-dependency-analyzer 6 tool maintained by the Apache foundation. For the construction of the dependency tree, DepClean relies on the maven-dependency-plugin with the copy-dependencies and tree goals. Internally, DepClean addresses some of the key aspects of static analysis in Java. It relies on the ASM 7 library to visit all the .class files in order to register bytecode calls towards classes, methods, fields, and annotations among Maven artifacts. To do so, we define a customized parser that reads entries in the constant pool table of .class files directly, in case it contains special references that ASM does not support. This allows the plugin to statically cap-ture reflection calls that are based on string literals and concatenations. De-pClean adds unique features to the maven-dependency-analyzer to detect and report transitive and inherited bloated dependencies, and to produce a debloated version of the POM file. DepClean is open-source and reusable from Maven Central, the source code is available at https://github.com/castorsoftware/depclean.

Experimental Methodology
In this section, we present the research questions that articulate our study. We also describe the experimental protocols used to select and analyze Maven artifacts for an assessment of the impact of bloated dependencies in this ecosystem.

Research Questions
Our investigation of bloated dependencies in the Maven ecosystem is composed of four research questions grouped in two parts. In the first part, we perform a large scale quantitative study to answer the following research questions: -RQ1: How frequently do bloated dependencies occur? With this research question, we aim at quantifying the amount of bloated dependencies among 9, 639 Maven artifacts. We measure direct, inherited and transitive dependencies to provide an in-depth assessment of the dependency bloat in the Maven ecosystem. -RQ2: How do the reuse practices affect bloated dependencies? In this research question, we analyze bloated dependencies with respect to two distinctive aspects of reuse in the Maven ecosystem: the additional complexity of the Maven dependency tree caused by transitive dependencies, and the choice of a multi-module architecture.
The second part of our study focuses on 30 notable Maven projects and presents the qualitative feedback about how developers react to bloated dependencies, and to the solutions provided by DepClean. It is guided by the following research questions:  Figure 4 shows our process to build a dataset of Maven artifacts in order to answer RQ1 and RQ2. Steps x and y focus on the collection of a representative set of Maven artifacts: we sample our study subjects from the whole Maven Dependency Graph, then we resolve the dependencies of each study subject. In steps z and {, we analyze dependency usages with DepClean and compute the set of metrics to answer RQ1 and RQ2.
x Filter artifacts. In the first step, we leverage the Maven Dependency Graph (MDG) from previous research [4], a graph database that captures the complete dependency relationships between artifacts in Maven Central at a given point in time. Then, we randomly select a representative sample of 14, 699 Maven artifacts. Representativeness is achieved by sampling over the probability distribution of the number of dependencies per artifact in the MDG, per the recommendation of Shull [38,Chapter 8.3.1]. From the sampled artifacts, we select as study subjects all the artifacts that meet the following additional criteria. This results in a dataset of 9, 639 artifacts.
-Public API: The subjects must contain at least one .class file with one or more public methods, i.e., can be reused via external calls to their API. y Resolve dependencies. In the second step, we download the binaries of all the selected artifacts and their POMs from Maven Central and we resolve all their direct and transitive dependencies to a local repository. To ensure the consistency of our analysis, we discard the artifacts that depend on libraries hosted in external repositories. In case of any other error when downloading some dependency, we exclude the artifact from our analysis. Table 2 shows the descriptive statistics about 9, 639 artifacts in our dataset for RQ1 and RQ2. The dataset includes 44, 488 direct, 180, 693 inherited, and 498, 263 transitive dependency relationships (723, 444 in total). We report about their dependencies with compile scope, since those dependencies are necessary to build the artifacts. Columns #C, #M, and #F give the distribution of the number of classes, methods, and fields per artifact (we count both the public and private API members). The size of artifacts varies, from small artifacts with one single class (e.g., org.elasticsearch.client:transport: 6.2.4), to large libraries with thousands of classes (e.g., org.apache.hive: hive-exec:3.1.0). In total, we analyze the bytecode of more than 30 millions of API members. Columns #D, #I, and #T accounts for the distributions of direct, inherited, and transitive dependencies, respectively. com.bbossgroups.pdp:pdp-system:5.0.3.9 is the artifact with the largest number of declared dependencies in our dataset, with 148 dependency declarations in its POM file, while be.atbash.json:octopus-accessors-smart: 0.9.1 has the maximum number of transitive dependencies: 1, 776. Interestingly, the distributions of direct and transitive dependencies are notably different, the total number of transitive dependencies is an order of magnitude larger than direct dependencies, with mean of 20 and 4, respectively. z Dependency usage analysis. This is the first step to answer RQ1 and RQ2: collect the status of all dependency relationships for each artifact in our dataset. For each artifact, we first unpack its JAR file, as well as its dependencies. Then, for each JAR file, we analyze all the bytecode calls to API class members using DepClean. This provides a Dependency Usage Tree (DUT) for each artifact, on which each dependency relationship is labeled with one of the six categories as we illustrated in Table 1: bloated-direct (bd), bloated-inherited (bi), bloated-transitive (bt), used-direct (ud), used-inherited (ui), or used-transitive (ut).
{ Collect dependency usage metrics. This last step consists of collecting a set of metrics about the global presence of bloated dependencies. We focus on the presence of bloat with respect to the complexity of the Maven dependency trees, and with respect to the multi-module Maven architecture. In RQ1, we analyze our dataset as a whole, looking at the usage status of dependency relationships from two perspectives.
Global distribution of dependency usage. This is the normalized distribution of each category of dependency usage, over each dependency relationship of each of the 9, 639 artifacts in our dataset.
Distribution of dependency usage type, per artifact. For each of the six types of dependency usage, we compute the normalized ratio over the total number of dependency relationships for each artifact in our dataset.
In RQ2, we analyze how the specific reuse strategies of Maven relate to the presence of bloated dependencies. First, we relate bloated dependencies to the complexity of the Maven dependency tree of each artifact, according to the following metrics.
Bloated dependencies w.r.t. the number of transitive dependencies. For each artifact that has at least one transitive dependency, we determine the relation between the ratio of transitive dependencies and the ratio of bloated dependencies.
Bloated-transitive dependencies w.r.t. to the height of the dependency tree. Given an artifact and its Maven dependency tree, the height of the tree is the longest path between the root and its leaves. To compute this metric, we group our artifacts according to the height of their tree. The maximum dependency tree height that we observed is 14. However, there are only 58 artifacts with a tree higher than 10. So, we group all artifacts with height ≥ 10. For each subset of artifact with the same height, we compute the size of the subset and the distribution of bloated-transitive dependencies of each artifact in the subset.
Second, we distinguish the presence of bloated dependencies between single and multi-module Maven projects, according to the following metrics: Global distribution of dependency usage in a single or multi-module project. We present two plots that measure the distribution of each type of dependency usage in the set of single and multi-module projects. It is to be noted that the plot for single-module projects does not include bloated-inherited (bi) and used-inherited (ui) dependencies since they have no inherited dependencies.
Distribution of dependency usage type, per artifact, in a single or multimodule project. We present two plots that provide six distributions each: the distribution of each type of dependency usage type for artifacts that are in a single-module or multi-module project.

Protocol of the Qualitative Study (RQ3 & RQ4)
In RQ3 and RQ4, we perform a qualitative assessment about the relevance of bloated dependencies for the developers of open-source projects. We systematically select 30 notable open-source projects hosted on GitHub to conduct this analysis. We query the GitHub API to list all the Java projects ordered by their number of stars. Then, we randomly select a set of projects that fulfil all the following criteria: (1) we can build them successfully with Maven, (2) the last commit was at the latest in October 2019, (3) they declare at least one dependency in the POM, (4) they have a description in the README about how to contribute through pull requests, and (5) they have more than 100 stars on GitHub. Table 3 shows the selected 30 projects per those criteria, to which we submitted at least one pull request. They are listed in decreasing order of stars on GitHub. The first column shows the name of the project as declared on GitHub, followed by the name of the targeted module if the project is multimodule. Notice that in the case of jenkins we submitted two pull requests targeting two distinct modules: core and cli. Columns two to four describe the projects according to its category as assigned to the corresponding released artifact in Maven Central, the number of commits in the master branch in October 2019, and the number of starts at the moment of conducting this study. Columns five to seven report about the total number of direct, inherited, and transitive dependencies included in the dependency tree of each considered project.
We answer RQ3 according to the following protocol: first, we run Dep-Clean and try to build the artifact with the debloated POM file; second, we analyze the history of the project to propose a relevant change to the developers; third, we propose a change in the POM file in the form of a pull request; four, we discuss the pull request through GitHub. Figure 5 shows an excerpt of the diff of such a change. Since our submitted pull requests represent a small modification in the POM, they have a good chance to be reviewed by developers and discussed before being merged or rejected.
ProTip! Use n and p to navigate between commits in a pull request. In the first step of the protocol, we use DepClean to obtain a report about the usage of dependencies. We analyze dependencies with both compile and test scope. Once a bloated-direct dependency is found, we remove it directly in the POM and proceed to build the project. If the project builds successfully after the removal (all the tests pass), we submit the pull request with the corresponding change. If after the removal of the dependency the build fails, then we consider the dependency as used dynamically and do not suggest to remove it. In the case of multi-module projects, with bloated dependencies in several modules, we submitted a single pull request per module. For each pull request, we analyze the Git history of the POM file to determine when the bloated dependency was declared or modified. Our objective is to collect information in order to understand how the dependencies of the projects change during their evolution. This allows us to prepare a more informative pull request message and to support our discussion with developers. We also report on the benefits of tackling these bloated dependencies by describing the differences between the original and the debloated packaged artifact of the project in terms of the size of the bundle and the complexity of its dependency tree, when the difference was significant. Each pull request includes an explanatory message. Figure 6 shows an example of the pull request message submitted to the project Undertow 8 . The message explains the motivations of the proposed change, as well as the negative impact of keeping these bloated dependencies in the project.
To answer RQ4, we follow the same pull request submission protocol as for RQ3. We use DepClean to detect bloated-transitive dependencies and submit pull requests suggesting the addition of the corresponding exclusion Hello, I noticed that dependencies undertow-servlet , undertow-websockets-jsr , jboss-loggingprocessor , xnio-nio , jmh-generator-annprocess , and httpmime are declared in the pom of the undertow-benchmarks module. However, these dependencies are not used in any of the four classes of the module. Removing these unused dependencies in the pom has an impact on the size of the packaged jar file for the benchmarks (reduction is more than 1MB), and also decreases the complexity of the Maven dependency tree of the module.
Contributor undertow-pull-request commented on Nov 13 Can one of the admins verify this patch?
fl4via commented on Nov 15 Hi, @cesarsotovalero ! Thanks for your PR, it will soon be tested!  clauses in each project POM. Figure 7 shows an example of a pull request message submitted to the project Apache Accumulo 9 , while Figure 8 shows an excerpt of the commit proposing the exclusion of the transitive dependency org.apache.httpcomponents:httpcore from the direct dependency org.apache.thrift:libthrift in its POM.

+18 −0
Exclude unused transitive dependencies #1421 Additional information related to the selected projects and the research methodology employed is publicly available as part of our replication package at https://github.com/castor-software/depclean-experiments.

Experimental Results
We now present the results of our in-depth analysis of bloated dependencies in the Maven ecosystem.

RQ1: How frequently do bloated dependencies occur?
In this first research question, we investigate the status of all the dependency relationships of the 9, 639 Maven artifacts under study. Figure 9 shows the overall status of the 723, 444 dependency relationships in our dataset. The x-axis represents the percentages, per usage type, of all the dependencies considered in the studied artifacts. The first observation is that the bloat phenomenon is massive: 543, 610 (75.1%) of all dependencies are bloated, they are not needed to compile and run the code. This bloat is divided into three separate categories: 19  Consequently, Figure 9 shows that 75.1% of the relationships (edges in the dependency usage tree) are bloated dependencies. Note that this observation does not mean that 723, 444 artifacts are unnecessary and can be removed from Maven Central. The same artifact can occur in several DUTs and can be part of a bloated dependency relationship in some DUTs and part of a used relationships in the other DUTs.
In the following, we discuss in more detail and give examples to illustrate the occurrence of the three types of bloated dependency relationships analyzed.
Bloated-direct. We found that 2.7% of the dependencies declared in the POM file of the studied artifacts are not used at all via bytecode calls. As an example, the Apache Ignite 10 project has deployed an artifact: org.apache.ignite:ignite-zookeeper:2.4.0, which contains only one class in its bytecode: TcpDiscoveryZookeeperIpFinder, and it declares a direct dependency in the POM towards slf4j, a widely used Java logging library. However, if we analyze the bytecode of ignite-zookeeper, no call to any API member of sl4j exist and therefore it is a bloated-direct dependency. After a manual inspection of the commit history of the POM, we found that sl4j was extensively used across all the modules of Apache Ignite at the early stages of the project, but it was later replaced by a dedicated logger, and its declaration remained intact in the POM.
Bloated-inherited. In our dataset, a total of 4, 963 artifacts are part of multi-module Maven projects. Each of these artifacts declares a set of dependencies in its POM file, and also inherits a set of dependencies from a parent POM. DepClean marks those inherited dependencies are either bloated-inherited or used-inherited. Our dataset includes a total of 111, 649 dependency relationships labeled as bloated-inherited, which represents 15.4% of all dependencies under study and 61.8% of the total of inherited dependencies. For example, the artifact org.apache.drill:drill-protocol:1.14.0 inherits dependencies commons-codec and commons-io from its parent POM org.apache.drill:drill-root:1.14.0, however, those dependencies are not used in this module, and therefore they are bloated-inherited dependencies.
Bloated-transitive. In our dataset, bloated-transitive dependencies represent the majority of the bloated dependency relationships: 412, 288 (57%). This type of bloat is a natural consequence of the Maven dependency resolution mechanism, which automatically resolves all the dependencies whether they are explicitly declared in the POM file of the project or not. Transitive dependencies are the most common type of dependency relationships, having a direct impact on the growth of the dependency trees. This type of bloat is the most common in the Maven ecosystem. For example, the artifact org.eclipse.milo:sdk-client:0.2.1 ships the transitive dependency gson in its MDT, induced from its direct dependency towards bsd-parser-core. However, the part of bsd-parser-core used by sdk-client does not calls any API member of gson, and therefore it is a bloated-transitive dependency.
In the following, we discuss the dependencies that are actually used. We observe that direct dependencies represent only 3.4% of the total of dependencies in our dataset. This means that the majority of the dependencies that are necessary to build Maven artifacts are not declared explicitly in the POM files of these artifacts.
It is interesting to note that 85, 975 of the dependencies used by the artifacts under study are transitive dependencies. This kind of dependency usage occurs in two different scenarios: (1) the artifact uses API members of some transitive dependencies, without declaring them in its own POM file; or (2) the transitive dependency is a necessary provider for another artifact in the dependency tree.
We now discuss an example of the first scenario based on the org.apache.streams:streams-filters:0.6.0 artifact from the Apache 1 package org.apache.streams.filters; 2 import com.google.common.base.Preconditions; 3 public class VerbDefinitionDropFilter implements StreamsProcessor { 4 ...  ... 12 } Listing 2 Code snippet of the class VerbDefinitionDropFilter present in the artifact org.apache.streams:streams-filters:0.6.0. The library com.google.guava:guava:20.0 is included in its classpath via transitive dependency and called from the source code, but no dependency towards guava is declared in its POM.
Streams 11 project. It contains two classes: VerbDefinitionDropFilter and VerbDefinitionKeepFilter. Listing 2 shows part of the source code of the class VerbDefinitionDropFilter, which imports the class PreCondition from library guava (line 2) and uses its static method checkArgument in line 8 of method process. However, if we inspect the POM of streams-filters, we notice that there is no dependency declaration towards guava. It declares a dependency towards streams-core, which in turn depends on the streams-utils artifact that has a direct dependency towards guava. Hence, guava is a used-transitive dependency of streams-filters, called from its source code.
Let us now present an example of the second scenario. Listing 3 shows an excerpt of the class AuditTask included in the artifact org.duracloud: auditor:4.4.3, from the project DuraCloud 12 . In line 6, the method getPropsSerializer instantiates the JaxbJsonSerializer object that belongs to the direct dependency org.duracloud:common-json:4.4.3. This object in turns creates an ObjectMapper from the transitive dependency jackson-mapper-asl. Hence, jackson-mapper-asl is a necessary, transitive provider for org.duracloud:auditor:4.4.3. Figure 10 shows the distributions of dependency usage types per artifact. The figure presents superimposed log-scaled box-plots and violin-plots of the number of dependency relationships corresponding to the six usage types studied. Box-plots indicate the standard statistics of the distribution (i.e., lower/upper inter-quartile range, max/min values, and outliers), while violin plots indicate the entire distribution of the data.
We observe that the distributions of the bloated-direct (bd) and bloatedtransitive (bt) dependencies vary greatly. Bloated-direct dependencies are distributed between 0 and 1 (1st-Q and 3rd-Q), with a median of 0; whereas the second ranges between 2 and 41 (1st-Q and 3rd-Q), with a median of 11. These 1 package org.duracloud.audit.task; 2 import org.duracloud.common.json.JaxbJsonSerializer; 3 public class AuditTask extends TypedTask { 4 ...  values are in line with the statistics presented in Table 2, since the number of direct and transitive dependencies in general differ approximately by one order of magnitude. Overall, from the 9, 639 Maven artifacts studied, 3, 472 (36%) have at least one bloated-direct dependency, while 8, 305 (86.2%) have at least one bloated-transitive. On the other hand, the inter-quartile range of bloated-direct (bd) dependencies is more compact than the used-direct (ud). In other words, the dependencies declared in the POM are mostly used. This result is expected, since developers have more control over the edition (adding/removing dependencies) of the POM file of their artifact.
The median number of used-transitive (ut) dependencies is significantly lower than the median number of bloated-transitive (bt) dependencies (2, vs. 11). This suggests that the the default dependency resolution mechanism of Maven is suboptimal.
The number of outliers in the box-plots differs for each usage type. Notably, the bloated-direct dependencies have more outliers (in total, 25 artifacts have more than 100 bloated-direct dependencies). In particular, the artifact com.bbossgroups.pdp:pdp-system:5.0.3.9 has the maximum number of bloated-direct dependencies: 133, out of the 147 declared in its POM. The total number of artifacts with at least one bloated-direct dependency in our dataset is 2, 298, which represents 23.8% of the 9, 639 studied artifacts.
Answer to RQ1: The analysis of the 723, 444 analysed dependency relationships in our dataset reveals that 543, 610 (75.1%) of them are bloated. Most of the bloated dependencies are transitive 412, 288 (57%). Overall, 36% of the artifacts have at least one bloated dependency that is declared in their POM file. To our knowledge, this is the first scientific observation of this phenomenon. Implications: Since developers have more control over direct dependencies, up to 17, 673 (2.7%) of dependencies can be removed directly from the POM of Maven artifacts, in order to obtain smaller binaries and reduced attack surface. RQ3 will explore the willingness of developers to do so.

RQ2: How do the reuse practices affect bloated dependencies?
Most of the bloated dependencies in our dataset are either transitive (57%) or inherited (15.4%). In this research question, we investigate how the reuse practices that lead to those types of dependencies relate to the type of bloated dependency that emerges in Maven artifacts. Figure 11 shows the distributions, in percentages, of the direct, inherited, and transitive dependencies for the 9, 639 studied artifacts. The artifacts are sorted, from left to right, in increasing order according to the ratio of direct dependencies. The y-axis indicates the ratio of each type of dependency for a give artifact. First, we observe that 4, 967 artifacts belong to multimodule projects. Among these artifacts, the extreme case (far left of the plot) is org.janusgraph:janusgraph-berkeleyje:0.4.0, which declares 1.4% of its dependencies in its POM, while the 48.6% of dependencies are inherited from parent POM files, and 50% are transitive. Second, we observe that the ratio of transitive dependencies is not equally distributed. On the right side of the plot, 879 (9.1%) artifacts have no transitive dependency (they have 100% direct dependencies). Meanwhile, 5, 561 (57.7%) artifacts have more than 50% transitive dependencies. The extreme case is org.apereo.cas: cas-server-core-api-validation:6.1.0, with 77.6% transitive dependencies.  The bar plot on top of Figure 13 indicates the number of artifacts that have the same height. We observe that most of the artifacts have a height of 4: 2, 226 artifacts in total. Considering the number of dependencies, this suggests that the dependency trees tend to be wider than deep. This is because any dependency that already appears at a level closer to the root will be omitted by Maven if it is referred to at a deeper level. Looking at the 58 artifacts with height ≥ 10, we notice that most of them belong to multi-module projects, and declare other modules in the same project as their direct dependencies. This is a regular practice of multi-module projects, which allows to release each module as an independent artifact. Meanwhile, this drastically increase the complexity of dependency trees. For example, the artifact org.wso2.carbon.devicemgt: org.wso2.carbon.apimgt.handlers:3.0.192 is the extreme case of this practice in our dataset, with a dependency tree of height 11 and two direct dependencies towards other modules of the same project that in turn depends on other modules of this project. As a result, this artifact has 342 bloatedtransitive and 87 bloated-inherited dependencies, and is part of a multi-module project with a total of 79 modules released in Maven Central.
The plot in Figure 13 shows a clear increasing trend of bloated-transitive dependencies as the height of the dependency tree increases. For artifacts with a dependency tree of height greater than 9, at least 28% of their transitive dependencies are bloated, while the median of the percentages of bloatedtransitive dependencies for artifacts with height larger than 5 is more than 50%. This finding confirms and complements the results of Figure 12, showing that the complexity of the dependency tree is directly related to the occurrence of bloat.
In order to validate the graphical finding, we perform a Spearman's rank correlation test between the number of bloated-transitive dependencies and the size of the dependency tree, i.e., the number of nodes in each tree. We found that there is a significant positive correlation between both variables (ρ =0.67, p-value < 0.01). This confirms that the actual usage of transitive dependencies decreases with the increasing complexity of the dependency tree. This result is aligned with our previous study that suggest that most of the public API members of transitive dependencies are not used by its clients [13].
In summary, our results point to the excess of transitive dependencies as one of the fundamental causes of the existence of bloated dependencies in the Maven ecosystem.

Single-module vs. multi-module
Let us investigate on the differences between single and multi-module architectures with respect to the presence of bloated dependencies. Figure 14 compares the distributions of bloated and used dependencies between multimodule and single-module artifacts in our dataset. We notice that, in general, multi-module artifacts have slightly more bloat than single-module, precisely 3.1% more (the percentage of bloat in single-module is 5.8% + 67.3% = 73.1% vs. 0.9% + 24.2% + 51.1% = 76.2% in multi-module). More interestingly, we observe that a majority of the inherited dependencies are bloated: 24.2% of the dependencies among multi-module project are bloated-inherited (bi), while only 15% are used-inherited (ui). This suggest that most of the dependencies inherited by Maven artifacts that belong to multi-module artifacts are not used by these modules. We observe that the percentage of bloated-direct dependencies in multimodule artifacts is very small (0.9%) in comparison with single-module (5.8%). Meanwhile, the percentage of bloated-transitive dependencies in single-module (67.3%) is larger than in multi-module (51.1%). This is due to the "shift" of a part of direct and transitive dependencies into inherited dependencies when using a parent POM. Indeed, the "shift" from direct to inherited is the main motivation for having a parent POM: to have one single declaration of dependencies for many artifacts instead of letting each artifact manage their own dependencies.
This "shift" in the nature of dependencies between single and multi-module artifacts is further emphasized in Figure 15. This plot shows superimposed log scaled box-plots and violin-plots comparing the distributions of the number of distinct dependency usage types per artifact, for single-module (top part of the figure) and multi-module (bottom part). We observe that multi-module artifacts have less bloated-direct (1st-Q = 0, median = 0, 3rd-Q = 0) and less bloated-transitive (1st-Q = 2, median = 9, 3rd-Q = 40), compared to single-modules, as shown in Figure 15. However, multi-module artifacts have a considerably larger number of bloated-inherited dependencies instead (1st-Q = 1, median = 5, 3rd-Q = 20). The extreme case in our dataset is the artifact co.cask.cdap:cdap-standalone:4.3.4, with 326 bloated-inherited dependencies in total.
In summary, the multi-module architecture in Maven projects contributes to limit redundant dependencies and facilitates the consistent versioning of dependencies in large projects. However, it introduces two challenges for developers. First, it leads to the emergence of bloated-inherited dependencies because of the friction of maintaining a common parent POM file: it is more difficult to remove dependencies from a parent POM than from an artifact's own POM. Second, it is more difficult for developers to be aware of and understand the dependencies that are inherited from the parent POM. This calls for better tooling and user interfaces to help developer grasp their inherited dependencies, which is virtually absent in the current Maven tooling ecosystem.
Answer to RQ2: Reuse practices through to complex dependency trees and multi-module architecture are correlated with the presence of bloated dependencies: the higher a dependency tree, the more bloatedtransitive dependencies; bloat is more pervasive in multi-module than single-module artifacts. Implications: Developers should carefully consider reusing artifacts with several dependencies because they introduce bloat. They should also contemplate the risks of having bloated dependencies when considering adopting a multi-module architecture.

RQ3:
To what extent are developers willing to remove bloated-direct dependencies?
In this research question, our goal is to see how developers react when made aware of bloated-direct dependencies in their projects. We do this by proposing the removal of bloated-direct dependencies to lead developers of mature opensource projects, as described in Subsection 4.2.2. Table 4 shows the list of 18 pull requests submitted. Each pull request proposes the removal of at least one bloated-direct dependency in the POM. We received response from developers for 15 pull request. The first and second columns in the table show the name of the project and the URL to the pull request on GitHub. Columns three and four represent the number of bloated dependencies removed in the POM and the total number of dependencies removed from the dependency tree with the proposed change, incl. transitive ones. The last column shows the status of the pull request ( accepted, accepted with changes, rejected, or / pending). The last row represent the acceptance rate calculated with respect to the projects with response, i.e., the total number of dependencies removed divided by the number of proposed removals. For example, for project undertow we propose the removal of 6 bloated dependencies in its module benchmarks. As a result of this change, 17 transitive dependencies were removed from the dependency tree the module.
Overall, from the pull requests with responses from developers, 14/15 were accepted and merged. In total, 68 dependencies were removed from the dependency trees of the projects. This result demonstrates the relevance of handling bloated-direct dependencies for developers, and the practical usefulness of De-pClean.
Let us now summarize the developer feedback. First, all developers agreed about the importance of refining the projects' POMs. This is reflected in the positive comments received. Second, their quick responses suggest that it is easy for them to understand the issues associated with the presence of bloateddirect dependencies in their projects. In 8/15 projects, the response time was less than 24 hours, which is an evidence that developers consider this type of improvement as a priority.
Our results also evidence the fact that we, as external contributors to those projects, were able to identify the problem and propose a solution using DepClean. In the following, we discuss four cases of pull requests that are particularly interesting and the feedback provided by developers. Developers also pointed out the fact that there is no classloader isolation in jenkins, and hence all dependencies in its core module automatically become part of its public API. A developer also referred to issues related to past experiences removing unused dependencies. He argued that external projects have depended on that inclusion and their builds were broken by such a removal. For example, the Git client plugin mistakenly included Java classes from certain Apache contrib authentication classes. When they removed the dependency, some downstream consumers of the library were affected, and they had to include the dependency directly.
Consequently, we received the following feedback from an experienced developer of jenkins: "We're not precluded from removing an unused dependency, but I think that the project values compatibility more than removal of unused dependencies, so we need to be careful that removal of an unused dependency does not cause a more severe problem than it solves." After some discussions, developers agreed with the removal of commons-codec in module cli. Our pull request was edited by the developers and merged to the master branch one month after.

checkstyle
DepClean identifies the direct dependency junit-jupiter-engine as bloated. This is a test scope dependency that was added to the POM of checkstyle when migrating integration tests to JUnit 5. The inclusion of this dependency was necessary due to the deprecation of junit-platform-surefire-provider in the Surefire Maven plugin. However, the report of DepClean about this bloated-direct dependency was a false positive. The reason for this output occurs because junit-jupiter-engine is commonly used through reflective calls that cannot be captured at the bytecode level.
Although this pull request was rejected, developers expressed interest in DepClean, which is encouraging. They also proposed a list of features for the improvement of our tool. For example, the addition of an exclusion list in the configuration of DepClean for dependencies that are known to be used dynamically, improvements on the readability of the generated report, and the possibility of causing the build process to fail in case of detecting the presence of any bloated dependency. We implemented each of the requested functionalities in DepClean. As a result, developers opened and issue to integrate DepClean in the Continuous Integration (CI) pipeline of checkstyle, in order to automatically manage their bloated dependencies 14 .

alluxio
DepClean detects that the direct dependency grpc-netty, declared in the module alluxio-core-transport is bloated. Figure 16 shows that this de-pendency also induces a total of 10 transitive dependencies that are not used (4 of them are omitted by Maven due to their duplication in the dependency tree). Developers accepted our pull request and also manifested their interest on using DepClean for managing unused dependencies in the future.  Fig. 16 Transitive dependencies induced by the bloated-direct dependency grpc-netty in the dependency tree of module alluxio-core-transport. The tree is obtained with the dependency:tree Maven goal.

undertow
DepClean detects a total of 6 bloated-direct dependencies in the benchmarks module of the project undertow: undertow-servlet, undertow-websocketsjsr, jboss-logging-processor, xnio-nio, jmh-generator-annprocess, and httpmime. In this case, we received a rapid positive response from the developers two days after the submission of the pull request. Removing the suggested bloated-direct dependencies has a significant impact on the size of the packaged JAR artifact of the undertow-benchmarks module. We compare the sizes of the bundled JAR before and after the removal of those dependencies: the binary size reduction represents more than 1MB. It is worth mentioning that this change also reduced the complexity of the dependency tree of the module.
Summary of RQ3: We use DepClean to propose 18 pull requests removing bloated-direct dependencies, from which 15/18 were answered. 14/15 pull requests with response were accepted and merged by open-source developers (68 dependencies were removed from the dependency tree of 14 projects). Implications: Removing bloated-direct dependencies is relevant for developers and it is perceived as a valuable contribution. This type of change in the POM files are small, and they can have a significant impact on the dependency tree of Maven projects.

RQ4:
To what extent are developers willing to exclude bloated-transitive dependencies?
In this research question, our goal is to see how developers react when made aware of bloated-transitive dependencies. We do this by proposing the exclusion of bloated-transitive dependencies to them, as described in Subsection 4.2.2. Table 5 shows the list of 13 pull requests submitted. Each pull request proposes the exclusion of at least one transitive dependency in the POM. We received response from developers for 8 pull requests. The first and second columns show the name of the project and the URL to the pull request on GitHub. Columns three and four represent the number of bloated-transitive dependencies explicitly excluded and the total number of dependencies removed in the dependency tree as resulting from the exclusion. The last column shows the status of the pull request ( accepted, rejected, or / pending). The last row represents the acceptance rate with respect to the projects with response. For example, for the project spoon we propose the exclusion of fourbloated transitive dependencies in its core module. As a result of this change, 31 transitive dependencies were removed from the dependency tree of this module.
Overall, from the pull requests with responses from developers, 4 were accepted and 4 were rejected. In total, 63 bloated dependencies were removed from the dependency trees of 4 projects. We notice that the accepted pull requests involve those projects for which the exclusion of transitive dependencies also represents the removal of a large number of other dependencies from the dependency tree. This result suggests that developers are more careful concerning this type of contributions.
As in RQ3, we obtained valuable feedback from developers about the pros and cons of excluding bloated-transitive dependencies. In the following, we provide unique qualitative insights about the most interesting cases and explain the feedback obtained from developers to the research community. Table 5 List of pull requests proposing the exclusion of bloated-transitive dependencies.

jenkins
DepClean detects the bloated-transitive dependencies constant-poolscanner and eddsa in the module core of jenkins. These bloated dependencies were induced through the direct dependencies remoting and cli, respectively. In the message of the pull request, we explain how their exclusion contributes to make the core of jenkins slimmer and the dependency tree clearer.
Although both dependencies were confirmed as unused in the core module of jenkins, developers rejected our pull request. They argue that excluding such dependencies has no valuable repercussion for the project and might actually affect its clients, which is correct. For example, constant-pool-scanner is used by external components, e.g., the class RemoteClassLoader in the remoting 15 project relies on this library to inspect the bytecode of remote dependencies.
As shown in the following quote from an experienced developer of Jenkins, there is a general consensus on the usefulness of removing bloated dependencies, but developers need strong facts to support the removal of transitive dependencies: "Dependency removals and exclusions are really useful, and my recommendation would be to avoid them if there is no substantial gain."

auto
DepClean reports on the bloated-transitive dependencies listenablefuture and auto-value-annotations in module auto-common of the Google auto project. We proposed the exclusion of these dependencies and submitted a pull request with the POM change.
Developers express several concerns related to the exclusion of these dependencies. For example, a developer believes that it is not worth maintaining exclusion lists for dependencies that cause no problem. They point out that although listenableFuture is a single class file dependency, its presence in the dependency tree is vital to the project, since it overrides the version of the guava library that have many classes. Therefore, the inclusion of this dependency is a strategy followed by guava to narrow the access to the interface ListenableFuture and not to the whole library 16 .
On the other hand, developers agree that auto-value-annotations is bloated. However, they keep it, arguing that it is a test-only dependency, and they prefer to keep annotation-only dependencies and let end users exclude them when desired.
The response from developers suggests that bloated dependencies with test scope are perceived as less harmful. This is reasonable since test dependencies are only available during the test, compilation, and execution phases and are not shipped transitively in the JAR of the artifact. However, we believe that although it is a developers' decision whether they keep this type of bloated dependency or not, the removal of testing dependencies is regularly a desirable refactoring improvement.

moshi
DepClean detects that the bloated-transitive dependency kotlin-stdlibcommon is present in the dependency tree of modules moshi-kotlin, moshikotlin-codegen, and moshi-kotlin-tests of project moshi. This dependency is induced from a common dependency of these modules: kotlinstdlib.
Developers rejected our pull requests, arguing that excluding such transitive dependency prevents the artifacts from participating in the proper dependency resolution of their clients. They suggest that clients interested in reducing the size of their projects can use specialized shrinking tools, such as ProGuard 17 , for this purpose.
Although the argument of developers is valid, we believe that delegating the task of bloat removal to their library clients imposes an unnecessary burden on them. On the other hand, recent studies reveal that library clients do not widely adopt the usage of dependency analysis tools for quality analysis purposes [29].

spoon
DepClean detects that the transitive dependencies org.eclipse.core. resources, org.eclipse.core.runtime, org.eclipse.core.filesystem, and org.eclipse.text org.eclipse.jdt.core are bloated. All of these transitive dependencies were induced by the inclusion of the direct dependency org.eclipse.jdt.core, declared in the POM of core module of the spoon library. Table 6 shows how the exclusion of these bloated-transitive dependencies has a positive impact on the size and the number of classes of the library. As we can see, by excluding these dependencies the size of the jar-with-dependencies of the core module of spoon is trimmed from 16.2MB to 12.7MB, which represents a significant reduction in size of 27.6%. After considering this improvements, the developers confirmed the relevance of this change and merged our pull request into the master branch of the project.

accumulo
DepClean detects the bloated-transitive dependencies listenablefuture, httpcore and netty in the core module of Apache accumulo. These dependencies were confirmed as bloated by the developers. However, they mani- Table 6 Comparison of the size and number of classes in the bundled JAR of the core module of spoon, before and after the exclusion of bloated-transitive dependencies. fested their concerns regarding their exclusion, as expressed in the following comment: "I'm not sure I want us to take on the task of maintaining an exclusion set of transitive dependencies from all our deps POMs, because those can change over time, and we can't always know which transitive dependencies are needed by our dependencies.".
After the discussion, developers decided to accept and merge the pull request. Overall, developers considered that the proposal is a good idea. They suggest that it would be better to approach the communities of each of the direct dependencies that they use, and encourage them to mark those dependencies as optional, thus they would not be automatically inherited by their users.

para
DepClean detects the bloated-transitive dependency flexmark-jiraconverter. This dependency is induced through the direct dependency flexmark-ext-emoji, declared in the core module of the para project. Our further investigation on the Maven dependency tree of this module revealed that this bloated dependency adds a total of 19 additional dependencies to the dependency tree of the project, of which 15 are detected as duplicated by Maven.
Because of this large number (19) of bloated-transitive dependencies removed, developers accepted the pull request and merged the change into the master branch of the project the same day of the pull request submission.
Answer to RQ4: We use DepClean to propose 13 pull requests excluding bloated-transitive dependencies. 4 out of 8 pull requests with response were accepted and merged by developers (63 dependencies were removed from the dependency tree of 4 projects). Implications: The handling of bloated-transitive dependencies is a topic with no clear consensus among developers. Developers consider this kind of bloat as relevant, but they are concerned about the maintenance of a list of exclusion directives. Some developers agree to remove them based on practical facts (e.g., JAR size reduction) while other developers prefer to keep bloated-transitive dependencies in order to avoid the potential negative impact on their clients.

Discussion
In this section, we discuss the implications of our findings and the threats to the validity of the results obtained.

Implications of Results
Our study of bloat in POM files complements previous studies about the challenges and risks of dependency management for security. In this context, bloated dependencies represent an unnecessary additional source of vulnerabilities, opening the door to potential attackers which may exploit code segments that could not have been reached otherwise [26,12]. The complexity of dependency management systems is a critical engineering concern since dependency issues can scale through entire ecosystems. For example, in 2027, the Equifax data breach 18 caused by a vulnerability in a Java library impacted the personal information of 147 million American citizens. Various initiatives have emerged to cope with this issue. GitHub tracks dependency vulnerability reports from various sources 19 and provides security alerts to affected repositories; dependabot 20 automatically submits pull requests to suggest dependency updates. DepClean can complement these security scanning solutions in order to determine if a dependency can be removed rather than upgraded.
Our results indicate that most of the dependency bloat is due to transitive dependencies and the Maven dependency heritage mechanism. The official Maven dependency management guidelines 21 encourage developers to take control over the dependency resolution process via their explicit declaration in the POM file. This is a good practice to provide better documentation for the project and to keep one artifact's dependencies independent from the choices of other libraries down the dependency tree. Dependencies declared in this way have priority over the Maven mediation mechanism, allowing developers to have a clear knowledge about which library version they are expecting to be used through transitive dependencies. However, since backward compatibility is not always guaranteed, having fixed transitive dependency versions, and therefore non-declared dependencies, still remains as a widely extended practice. In this context, the introduction of the module construct in Java 9 provides a higher level of aggregation above packages. This new language element, if largely adopted, may help to reduce the transitive explosion of dependencies. Indeed, this mechanism forces developers to declare explicitly what other modules are required to use in a given module. This leads to two benefits: (1) it enables reuse declaration at a finer grain than dependencies, and (2) it makes the debloat techniques described in this work safer as it constrains reflection to white-listed modules.
Our results show that even notable open-source projects, which are maintained by development communities with strict development rules, are affected by dependency bloat. Developers confirmed and removed most of the reported bloated-direct dependencies detected by DepClean. However, they are more careful about excluding bloated-transitive dependencies. The addition of exclusion clauses to the POM files is perceived by some developers as an unnecessary maintainability burden. Interestingly, our quantitative results indicate that bloated-transitive dependency relationships represent the largest portion of bloated dependencies, yet, our qualitative study reveals that these bloated relationships are also the ones that developers find the most challenging to handle and reason about. Overall, this work opens the door to new research opportunities on debloating POMs and other build files.

Threats to Validity
In the following, we discuss about construct, internal and external threats to the validity of our study.
Construct validity. The threats to construct validity are related to the novel concept of bloated dependencies and the metrics utilized for its measurement. For example, the DUT constructed by DepClean could be incomplete due to issues during the resolution of the dependencies. We mitigate this threat by building DepClean on top of Maven plugins to collect the information about the dependency relationships. We also exclude from the study those artifacts for which we were unable to retrieve the full dependency usage information.
It is possible that developers repackage a library as a bundle JAR file along with its dependencies, or copy the source code of dependencies directly into their source code, in order to avoid dependency related issues. Consequently, DepClean will miss such dependencies, as they are not explicitly declared in the POM file. Thus, the analysis of dependencies can underestimate the part of bloated dependencies. However, considering the size of our dataset and the feedback obtained from actively maintained projects, we believe that these corner cases do not affect our main results.
Internal validity. The threats to internal validity are related to the effectiveness of DepClean to detect bloated dependencies. The dynamic features of the Java programming language, e.g., reflection or dynamic class loading present particular challenges for any automatic analysis of Java source code [21,23]. Since DepClean statically analyzes bytecode, anything that does not get into the bytecode is not detected (e.g., constants, annotations with source-only retention police, links in Javadocs), which can lead to false positives. To mitigate this threat, DepClean can detect classes or class members that are created or invoked dynamically using basic constructs such as class.forName("someClass") or class.getMethod("someMethod", null). We also use an exclusion list of dependencies that are known to be used only dynamically.
External validity. The relevance of our findings in other software ecosystems is one threat to external validity. Our observations about bloated dependencies are based on Java and the Maven ecosystem and our findings are restricted to this scope. More studies on other dependency management systems are needed to figure out whether our findings can be generalized. Another external threat relates to the representativeness of the projects considered for the qualitative study. To mitigate this threat, we submitted pull requests to a set of diverse, mature, and popular open-source Java projects that belong to distinct communities and cover various application domains. This means that we contributed to improving the dependency management of projects that are arguably among the best of the open-source Java world, which aims to get as strong external validity as possible.

Related Work
In this work, we propose the first systematic large-scale analysis of bloat in the Maven ecosystem. Here, we discuss the related works in the areas of software debloating and dependency management.

Analysis and Mitigation of Software Bloat
Previous studies have shown that software tends to grow over time, whether or not there is a need for it [15,32]. Consequently, software bloat appears as a result of the natural increase of software complexity, e.g., the addition of nonessential features to programs [6]. This phenomenon comes with several risks: it makes software harder to understand and maintain, increases the attack surface, and degrades the overall performance. Our paper contributes to the analysis and mitigation of a novel type of software bloat: bloated dependencies.
Celik et al. [8] presented Molly, a build system to lazily retrieve dependencies in CI environments and reduce build time. For the studied projects, the build time speed-up reaches 45% on average compared to Maven. Dep-Clean operates differently than Molly: it is not an alternative to Maven as Molly is, but a static analysis tool that allows Maven users to have a better understanding and control about their dependencies.
Yu et al. [43] investigated the presence of unnecessary dependencies in header files of large C projects. Their goal was to reduce build time. They proposed a graph-based algorithm to statically remove unused code from applications. Their results show a reduction of build time of 89.70% for incremental builds, and of 26.38% for fresh builds. Our work does not focus on build performance, we analyze the pervasiveness of dependency bloat across a vast and modern ecosystem of Maven packages.
In recent years, there has been a notable interest in the development of debloating techniques for program specialization. The aim is to produce a smaller, specialized version of programs that consume fewer resources while hardening security [1]. They range from debloating command line programs written in C [37], to the specialization of JavaScript frameworks [41] and fully fledged containerized platforms [33]. Most debloating techniques are built upon static analysis and are conservative in the sense that they focus on trimming unreachable code [17], others are more aggressive and utilize advanced dynamic analysis techniques to remove potentially reachable code [14]. Our work addresses the same challenges at a coarser granularity. DepClean removes unused dependencies, which is, according to our empirical results, a significant cause of program bloat.
Qiu et al. [31] empirically shows evidence that a considerable proportion of API members are not widely used, i.e., many classes, methods, and fields of popular libraries are not used in practice. Nguyen et al. [30] implement a bytecode based analysis tool to learn about the actual API usage of Android frameworks. Lämmel et al. [20] perform a large-scale study on API usage based on the migration of AST code segments. Other studies have focused on understanding how developers use APIs on the daily basis [34,2]. Some of the motivations include improving API design [27,13] and increasing developers productivity [22]. All these studies hint at the presence of bloat in APIs. Yet, none of the aforementioned studies investigated the reaction of developers to bloated dependencies. In this paper, we complement the quantitative study of dependency bloat in Maven Central with practical insights gathered from our contributions to recognized open-source projects.

Dependency Management in Software Ecosystems
Library reuse and dependency management has become mainstream in software development. McIntosh et al. [24] analyze the evolution of automatic build systems for Java (ANT and Maven). They found that Java build systems follow linear or exponential evolution patterns in terms of size and complexity. In this context, we interpret bloated dependencies as a consequence of the tendency of build automation systems of evolving towards open-ended complexity over time.
Decan et al. [11,10] studied the fragility of packaging ecosystems caused by the increasing number of transitive dependencies. Their findings corroborate our results, showing that most clients have few direct dependencies but a high number of transitive dependencies. They also found that popular libraries tend to have larger dependency trees. However, their work focuses primarily on the relation between the library users and their direct providers and does not take into account the inherited or transitive dependencies of those providers. We are the first, to the best of our knowledge, to conduct an empirical analysis of bloated dependencies in the Maven ecosystem considering both, users and providers, as potential sources of software bloat.
Bezemer et al. [5] performed a study of unspecified dependencies, i.e., dependencies that are not explicitly declared in the build systems. They found that these unspecified dependencies are subtle and difficult to detect in make-based build systems. Seo et al. [36] analyzed over 26 millions builds in Google to investigate the causes, types of errors made, and resolution efforts to fix the failing builds. Their results indicate that, independent of the programming language, dependency errors are the most common cause of failures, representing more than two thirds of fails for Java. Based on our results, we hypothesize that removing dependency bloat would reduce spurious CI errors related to dependencies.
Jezek et al. [16] describe, with practical examples, the issues caused by transitive dependencies in Maven. They propose a static analysis approach for finding missing, redundant, incompatible, and conflicting API members in dependencies. Their experiments, based on a dataset of 29 Maven projects, show that problems related to transitive dependency are common in practice. They identify the use of wrong dependency scopes as a primary cause of redundancy. Our quantitative study extends this work to the scale of the Maven Central ecosystem, and provides additional evidence about the persistence of the dependency redundancy problem.
Callo et al. [7] performed a systematic review about dependency analysis solutions in software-intensive systems. Bavota et al. [3] studied performed an empirical study on the evolution of declared dependencies in the Apache community. They found that build system specifications tend to grow over time unless explicit effort is put into refactoring them. Our qualitative results complement previous studies that present empirical evidence that developers do not systematically update their dependency configuration files [25,19].

Conclusion
In this work, we presented a novel conceptual analysis of a phenomenon originated from the practice of software reuse, which we coined as bloated dependencies. This type of dependency relationship between software artifacts is two-side intriguing: from the perspective of the dependency management systems that are unable to avoid it, and from the standpoint of developers who declare dependencies but do not use them in their applications.
We performed a quantitative and qualitative study of bloated dependencies in the Maven ecosystem. To do so, we implemented a tool, DepClean, which analyzes the bytecode of an artifact and all its dependencies that are resolved by Maven. As a result of the analysis, DepClean provides a report of the bloated dependencies, as well as a new version of its POM file which removes the bloat. We use DepClean to analyze the 723, 444 dependency relationships of 9, 639 artifacts in Maven Central. Our results reveal that 2.7% of them are bloated (2.7% are direct dependencies, 15.4% are inherited from parent POMs, and 11.9% are transitive dependencies). Based on these results, we distilled two possible causes: the cascade of unwanted transitive dependencies induced by direct dependencies, and the dependency heritage mechanism of multi-module Maven projects.
We complement our quantitative study of bloated dependencies with an in-depth qualitative analysis of 21 mature Java projects. We use DepClean to analyze these projects and submit the results obtained as pull request on GitHub. Our results indicate that developers are willing to remove bloateddirect dependencies: 14 out of 15 answered pull requests were accepted and merged by the developers in their code base. On the other hand, we found that developers tend to be skeptical regarding the exclusion of bloated-transitive dependencies: 4 out of 8 answered pull requests were accepted. Overall, the feedback from developers reveals that the removal of bloated dependencies clearly worth the additional analysis and effort.
Our study stresses the need to engineer, i.e., analyze, maintain, test POM files. The feedback from developers shows interest in DepClean to address this challenge. While the tool is robust enough to analyze a variety of realworld projects, developers now ask questions related to the methodology for dependency debloating, e.g., when to analyze bloat? (in every build, in every release, after every POM change), who is responsible for debloat of direct or transitive dependencies? (the lead developers, any external contributor), how to properly managing complex dependency trees to avoid dependency conflicts? These methodological questions are part of the future work to further consolidate DepClean.