[Breaking] Split generated model Codable into Encodable and Decodable - #1289
Draft
martinmitrevski wants to merge 1 commit into
Draft
martinmitrevski wants to merge 1 commit into
martinmitrevski wants to merge 1 commit into
Conversation
Request models keep only Encodable, response models only Decodable, so the compiler stops synthesising the half nobody calls. The split is derived from the generated client - response types from what it decodes, request types from what it encodes, each closed over stored-property types - and an ALWAYS_CODABLE policy list pins the models an app is most likely to round-trip, together with everything they contain. Because a conformance is public API, the derived result is locked to Scripts/generated-model-conformance.json. A spec change that would move a type between Codable, Decodable and Encodable fails the codegen script with a per-model diff instead of shipping silently. Measured on Release/arm64/device, stripped, against develop: StreamVideo goes from 10355.1 KB to 10012.4 KB, of which 262.7 KB is this change and the rest is #1286 underneath it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Advanced Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
martinmitrevski
marked this pull request as draft
September 17, 2026 20:27
Collaborator
SDK Size
|
Collaborator
StreamVideo XCSize
Show 174 more objects
|
Generated by 🚫 Danger |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🔗 Issue Links
Follows #1286.
🎯 Goal
Sources/StreamVideo/OpenApi/generatedis the largest SDK-owned area of the binary — 2,986 KB of 10,355 KB (33%). Most of it is synthesized frompublic+Codable+Hashableon the models:init(from:)278 KB,encode(to:)196 KB,==162 KB,hash(into:)147 KB, witness tables 250 KB.A request model is only ever encoded and a response model is only ever decoded, so roughly half of that
Codablemachinery is synthesized for calls nobody makes.📝 Summary
Encodable, response models toDecodable, instead of both conforming toCodable.Scripts/generated-model-conformance.json— 249 entries, one per model.ALWAYS_CODABLEin the codegen script pins models that keep both halves.StreamVideo: 10,355.1 KB → 10,012.4 KB (−342.6 KB, −3.31%) combined with [Improvement] Reduce SDK size in the generated API client #1286; −262.7 KB for this PR alone. Release/arm64/device, stripped.🛠 Implementation
Deriving the split. Response types come from
jsonDecoder.decode(X.self)inDefaultAPIplus the WebSocket decode sites; request types from therequest:parameter types. Each set is closed over stored-property types, because aDecodableclass needs every property type to beDecodabletoo. Result: 111Decodable, 64Encodable, 74 stayingCodable.JSONEncodablecannot outliveEncodable. StreamCore suppliesencodeToJSON()only viaextension JSONEncodable where Self: Encodable, so a response model that dropsEncodabledropsJSONEncodablewith it — two public conformances per type, not one.Locking. A conformance is public API, so the derived result is checked in and compared on every run. Drift fails the script before it touches a file:
Without this, a spec change could quietly move a public type from
CodabletoDecodablein a routine regeneration.Two internal follow-ons.
send<Response: Codable>becomessend<Response: Decodable>, andVideoEvent— the only model with a hand-writtenencode(to:)— loses those 5.6 KB of source along with itsEncodableconformance.699 exported symbols are removed: 111 public types lose
EncodableandJSONEncodable, 64 loseDecodable.Containment pulls the highest-traffic types back to full
Codable—CallResponse,CallSettingsResponse,GetCallResponse,OwnUserResponse,MemberResponse,CallSessionResponse,EgressResponse. Still stripped are all ~50 WebSocket event types (CallEndedEvent,CallRingEvent,CallSessionParticipantJoinedEvent, …) and most action responses (JoinCallResponse,GetOrCreateCallResponse,AcceptCallResponse). An app that logs events for analytics or persists aJoinCallResponsebreaks on upgrade.The open question for review:
ALWAYS_CODABLEcurrently lists the 8 types Stream's own code and tests encode. That is a proxy for what apps encode, not a survey of it — and the four most important protected types are protected by containment, not by choice. If we want them protected deliberately, they should be listed explicitly so a spec change that reshuffles containment cannot drop them. Worth agreeing the policy before this merges.Also worth noting: the keep-list took two rounds of compiler errors to find. The first build reported 11 errors naming 7 types; fixing those surfaced
QueryCallsResponsein a file group the compiler had not reached. Swift batches errors per file group, so "fix what breaks" is iterative discovery. The rest were found by grepping everyencode(site in the test targets.🧪 Manual Testing Notes
OpenApi/generatedfromdevelopand runningScripts/optimizeGeneratedCodeSize.pyproduces this branch'sModels/byte-for-byte.StreamVideoTests: 2,175 pass. Two failed in the parallel run (CallStateMachineStageJoinedStage_Tests.test_joiningTransition_ownCapabilitiesChanged_controllerReceivesUpdate,Call_IntegrationTests.test_audioRoom_participantRequestsSpeakPermission_hostRejects_participantCannotToggleMicrophone) and both pass in isolation — the suite's usual load-related flakiness.as? Encodablecast is inSelectiveEncodable, whose conformers are exclusivelyStream_Video_Sfu_*protobuf types that never hold OpenAPI models;AnyEncodable.inittakes an existential and is compile-checked.☑️ Contributor Checklist
🤖 Generated with Claude Code