Compile Less With SOLID

Hey Android developers! Are you excited about Jetpack compose? I bet you are! And what about a talk covering SOLID principles? Not so cool, huh?

Wait, wait, don’t close the tab. Let me speak about just one of them. It’s not going to be theoretical or boring, I promise️! I recently bumped into SOLID again and I’d like to show you a hidden power, which I had not noticed before.

Let’s take one more step into the past and create a Gradle project from scratch, written in Java (sorry, Java). No additional build configuration is applied. The structure of the project is shown in the following diagram.


The Gradle version, at the time of writing this article, is v6.8.3. The incremental compilation feature is enabled by default with the Java plugin.

Imagine that Client1 only uses method m1, Client2 only uses method m2, and Client3 only uses method m3 of Service. What do you think will happen if we change the body of m3 and recompile the project?

You may think that once a change to the body of a method is a binary compatible change, only Service would be recompiled. I get it... but you are wrong.  The reality is, the whole project recompiles.


Those who got it right have gained my respect. You may close the tab now. :)

To see the result directly from Gradle, we take advantage of the listFiles option for Java compilation that logs the files to be compiled.

tasks.withType(JavaCompile) {
	options.listFiles = true

We run a compilation of Java source files using the JDK compiler by calling the compileJava task that is normally executed with every build or assemble task.

./gradlew compileJava

Finally, we can see that all source files got recompiled!

> Task :compileJava
Source files to be compiled:

Why Is It Like That?

There is nothing wrong with Gradle. The result we got is expected Java behavior. The binary compatibility has no impact on the list of the compiled source files.

All declarations that users must import in source code create a source code dependency. The source code of Client1 then depends on methods m2 and m3, even though it does not call them. This dependence means that a change to the source code of m2 or m3 in Service will trigger a recompilation of Client1, despite the fact that it does not care about the change.


The Interface Segregation Principle (ISP) states that no client should be forced to depend on methods it does not use.

Let’s follow it by creating a separate interface for each method. Every interface exposes to a particular Client only what the client really needs. The structure is illustrated in the following diagram.


Now the same question, once again. What do you think will happen if we change the body of the m3 method of Service class, which is a binary compatible change, and recompile the project?

./gradlew compileJava


> Task :compileJava
Source files to be compiled:

Only Service got recompiled, thanks to introducing interfaces!

What About Kotlin?

Kotlin compiler is smart enough to only recompile affected files by tracking Application Binary Interface changes (“API” for the binaries — you can read more about the process here). However, this does not make the principle obsolete! The compiler just makes it a bit easier for you. Anyway, it is good to know that extra work has to be done.


Every developer familiar with OOP should have heard about SOLID principles. We follow them on a daily basis, many times without noticing. Even though we all have them rooted deep down in our minds, it is worth giving them special attention. SOLID principles matter. And their boundaries do not end with a system’s architecture.


Thank you to Alexander Kovalenko for adding a spark to the article and Tomas Mlynaric with Lubos Mudrak for consultations.

Share Article
Iveta Jurcikova

Iveta Jurcikova

I am a Slovak living in Prague, working as an Android developer at STRV. I am passionate about coding and love creating exciting projects for mobile.

You might also like...