GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — refactor: reduce boilerplate code · Issue #461 · spotify-web-api-java/spotify-web-api-java

refactor: reduce boilerplate code #461

New issue
Copy link
New issue
Copy link
Closed
Closed
refactor: reduce boilerplate code#461
Copy link

Description

@dargmuesli
dargmuesli
opened on Apr 30, 2026
Issue body actions

Migration: Boilerplate Reduction

Summary

Incrementally modernize the project by:

  • Migrating Maven → Gradle
  • Introducing Kotlin
  • Replacing boilerplate in models and requests

Strict requirement: No breaking changes to public API.


Execution Rules

  • Work in small, reviewable commits
  • Each step must:
    • Compile successfully
    • Pass all tests
  • Do NOT combine phases in a single commit
  • Prefer incremental migration over large rewrites
  • Maintain Java ↔ Kotlin interoperability at all times

Phase 1: Gradle Migration

Tasks

  • Create build.gradle.kts
  • Port dependencies from pom.xml
  • Add required plugins (java, jacoco, publishing)
  • Keep existing directory structure
  • Update CI to use Gradle

Checkpoint

  • ./gradlew build succeeds
  • All tests pass

Commit Boundary

  • chore: migrate build system from Maven to Gradle

Phase 2: Enable Kotlin

Tasks

  • Add Kotlin plugin and stdlib
  • Verify mixed Java/Kotlin compilation works

Checkpoint

  • Project builds with Kotlin enabled
  • No functional changes

Commit Boundary

  • chore: add Kotlin support to project

Phase 3: Model Migration (Incremental)

Tasks

  • Convert model classes to a Kotlin representation in small batches, respecting this library's public API stability as described by jakewharton.com/public-api-challenges-in-kotlin.
  • Use kotlinx.serialization for JSON handling
  • Remove custom JSON utilities only for migrated models

Execution Strategy

  • Start with simple, dependency-free models
  • Progress to dependent and complex models

Per-Batch Checkpoint

  • Build succeeds
  • Serialization/deserialization tests pass
  • No API breakage

Commit Boundaries (repeat per batch)

  • refactor(models): convert <ModelName> to Kotlin data class

Phase 4: Request Layer Simplification

Tasks

  • Introduce a mechanism to reduce request boilerplate:
    • Option A: Code generation
    • Option B: Shared abstractions/annotations
  • Apply incrementally to request classes

Execution Strategy

  • Start with simplest request classes
  • Migrate complex ones later

Per-Batch Checkpoint

  • Requests produce identical HTTP behavior
  • Tests pass

Commit Boundaries (repeat per batch)

  • refactor(requests): simplify <RequestName> implementation

Phase 5: Remove Remaining JSON Boilerplate

Tasks

  • Eliminate remaining custom JSON parsing
  • Centralize common behavior if needed

Checkpoint

  • All JSON fixtures deserialize correctly
  • No regressions

Commit Boundary

  • refactor: remove remaining JSON utility boilerplate

Phase 6: Kotlin Conversion (Core)

Tasks

  • Convert core classes (API entry point, base request classes, utilities) to Kotlin
  • Improve readability using Kotlin features (null safety, defaults)

Checkpoint

  • Public API unchanged
  • Full test suite passes

Commit Boundaries

  • refactor(core): convert core classes to Kotlin

Phase 7: Tests & Quality

Tasks

  • Migrate test utilities to Kotlin (optional but preferred)
  • Improve readability and reduce duplication
  • Ensure coverage is maintained or improved

Checkpoint

  • All tests pass
  • Coverage ≥ previous baseline

Commit Boundary

  • test: modernize and clean up test suite

Phase 8: Documentation & Release

Tasks

  • Update README (Gradle + Kotlin usage)
  • Document contribution workflow
  • Prepare release notes

Checkpoint

  • Docs reflect new setup accurately

Commit Boundary

  • docs: update documentation for Kotlin + Gradle migration

Final Validation

  • Full build passes
  • All tests pass
  • No public API changes
  • Artifacts publish successfully

Guidance

  • Prefer safe refactors over clever optimizations
  • If uncertain, keep existing behavior unchanged
  • Avoid large diffs; split work into minimal steps
  • Validate continuously after each change

