square / square/wire

[Feature Request] Kotlin codegen improvements for naming, constructors, and optional field access

Open
#3,483 3 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Kotlin
Stars
4.4k
Forks
627
Avg merge
3d 15m
Merged PRs (30d)
20

Description

Hi Wire team 👋,

First of all, thanks for Wire — it’s a fantastic tool and we’re happily using it in production with Kotlin and gRPC. I really like the wire function that compiling the optional fields to nullable properties.

While integrating Wire into a multi-language environment, we ran into a few Kotlin codegen limitations that I believe could be valuable feature additions. I’d like to propose the following enhancements:


1. Automatic field name conversion to lowerCamelCase in Kotlin

Problem

Our .proto files are shared across multiple languages (Go, Python, JS, Kotlin, etc.), so we use snake_case field names in proto files for consistency.

Example:

message FeatureReq {
  string feature_name = 1;
  int64 created_at = 2;
}

Currently, Wire generates Kotlin properties exactly as-is:

val feature_name: String
val created_at: Long

However, idiomatic Kotlin strongly prefers lowerCamelCase:

val featureName: String
val createdAt: Long
Request

Provide an option in Kotlin codegen to automatically convert proto field names to lowerCamelCase, while still mapping correctly to the original proto names.

Possible approaches: A codegen flag (e.g. kotlin { camelCaseFields = true }). Or default behavior aligned with Kotlin conventions (similar to how some JSON serializers behave)

2. Additional constructor with required (non-optional) fields only

Problem

Wire-generated Kotlin message classes typically expose a single constructor where all fields (including optional ones) are nullable or defaulted.

For example:

FeatureReq(
  featureName = featureName,
  createdAt = createdAt,
  description = null
)

This makes it easy to accidentally construct invalid domain objects and weakens compile-time guarantees.

Request

If there are some optional fields, generate an additional constructor where:

  • Non-optional fields are required parameters

  • Optional fields are omitted or defaulted

Example:

// existing constructor
FeatureReq(
  featureName: String = "",
  createdAt: Long = 0L,
  description: String? = null
)

// new constructor
constructor(
  featureName: String,
  createdAt: Long,
  description: String? = null
)

This would significantly improve Kotlin ergonomics and domain safety.

3. fieldOrFail accessors for optional fields

Problem

Optional fields are nullable in Kotlin, which is correct at the model level, but in some layers (e.g. application logic) a missing value should be treated as a programmer error.

Currently, users must manually write:

val value = featureReq.description
  ?: error("description is required")
Request

For optional fields, generate an additional accessor:

val descriptionOrFail: String

Which throws a clear exception if the field is null.

This pattern is very common in Kotlin codebases and would greatly reduce boilerplate while making intent explicit.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

No source files or tests are named. Begin by locating the Kotlin codegen entry points and compare current generated naming, constructors, and optional-field accessors with the requested examples; done means all three behaviors are implemented while preserving the original proto field mappings.

Written by the indexing model from the issue text.

Assessment

Tech stack
kotlin
Domain
tooling
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.