DEV Community

Cover image for SSKCore: Turning Production Pain Into an Android Platform [PART-2]
Shubham Nanche
Shubham Nanche

Posted on

SSKCore: Turning Production Pain Into an Android Platform [PART-2]

📚 This is part 2 of a series.


Let me tell you about the day my crash reporting UI crashed.

The Grey Screen

One afternoon, my Android app's crash screen rendered all-grey. No content. No report button. Just a blank slate where the app's last line of defense should have been.

The root cause? A stale file from Gradle's build cache after a major refactor. The compiled resource IDs no longer matched the packaged resource table. ViewBinding inflated the wrong layout, and a silent NullPointerException killed the crash screen itself.

It was invisible in CI. It only appeared in specific rebuild scenarios. And it took hours to trace.

That bug taught me something important: The fix isn't done when the patch ships. It's done when the lesson becomes automated.

So I wrote a build-time task that reads the compiled class files directly, compares them against the final packaged resources, and verifies every constant matches. It runs automatically after every packaging step. You never have to remember to invoke it.

That was the first of many incident-driven tools I built.

The FAB That Disappeared

A few weeks later, a developer tools Floating Action Button vanished from consumer apps. Debug menus inaccessible. Secure screens incorrectly enabled.

Turns out, my shared library's BuildConfigUtils was reading the library's own BuildConfig—which is baked as "release" at publish time. An AAR can never know the consumer's build type. 25 files across 34 call sites were silently broken.

I built a Gradle plugin that generates a SskBuildConfig object per consumer module, per variant, using AGP's onVariants callback. It registers generated source via KotlinCompile.source()—not reflection, which broke across AGP versions. It detects Android plugins by extension type, not hardcoded IDs, so it works with com.android.application, com.android.library, com.android.dynamic-feature, and any future Google plugin.

Same package as the deprecated BuildConfigUtils. Migration was just renaming the receiver. Zero import changes.

The Permission That Leaked

An app depending on core-base suddenly gained AD_ID permission and started initializing Google Mobile Ads unconditionally—even for users who'd never see an ad.

The root cause? core-base hard-depended on play-services-ads. Every consumer inherited it.

The fix: extract the ad code into an opt-in module. Apps that need ads explicitly add the dependency. Apps that don't, don't inherit the permission.

That structural separation taught me: Modularization isn't just about code organization. It's about controlling what your consumers accidentally inherit.

The Platform That Emerged

These incidents weren't isolated. They were symptoms of a larger problem: building multiple Android apps that share code is genuinely hard. Not "write a library and import it" hard. "Gradle composite builds shadow your settings classpath, build-cache serves stale intermediates, AAR limitations hide variant information, and module extraction silently leaves resource shadows that AGP resolves by merge priority" hard.

So I built SskCore. 18 published library modules. 60+ custom Gradle tasks. All built by one developer—me—from real production pain points.

What Makes It Different

Automatic Source/Binary Switching

Non-release branches compile SskCore from source. Release branches use published AARs. Detected from .git/HEAD. No flags, no manual configuration.

60+ Custom Gradle Tasks

Each task exists because a real bug or real pain point was encountered:

  • Resource-ID drift detection
  • Secret scanning
  • Accessibility auditing
  • Security auditing
  • Navigation graph analysis
  • Translation gap analysis
  • Build matrix visualization
  • Release checklists
  • Bundletool automation
  • Dependency freshness checking
  • Obfuscation mapping collection

ModuleMap Architecture

Crosses Gradle composite build session boundaries for holistic analysis—one command catches duplicates across the library and app boundary, which includeBuild normally isolates.

Catalog-Driven Module Discovery

Adding a new SskCore module requires one line in the consumer's version catalog. No republish of build-logic needed.

Documentation With Incident Context

Every design decision is documented with the incident that caused it. Not "we decided to do X" but "on Aug 10, the crash screen rendered all-grey because of a stale R.jar. Here's the evidence chain. Here's the fix. Here's the prevention."

The Solo Developer Perspective

Here's the thing: I built this alone. Every module, every Gradle plugin, every task, every documentation file—all from one person's keyboard.

That changes the calculus. You don't have a team to rely on. Every complexity tradeoff is yours to own. The upside? You can move fast, make architectural decisions unilaterally, and build exactly what the apps need without organizational friction. The downside? The learning curve is steep, and the cognitive load is real.

But I learned something important: It's not about scale. It's about leverage. Each tool I build saves me days across multiple apps. Each automated check prevents a future debugging session. Each documentation file preserves a lesson I'd otherwise forget.

The Real Engineering Lesson

The 60+ tasks sound like overkill until you've debugged a stale-R crash at 2am. The composite build setup is complex until you've shipped a release with the wrong variant checks because you forgot a flag. The documentation is extensive until you're the one who has to re-derive why something was done a certain way.

Every piece of complexity exists because a simpler approach failed in production.

What's Next

I'm considering open-sourcing the build-logic separately. The 18 modules are app-specific enough that they need context, but the 60+ tasks and the composite-build infrastructure are genuinely useful for any multi-module Android project. The Resource-ID drift guard alone would have saved me days.

Why This Matters to Enterprise applications

SskCore demonstrates work beyond feature delivery:

  • Turning repeated app work into platform APIs
  • Designing Android module boundaries
  • Building Gradle plugins and custom tasks
  • Improving release confidence
  • Managing binary/source consumption tradeoffs
  • Debugging production-like failures
  • Designing reusable components with host-app contracts
  • Writing documentation that preserves engineering decisions

This is relevant to teams working on Android platform engineering, mobile infrastructure, build systems, developer productivity, internal SDKs, release engineering, and app quality automation.


One developer. 18 modules. 60+ Gradle tasks. 20+ documentation files.

This is SskCore—the platform I built from production pain.

If you've built similar shared infrastructure for Android, I'd love to hear how you approached the composite-build / binary-mode switching problem. Or if you've hit the stale-R.jar bug—I now have a very thorough write-up.


📚 This is part 2 of a series.

Top comments (0)