Activity

  1. WhosNickDoglio commented on May 16, 2026

    @WhosNickDoglio
    WhosNickDoglio
    on May 16, 2026
    More actions

    Are you open to feedback or contributions for any of this? Recently started playing around with this library and have primarily worked in Kotlin/Gradle for a long time.

    Convert model classes to Kotlin data classes in small batches

    I'd suggest avoid data classes for things in the public API and instead use something like Poko, https://jakewharton.com/public-api-challenges-in-kotlin/

    Replace manual JSON parsing with Jackson annotations

    Do you have a strong preference for Jackson? kotlinx.serialization is first party and quite nice.

    Reacted by Jonas Thelemann
  2. dargmuesli commented on May 20, 2026

    @dargmuesli
    dargmuesli
    on May 20, 2026
    MemberAuthor
    More actions

    Super nice @WhosNickDoglio, very valuable insights! 💚 And of course kotlinx.serialization is preferred. I plan to kick off an agent run for this refactor (right after releasing the next major version including merging #460) and see how far it goes.

    Reacted by Nicholas Doglio
  3. dargmuesli
    added 15 commits that reference this issue on Nov 3, 2026

    refactor(requests): read responses without going through model JsonUt…

    c8af03d

    refactor(requests): share the not-null-and-not-empty argument assertion

    db33a78

    refactor(models): deprecate the JsonUtil classes

    d1871bc

    feat(models): add ModelObjects for reading model objects from JSON

    c5165d6

    refactor(requests): read responses without going through model JsonUt…

    50b9df2

    refactor(requests): share the not-null-and-not-empty argument assertion

    7e4f524

    refactor(models): deprecate the JsonUtil classes

    bf1a7eb

    feat(models): add ModelObjects for reading model objects from JSON

    aef4893

    refactor(requests): share the not-null-and-not-empty argument assertion

    5cdfd58

    refactor(models): deprecate the JsonUtil classes

    74a4ca0

    feat(models): add ModelObjects for reading model objects from JSON

    a90fe14

    feat(codegen)!: generate the data requests and their SpotifyApi metho…

    27f0a9b

    docs: document the generated request API and check it in CI

    2c679ae

    refactor(models)!: remove the deprecated JsonUtil classes

    acbdd85

    refactor(models)!: remove the deprecated JsonUtil classes

    6c5a6f4
  4. dargmuesli commented on Nov 5, 2026

    @dargmuesli
    dargmuesli
    on Nov 5, 2026 – with Claude
    MemberAuthor
    More actions

    Status update

    I re-evaluated the plan against the current codebase. Before #473 (10.0.0), most of the boilerplate was hand-written JSON parsing, and that PR already replaced it with reflective Gson. What remained was mostly Javadoc and Java-facing builders that have to stay for compatibility. Rewriting in Kotlin or moving to Gradle would not remove those, so I kept Java and Maven and went after the remaining boilerplate directly.

    Done

    Plan item Outcome
    "No breaking changes" as a hard requirement Now checked in CI: japicmp compares every build against the last stable release and fails on binary or source incompatible changes, except across a major version bump (#488, #491).
    Phase 5: remove remaining JSON boilerplate Requests no longer go through per-model JsonUtil classes, and the new public ModelObjects (fromJson, arrayFromJson, pagingFromJson, pagingCursorbasedFromJson) covers callers who parse JSON themselves. In 10.x the JsonUtil classes are only deprecated (#489, no API change). For v11 they are removed, together with PlaylistItemFactory (#493).
    Phase 4: request layer simplification, Option A (code generation) 106 of 108 data request classes, and the SpotifyApi methods that create them, are generated from the vendored spec/openapi.yaml by codegen/generate.py, into their own source root src/generated/java (#487, #492). Each class is one line in codegen/requests.yaml, and CI fails when the generated code drifts from the spec. The generated API follows the spec where the hand-written one had drifted. Request bodies take List/Map instead of Gson types, and every change is listed in MIGRATION.md.
    Phase 8: documentation CONTRIBUTING.md, AGENTS.md and MIGRATION.md are updated in the PRs above. Adding an endpoint is now one descriptor line plus a fixture, a test and an example.

    Decided against

    Plan item Why
    Phase 1: migrate Maven → Gradle Brings nothing for a single-module library: Maven builds Kotlin fine if that's ever needed. It would mean rewriting the release path, CI, jitpack.yml, the separate examples project and the docs checks.
    Phase 2, 3, 6: Kotlin models and core, kotlinx.serialization Conflicts with the no-breaking-changes requirement. Gson was part of the public API (requires transitive, Gson types in public signatures). Kotlin's getter naming changes existing names (getIs_playing() would become is_playing()). Every Java user would gain a kotlin-stdlib dependency. Data classes and Poko still need hand-written builders for Java callers, so the saving is small. The Javadoc pipeline (doclint, the published apidocs/) would also have to move to Dokka.
    Phase 7: tests in Kotlin Follows from the above; the tests stay in Java and were updated alongside the changes.

    If Kotlin support is still wanted, I think the cleaner route is a separate, optional spotify-web-api-kotlin artifact (e.g. suspend wrappers over executeAsync(), Kotlin-friendly builders) on top of the Java library, so Java users get no new dependency. Thanks @WhosNickDoglio for the Poko and kotlinx.serialization pointers. They shaped this assessment, and they'd be the starting point for such a module.

    Remaining

    • With feat(codegen)!: generate the data requests and their SpotifyApi methods from the spec #487 and refactor(models)!: remove the deprecated JsonUtil classes #493 merged, no public signature uses a Gson type anymore, so module-info.java can drop Gson from requires transitive to a plain requires.
    • All of the above ships with v11 (ci(release): v11 #484).

    Generated by Claude Code

  5. dargmuesli
    added a commit that references this issue on Nov 5, 2026

    refactor(module)!: require Gson without re-exporting it

    dcc5dd8
  6. dargmuesli
    closed this as completedon Nov 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      You are visitor 01402520 — GeezerVison does not store the pages you look up.
      Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.