DEV Community

Cover image for Dependency: That One Line in build.gradle You Copy Without Thinking? Let's Actually Understand It.
Aalaa Fahiem
Aalaa Fahiem

Posted on

Dependency: That One Line in build.gradle You Copy Without Thinking? Let's Actually Understand It.

You know the drill.

You want to add Retrofit to your project. You Google "add Retrofit Android.", take it from official documentation You find the line. You paste it into your build.gradle. You sync. It works. You move on.

And then one day it doesn't work. Maybe the version is wrong. Maybe there's a red error about a duplicate class. Maybe Gradle is screaming about something conflicting. And you're standing there with no idea what that line you've been pasting for months actually does.

That was me. For longer than I'd like to admit.

So let's fix that. Let's talk about dependencies — what they really are, how to read them, and what the difference between implementation and api is (because it actually matters and nobody explains it properly).


First, What Even Is a Dependency?

A dependency is just a library someone else wrote that you want to use in your app.

Instead of you writing your own networking code from scratch, you pull in Retrofit. Instead of writing your own image loading logic, you pull in Coil. A dependency is just code you didn't write but want to include in your project.

The dependencies { } block in your build.gradle is where you tell Gradle: "hey, go find this library and make it available in my code."

dependencies {
    implementation("androidx.core:core-ktx:1.16.0")
    implementation("com.squareup.retrofit2:retrofit:2.11.0")
}
Enter fullscreen mode Exit fullscreen mode

Those two lines are Gradle saying: "before you build this app, go download these two libraries and include them." You don't touch a .jar file. You don't manage folders. You just declare what you need and Gradle handles the rest.


How to Actually Read a Dependency Line

Every dependency line follows the same pattern:

implementation("group:artifact:version")
Enter fullscreen mode Exit fullscreen mode

Let's break down a real one:

implementation("com.squareup.retrofit2:retrofit:2.11.0")
//              └─ group ────────────┘  └─ name ┘  └─ version ┘
Enter fullscreen mode Exit fullscreen mode
  • Group → usually the company or project name (com.squareup.retrofit2)
  • Artifact → the specific library within that group (retrofit)
  • Version → which version you want (2.11.0)

That's it. Three parts, colon-separated. Once you see that pattern, every dependency line becomes readable — not just a magic string you paste and hope for the best.


implementation vs api — The One That Actually Matters

This is the part that confused me . Why are there different keywords? What's the difference?

Here's the honest, simple version:

implementation — Keep it private

implementation("com.squareup.retrofit2:retrofit:2.11.0")
Enter fullscreen mode Exit fullscreen mode

This is the one you'll use 99% of the time. It means: "add this library to my module, but don't expose it to anyone who depends on my module."

In plain terms — Retrofit is yours to use inside your app module. Other modules in your project can't accidentally reach into it.

api — Share it outward

api("com.squareup.retrofit2:retrofit:2.11.0")
Enter fullscreen mode Exit fullscreen mode

This says: "add this library AND expose it to anyone who depends on my module." If module A uses api to include Retrofit, and module B depends on module A, then module B can also use Retrofit directly — without declaring it themselves.

Why does this matter for beginners? It mostly doesn't — yet. If you have a single-module app (which most beginners do), there's no difference in practice. But the rule of thumb is:

Default to implementation. Only use api when you intentionally want to expose a library to other modules.

Using api everywhere is a common beginner mistake that makes builds slower and creates unexpected compile-time dependencies. Just use implementation unless you have a specific reason not to.


The Compose BOM — Because Managing 10 Versions Is Painful

If you're using Jetpack Compose, you've probably seen something like this in your build.gradle:

dependencies {
    // Specify the Compose BOM with a version definition
    val composeBom = platform("androidx.compose:compose-bom:2026.03.00")
    implementation(composeBom)
    testImplementation(composeBom)
    androidTestImplementation(composeBom)

    // Specify Compose library dependencies without a version definition
    implementation("androidx.compose.foundation:foundation")
    // ..
    testImplementation("androidx.compose.ui:ui-test-junit4")
    // ..
    androidTestImplementation("androidx.compose.ui:ui-test")
}
Enter fullscreen mode Exit fullscreen mode

Notice something? Those last four lines have no version number. That feels wrong — but it's intentional.

The platform(...) line imports the BOM (Bill of Materials). Think of the BOM as a version cheat sheet that Google maintains. It says: "if you're using Compose BOM version 2026.03.00, here's exactly which version of every Compose library you should use."

By importing the BOM first, you're telling Gradle: "figure out the right version for each Compose library automatically." You don't manage 10 different Compose library versions anymore. You manage one BOM version.

When a new Compose release drops, you update one number. Everything else stays consistent.

As of writing this article, the latest stable BOM is 2026.04.00 and the latest Kotlin is 2.3.20. Keep these updated — the Android ecosystem moves fast.

Reading Dependency Errors

This is the part nobody teaches but everyone needs.

Here are the three errors you'll see constantly, and what they actually mean:

Could not resolve com.squareup.retrofit2:retrofit:2.99.0

Gradle can't find that version. Either you typed the version wrong, or it doesn't exist. Fix: check the library's GitHub or Maven Central for the correct version number.

Duplicate class kotlin.collections.jdk8.CollectionsJDK8Kt found in modules

Two of your dependencies are pulling in the same thing — usually different versions. Fix: this usually means one of your library versions is too old. Update your dependencies and it typically resolves itself.

Unresolved reference: Retrofit

You haven't added the dependency, or you added it but Gradle hasn't synced yet. Fix: check your dependencies block, then hit "Sync Now" in the yellow bar at the top.

The golden rule: read the error before Googling it. Gradle errors are verbose but usually descriptive once you slow down and actually read the message.


A Clean Real-World dependencies Block

Here's what a modern Android project's dependency block looks like, with everything in its right place:

dependencies {
    // Kotlin & Core
    implementation("androidx.core:core-ktx:1.16.0")
    implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.9.0")

    // Jetpack Compose (using BOM — no versions needed below)
    val composeBom = platform("androidx.compose:compose-bom:2026.03.00")
    implementation(composeBom)
    implementation("androidx.compose.ui:ui")
    implementation("androidx.compose.ui:ui-graphics")
    implementation("androidx.compose.material3:material3")
    implementation("androidx.activity:activity-compose:1.10.1")
    debugImplementation("androidx.compose.ui:ui-tooling")
    debugImplementation("androidx.compose.ui:ui-test-manifest")

    // Networking
    implementation("com.squareup.retrofit2:retrofit:2.11.0")
    implementation("com.squareup.retrofit2:converter-gson:2.11.0")

    // Image loading
    implementation("io.coil-kt:coil-compose:2.7.0")

    // Testing
    testImplementation("junit:junit:4.13.2")
    androidTestImplementation("androidx.test.ext:junit:1.2.1")
}
Enter fullscreen mode Exit fullscreen mode

Organized. Readable. Every line has a purpose. And now you can actually read what it's doing instead of just hoping it works.


One Last Thing

Dependencies aren't magic. They're just other people's code that Gradle downloads and includes in your build. The implementation keyword is how you invite that code in. The version number is which version of it you want. The BOM is a shortcut for managing multiple related versions at once.

That's the whole mental model.

Next time you paste a dependency line, take two extra seconds to read it. What's the group? What's the artifact? What version? Once you start seeing the pattern, it stops feeling like config soup and starts feeling like actual configuration you understand and control.

And that's a really good feeling.

Top comments (0)