<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Seb's Coding Blog]]></title><description><![CDATA[Seb's Coding Blog]]></description><link>https://blog.sellmair.io</link><generator>RSS for Node</generator><lastBuildDate>Sat, 05 Sep 2026 12:02:53 GMT</lastBuildDate><atom:link href="https://blog.sellmair.io/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[The Kotlin ecosystem might not choose a strict 'SemVer' notation]]></title><description><![CDATA[While other ecosystems (such as Rust or the Web) seem to have well-established conventions for package and library versioning, even a quick glance at the Kotlin ecosystem reveals disagreement. Without any official authority providing guidance, each c...]]></description><link>https://blog.sellmair.io/the-kotlin-ecosystem-might-not-choose-a-strict-semver-notation</link><guid isPermaLink="true">https://blog.sellmair.io/the-kotlin-ecosystem-might-not-choose-a-strict-semver-notation</guid><category><![CDATA[Kotlin]]></category><category><![CDATA[semantic versioning]]></category><dc:creator><![CDATA[Sebastian Sellmair]]></dc:creator><pubDate>Sat, 17 Jan 2026 16:05:24 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1768656683969/a2497bc3-c827-4a4e-bcf2-ed33d14976db.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>While other ecosystems (such as Rust or the Web) seem to have well-established conventions for package and library versioning, even a quick glance at the Kotlin ecosystem reveals disagreement. Without any official authority providing guidance, each company or team is tasked to choose whatever fits their needs. Even we at JetBrains failed to establish a common convention for versioning our Kotlin packages. For some, this might just be an aesthetic issue, merely a small inconsistency when looking at their project’s build files. For others, the inability to build an intuition around version strings leads to frustration.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768658580102/01366aad-c866-42aa-aa13-ee3ce7b66812.png" alt class="image--center mx-auto" /></p>
<p>There are several reasons why such inconsistencies happen. Take Ktor, for example: Until recently, the project belonged to a very different department than Kotlin itself. Compose Multiplatform, on the other hand, has good reasons matching the versions from Google’s Jetpack Compose. And kotlinx libraries might just have suffered from a lack of defined conventions and the nature of such issues: They will never become the #1 top priority and always stay within the realm of ‘would be nice to address eventually’.</p>
<p>I previously <a target="_blank" href="https://kotlinlang.slack.com/archives/C8C4JTXR7/p1768315825123849">invited the community</a> to a debate about a proposal attempting to unify the versioning schema used by the Kotlin ecosystem. As of writing this article, there are two top contenders:</p>
<p><strong>Strict</strong> <a target="_blank" href="https://semver.org/"><strong>SemVer</strong></a><br /><code>1.0.0-alpha.1</code>, <code>1.0.0-beta.1</code>, <code>1.0.0-rc.1</code></p>
<p><strong>Google / AndroidX</strong><br /><code>1.0.0-alpha01</code>, <code>1.0.0-beta01</code>, <code>1.0.0-rc01</code></p>
<p>While <a target="_blank" href="https://x.com/Sellmair/status/2011086287059751085"><em>some audiences</em></a> prefer the ‘Google Notation’, <a target="_blank" href="https://mbonnin.net/2026-01-16-semver-is-great-for-your-kotlin-libraries/">seasoned library developers often argue for the ‘Strict Semver’</a>. Interestingly, my very first draft of a versioning proposal, shared with the Kotlin department, also proposed this notation. It seems like a decent convention used by a very significant share of our industry. It has clearly defined semantics and ‘easy to understand’ rules. I fully agree with Martin Bonnin and would also call it a ‘<a target="_blank" href="https://mbonnin.net/2026-01-16-semver-is-great-for-your-kotlin-libraries/">great versioning scheme for Kotlin libraries</a>’.</p>
<h1 id="heading-the-ideal-diversion">The ‘ideal’ diversion</h1>
<p>I am really fascinated by the concept of ‘engineering emotions’, and I strongly believe that all good engineers eventually have to overcome many of their ‘guiding feelings’: The idealism trying to satisfy our sense of beauty and order, the subtle irritation we notice when reading code that certainly <em>works</em>, but somehow does not fit our philosophy.</p>
<p>I failed to manage my emotions when I initially proposed that the Kotlin Department adopt a strict SemVer (1.0.0-alpha.1) notation. I noticed the inconsistency within our company’s libraries when starting a new project and had to choose a versioning schema. After realising we do not have a written convention, I considered using the same schema as other libraries and noticed the lack of consistency, which motivated me to address this. I had clear and simple goals:</p>
<p>I wanted to find a versioning schema/notation which</p>
<ul>
<li><p>Works predictably for practical use cases (e.g., is handled well by Build Systems like Maven, Gradle, and Amper)</p>
</li>
<li><p>Is able to convey compatibility semantics for all our projects</p>
</li>
<li><p>Can be adopted by many projects to eventually increase the entire ecosystem’s consistency</p>
</li>
</ul>
<p>And I found that ‘<a target="_blank" href="https://semver.org/">SemVer</a>’ already defines clear rules for pre-release classifiers (alpha, beta, rc), and I consider those rules very beautiful. The fact that other ecosystems seem to converge on this notation made me very excited, and I got started writing up a proposal to the team, not knowing that what I had taken to be an ‘ideal’ would, very quietly, change my initial goals! I wanted to move <strong>our Kotlin ecosystem</strong> toward a more consistent, predictable state. My proposal, however, aimed to align Kotlin with a supposedly ‘industry standard,’ well knowing that many of our projects and key ecosystem libraries would not be able to adapt to it. The entire ‘androidx’ ecosystem of libraries, including Compose, a vital part of our Kotlin Multiplatform technology, has already settled on the ‘Google’/’AndroidX’ notation. My initial goals, therefore, cannot be achieved by my very own proposal, and this is not the first time this has happened. During my research, I found basically no practical differences between the two notations. Build Systems handle them well; both look good, and both have easy-to-explain rules. The worst part: While aggressively seeking good reasons to switch to an entirely new notation (SemVer), I even found situations where the Google notation objectively works better. I still ended up writing a proposal, ignoring my initial goals and even ignoring my own research. All in order to satisfy my own sense of beauty and ‘greater ideal’.</p>
<h1 id="heading-my-proposal-100-rc01">My Proposal 1.0.0-rc01</h1>
<p>As I noticed how my emotions guided me into missing my very objectives, my fix is easy:<br />I’ll correct my proposal to use the ‘Google’ notation. It works just as well and can be adopted across all projects within the Kotlin Ecosystem Department (speaking Kotlin, kotlinx, ktor, …). The notation is well known by many of our developers and has some nice properties</p>
<h2 id="heading-still-semverhttpssemverorg-compliant">Still ‘<a target="_blank" href="https://semver.org/">SemVer</a>’ compliant</h2>
<p><em>From the official guide</em></p>
<blockquote>
<p>A pre-release version MAY be denoted by appending a hyphen and a series of dot separated identifiers immediately following the patch version. Identifiers MUST comprise only ASCII alphanumerics and hyphens [0-9A-Za-z-]. Identifiers MUST NOT be empty. Numeric identifiers MUST NOT include leading zeroes</p>
</blockquote>
<p>Note that only <strong>numeric</strong> identifiers are not allowed to start with a 0 (e.g. <code>1.0.0-0</code> ) would be illegal, whereas <code>1.0.0-alpha01</code> is legal and is compliant with ordering rules for up to 99 alpha releases (Again, real-world build systems will handle even more releases just fine)</p>
<h2 id="heading-when-100-rc01-is-better-than-100-rc1">When 1.0.0-rc01 is better than 1.0.0-rc.1</h2>
<p>Please keep in mind that the entire section here is me falling into an emotional trap again: Post-Hoc Rationalization: Coming up with reasons why my decision makes sense, after making it, that is. I still include it, because it’s fun to talk about. While I acknowledge the trap, I am still convinced that the decision between the two styles is simple, since only one can justify the original goals</p>
<h3 id="heading-naive-version-parsers">Naive Version Parsers</h3>
<p>While ‘SemVer’ offers a robust set of rules, we have to acknowledge that many real-life projects may need to quickly parse version numbers and compare them. I have written such code many times to manage integration tests, depending on the Kotlin version, or to manage builds. Since most of those implementations are naive or even lazy, I am a little too embarrassed to link them; have mercy on me! Such code usually works by splitting the version by the <code>.</code> character, converting <code>major</code>, <code>minor</code> and <code>patch</code> to integers, then comparing <code>-pre-release-classifier</code> alphabetically. While I agree that the implementation is wrong, we have to give credit to Google Notations’ more lenient properties towards such mistakes!</p>
<p>In this case, <code>2.14.3-alpha.20</code> would be wrongfully evaluated as ‘lower’ compared to the <code>-alpha.5</code> counterpart</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768663292342/51cb13c8-7473-4990-a7c5-417e6df0fb2e.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768665740794/7e3415fb-981d-442f-bc3d-52621ce03874.png" alt class="image--center mx-auto" /></p>
<p>This effect is also visible in systems that do not care about library versions at all, such as simple indices on webservers or even filesystems. Again: I am not saying that the notation, therefore, is better, but we have to acknowledge its strengths.</p>
<h2 id="heading-our-release-philosophy">Our release philosophy</h2>
<p>During a call about Google’s versioning schema, <a target="_blank" href="https://www.liutikas.net/">Aurimas Liutikas</a> also mentioned that the notation more closely aligns with how we actually think about our pre-releases, and I can see a lot of beauty in this argument: The ‘SemVer’ notation might lead to interpreting pre-releases within a given maturity level as ‘rolling’. E.g. <code>1.0.0-alpha.1</code> only being the first published build with the <code>alpha</code> maturity and <code>1.0.0-alpha.2</code> being the second public build with <code>alpha</code> maturity. The alpha, therefore, carries the maturity semantics <strong>independently</strong> of the <em>build number (.1, .2, .3, …).</em> AndroidX, however, wants to convey a publication as ‘complete’. <code>alpha01</code> therefore being more distinct from <code>alpha02</code>. This comes in handy, as AndroidX already provides ABI-compatibility guarantees for pre-releases!</p>
<h3 id="heading-summary">Summary</h3>
<p>I think the last two arguments presented do not matter much (but were fun to make). What matters is a consistent schema across the ecosystem that works and can be adopted by many projects. I will proceed with my proposal, using the Google notation. Multiple people within JetBrains will review this proposal. I will keep you up to date on any changes to it. Once approved, you can expect a public, official KEEP to be created to promote this proposal beyond JetBrains.</p>
]]></content:encoded></item><item><title><![CDATA[Being Quick: Resolve what's dirty.]]></title><description><![CDATA[Part 1 will describe the technical aspects of this story. Part 2 will focus on some aspects that could potentially apply to other teams and projects.

Sometimes finding a solution in O(n²) might be pretty awesome. Sometimes it's a respectable achieve...]]></description><link>https://blog.sellmair.io/being-quick-resolve-whats-dirty</link><guid isPermaLink="true">https://blog.sellmair.io/being-quick-resolve-whats-dirty</guid><category><![CDATA[comose]]></category><category><![CDATA[compose hot reload]]></category><category><![CDATA[Kotlin]]></category><category><![CDATA[Kotlin Multiplatform]]></category><category><![CDATA[compose multiplatform]]></category><dc:creator><![CDATA[Sebastian Sellmair]]></dc:creator><pubDate>Fri, 21 Mar 2025 10:42:33 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1742551684169/f590b727-3ca4-4945-ae84-b3402ae4467f.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Part 1</strong> <em>will describe the technical aspects of this story. Part 2 will focus on some aspects that could potentially apply to other teams and projects.</em></p>
<p><img src="https://i.imgflip.com/9o0zuj.jpg" alt class="image--center mx-auto" /></p>
<p>Sometimes finding a solution in O(n²) might be pretty awesome. Sometimes it's a respectable achievement. In my case, it was good enough to get started.</p>
<p>I am talking about the current ‘project firework’ aka. “Compose Hot Reload”. As presented in some internal talks or YouTube videos, the crucial part of implementing Hot Reload is figuring out which parts of your application are dirty after some code was changed. Luckily, Compose is a functional framework, so we mostly care about functions (or some scopes within a function, to be more precise)</p>
<p>For the sake of this blog post, some simplifications will be made:<br />Let’s accept the fact that Compose Hot Reload has the concept of ‘scopes’ inside functions and a way to identify them. We also assume that Compose Hot Reload knows the entire state of your application before any request to reload classes arrives. Knowing a scope also means knowing a ‘hash code’ of the code inside the scope. As a simplification, you can think of this hash code as the hash of all bytecode instructions. I will present more details on the exact calculation of this hash in some other materials.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742483953546/f699d53c-0c48-4a10-97ed-6c2258af07bb.png" alt class="image--center mx-auto" /></p>
<p>In the image above, you can see an example of the tracked runtime, where each scope gets its associated hash value. Note: The hash value includes only the code of the scope, child scopes are not included! This can then be represented as a tree/graph:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742485201243/d17a06f6-34a5-495d-82f6-da97d08f8f01.png" alt class="image--center mx-auto" /></p>
<p><em>(Those graphics skills are unmatched, I know).</em></p>
<p>We can represent two kinds of relations: Either one scope is just a child of another scope<br />Or one scope has an explicit dependency on another scope (e.g. by calling a function).<br />In the example above, the code calls into other <code>@Composable</code> functions such as <code>Button</code>, <code>Text</code>, or <code>Column</code> as well as some utility function <code>fontSize()</code></p>
<h2 id="heading-on-good-enough-until-it-is-not">O(n²): good enough, until it is not</h2>
<p>So here is how Hot Reload, before 1.0.0-alpha03, figured out which <code>@Composable</code> scopes are ‘dirty’ and have to be invalidated.</p>
<h3 id="heading-the-first-n-number-of-composable-functionsscopes">The first ‘n’: Number of <code>@Composable</code> functions/scopes</h3>
<p><em>It’s fair to assume that in user-facing applications, the number of</em> <code>@Composable</code> <em>scopes grow linear to the size of the codebase</em> <strong><em>n</em></strong>.</p>
<p>Hot Reload knows the entire state of the runtime before and <strong>after</strong> the reload happens. We take each known <code>@Composable</code> scope and perform a diffing algorithm. For the sake of this example, let’s assume we do change the <code>fontSize()</code> function to now return <code>72.sp</code> instead of <code>48.sp</code>.</p>
<p>As readers, we know that this means that the <code>App$Column</code> scope shall be marked as dirty</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742541970090/9a8f562b-fd27-41e3-85cd-fded3dc1ba06.png" alt class="image--center mx-auto" /></p>
<p>This becomes pretty clear when looking at the tracked runtime information presented as a tree:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742542822784/ebba35fd-6a7f-4344-aa9e-7e9fa4e475f8.png" alt class="image--center mx-auto" /></p>
<p>All <code>@Composable</code> scopes are marked with the small Compose logo<sup>.</sup><br />In the example, there are four of them! <code>App</code>, <code>App$Column</code>, <code>MyButton</code> and <code>content</code>. After reloading arbitrary code, any of those scopes could get dirty. There, each scope will be enqueued to check for dirtiness.<br />But wait, we agreed that the number of <code>@Composable</code> scopes is linearly proportional to the entire codebase in Compose projects. We found the first <code>n</code> 😎</p>
<h3 id="heading-who-is-dirty">Who is dirty?</h3>
<p>Since <code>App$Column</code> calls into the <code>fontSize()</code> function, we know that it has to be marked as dirty, should drop its UI state and re-render. However, we know that keeping the state in other <code>@Composable</code> scopes is totally fine. We can conclude that dirtiness follows only <code>dependency</code> edges and does not cross <code>Child/Parent</code> relationships.</p>
<p>Each scope will now calculate a new hash code, called the “Invalidation Key” by including all <strong>transitive</strong> dependency scopes. For the sake of simplicity, let’s also assume that dependencies between <code>@Composable</code> functions are also to be ignored.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742544198552/7df1a975-494e-49bb-99bf-8e1cb9605ae2.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-the-second-n-graph-traversal">The second ‘n’: Graph traversal</h3>
<p>The example above seems easy: Again, for each <code>@Composable</code> scope, we will calculate this new hash code by traversing all transitive dependencies. The above image looks simple and flat because the graph we took as an example is simple: Only a single dependency from <code>Appp$Column</code> to <code>fontSize()</code> which we had to traverse. However, even with this simple code example, the ‘real life’ graph will look much, much more complicated. You see that the <code>fontSize()</code> method also depends on the <code>.sp</code> extension property, the getter of which will bring in more and more dependencies as well.</p>
<p>But how much work will this be for each scope to traverse its dependency graph? Certainly, this depends a lot on the structure of the code and its dependencies. It’s hard to predict how deep the dependency graph will be for each scope. Let’s do a field experiment:</p>
<p>Let’s run a huge application, such as IntelliJ itself, setup Hot Reload, perform a code change, and measure how long the resolution of all scopes will take:</p>
<p>Let’s change a simple String literal and reload: <code>59ms</code><br />Let’s perform some dummy changes in a base module: <code>53ms</code></p>
<p>That does not look too bad, right? Unless we realize two things:</p>
<ol>
<li><p>When doing the experiment there were only 300 <code>@Composable</code> scopes loaded and the time it will take this algorithm will grow linearly with the number of those. Even having 30K scopes would not be considered a “huge” app and we can expect the algorithm to take ~5s, which is a deal breaker. <em>Note: 30K scopes does not mean 30K</em> <code>@Composable</code> <em>funcitons. Each function, especially when using control flow, will have multiple child scopes.</em></p>
</li>
<li><p>Those results were only reached by manually disabling one rather important kind of dependency edge:<br /> What happens if a function depends on an interface function, or any <code>open</code> or <code>abstract</code> function for that matter? Then it certainly shall be marked dirty when any of the implementations change. This, however, will bring in many, actually unnecessary nodes in our dependency graph to traverse.<br /> Imagine depending on an interface <code>MyInterface</code> and it has 100 implementations. You changed one and now all those 99 unnecessary implementations will be part of the dependency graph (and their dependencies as well!). But what happens if those implementations rely on other interfaces with many implementations themselves? This is what I called the ‘super class bomb’. When this happens (and this happens in large projects), then the dependency graph, for each <code>@Composable</code> , is proportional to the size of the code base. Jeez, here we go: We found the second <code>n</code> , did we?</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742546870754/a1f6ca48-60fa-4383-a1a2-24912d4c06d9.png" alt class="image--center mx-auto" /></p>
<p>Let’s re-enable this part of hot reload (virtualMethodResolve) and test IntelliJ changes again:</p>
<ul>
<li><p><strong>simple String literal and reload</strong>:<br />  <em>I had to cancel this test, because I am impatient as hell. However, my estimate from the progress bar would have been</em>: <strong>225 Minutes 💣🤯</strong></p>
</li>
<li><p><strong>some dummy changes in a base module</strong><br />  <em>This one, I was patient enough to wait through:</em> <strong>8m 28 💣 🥺</strong></p>
</li>
</ul>
<p><em>Note, I was unable to come up with a theory as to why the first ETA is so much higher than the second result. From what we have described about the algorithm, so far, it should not matter where the change is located: We’re doing the same work after all. My best theory is that there might be just one bomb, which takes almost all the time. Maybe If I would have waited just a little bit longer, then we would have seen ~9m as well?</em></p>
<h2 id="heading-fighting-n-and-n">Fighting ‘n’ and ‘n’</h2>
<p>The ‘two hashes’ idea, the first hash being only the ‘semantic hash’ of the scope in isolation, the second hash being this ‘invalidation key’ was pretty elegant <em>imho</em>. It served me pretty well in understanding how the system behaved and made it easy to debug. However, we’re certainly doing a lot of work that we do not care about: Who cares about the hash code? All we actually care about is whether or not it has changed or not, right? Equally: When trying to find what is dirty, it seems like the old algorithm was searching for the needle in the haystack… 👀 after throwing it in <strong>itself</strong>.<br />See: The runtime knows exactly which classes changed to begin with because the user sent a request to reload a set of particular classes! We know that nothing else could have changed. This information was not used in the algorithm. The direction of following from interfaces to their implementations also is unfavorable. Methods might have many implementations, but we can think of an implementation mostly having one super!</p>
<h3 id="heading-turnaround-every-now-and-then-i-get-a-little-bit-terrified">Turnaround: 🎶 <em>Every now and then I get a little bit terrified… 🎤</em></h3>
<p>Let’s build another algorithm, that does not start at the <code>@Composable</code> scopes, but at the classes which the users requested to reload. This number is rather stable (and certainly does not depend on the size of your project, <code>n</code>). We can identify the scopes, inside the ‘to be reloaded’ classes as dirty or not, by comparing their hashes against the previous ones. From here, we do the same walk, but in the inverse direction: We start from what is dirty, find scopes that depend on the dirty scope, mark them as dirty, and repeat!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742549160214/5570b58c-6751-4942-a0ba-b449726f19d0.png" alt class="image--center mx-auto" /></p>
<p>Another crucial difference: Even though we might initially enqueue multiple scopes as ‘dirty’, we still only have to traverse the dependency graph once, instead of doing it for each starting point (previously <code>@Composable</code> scope). One ‘n’ seems to be killed! How about the number of nodes and vertices in this dependency graph? The static dependencies between code stays still the same, however, we drastically reduced the number of edges because of the 1:m relation between super and subclasses! We can straight up follow into the super method and mark it as dirty (compared to walking from one abstract method into all implementations!). This will help significantly, but theoretically, we can still have dependencies inside the code which might require traversing a huge part of the graph!</p>
<h3 id="heading-turnaround-every-now-and-then-i-get-a-little-bit-nervous">Turnaround: <em>🎶 Every now and then I get a little bit nervous … 🎤</em></h3>
<p>Let’s face some emotions: Yes, I am proud that changing an implementation of an interface on which a static field depends, which is used in a <code>@Composable</code> function depends upon will refresh the <code>@Composable</code> . It’s awesome, it’s a nice brag and it makes for cool demos. Simply speaking: I am proud as a developer of how predictable this system would work. But is me being proud of fancy demos really worth following this graph into 15 levels deep? I mean: If the code change did not affect the <code>@Composable</code> scope directly, nor any immediate dependencies of this scope, then it’s pretty fair to say that the state kept in his scope is still OK. No need to invalidate! Therefore, we can stab the remaining potential <code>n</code> by just limiting our ‘mark dirty’ graph walk to a certain depth and calling it a day. We walk 3, 5, and 10 edges: If we have not found any <code>@Composable</code> scope, then fine: Let’s keep it!</p>
<h2 id="heading-result">Result</h2>
<p>There has been a small benchmark written for this composable:<br />It will compile 1K or 10K synthetic Kotlin source files, which depend on each other, and perform a reload of 10 or 100 classes.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1742550888976/5822a513-7051-45da-90d1-455dfa937950.png" alt class="image--center mx-auto" /></p>
<p>This benchmark at least confirms our suspicion: The number of total classes is irrelevant for this algorithm (first <code>n</code>). If we also accept the fact that during typical development flows the number of classes reloaded is pretty stable and does not depend on n, then we could say that we fully eliminated <code>n: Total number of classes in the project</code> from the equation!</p>
<p>But synthetic benchmarks aside:<br />Let’s spin up IntelliJ and make the same changes, which previously provoked the ‘super class bomb’!</p>
<ul>
<li><p><strong>simple String literal and reload</strong>:<br />  <em>previous (ETA)</em> <strong>225 Minutes 💣🤯 →</strong> <em>new</em> <strong>500 us</strong></p>
</li>
<li><p><strong>some dummy changes in a base module</strong><br />  <em>previous</em> <strong>8m 28 💣 🥺 →</strong> <em>new</em> <strong>18 ms</strong></p>
</li>
</ul>
<p>The new algorithm will be available on <code>1.0.0-alpha03</code> , which also reduced the memory footprint of the runtime-tracking significantly.</p>
]]></content:encoded></item><item><title><![CDATA[DX; Deep Debugging and my new favorite System Property]]></title><description><![CDATA[I think nobody here would disagree with me, saying that 'good Developer Experience (DX) is key to successful projects and teams'. Nice tests, reliable IDE integration and smooth CI are obvious. Sometimes, however, things get dirty: We do have complic...]]></description><link>https://blog.sellmair.io/dx-deep-debugging-and-my-new-favorite-system-property</link><guid isPermaLink="true">https://blog.sellmair.io/dx-deep-debugging-and-my-new-favorite-system-property</guid><category><![CDATA[debugging]]></category><category><![CDATA[Kotlin]]></category><category><![CDATA[Java]]></category><category><![CDATA[gradle]]></category><category><![CDATA[intellij]]></category><dc:creator><![CDATA[Sebastian Sellmair]]></dc:creator><pubDate>Fri, 07 Feb 2025 14:22:27 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1738937840665/304c3de5-829b-42c9-8da0-7d440fa32233.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I think nobody here would disagree with me, saying that '<strong><em>good Developer Experience (DX)</em></strong> <em>is key to successful projects and teams</em>'. Nice tests, reliable IDE integration and smooth CI are obvious. Sometimes, however, things get dirty: We do have complicated projects with complex environments, and I have worked in projects where debugging a broken test was almost impossible, or required, significant knowledge about the infrastructure and ceremony.</p>
<p>In this blogpost, I would like to share my new favourite system property, how it can be used for significantly improving DX and thank the team who invented it in IntelliJ.</p>
<p><em>And who knows, maybe other engineers will share even cooler techniques in the comments or follow-up blogposts as well? 👀</em></p>
<h1 id="heading-my-fault">My Fault</h1>
<p>One of the projects where I was not too happy with the DX, debugging broken tests was set-up by my very own...</p>
<p><img src="https://slm-assets.secondlife.com/assets/21096610/view_large/shame.jpg?1531839295" alt="Game of Thrones - Shame!" /></p>
<p><a target="_blank" href="https://blog.sellmair.io/say-hello-to-compose-hot-reload-firework">In my previous blogpost, introducing 'Poject Firework; AKA Compose Hot Reload'</a>, I proudly talked about my screenshot tests and how quickly they made me confident about the code. But did I tell you about the ceremony of debugging those tests? I mean, of course it's easy to just press 'Debug' in IntelliJ on the test's run gutter and hit a breakpoint in the code that executes the test, but…</p>
<h2 id="heading-some-tests-will-launch-new-processes">Some Tests will launch new processes</h2>
<p>** which will launch new processes, which will launch a new process, …</p>
<p>My project's main component is a javaagent, which can be attached to Compose applications, making them hot-reloadable. Integration Tests will run against different versions of Gradle, Kotlin and Compose. So typically, the <code>@Test</code> (or <code>@HotReloadTest</code> in my case) annotated method will just coordinate launches of new processes to test the entire build of a project.</p>
<p>This means, when I click the test-run-gutter, IntelliJ will</p>
<ol>
<li><p>Launch a Gradle Daemon (if necessary) | JVM #1</p>
</li>
<li><p>The Gradle Daemon will launch a JVM to execute the tests within (Test Engine) | JVM #2</p>
</li>
<li><p>The Test will decide which parameters (e.g. which Gradle version) to run against and then create the test project, launching a new Gradle Daemon | JVM #3</p>
</li>
<li><p>The Test Gradle Daemon will then launch a test application which can be used to run the test against | JVM #4</p>
</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738937922667/fa1a8124-abb6-4903-98f2-a3997132b7b7.png" alt class="image--center mx-auto" /></p>
<p>If there are issues (e.g. a failing test, or god forbid: a bug 😳), the problem is mostly in the code of the 'javaagent', however, this code runs in the 4th JVM in my chain of sub-processes! While pressing 'Debug' in IntelliJ will stop breakpoints in the test infrastructure code (JVM #2), it won't affect the part I actually care about the most 🥺</p>
<p><em>So, when I had to debug a given test, I had to start a separate 'Debug Server' first and instruct JVM#4 to connect to it; However, this was also not ideal: Either I made the debugger connection suspending, this would fail the test if no debug server was started before. A non-suspending connection can be added to the JVM invocation unconditionally, but then my 'premain' function might run before the debugger connection is established. Therefore, I decided to introduce a @Debug annotation that a developer should add to the test, after launching the Debug Server manually</em> <em>(<strong><strong>cough</strong></strong>,</em> <strong><em>cough</em></strong>*: 😩* <strong><em>Ceremony! 😩</em></strong>*)*</p>
<h2 id="heading-there-has-to-be-a-better-way-my-new-favourite-system-property">There has to be a better way: My new favourite System Property!</h2>
<p>It is clear how the ideal solution would work: Press Debug in IntelliJ and get the breakpoints in the sources you care about engaged, ready to debug!</p>
<p>And wait, how does the IntelliJ debugger even know about the JUnit Test Engine JVM? What kind of magic will connect IntelliJ -&gt; Gradle -&gt; JUnit?</p>
<p>The answer is the System Property I have teased:</p>
<p>🎉 <code>idea.debugger.dispatch.port</code></p>
<p>And I think the name is almost self-explanatory!</p>
<p>When running in 'Debug' mode, IntelliJ will launch a special 'Debugger Dispatch' server and inject an init-script into the build process. Before each 'JavaExec' task (tasks which will launch a new JVM) is executed, the task will connect to this 'Debugger Dispatch' socket and a new 'Debug Server' will be provisioned. Once the 'Debug Server' is started, the new JVM launches with suitable JVM args to connect to it.</p>
<p>And we can re-use the same mechanics to dynamically provision a debug server from IntelliJ for any other purpose! No matter where our code is running. All we need to do is: Check for the presence of the <code>idea.debugger.dispatch.port</code> System Property, connect to the socket and request a new Debug Server!</p>
<p>The protocol is straightforward: It uses <code>java.io.DataOutputStream</code> and expects three Strings being sent through the pipeline.</p>
<ol>
<li><p>The 'Debugger ID': This will always be Gradle JVM in our case</p>
</li>
<li><p>The name of the process: This can be picked freely</p>
</li>
<li><p>Arguments (In our case we can just supply DEBUG_SERVER_PORT=port</p>
</li>
</ol>
<p>Here is an example function which will find a free port, connect to the 'Debugger Dispatch Server' and request a 'Debug Server' being started. Once a single byte is received in response, we can assume that the 'Debug Server' is launched and we can proceed to start a new JVM with the debugging arguments.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738938043235/e134b9f7-b31e-4161-87d3-9373eb19580d.png" alt class="image--center mx-auto" /></p>
<p>And just like this, all the ceremony can be brought into core test-infrastructure, and a developer can just press 'Debug' and it <strong>just works</strong>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1738938061868/c72b0b56-f02b-4050-809e-256553e5c883.gif" alt class="image--center mx-auto" /></p>
<h2 id="heading-xf9cx1xf">___</h2>
<p>Thanks to the IntelliJ Java Build tools team who introduced the 'Debugger Dispatch Server'</p>
]]></content:encoded></item><item><title><![CDATA[Say Hello to 'Compose Hot Reload' (Firework)]]></title><description><![CDATA[Disclaimer: This is a public version of an internal blog post within JetBrains. Some parts were removed to save some surprises for later.
Video Version: https://youtu.be/I_FMnPaEBEA
Gullible me; I posted a screen recording into the #kotlin-random Sla...]]></description><link>https://blog.sellmair.io/say-hello-to-compose-hot-reload-firework</link><guid isPermaLink="true">https://blog.sellmair.io/say-hello-to-compose-hot-reload-firework</guid><category><![CDATA[compose]]></category><category><![CDATA[compose multiplatform]]></category><category><![CDATA[Kotlin]]></category><category><![CDATA[Kotlin Multiplatform]]></category><category><![CDATA[firework]]></category><dc:creator><![CDATA[Sebastian Sellmair]]></dc:creator><pubDate>Thu, 19 Dec 2024 14:00:05 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1734616770684/334c0937-2138-4ae4-8b19-76462d50db1a.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>Disclaimer: This is a public version of an internal blog post within JetBrains. Some parts were removed to save some surprises for later.</em></p>
<p><strong><em>Video Version:</em></strong> <a target="_blank" href="https://youtu.be/I_FMnPaEBEA"><strong><em>https://youtu.be/I_FMnPaEBEA</em></strong></a></p>
<p>Gullible me; I posted a screen recording into the #kotlin-random Slack channel on October 8th. Alongside this screen recording, I claimed:<br />"<em>I think it would be easily doable to build a stateful hot-reload</em> <strong><em>if</em></strong>*[…]*".</p>
<p>I wanted to create educational content about ClassLoaders and thought it would be great to showcase building a hot reloadable UI for Compose.</p>
<p>The <strong><em>if</em></strong> part in my initial message was much bigger than expected (who would have thought). But the demos sparked a lot of interest, and with the motivation and many good technical suggestions from our colleagues, two months later, a promising prototype is available.</p>
<p>This blogpost will explain the current state of said prototype (with a few demos), tries to give some technical insights on how it works and finally shares the plans on moving forward.</p>
<h2 id="heading-the-current-state-of-compose-hot-reload">The current state of Compose Hot Reload</h2>
<p>Prototype: <a target="_blank" href="https://github.com/JetBrains/compose-hot-reload">https://github.com/JetBrains/compose-hot-reload</a></p>
<p>The state of the current prototype can be quickly summarized as:</p>
<ul>
<li><p>Almost all changes to your code are allowed, except removing supertypes from a class (see DCEVM part later)</p>
</li>
<li><p>Changes in bytecode associated with a given "scope" in Compose will lead to this scope being invalidated (remembered values will be re-calculated)</p>
</li>
<li><p>Gradle is fully implemented, Amper is 'work in progress', Support for the IntelliJ build will be next</p>
</li>
</ul>
<h3 id="heading-demo-1-simple-literal-change">Demo 1: Simple Literal Change</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1734614145423/be5ac047-e1c9-4609-9124-c85deac0838b.gif" alt class="image--center mx-auto" /></p>
<p>This is the 'Hello World' of Hot Reload; Notice the reloading indicator getting yellow once the compilation starts and turning green after the reload is finished.</p>
<hr />
<h3 id="heading-demo-2-simple-layout-change">Demo 2: Simple Layout Change</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1734614168860/93e1e43f-4910-4903-8152-556dbce926a0.gif" alt class="image--center mx-auto" /></p>
<p>As it was promised earlier that almost arbitrary code changes are possible, I expect this demo to be almost boring. The UI switches the Layout from a Row to a Column and back.</p>
<hr />
<h3 id="heading-demo-3-add-state-and-change-the-interaction-with-it">Demo 3: Add State and change the interaction with it</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1734614183710/0f3cdda9-026d-4388-a211-45339eab6f3f.gif" alt class="image--center mx-auto" /></p>
<p>Now it seems to get more interesting. This demo will introduce a 'remembered' state (count), adds a Button which will increase this count and add a <code>Text</code> to render it. You can see that pressing the Button will increase the counter on screen. Changing <code>count++</code> to <code>count++</code> will change the behaviour of the Button and the counter gets decreased afterwards.</p>
<hr />
<h3 id="heading-demo-4-adding-a-new-function">Demo 4: Adding a new function</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1734614200990/a7a0c832-3df9-48d6-899b-cfa14b3e10a6.gif" alt class="image--center mx-auto" /></p>
<p>This Demo shows the power of our JetBrains Runtime. Adding this new 'NewComposable' function is not a problem. Traditionally, such changes are not permitted in the JVM world.</p>
<hr />
<h3 id="heading-demo-5-rename-a-function-abi-changes">Demo 5: Rename a function (ABI changes)</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1734614218418/a3ee7710-860f-4944-89f1-77ea6157603a.gif" alt class="image--center mx-auto" /></p>
<p>This demo also shows a typical limitation of hot reloading being lifted (thanks to the JetBrains Runtime). Renaming the '<code>NewComposable</code>' function to '<code>NewName</code>' is possible. Adding the <code>:)</code> afterwards reveals that the hot reload is still alive.</p>
<hr />
<h3 id="heading-demo-6-adding-a-new-class">Demo 6: Adding a new Class</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1734614245247/78dd4e96-01ab-4c1d-9f91-4b3a123968f9.gif" alt class="image--center mx-auto" /></p>
<p>A new class '<code>NewClass</code>' is added and rendered as a String. You can see the default <code>.toString()</code> method rendering the instance after reloading.</p>
<hr />
<h3 id="heading-demo-7-adding-a-new-property-to-an-existing-class">Demo 7: Adding a new property to an existing class</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1734614259770/67047a74-78a7-44d4-aa32-76c71889245d.gif" alt class="image--center mx-auto" /></p>
<p>One of the first questions people do have with such a potent hot reload implementation is "What will happen if we add a field/property to an existing class?" . The demo above shows the <code>App</code> Composable being invalidated, a new instance being created and rendered. This is the easier case. The JetBrains runtime also supports keeping previous instances when adding fields. In this case the JVM will fill new fields with the JVM defaults (null, 0, false).</p>
<hr />
<h3 id="heading-demo-8-compose-hot-reload-understands-code-dependencies">Demo 8: Compose Hot Reload understands code dependencies!</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1734614276566/fb610ebc-5208-4442-ab31-956cde1d8490.gif" alt class="image--center mx-auto" /></p>
<p>This one makes me very proud; however, it is a little bit tricky to see what is going on here.</p>
<p>Compose Hot Reload understands which code (and its associated Compose Groups) depend on other functions and 'scopes' (more technical explanations can be found in later sections).</p>
<p>In this demo, we can see the <code>App</code> Composable picking a random value on the first run and remembering it, even retaining it in later changes. This random value is produced by the <code>calcualteMagic</code> function. Now look close! The <code>App</code> Composable gets invalidated, and a new value will be picked by us changing the body of the <code>calculateMagic</code> function. The runtime will track which code is changed and will resolve all Composable groups which are supposed to be invalidated.</p>
<hr />
<h3 id="heading-demo-9-reload-status-indicator">Demo 9: Reload Status Indicator</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1734614302548/9441f379-7eaf-46b0-a812-9a7df152a988.gif" alt class="image--center mx-auto" /></p>
<p>We all have been there once in our professional life: Debugging for hours on end, starting to doubt our entire model of the universe, just to find out that our code was not even running at all. This is a very unpleasant experience and one of the reasons why developers might lose trust in ‘hot reload technologies’. In this demo, you can see a status indicator glowing red, next to your application, making it obvious that your code was not reloaded.</p>
<p>Once the compilation error was fixed, the indicator jumps from red to yellow back to green.</p>
<hr />
<h3 id="heading-demo-10-dev-tooling-clean-composition">Demo 10: 'Dev Tooling': Clean Composition</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1734614340574/98af21bd-000d-4918-9edc-4f7cc054f15a.gif" alt class="image--center mx-auto" /></p>
<p>The floating 'Compose Icon', following your application, can also be pressed to open advanced tooling. This is tooling is rendered in a separate process, which can be helpful to recover your application from a very broken state. In this demo, the 'Clean Composition' button is pressed, which will clear all remembered states of the UI and cancels all effects.</p>
<h2 id="heading-implementation-details-technical-part">Implementation Details / Technical part</h2>
<h3 id="heading-overview">Overview</h3>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1734614427556/bee0ec94-8846-4845-bf2e-a1e2201f8b8f.jpeg" alt class="image--center mx-auto" /></p>
<p>The prototype contains several conceptional parts:</p>
<h3 id="heading-build-tool-integration">Build Tool Integration</h3>
<p><em>While there is an Amper integration currently 'work in progress', lets focus on the Gradle implementation for the sake of simplicity.</em></p>
<p><a target="_blank" href="https://github.com/JetBrains/compose-hot-reload/tree/master/hot-reload-gradle-plugin/src/main/kotlin/org/jetbrains/compose/reload">https://github.com/JetBrains/compose-hot-reload/tree/master/hot-reload-gradle-plugin/src/main/kotlin/org/jetbrains/compose/reload</a></p>
<p>The build tools job is to launch a given Compose application in a special 'hot' mode. This requires</p>
<ul>
<li><p>Ensure a JBR is available (download if necessary)</p>
</li>
<li><p>Set necessary JVM arguments to allow redefining classes</p>
</li>
<li><p>Attach and setup the 'Compose Hot Reload Agent'</p>
</li>
<li><p>Provide and 'engage' the 'Compose Hot Reload Runtime'</p>
</li>
<li><p>Provide instructions to the application on how to file a 'recompile request' back to the build tool.</p>
</li>
</ul>
<h3 id="heading-orchestration-server">Orchestration Server</h3>
<p><a target="_blank" href="https://github.com/JetBrains/compose-hot-reload/tree/master/hot-reload-orchestration/src/main/kotlin/org/jetbrains/compose/reload/orchestration">https://github.com/JetBrains/compose-hot-reload/tree/master/hot-reload-orchestration/src/main/kotlin/org/jetbrains/compose/reload/orchestration</a></p>
<p>We will see later that multiple processes are involved in 'orchestrating' the hot reload application (Dev Tooling Process, Recompiler Process, optionally IDE tooling, …). Those processes communicate through a simple protocol which allows sending messages (OrchestrationMessage) as broadcasts to all participants. If not hosted outside (e.g. by the IDE), the 'Hot Reload Agent' will start this Orchestration Server and tell child processes how to connect to it.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1734614477917/cb467c98-1216-4782-b449-60160d1e07f7.jpeg" alt class="image--center mx-auto" /></p>
<h3 id="heading-compose-hot-reload-agent">Compose Hot Reload Agent</h3>
<p><a target="_blank" href="https://github.com/JetBrains/compose-hot-reload/tree/master/hot-reload-agent/src/main/kotlin/org/jetbrains/compose/reload/agent">https://github.com/JetBrains/compose-hot-reload/tree/master/hot-reload-agent/src/main/kotlin/org/jetbrains/compose/reload/agent</a></p>
<p>The agent is responsible for setting up the Hot Reload environment. It will try to find the Compose Runtime and enable a 'Hot Reload Mode', which was initially intended to be used for Android (Live Edit). This mode will allow runtime errors to potentially be recoverable.</p>
<p>If not hosted somewhere outside (e.g. by an IDE plugin), the agent will start the 'Orchestration Server' and launch the 'Dev Tooling Process' as well as the 'Recompiler Process'.</p>
<p>A hook into the JVM will be installed. This hook will allow analyzing the actual bytecode when initially loading a class and when redefining it. This 'runtime analysis' will then know which 'Compose Groups' to invalidate by looking at changes to the bytecode.</p>
<p>The Agent will handle 'ReloadClassesRequest' messages, sent from the 'Recompiler Process', using the JBRs implementation of <a target="_blank" href="https://dcevm.github.io/">DCEVM</a> (Enhanced class redefinition for Java)</p>
<h3 id="heading-runtime-bytecode-analysis">Runtime Bytecode Analysis</h3>
<p>Let's look into how the Compose Hot Reload Agent knows which states / Compose Groups to invalidate. We'll pick a simple example, where <code>@Composable fun Foo</code> has a simple remembered state, a <code>Button</code> to interact with said state and two utility functions.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1734614647296/f059bca4-4c58-431e-b6a1-e04a4083408a.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1734614654827/cc5bd2bf-a238-4611-8683-2ebf3f9c8118.gif" alt class="image--center mx-auto" /></p>
<p>When initially loading the class containing the <code>Foo</code> function, the Agent will track the 'Runtime Information'. After building 'Compose Aware' bytecode tokens, all functions will be parsed into a 'Runtime Tree'</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1734614679838/bc6a2523-d7d4-4ec8-9562-39190157e88b.png" alt class="image--center mx-auto" /></p>
<p>This 'Runtime Tree' will understand the bytecode in functions and builds the corresponding scopes (called 'Compose Group'). You can see that the simple example will start a <code>RestartGroup</code> in the Function <code>Foo</code>. The tree will also include branches in your code.</p>
<p>Once all scopes/groups are parsed, the Runtime Info will be constructed, assigning each scope a 32-bit hash value. This hash value is almost the hash of the bytecode inside the given scope. For obvious reasons some parts are ignored (such as line number depending constants/instructions). If this hash changes, we can assume that the body of the function changed significantly enough to invalidate the group.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1734614702224/b8475dc1-1d6e-4515-a76e-6b509402b068.png" alt class="image--center mx-auto" /></p>
<p>This 'Runtime Info' also tracks dependencies between functions!</p>
<p>See how <code>Foo</code> depends on <code>FooKt.paddingValue</code> and <code>FooKt$Foo$2</code> (lambda inside the <code>Button {}</code> call) depends on <code>FooKt.buttonText</code></p>
<p>When classes are 'reloaded'/'redefined', the Compose Hot Reload agent will calculate which Groups to invalidate. Another 'key' will be resolved, the 'Invalidation Hash Key'. This hash is calculated by creating a compound hash of a scope with all its dependencies included (transitively). If such an 'Invalidation Hash Key' changes after reloading classes, the associated Compose Groups will be invalidated.</p>
<p><strong>Note</strong></p>
<p>This obviously requires the actual Compose Group Keys to be stable during a reload. The Compose compiler required three patches to guarantee stable enough keys. Those patches will also improve the reliability of other tools (such as Google's 'Live Edit'). Two out of those three patches landed in kotlin.git/master (compose compiler). The third patch is currently pending. Therefore, custom 'firework' builds of the Kotlin/Compose Compiler are required when using the prototype.</p>
<h3 id="heading-engineering-goode-look-ma-screenshot-tests-with-hot-reload">Engineering Goode: Look Ma, Screenshot Tests with Hot Reload</h3>
<p>Testing a system across multiple processes is tricky. Gaining confidence of Hot Reload really requires some kind of 'visual verification'. Screenshot tests, for example, will host the Orchestration Server in the test process, compile provided source code, spin up a special 'Application Under Test' and communicate with the Application Process by sending 'Orchestration Messages'.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1734614750724/1ba29e1c-11da-408b-87e7-0e987f58eaa4.png" alt class="image--center mx-auto" /></p>
<p>The 'initial source code' here will define an application with a simple counter-state, which will render a text <code>Before: 0</code> initially. You can see that the 'application under test' allows reacting to 'TestEvents'. Receiving a 'TestEvent' will increase the counter.</p>
<p>We'll take an initial screenshot, send two events, verifying the counter works by taking screenshots 'before-1' and 'before-2'.</p>
<p>Finally, the text "Before" will be replaced with "After" in code and the application reloads hot. The final screenshot will verify the state being retained (by rendering <code>After: 2</code>)</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1734614769747/f95774cc-811c-444d-8a86-0df5ae9f7833.png" alt class="image--center mx-auto" /></p>
<p>A test failure would then show up as:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1734614792355/1401e4cf-51d2-4ba4-ac55-a7de84cf7cdf.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-future-plans">Future Plans</h2>
<p>The time since October was mainly used to validate the concept, trying to find as many issues as possible, trying to find any good reason not to continue. There was plenty of sweat and blood put into this project <em>(although, no tears, because I am a brave engineer, not even a little, I promise 👀)</em>. My goal was to finish this experimental stage before new year.</p>
<p>I am happy to announce that the project is moving into its next phase: Stabilization. 🎇</p>
<h3 id="heading-stabilization">Stabilization</h3>
<p>Stabilizing the JBR implementation of Hot Reload requires checking the following boxes</p>
<p><em>(Next to the obvious part of finding bugs and issues and fixing them)</em></p>
<ul>
<li><p>All patches into the Compose Compiler need to be available in a stable Kotlin Release</p>
<ul>
<li>Target: Kotlin 2.1.20 | Potentially Kotlin 2.2</li>
</ul>
</li>
<li><p>Ide Plugin is available</p>
<ul>
<li><p>Currently prototyped by <a target="_blank" href="https://jetbrains.team/m/Konstantin.Tskhovrebov">@Konstantin Tskhovrebov</a></p>
</li>
<li><p>Allows starting 'DevelopmentEntryPoint' annotated <code>@Composable</code> functions</p>
</li>
<li><p>Hosting the OrchestrationServer + ensuring that reloads are explicit by the user (CMD + S)</p>
</li>
<li><p>Provides the JBR, no need to download another one.</p>
</li>
</ul>
</li>
<li><p>Support for IntelliJ Plugin development (Optional, but very desirable)</p>
<ul>
<li>The IntelliJ Plugin Gradle plugin integrates Compose Hot Reload. Allowing for a very smooth plugin development experience.</li>
</ul>
</li>
<li><p>Support for IntelliJ development in <code>ultimate.git</code> (Optional, but desirable, help needed!)</p>
<ul>
<li>When working on IntelliJ inside our monorepo with Compose, allowing run configurations to start in 'Compose Hot Reload Mode' would be great.</li>
</ul>
</li>
</ul>
<h3 id="heading-dreaming-bigger-1-kotlin-preview-jvm-compiler">Dreaming Bigger 1: Kotlin Preview JVM Compiler</h3>
<p>The first stabilization focuses on the JVM and the JetBrains Runtime in particular. However, there might be many projects which might only target Android or (Android + iOS). Adding a JVM target to the project is doable but requires restructuring code and is not particularly cheap (especially for larger projects). One of the bigger pain points might be providing all 'actual' implementations on the JVM which have been defined in common code.</p>
<p>A special 'JVM Preview Compiler' is imagined, which is able to take your common code, producing executable JVM bytecode. Missing 'actuals' can be mocked! If your hot reload session encounters an actual implementation, which has not been defined for the JVM, then certain levels of gracefulness can be used.</p>
<p>Missing <code>actuals</code> returning <code>Unit</code> or nullable types could return <code>null</code> or <code>Unit</code> .</p>
<p>A placeholder box<code>@Composable</code> could replace missing functions <code>@Composable</code> functions.</p>
<p>Exceptions can be thrown otherwise.</p>
<p>All incidents, when your 'preview hot reload session' runs into a missing actual will be reported by the runtime as 'Orchestration Message', which could be picked up by the IDE suggesting to create the missing actual for you.</p>
<p><em>Note: This is a simple description of what we might want to work on next. I am sure that many readers will be able to point out many issues and flaws with the exact workings described above. Thats OK.</em></p>
<h3 id="heading-dreaming-bigger-2-next-targets-android-ios-wasm">Dreaming Bigger 2: Next Targets | Android, iOS, Wasm</h3>
<h4 id="heading-to-be-continued">// To be continued…</h4>
]]></content:encoded></item><item><title><![CDATA[Setting up Kotlin Multiplatform (+Compose)]]></title><description><![CDATA[Kotlin Multiplatform (De)Wizard
Project Wizards can be great; they spare us from doing repetitive, boring, boilerplate tasks over and over again. However: Wizards are no replacement for education or documentation: I have seen so many engineers gettin...]]></description><link>https://blog.sellmair.io/setting-up-kotlin-multiplatform-compose</link><guid isPermaLink="true">https://blog.sellmair.io/setting-up-kotlin-multiplatform-compose</guid><category><![CDATA[Kotlin]]></category><category><![CDATA[Kotlin Multiplatform]]></category><category><![CDATA[compose]]></category><category><![CDATA[compose multiplatform]]></category><category><![CDATA[Tutorial]]></category><category><![CDATA[guide]]></category><category><![CDATA[iOS]]></category><category><![CDATA[Android]]></category><category><![CDATA[desktop]]></category><dc:creator><![CDATA[Sebastian Sellmair]]></dc:creator><pubDate>Mon, 22 Jul 2024 14:52:54 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1721642070992/536c361b-ea27-4bc1-9ead-f5353d26d194.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-kotlin-multiplatform-dewizard">Kotlin Multiplatform (De)Wizard</h1>
<p>Project Wizards can be great; they spare us from doing repetitive, boring, boilerplate tasks <strong>over</strong> and <strong>over</strong> again. However: Wizards are <strong>no replacement for education or documentation</strong>: I have seen so many engineers getting so used to <em>only using wizards</em> that they have no idea what their builds are <em>actually</em> doing or what certain blocks in their buildscripts are intended to do. Once shit hits the fan and something does not work out anymore, frustration builds up quickly, and tools like Gradle are blamed for the <a target="_blank" href="https://en.wikipedia.org/wiki/Learned_helplessness">learned helplessness</a> which has anchored itself over years and years.</p>
<p>This blog post is intended to "de-wizard" setting up Kotlin Multiplatform projects by explaining <strong>every step</strong> required to create a Compose app by hand (w/o using the wizards). Every step will be described in detail, and an on-screen video tutorial/commentary will also be attached.</p>
<p>ℹ️ You can also watch the full 'walkthrough' here:</p>
<p><a target="_blank" href="https://youtu.be/fmFezt-2IBo">https://youtu.be/fmFezt-2IBo</a></p>
<p>ℹ️ Alongside this post and videos, I also pushed this GitHub project, which contains the setup steps as individual commits in the 'main' branch:<br /><a target="_blank" href="https://github.com/sellmair/kotlin-multiplatform-getting-started">https://github.com/sellmair/kotlin-multiplatform-getting-started</a></p>
<h2 id="heading-create-a-new-empty-project-gitignore">Create a new (empty) project / .gitignore</h2>
<p>In the beginning, there was nothing. Let there be a .<code>gitignore</code> file, so we can start checking in our project into VCS. We're planning to use IntelliJ and Gradle for the Kotlin Multiplatform (KMP) app. I recommend keeping the <code>.gitignore</code> file simple</p>
<pre><code class="lang-plaintext">.idea
.kotlin
.gradle
**/build
</code></pre>
<ul>
<li><p><code>.idea</code>: IntelliJ likes to check in configuration files here. For Gradle projects, most of the configuration is done within Gradle, so a <code>Gradle sync</code> should be all you need. I recommend checking in files from within .idea only if you really want to share them (e.g. some useful run-configurations or shared dictionaries)</p>
</li>
<li><p><code>.kotlin</code>: Used by the Kotlin Gradle Plugin (KGP) to store project-level intermediate files, or files produced for the IDE (by e.g. transforming dependencies)</p>
</li>
<li><p><code>.gradle</code>: <a target="_blank" href="https://docs.gradle.org/current/userguide/directory_layout.html#dir:project_root">Project-specific cache directory generated by Gradle.</a></p>
</li>
<li><p><code>**/build</code>: Gradle (and Gradle Plugins) conventionally put the build output in a <code>build</code> folder.</p>
</li>
</ul>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/fi85FaavuJw">https://youtu.be/fi85FaavuJw</a></div>
<p> </p>
<hr />
<h2 id="heading-creating-the-initial-settingsgradlekts">Creating the initial <code>settings.gradle.kts</code></h2>
<p>Creating the <code>settings.gradle.kts</code> file will get us started with using Gradle. The first piece we should configure is telling Gradle where to download libraries and plugins from. There are two top-level blocks to consider:</p>
<pre><code class="lang-kotlin">pluginManagement {
    repositories {
        mavenCentral()
    }
}

dependencyResolutionManagement {
    repositories {
        mavenCentral()
    }
}
</code></pre>
<p>The <code>pluginManagement</code> block will configure Gradle to download Gradle plugins from <code>mavenCentral</code>, whereas the <code>dependencyResolutionManagement</code> is configuring the repositories for downloading <em>libraries.</em></p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/-HaDkR4hz8I">https://youtu.be/-HaDkR4hz8I</a></div>
<p> </p>
<hr />
<h2 id="heading-loading-the-kotlin-multiplatform-plugin">Loading the Kotlin Multiplatform Plugin</h2>
<p>We told Gradle from where to download plugins and libraries: Let's request the Kotlin Multiplatform plugin now. Gradle's plugin loading is not straightforward as it <em>might</em> isolate certain Gradle plugins from each other (in which case you might run into hard-to-understand and debug issues). One strange looking, yet effective way of avoiding such issues is to load plugins in the <strong>root</strong> <code>build.gradle.kts</code> . Using <code>apply false</code> will load the plugin and define its version for all 'subprojects' / 'submodules', but not apply them.</p>
<pre><code class="lang-kotlin">plugins {
    kotlin(<span class="hljs-string">"multiplatform"</span>) version <span class="hljs-string">"2.0.0"</span> apply <span class="hljs-literal">false</span>
}
</code></pre>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/p3HaaYlPBiM">https://youtu.be/p3HaaYlPBiM</a></div>
<p> </p>
<hr />
<h2 id="heading-loading-the-android-gradle-plugin">Loading the Android Gradle Plugin</h2>
<p>Similarly, the Android (Application) Gradle plugin can be loaded in the root <code>build.gradle.kts</code> file.</p>
<pre><code class="lang-kotlin">plugins {
    kotlin(<span class="hljs-string">"multiplatform"</span>) version <span class="hljs-string">"2.0.0"</span> apply <span class="hljs-literal">false</span>
    id(<span class="hljs-string">"com.android.application"</span>) version <span class="hljs-string">"8.5.1"</span> apply <span class="hljs-literal">false</span>
}
</code></pre>
<p>However, we will be unable to download the Android Gradle Plugin from Maven Central. We instead need to configure the <code>google()</code> maven repository additionally. Since Gradle will respect the order of repositories, trying to download plugins and libraries sequentially, I also recommend configuring the <code>google()</code> repository to only be used for <code>.*android.*</code> or <code>.*google.*</code> packages. This will decrease initial IDE sync and CLI build times, by avoiding many unnecessary network requests</p>
<pre><code class="lang-kotlin">repositories {
        google {
            mavenContent { 
                includeGroupByRegex(<span class="hljs-string">".*google.*"</span>)
                includeGroupByRegex(<span class="hljs-string">".*android.*"</span>)
            }
        }

        mavenCentral()
    }
</code></pre>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/L-lCKTNl66Q">https://youtu.be/L-lCKTNl66Q</a></div>
<p> </p>
<hr />
<h2 id="heading-setting-up-the-app-module-kotlin-targets">Setting up the ':app' module / Kotlin Targets</h2>
<p>In the <code>settings.gradle.kts</code> we can use the <code>include(":app")</code> function to register a "Gradle <em>Subproject"</em> (which is also often called a <em>module</em> in the community). To configure this module, creating a <code>app/build.gradle.kts</code> file is required.</p>
<p>Typically the <code>build.gradle.kts</code> file starts by loading plugins. In our case, we can start loading (and now actually <em>applying</em>) the Kotlin Multiplatform, as well as the Android Application plugin.</p>
<pre><code class="lang-kotlin">plugins {
    kotlin(<span class="hljs-string">"multiplatform"</span>)
    id(<span class="hljs-string">"com.android.application"</span>)
}
</code></pre>
<p><em>Note: Since we already 'loaded' the plugins in the root</em> <code>build.gradle.kts</code> <em>with a specific version, we are not required to declare a version here again. As we want to use those plugins in the. ':app' module,</em> <code>apply false</code> <em>is obviously also not required.</em></p>
<p>Kotlin is capable of compiling for many <em>"Kotlin Targets"</em>: It is required to define the targets your code is supposed to build for. Since this guide is showcasing Kotlin for Android, iOS, and Desktop (JVM), the following targets will be declared:</p>
<pre><code class="lang-kotlin">kotlin {
    jvm() <span class="hljs-comment">// &lt;- for our Desktop app</span>
    androidTarget() <span class="hljs-comment">// &lt;- Obviously to support Android</span>

    iosX64() <span class="hljs-comment">// &lt;- Simulator for x64 host machines</span>
    iosArm64() <span class="hljs-comment">// &lt;- physical iPhone</span>
    iosSimulatorArm64() <span class="hljs-comment">// &lt;- Simulator for arm based host machines</span>
}
</code></pre>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/daABLYBBnkc">https://youtu.be/daABLYBBnkc</a></div>
<p> </p>
<hr />
<h2 id="heading-setting-up-the-compose-plugins">Setting up the Compose plugins</h2>
<p>There are two Compose (Gradle) plugins that we should load when using the Compose framework</p>
<ol>
<li><p>The <code>kotlin("plugins.compose")</code> plugin will load the "Compose Compiler Plugin" for Kotlin, which will do all of the magic, transforming your code while compiling Kotlin.</p>
</li>
<li><p>The <code>id("org.jetbrains.compose")</code> plugin will set up your build to support packaging your application, managing resources, ...</p>
</li>
</ol>
<p>Similarly to the Kotlin Multiplatform and the Android Application Plugin, it makes sense to load those plugins in the root <code>build.gradle.kts</code> and then apply them in <code>app/build.gradle.kts</code></p>
<pre><code class="lang-kotlin"><span class="hljs-comment">/* root build.gradle.kts */</span>
plugins {
    kotlin(<span class="hljs-string">"multiplatform"</span>) version <span class="hljs-string">"2.0.0"</span> apply <span class="hljs-literal">false</span>
    kotlin(<span class="hljs-string">"plugin.compose"</span>) version <span class="hljs-string">"2.0.0"</span> apply <span class="hljs-literal">false</span>
    id(<span class="hljs-string">"com.android.application"</span>) version <span class="hljs-string">"8.5.1"</span> apply <span class="hljs-literal">false</span>
    id(<span class="hljs-string">"org.jetbrains.compose"</span>) version <span class="hljs-string">"1.6.11"</span> apply <span class="hljs-literal">false</span>
}
</code></pre>
<pre><code class="lang-kotlin"><span class="hljs-comment">/* app/build.gradle.kts */</span>
plugins {
    kotlin(<span class="hljs-string">"multiplatform"</span>)
    kotlin(<span class="hljs-string">"plugin.compose"</span>)
    id(<span class="hljs-string">"org.jetbrains.compose"</span>)
    id(<span class="hljs-string">"com.android.application"</span>)
}
</code></pre>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/O2P1AdJnQYk">https://youtu.be/O2P1AdJnQYk</a></div>
<p> </p>
<hr />
<h2 id="heading-writing-the-first-composable-function">Writing the first <code>@Composable</code> function</h2>
<p>Before we can use the <code>@Composable</code> annotation from Compose, declaring <em>foundational</em> dependencies is required. Since the compose dependencies are expected to be shared across all Kotlin targets, we can use the <code>commonMain</code> source set to add those dependencies.</p>
<pre><code class="lang-kotlin"><span class="hljs-comment">/* app/build.gradle.kts */</span>
kotlin {
    sourceSets.commonMain.dependencies {
        implementation(compose.foundation)
        implementation(compose.material3)
        implementation(compose.runtime)
    }
}
</code></pre>
<p>Those <code>compose.{xyz}</code> libraries will be available in the build script since we have loaded the <code>org.jetbrains.compose</code> plugin. Adding those three dependencies will not only resolve the <code>@Composable</code> annotation, but would even allow us to use the <code>material3</code> components (optional, of course).</p>
<p>The first composable can then be written in <code>src/commonMain/kotlin/</code></p>
<pre><code class="lang-kotlin"><span class="hljs-keyword">package</span> io.sellmair.app

<span class="hljs-keyword">import</span> androidx.compose.material3.Text
<span class="hljs-keyword">import</span> androidx.compose.runtime.Composable
<span class="hljs-keyword">import</span> androidx.compose.ui.unit.sp

<span class="hljs-meta">@Composable</span>
<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">MainScreen</span><span class="hljs-params">()</span></span> {
    Text(
        <span class="hljs-string">"Hello from Kotlin!"</span>,
        fontSize = <span class="hljs-number">48</span>.sp
    )
}
</code></pre>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/ukv8YZYrEpQ">https://youtu.be/ukv8YZYrEpQ</a></div>
<p> </p>
<hr />
<h2 id="heading-android-creating-the-mainactivity">Android: Creating the <code>MainActivity</code></h2>
<p>After we have created the <code>@Composable</code> function, we can wire Android up and create the <code>MainActivity</code>.</p>
<p>Code, specifically written for Android, can be placed into the <code>src/androidMain/kotlin</code> source directory. Here, we can just create a <code>MainActivity.kt</code> as usual.</p>
<p>However, since we want to use Compose on Android we have to perform several configurations:</p>
<h3 id="heading-adding-android-specific-dependencies">Adding Android-specific dependencies</h3>
<p>The <code>androidMain</code> source set can be used to add dependencies, specifically for Android. The <code>activity-compose</code> and <code>appcompat</code> libraries are recommended:</p>
<pre><code class="lang-kotlin"><span class="hljs-comment">/* app/build.gradle.kts */</span>
kotlin {
   sourceSets.androidMain.dependencies {
        implementation(<span class="hljs-string">"androidx.activity:activity-compose:1.9.0"</span>)
        implementation(<span class="hljs-string">"androidx.appcompat:appcompat:1.7.0"</span>)
    }
}
</code></pre>
<h3 id="heading-androiduseandroidx">android.useAndroidX</h3>
<p>Android requires to explicitly 'opt-in' when using <code>androidx</code>. Do not worry: If you forgot to do this, a nice error message with instructions will be printed.<br />To opt-in, the <code>gradle.properties</code> file needs to contain the following line</p>
<pre><code class="lang-kotlin">android.useAndroidX=<span class="hljs-literal">true</span>
</code></pre>
<h3 id="heading-configure-android-compilesdk-namespace-applicationid">Configure Android: compileSdk, namespace, applicationId</h3>
<p>Creating an Android app requires some Android-specific configuration to be done. This includes choosing</p>
<ul>
<li><p><a target="_blank" href="https://stackoverflow.com/questions/26694108/what-is-the-difference-between-compilesdkversion-and-targetsdkversion"><code>compileSdk</code></a>: Which "Android version" you want to compile against (aka. which version of the APIs you want to see when coding)</p>
</li>
<li><p><code>minSdk</code>: The minimum "Android version" you want to support in your app</p>
</li>
<li><p><a target="_blank" href="https://stackoverflow.com/questions/26694108/what-is-the-difference-between-compilesdkversion-and-targetsdkversion"><code>targetSdk</code></a>: Which "Android version" do you 'target' as in 'support all features of'.</p>
</li>
<li><p><a target="_blank" href="https://developer.android.com/build/configure-app-module#set-namespace"><code>namespace</code></a>: Under which package shall the 'generated' code from Android be placed</p>
</li>
<li><p><a target="_blank" href="https://developer.android.com/build/configure-app-module#set-application-id"><code>applicationId</code></a>: Unique ID for your application (suggested to be the same as <code>namespace</code>.</p>
</li>
</ul>
<h3 id="heading-creating-the-androidmanifestxml">Creating the AndroidManifest.xml</h3>
<p>Shipping Android apps also requires declaring an <code>AndroidManifest.xml</code>. The file can be created under <code>app/src/androidMain/AndroidManifest.xml</code></p>
<p>The minimal setup, to create an app, is to</p>
<ul>
<li><p>Provide a 'label' (a.k.a. a name for your app)</p>
</li>
<li><p>Select a theme</p>
</li>
<li><p>Declare the activity</p>
</li>
</ul>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">manifest</span> <span class="hljs-attr">xmlns:android</span>=<span class="hljs-string">"http://schemas.android.com/apk/res/android"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">application</span>
        <span class="hljs-attr">android:label</span>=<span class="hljs-string">"KMP Setup Sample App"</span>
        <span class="hljs-attr">android:theme</span>=<span class="hljs-string">"@style/Theme.AppCompat.Light.NoActionBar"</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">activity</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"io.sellmair.app.MainActivity"</span> <span class="hljs-attr">android:exported</span>=<span class="hljs-string">"true"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">intent-filter</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">action</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.intent.action.MAIN"</span>/&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">category</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.intent.category.LAUNCHER"</span>/&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">intent-filter</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">activity</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">application</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">manifest</span>&gt;</span>
</code></pre>
<h3 id="heading-selecting-a-jvm-toolchain">Selecting a JVM Toolchain</h3>
<p>When compiling code for Android we want to make sure that we consistently use one JVM toolchain. If not set correctly, we might be greeted with an error message like<br />"<em>Inconsistent JVM-target compatibility detected for tasks"...</em></p>
<p>While some teams have more complicated requirements for their JVM toolchains, most projects are very well advised to just use one consistent <code>jvmToolchain</code> for their <code>module</code>. In the app <code>build.gradle.kts</code> do:</p>
<pre><code class="lang-kotlin"><span class="hljs-comment">/* app/build.gradle.kts */</span>
kotlin {
    jvmToolchain(<span class="hljs-number">17</span>)
}
</code></pre>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/xgjMEYxfhxQ">https://youtu.be/xgjMEYxfhxQ</a></div>
<p> </p>
<p>✅ After seeing the Composable on an emulator screen, we can consider the Android setup done.</p>
<hr />
<h2 id="heading-ios-building-the-framework">iOS: Building the .framework</h2>
<p>The architecture for integrating our Kotlin code into an iOS app looks something like</p>
<p>-&gt; Compile Kotlin -&gt; Build iOS .framework files -&gt; Compile Swift -&gt; Profit.</p>
<p>However, just declaring the iOS targets in the <code>kotlin {}</code> block will not yet build the <code>.framework</code> files from Kotlin. We need to configure the creation of those binaries for all iOS targets</p>
<pre><code class="lang-kotlin"><span class="hljs-keyword">import</span> org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget

<span class="hljs-comment">// ... </span>

kotlin.targets.withType&lt;KotlinNativeTarget&gt;().configureEach {
    binaries.framework {
        baseName = <span class="hljs-string">"KmpApp"</span>
        isStatic = <span class="hljs-literal">true</span>
    }
}
</code></pre>
<p>In this (example) case, we would like to build a 'static' framework with the name 'KmpApp' for any "KotlinNativeTarget".</p>
<p>When building the app (e.g by invoking <code>./gradlew assemble</code>) We should see the frameworks being located in <code>app/build/bin</code></p>
<p>However, at this point it might be wise to increase the maximum amount of heap memory Gradle is allowed to allocate. Adding the following line to the <code>gradle.properties</code> would allow up to 6 GB of heap.</p>
<pre><code class="lang-plaintext">org.gradle.jvmargs=-Xmx6g
</code></pre>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/5eEl3zqHOfs">https://youtu.be/5eEl3zqHOfs</a></div>
<p> </p>
<hr />
<h2 id="heading-creating-the-xcode-project">Creating the Xcode project</h2>
<p>When creating a new Xcode project we can select <code>iOS</code> / <code>App</code> and put the project into our <code>:app</code> module (e.g. into an <code>iosApp</code> folder)</p>
<h3 id="heading-create-compile-kotlin-run-script-phase">Create 'Compile Kotlin' run script 'phase'</h3>
<p>As mentioned previously, we want to ensure that Kotlin produces its <code>.framework</code> before we compile our swift code against it. We can add a "Run Script Phase" to the Xcode "Build Phases" and call it "Compile Kotlin".</p>
<p>In this script, we are allowed to invoke the Gradle build to produce the requested <code>.framework</code> files. Using the <code>embedAndSignAppleFrameworkForXcode</code> Gradle task will allow the Kotlin Gradle Plugin to read the environment variables from Xcode, which will lead to building exactly the <code>.framework</code> for the 'configuration' currently selected by Xcode.</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> <span class="hljs-string">"<span class="hljs-variable">$SRCROOT</span>/../../../"</span>
./gradlew :app:embedAndSignAppleFrameworkForXcode
</code></pre>
<p><em>Note: The</em> <code>./gradlew</code> <em>invocation is prefixed by a</em> <code>cd</code> <em>command to change the current working directory to the root of the Gradle project (which also will contain the</em> <code>gradlew</code> <em>file. How you change the working directory obviously depends on the location of the Xcode project.</em></p>
<h3 id="heading-disable-user-script-sandboxing">Disable "user script sandboxing"</h3>
<p>Before we can test the Xcode build, we have to disable "user script sandboxing", as the Gradle build step is not supposed to run in a 'sandbox' as it's a grown-up part of our build chain now.</p>
<h3 id="heading-adding-our-framework-to-the-framework-search-paths">Adding our .framework to the 'Framework Search Paths'</h3>
<p>To compile and link against our Kotlin code (from within Xcode), we need to add the locations of our <code>.framework</code> files to the 'Framework Search Paths'.</p>
<pre><code class="lang-bash"><span class="hljs-variable">$SRCROOT</span>/../../build/xcode-frameworks/<span class="hljs-variable">${CONFIGURATION}</span>/<span class="hljs-variable">${SDK_NAME}</span>
</code></pre>
<p>When using the <code>embedAndSignAppleFrameworkForXcode</code>, the expected location of the framework is inside the 'build' directory under 'xcode-frameworks'. The Kotlin Gradle Plugin will use the <code>CONFIGURATION</code> and <code>SDK_NAME</code> environment variables as subdirectories (those variables will be provided by Xcode)</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/x2hcLokeQR0">https://youtu.be/x2hcLokeQR0</a></div>
<p> </p>
<hr />
<h2 id="heading-ios-creatingshowing-the-viewcontroller">iOS: Creating/Showing the ViewController</h2>
<p>Since we have connected Gradle (Kotlin Compile) to our Xcode project and wired everything up, we can implement the <code>UIViewController</code> which can show our Compose UI. Similarly to <code>androidMain</code>, we can write code, specifically for iOS in the source set called <code>iosMain</code>. In there, we can use the <code>ComposeUIViewController</code> function as an entry point into our Compose app.</p>
<pre><code class="lang-kotlin"><span class="hljs-comment">/* app/src/iosMain/kotlin/.../SampleViewController.kt */</span>
<span class="hljs-keyword">package</span> io.sellmair.app

<span class="hljs-keyword">import</span> androidx.compose.ui.window.ComposeUIViewController

<span class="hljs-meta">@Suppress(<span class="hljs-meta-string">"unused"</span>)</span> <span class="hljs-comment">// Used by Swift</span>
<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">create</span><span class="hljs-params">()</span></span> = ComposeUIViewController {
    MainScreen()
}
</code></pre>
<p>It is fair to suppress the 'unused code' warning in IntelliJ, as this code might only be used by Swift.</p>
<p><em>Note: Kotlin Multiplatform Tooling in Fleet can provide Kotlin &lt;-&gt; Swift cross-language capabilities. You're welcome to</em> <a target="_blank" href="https://www.jetbrains.com/help/kotlin-multiplatform-dev/fleet.html"><em>check this out</em></a> <em>and provide feedback.</em></p>
<p>The <code>SampleViewController.create</code> can now be used inside Xcode to present the UI on screen. When using SwiftUI, it is as easy as implementing a <code>UIViewControllerRepresentable</code> and displaying the <code>ComposeView</code></p>
<pre><code class="lang-swift"><span class="hljs-keyword">import</span> SwiftUI
<span class="hljs-keyword">import</span> KmpApp <span class="hljs-comment">// &lt;- Our Kotlin Framework</span>

<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ComposeView</span>: <span class="hljs-title">UIViewControllerRepresentable</span> </span>{
    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">makeUIViewController</span><span class="hljs-params">(context: Context)</span></span> -&gt; some <span class="hljs-type">UIViewController</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-type">SampleKmpViewControllerKt</span>.create()
    }

    <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">updateUIViewController</span><span class="hljs-params">(<span class="hljs-number">_</span> uiViewController: UIViewControllerType, context: Context)</span></span> {

    }
}

<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ContentView</span>: <span class="hljs-title">View</span> </span>{
    <span class="hljs-keyword">var</span> body: some <span class="hljs-type">View</span> {
        <span class="hljs-type">ComposeView</span>()
    }
}
</code></pre>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/usj1Mg8wPNI">https://youtu.be/usj1Mg8wPNI</a></div>
<p> </p>
<p>✅ After launching the iOS app and seeing the Compose UI on the screen, we can call the iOS setup to be done</p>
<hr />
<h2 id="heading-setting-up-the-desktop-target">Setting up the Desktop target</h2>
<p>Now that the application shows the UI successfully on iOS as well as Android, the remaining target to configure is the "Desktop".</p>
<p>Again, similar to the <code>androidMain</code> and <code>iosMain</code> source sets, the <code>jvmMain</code> source set can be used to declare dependencies, specifically for the JVM, as well as put code for the JVM only.</p>
<p>As of writing this article, "Compose for Desktop" requires adding one <code>compose.desktop</code> library.</p>
<pre><code class="lang-kotlin">kotlin {
   sourceSets.jvmMain.dependencies {
        implementation(compose.desktop.currentOs)
    }
}
</code></pre>
<p>After the project synced with Gradle, we can create a <code>Main.kt</code> file under <code>src/jvmMain/kotlin</code> and use the convenient <code>application {}</code> and <code>Window {}</code> functions to show our Compose UI.</p>
<pre><code class="lang-kotlin"><span class="hljs-keyword">package</span> io.sellmair.app

<span class="hljs-keyword">import</span> androidx.compose.ui.window.Window
<span class="hljs-keyword">import</span> androidx.compose.ui.window.application

<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> = application {
    Window(title = <span class="hljs-string">"KMP Demo"</span>, onCloseRequest = ::exitApplication) {
        MainScreen()
    }
}
</code></pre>
<p>After pressing the green 'run gutter' within the IDE, our Compose Desktop App should show on the screen.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://youtu.be/Is2LOodtcD4">https://youtu.be/Is2LOodtcD4</a></div>
<p> </p>
<hr />
<h3 id="heading-attribution">Attribution</h3>
<p>Title Wizard Hat Image by <a target="_blank" href="http://Vectorportal.com">Vectorportal.com</a>, <a target="_blank" href="https://creativecommons.org/licenses/by/4.0/">CC BY</a></p>
]]></content:encoded></item><item><title><![CDATA[Composition over Inheritance: My refactoring recipe]]></title><description><![CDATA["DRy", "KisS", "CLEAn", "SoLid". We all read these almost meaningless terms being thrown around in our code reviews. Whilst just mentioning some acronym never really seemed like a convincing argument to me, the intention of the reviewer was mostly al...]]></description><link>https://blog.sellmair.io/composition-over-inheritance-my-refactoring-recipe</link><guid isPermaLink="true">https://blog.sellmair.io/composition-over-inheritance-my-refactoring-recipe</guid><category><![CDATA[Kotlin]]></category><category><![CDATA[composition]]></category><category><![CDATA[Composition over inheritance]]></category><category><![CDATA[Composition vs Inheritance]]></category><dc:creator><![CDATA[Sebastian Sellmair]]></dc:creator><pubDate>Mon, 18 Mar 2024 20:19:51 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1710839696634/79dcdfba-4e7a-4dcc-a3d1-c9626a72b870.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>"DRy", "KisS", "CLEAn", "SoLid". We all read these almost meaningless terms being thrown around in our code reviews. Whilst just mentioning <em>some</em> acronym never really seemed like a convincing argument to me, the intention of the reviewer was mostly always clear: The code, currently under review, in the eyes of the reviewer, seems to violate some <em>seemingly universally agreed upon</em> idiom, and the reviewer is concerned. Sometimes the concern is really valid: If you just copy and paste 20 lines of code with lots of magic values, modifying one line, you're likely to have a bad time maintaining this. Even though just casually placing a "DRY" in the code-review is not good practice, it would seem fair to me.</p>
<p>One of such <em>seemingly universally agreed upon</em> idioms seems to be <a target="_blank" href="https://en.wikipedia.org/wiki/Composition_over_inheritance">"Composition over Inheritance"</a> and During my entire career at JetBrains, I never heard any engineer even remotely trying to argue with this. The tradeoffs of what most devs would call 'composition' over 'inheritance' seem to be very well understood, and in most cases it is very much advisable not use abstract classes! Let me even drop the first very controversial statement early on in this post:</p>
<blockquote>
<p>If you are a young engineer, you should <strong>never use any abstract class</strong> unless you are <strong>very, very sure of</strong> what you are doing. Just <strong>do not</strong>. Pretend that your favourite language (since you're reading this, Kotlin, I assume), does not have such a language feature.</p>
</blockquote>
<p>There are some good exceptions, and I will show two cases where I, personally, like using abstract classes.</p>
<h1 id="heading-expectation-vs-reality">Expectation vs Reality</h1>
<p>The biggest downsides of abstract classes are</p>
<ul>
<li><p><strong>Bad code re-use-ergonomics:</strong> You can only inherit code from one superclass, therefore, code sharing can only be 'linear' which is very limiting, often leading to code duplication or bad abstractions.</p>
</li>
<li><p><strong>Missing scopes and clear boundaries / tight coupling:</strong> Sharing logic with abstract classes mostly leads to complex class hierarchies. Understanding small pieces of code in isolation becomes impossible because every unit of code depends (and potentially interacts) with all other units. To understand one class, you need to understand all classes in the hierarchy. To understand one function, you need to read and understand all private states this function has access to and all other private/protected functions it can call.</p>
</li>
<li><p><strong>Difficulty in Testing:</strong> Since code is tightly coupled and no isolated units of code even exist, it can become quite hard to test in abstract base classes.</p>
</li>
<li><p>...</p>
</li>
</ul>
<p>And even though many more problems can be listed against using abstract classes, the reality is (at least for me): Real life code is filled with classes like <code>AbstractFoo</code> or <code>BaseXyz</code> used for sharing code. I would like to complain even further: Most of the time I struggled hard to understand code, when most time was spent <strong>not writing code</strong>, but wondering 'where to extend or modify existing code', when frustration rose high: It was almost always some abstract class hierarchy, sharing logic.</p>
<h1 id="heading-refactoring-abstract-classes-my-recipe">Refactoring abstract classes: My recipe</h1>
<p>Over the time of my career, I have encountered frustrations with such abstractions often enough that I am now following a pretty easy 'refactoring recipe' which I would like to share with practical code examples. Note: As with any other recipe you will find online, this one here is also influenced heavily by <strong>my own personal taste</strong>. However: As with other recipes: If you don't like it too sweet: Just put in less sugar! If you prefer more spice, use more chilies 🌶️.</p>
<h2 id="heading-showcase-kotlins-notion-of-a-compilation">Showcase: Kotlin's notion of a 'Compilation'</h2>
<p>My previous job at JetBrains was to work on Kotlin Multiplatform, fixing many issues, working on the technology for several years until reaching an initial 'stable release'.</p>
<p>One of the hardest to work with parts in the build tooling code, which still gives me nightmares, was the code we had for modelling all the different kinds of 'compilations' we support in Kotlin.</p>
<p>Here is an absolutely horrendous diagram of this code before the refactoring:</p>
<p><img src="https://pbs.twimg.com/media/FgEqH8BXwAQQA8O?format=png&amp;name=4096x4096" alt="Before" /></p>
<p>You can see: There is no clear structure (granted, maybe other ways of building diagrams would potentially yield some structure). Working on such class hierarchies is almost impossible. Debugging is a nightmare, fixing bugs takes crazy amounts of time.</p>
<p><a target="_blank" href="https://twitter.com/Sellmair/status/1585595608236261377">By applying the recipe</a>, which is shared in this blogpost, the code was refactored to look something like this:</p>
<p><img src="https://pbs.twimg.com/media/FgEqYWbX0AE5z7t?format=png&amp;name=4096x4096" alt="After" /></p>
<h2 id="heading-step-1-analyse-your-class-hierarchy">Step 1: Analyse your class hierarchy</h2>
<blockquote>
<p>The example shown in the blogpost can be found here</p>
<p><a target="_blank" href="https://github.com/sellmair/blog_composition_over_inheritance/tree/master/src/main/kotlin/io/sellmair/sample/inheritance">GitHub: Example Code: Inheritance</a></p>
<p><a target="_blank" href="https://github.com/sellmair/blog_composition_over_inheritance/blob/master/src/main/kotlin/io/sellmair/sample/composition/GameStateSaver.kt">GitHub: Example Code: Composition (Result)</a></p>
</blockquote>
<p>In order to make this recipe practical, we will use an imaginary code example: In this example, let's pretend to have some entity as a <code>GameState</code> representing the current state of a Game. The abstract class hierarchy we would like to refactor is used for saving this <code>GameState</code> . The abstract class is called <code>AbstractGameStateSaver</code></p>
<pre><code class="lang-kotlin"><span class="hljs-comment">/**
 * Base class for saving the current state:
 * See the [save] method accepting a 'GameState' that can be persistet
 */</span>
<span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AbstractGameStateSaver</span> </span>{
    <span class="hljs-keyword">open</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">save</span><span class="hljs-params">(state: <span class="hljs-type">GameState</span>)</span></span> {
        storeGameState(
            encodeGameState(state)
        )
    }

    <span class="hljs-comment">/*
    To persist the "GameState" we need to encode the state to bytes and then store
    those bytes 'somewhere'
     */</span>
    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">abstract</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">encodeGameState</span><span class="hljs-params">(gameState: <span class="hljs-type">GameState</span>)</span></span>: ByteArray
    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">abstract</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">storeGameState</span><span class="hljs-params">(byteArray: <span class="hljs-type">ByteArray</span>)</span></span>
}
</code></pre>
<p>IntelliJ allows us to see a nice diagram of the <code>AbstractGameStateSaver</code> hierarchy. For the sake of simplicity in this example, the hierarchy is kept not too complicated here as well. However, the recipe will work equally well for more complex hierarchies! It's just harder to put more complex cases in the format of a blogpost!</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1710705584791/192c9921-901c-427e-a767-478d13005a04.png" alt class="image--center mx-auto" /></p>
<p>Analysing the hierarchy will provide us with some kind of intuition of what code might be actually shared! It seems, just by looking at it that the game has four implementations of saving the current state. However, there might just be two dimensions:</p>
<ol>
<li><p><strong>Format</strong>: Two formats for storing the Game state are supported: Json and Java Serializable</p>
</li>
<li><p><strong>Manual vs Auto save</strong>: It seems that the implementations somehow differentiate between saving manually (assumable by a player pressing some kind of <code>save</code> button) and automatic saving</p>
</li>
</ol>
<p>Both dimensions are combined to offer four implementations</p>
<ul>
<li><p>Manual Save, Json</p>
</li>
<li><p>Auto Save, Json</p>
</li>
<li><p>Manual Save, Java Serializable</p>
</li>
<li><p>Auto Save, Java Serializable</p>
</li>
</ul>
<h2 id="heading-step-2-take-the-abstract-base-class-and-convert-it-to-a-non-abstract-class">Step 2: Take the abstract base class and convert it to a non-abstract class</h2>
<p>This means we will convert</p>
<p><code>abstract class AbstractGameStateSaver {</code> to <code>class GameStateSaver {</code></p>
<p>We can do this by also replacing all abstract functions with 'initial draft' interfaces that get passed to the <code>GameStateSaver</code> 's constructor. This saver requires some function to encode the current <code>GameState</code> to a <code>ByteArray</code>? Sure, let’s convert the <code>encodeGameState</code> function to an interface. Some implementation to write the encoded <code>ByteArray</code> to some persistent place is required? Sure, let’s create an interface!</p>
<pre><code class="lang-kotlin"><span class="hljs-comment">// Before</span>
<span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AbstractGameStateSaver</span> </span>{
    <span class="hljs-keyword">open</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">save</span><span class="hljs-params">(state: <span class="hljs-type">GameState</span>)</span></span> {
        storeGameState(encodeGameState(state))
    }

    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">abstract</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">encodeGameState</span><span class="hljs-params">(gameState: <span class="hljs-type">GameState</span>)</span></span>: ByteArray
    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">abstract</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">storeGameState</span><span class="hljs-params">(byteArray: <span class="hljs-type">ByteArray</span>)</span></span>
}
</code></pre>
<pre><code class="lang-kotlin"><span class="hljs-comment">// After</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">GameStateSaver</span></span>(
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> encoder: Encoder, 
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> writer: Writer,
){
    <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Encoder</span> </span>{
        <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">encodeSaveState</span><span class="hljs-params">(gameState: <span class="hljs-type">GameState</span>)</span></span>: ByteArray
    }

    <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Writer</span> </span>{
        <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">writeSaveState</span><span class="hljs-params">(encoded: <span class="hljs-type">ByteArray</span>)</span></span>
    }

    <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">save</span><span class="hljs-params">(gameState: <span class="hljs-type">GameState</span>)</span></span> {
        gameStateWriter.writeSaveState(
            encoder.encodeSaveState(gameState)
        )
    }
}
</code></pre>
<h2 id="heading-step-3-implement-interfaces-for-the-previously-analysed-use-cases">Step 3: Implement interfaces for the previously analysed use-cases</h2>
<p><strong>By also using composition!</strong></p>
<p>For the sake of simplicity, all implementations only support writing to the Game state to a file (see that every class inherits from <code>AbstractFileGameStateSaver</code> 🎉) It should be simple to convert the code! Let’s look into this abstract class as well!</p>
<pre><code class="lang-kotlin"><span class="hljs-comment">/**
 * Base Class for 'File based' Save States:
 * Will encode the [GameEntity] parts of the GameState and write it to the provided [file]
 */</span>
<span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AbstractFileGameStateSaver</span></span>(<span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> file: File) : AbstractGameStateSaver() {

    <span class="hljs-comment">/**
     * Easy: We know how to store the bytes in a file!
     */</span>
    <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">storeGameState</span><span class="hljs-params">(byteArray: <span class="hljs-type">ByteArray</span>)</span></span> {
        file.writeBytes(byteArray)
    }

    <span class="hljs-comment">/*
    This abstraction relies on somebody else providing the exact implementation
    of encoding the individual game entities
    */</span>
    <span class="hljs-keyword">abstract</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">encodeGameEntity</span><span class="hljs-params">(gameEntity: <span class="hljs-type">GameEntity</span>)</span></span>: ByteArray

    <span class="hljs-comment">/*
    This abstraction relies on somebody else being able to create the "SaveFileHeader"
     */</span>
    <span class="hljs-keyword">abstract</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">createSaveStateHeader</span><span class="hljs-params">()</span></span>: SaveStateHeader

    <span class="hljs-keyword">open</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">writeGameEntityHeader</span><span class="hljs-params">(
        stream: <span class="hljs-type">ByteArrayOutputStream</span>, gameEntity: <span class="hljs-type">GameEntity</span>, encoded: <span class="hljs-type">ByteArray</span>
    )</span></span> {
        stream.write(ByteBuffer.allocate(<span class="hljs-number">4</span>).also { buffer -&gt; buffer.putInt(encoded.size) }.array())
    }

    <span class="hljs-keyword">open</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">writeGameEntity</span><span class="hljs-params">(stream: <span class="hljs-type">ByteArrayOutputStream</span>, gameEntity: <span class="hljs-type">GameEntity</span>, encoded: <span class="hljs-type">ByteArray</span>)</span></span> {
        writeGameEntityHeader(stream, gameEntity, encoded)
        stream.write(encoded)
    }

    <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">encodeGameState</span><span class="hljs-params">(gameState: <span class="hljs-type">GameState</span>)</span></span>: ByteArray {
        <span class="hljs-keyword">return</span> ByteArrayOutputStream().also { <span class="hljs-keyword">out</span> -&gt;
            <span class="hljs-keyword">val</span> header = createSaveStateHeader()
            writeGameEntity(<span class="hljs-keyword">out</span>, header, encodeGameEntity(header))
            writeGameEntity(<span class="hljs-keyword">out</span>, gameState.levelState, encodeGameEntity(gameState.levelState))
            writeGameEntity(<span class="hljs-keyword">out</span>, gameState.playerState, encodeGameEntity(gameState.playerState))
        }.toByteArray()
    }
}
</code></pre>
<p>Oouf 😰: A typical example of an abstract class being an abstract class: A lot is going on and it might not be immediately clear, at first sight, what should be done here? Luckily, the override for <code>storeGameState</code> looks actually simple: The class requests a <code>File</code> in the constructor and will just store the encoded state to this file. The remaining code seems to belong to the <code>Encoder</code> part (for later).</p>
<p>This <code>storeGameState</code> can easily be isolated and extracted into an implementation of the previously established <code>Writer</code></p>
<pre><code class="lang-kotlin"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">FileGameStateWriter</span></span>(
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> file: File
) : GameStateSaver.Writer {
    <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">writeSaveState</span><span class="hljs-params">(encoded: <span class="hljs-type">ByteArray</span>)</span></span> {
        file.writeBytes(encoded)
    }
}
</code></pre>
<p>First interface implementation done ✅</p>
<p>The encoding part, however, still relies on some abstract methods. We therefore just create a <code>Encoder</code> implementation and will take the same approach: Convert each abstract function to a new interface which will be requested in the <code>Encoder</code>'s constructor!</p>
<pre><code class="lang-kotlin"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">GameStateEncoder</span></span>(
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> saveStateHeaderFactory: SaveStateHeaderFactory,
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> gameEntityEncoder: GameEntityEncoder
) : GameStateSaver.Encoder {

    <span class="hljs-comment">/**
     * The encoded Game State will include some kind of header. 
     * This information will include whether or not the save was done automatically
     * and when the save happened.
     */</span>
    <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">SaveStateHeaderFactory</span> </span>{
        <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">createSaveStateHeader</span><span class="hljs-params">()</span></span>: SaveStateHeader
    }

    <span class="hljs-comment">/**
     * The [GameState] consists out of several parts ([GameEntity]). 
     * We will encode each part individually with this encoder
     */</span>
    <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">GameEntityEncoder</span> </span>{
        <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">encodeGameEntity</span><span class="hljs-params">(gameEntity: <span class="hljs-type">GameEntity</span>)</span></span>: ByteArray
        <span class="hljs-keyword">companion</span> <span class="hljs-keyword">object</span>
    }

    <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">encodeSaveState</span><span class="hljs-params">(gameState: <span class="hljs-type">GameState</span>)</span></span>: ByteArray {
        <span class="hljs-keyword">return</span> ByteArrayOutputStream().also { <span class="hljs-keyword">out</span> -&gt;
            <span class="hljs-keyword">val</span> header = saveStateHeaderFactory.createSaveStateHeader()
            <span class="hljs-keyword">out</span>.write(gameEntityEncoder.encodeGameEntity(header))
            <span class="hljs-keyword">out</span>.write(gameEntityEncoder.encodeGameEntity(gameState.levelState))
            <span class="hljs-keyword">out</span>.write(gameEntityEncoder.encodeGameEntity(gameState.playerState))
        }.toByteArray()
    }
}
</code></pre>
<h2 id="heading-step-4-repeat-until-all-interfaces-are-fully-implemented">Step 4: Repeat until all interfaces are fully implemented</h2>
<p>In the current example, there are two more interfaces to implement: <code>SaveStateHeaderFactory</code> and <code>GameEntityEncoder</code>: For this blogpost I will only show implementing the <code>GameEntityEncoder</code> for json and Java Serializable.</p>
<p>We therefore copy the respective code, again, from the abstract class into an implementation of the previously created <code>GameEntityEncoder</code>.</p>
<pre><code class="lang-kotlin"><span class="hljs-comment">// Before</span>
<span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AbstractJsonFileGameStateSaver</span></span>(file: File) : AbstractFileGameStateSaver(file) {
    <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">writeGameEntityHeader</span><span class="hljs-params">(
        stream: <span class="hljs-type">ByteArrayOutputStream</span>, gameEntity: <span class="hljs-type">GameEntity</span>, encoded: <span class="hljs-type">ByteArray</span>
    )</span></span> {
        stream.write(<span class="hljs-string">"json"</span>.encodeToByteArray())
        <span class="hljs-keyword">super</span>.writeGameEntityHeader(stream, gameEntity, encoded)
    }

    <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">encodeGameEntity</span><span class="hljs-params">(gameEntity: <span class="hljs-type">GameEntity</span>)</span></span>: ByteArray {
        <span class="hljs-keyword">return</span> gameEntity.encodeAsJson().encodeToByteArray()
    }
}
</code></pre>
<pre><code class="lang-kotlin"><span class="hljs-comment">// After</span>
<span class="hljs-keyword">object</span> JsonGameEntityEncoder : GameStateEncoder.GameEntityEncoder {
    <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">encodeGameEntity</span><span class="hljs-params">(gameEntity: <span class="hljs-type">GameEntity</span>)</span></span>: ByteArray {
        <span class="hljs-comment">// Assume we have the encodeAsJson implemented somewhere</span>
        <span class="hljs-keyword">val</span> jsonAsBytes = gameEntity.encodeAsJson().encodeToByteArray()

        <span class="hljs-keyword">return</span> ByteArrayOutputStream().also { <span class="hljs-keyword">out</span> -&gt;
            <span class="hljs-keyword">out</span>.writeString(<span class="hljs-string">"json"</span>)
            <span class="hljs-keyword">out</span>.writeInt(jsonAsBytes.size)
            <span class="hljs-keyword">out</span>.write(jsonAsBytes)
        }.toByteArray()
    }
}
</code></pre>
<p>Note: Just by reading the <code>AbstractJsonFileGameStateSaver</code> it is very hard to understand how the actual encoding would work. This is because of the opaque relation between <code>writeGameEntityHeader</code> and <code>encodeGameEntity</code> . Such hard to reason about code is a widespread symptom of abstract class hierarchies <em>(and was purposely written this way)</em></p>
<h2 id="heading-step-5-refine-the-new-created-interfaces-and-composable-implementations">Step 5: Refine the new created interfaces and composable implementations</h2>
<p>Refine the interfaces and their implementations, provide nice discoverable APIs, and provide factory functions!</p>
<p>For this step, companion objects will be used in Kotlin to make certain APIs discoverable and uppercase factory functions to create actual instances of our <code>GameStateSaver</code>. Again: If you do not like it sweet, feel free to put less sugar into the cake!</p>
<p>Here is an example, making the previously implemented <code>FileGameStateWriter</code> file private and providing some discoverable API by extending the interfaces <code>companion</code>:</p>
<pre><code class="lang-kotlin"><span class="hljs-function"><span class="hljs-keyword">fun</span> GameStateSaver.Writer.Companion.<span class="hljs-title">file</span><span class="hljs-params">(file: <span class="hljs-type">File</span>)</span></span>: GameStateSaver.Writer = FileGameStateWriter(file)

<span class="hljs-keyword">private</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">FileGameStateWriter</span></span>(
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> file: File
) : GameStateSaver.Writer {
    <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">writeSaveState</span><span class="hljs-params">(encoded: <span class="hljs-type">ByteArray</span>)</span></span> {
        file.writeBytes(encoded)
    }
}
</code></pre>
<h2 id="heading-step-6-declare-the-final-entities">Step 6: Declare the final entities</h2>
<p>In Step 1, we were able to see that we had four implementations of the <code>GameStateSaver</code> . To finish our refactoring, we need to declare 4 such <code>GameStateSaver</code> as well.</p>
<p>Previously, these four implementations were just this: Implementations of the respective base classes. This was actually quite simple, yet very inflexible:</p>
<pre><code class="lang-kotlin"><span class="hljs-comment">/**
 * Implementation of a save state which is
 *  - file-based
 *  - uses json as encoding
 *  - creates a header with current time and 'isAutoSave = true'
 */</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AutoSaveJsonFileGameStateSaver</span></span>(file: File) : AbstractJsonFileGameStateSaver(file) {
    <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">createSaveStateHeader</span><span class="hljs-params">()</span></span>: SaveStateHeader {
        <span class="hljs-keyword">return</span> SaveStateHeader(isAutoSave = <span class="hljs-literal">true</span>, saveTime = Clock.System.now())
    }
}

<span class="hljs-comment">/**
 * Implementation of a save state which is
 *  - file-based
 *  - uses java.io.Serializable as encoding
 *  - creates a header with current time and 'isAutoSave = true'
 */</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AutoSaveJavaSerializableFileGameStateSaver</span></span>(file: File) : AbstractJavaSerializableFileGameStateSaver(file) {
    <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">createSaveStateHeader</span><span class="hljs-params">()</span></span>: SaveStateHeader {
        <span class="hljs-keyword">return</span> SaveStateHeader(isAutoSave = <span class="hljs-literal">true</span>, saveTime = Clock.System.now())
    }
}

<span class="hljs-comment">/**
 * Implementation of a save state which is
 *  - file-based
 *  - uses json as encoding
 *  - creates a header with current time and 'isAutoSave = false'
 */</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ManualSaveJsonFileGameStateSaver</span></span>(file: File) : AbstractJsonFileGameStateSaver(file) {
    <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">createSaveStateHeader</span><span class="hljs-params">()</span></span>: SaveStateHeader {
        <span class="hljs-keyword">return</span> SaveStateHeader(isAutoSave = <span class="hljs-literal">true</span>, saveTime = Clock.System.now())
    }
}


<span class="hljs-comment">/**
 * Implementation of a save state which is
 *  - file-based
 *  - uses java.io.Serializable as encoding
 *  - creates a header with current time and 'isAutoSave = false'
 */</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ManualSaveJavaSerializableFileGameStateSaver</span></span>(file: File) : AbstractJavaSerializableFileGameStateSaver(file) {
    <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">createSaveStateHeader</span><span class="hljs-params">()</span></span>: SaveStateHeader {
        <span class="hljs-keyword">return</span> SaveStateHeader(isAutoSave = <span class="hljs-literal">false</span>, saveTime = Clock.System.now())
    }
}
</code></pre>
<p>Now we can just compose our instance using our newly introduced building blocks. To do so, the following code will use uppercase factory functions:</p>
<pre><code class="lang-kotlin"><span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">AutoSaveJsonGameStateSaver</span><span class="hljs-params">(file: <span class="hljs-type">File</span>)</span></span>: GameStateSaver {
    <span class="hljs-keyword">return</span> GameStateSaver(
        gameStateWriter = GameStateSaver.Writer.file(file),
        encoder = GameStateEncoder(
            gameEntityEncoder = GameEntityEncoder.javaSerialization,
            saveStateHeaderFactory = SaveStateHeader.factory(
                isAutoSave = <span class="hljs-literal">true</span>
            ),
        ),
    )
}

<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">AutoSaveJavaSerializableGameStateSaver</span><span class="hljs-params">(file: <span class="hljs-type">File</span>)</span></span>: GameStateSaver {
    <span class="hljs-keyword">return</span> GameStateSaver(
        gameStateWriter = GameStateSaver.Writer.file(file),
        encoder = GameStateEncoder(
            gameEntityEncoder = GameEntityEncoder.json,
            saveStateHeaderFactory = SaveStateHeader.factory(
                isAutoSave = <span class="hljs-literal">true</span>
            ),
        ),
    )
}

<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">ManualSaveJsonSaveGameStateSaver</span><span class="hljs-params">(file: <span class="hljs-type">File</span>)</span></span>: GameStateSaver {
    <span class="hljs-keyword">return</span> GameStateSaver(
        gameStateWriter = GameStateSaver.Writer.file(file),
        encoder = GameStateEncoder(
            gameEntityEncoder = GameEntityEncoder.json,
            saveStateHeaderFactory = SaveStateHeader.factory(
                isAutoSave = <span class="hljs-literal">false</span>
            ),
        )
    )
}

<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">ManualSaveJavaSerializableGameStateSaver</span><span class="hljs-params">(file: <span class="hljs-type">File</span>)</span></span>: GameStateSaver {
    <span class="hljs-keyword">return</span> GameStateSaver(
        gameStateWriter = GameStateSaver.Writer.file(file),
        encoder = GameStateEncoder(
            gameEntityEncoder = GameEntityEncoder.json,
            saveStateHeaderFactory = SaveStateHeader.factory(
                isAutoSave = <span class="hljs-literal">false</span>
            ),
        )
    )
}
</code></pre>
<p>At this point, the positive as well as the negative points about this approach should be rather clear</p>
<ul>
<li><p><strong>The composition is explicit and more verbose</strong></p>
</li>
<li><p><strong>The composition can be easily extended</strong></p>
<p>  <em>Just imagine adding support for writing the</em><code>GameState</code><em>to an http server. A nightmare to extend the abstract classes, just one more implementation of the</em><code>Writer</code><em>interface using the composition</em></p>
</li>
<li><p><strong>The composition can re-use components very easily</strong></p>
<p>  <em>New</em><code>GameState</code><em>writer instances can be declared freely, re-using any arbitrary building block which currently exists.</em></p>
</li>
<li><p><strong>Any source file in the composition can be understood on its own!</strong></p>
</li>
</ul>
<p><em>If you're a Kotlin application developer, the above code might remind you of writing user interfaces with</em><a target="_blank" href="https://github.com/JetBrains/compose-multiplatform"><em>Compose</em></a><em>. While 'Compose the UI framework' leverages many more advanced techniques, the concept of 'composition' is contributing heavily to the ergonomics of Compose and likely fundamental to its success.</em></p>
<h1 id="heading-reasons-to-use-abstract-classes">Reasons to use abstract classes</h1>
<p>There might be actually many good reasons for using abstract classes, I guess? But as promised at the beginning of this blogpost, I now list the two cases where I, personally, use abstract classes:</p>
<h2 id="heading-junit-4-aka-for-historical-reasons">junit 4 (a.k.a for historical reasons)</h2>
<p>Do I need to say much more? My daily job is to contribute to either <a target="_blank" href="https://github.com/JetBrains/kotlin">kotlin.git</a> or <a target="_blank" href="https://github.com/JetBrains/intellij-community">intellij.git</a>. Both projects already accumulated a lot of history, and many fundamental tests and test infrastructures rely on junit 4 therefore, naturally, share plenty of testing code using inheritance. This is legacy and might change, maybe, but I actually think that writing tests using inheritance <em>is kind of ok.</em></p>
<h2 id="heading-interface-vs-abstract-class">interface vs abstract class</h2>
<p>Sometimes, rarely, but sometimes, I actually want to enforce that an implementor of a given contract (represented either as <code>interface</code> or <code>abstract class</code>) cannot implement another such contract. In this case, using abstract classes seems like a perfect solution to me:</p>
<pre><code class="lang-kotlin"><span class="hljs-keyword">sealed</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Result</span></span>
<span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Success</span>: <span class="hljs-type">Result</span></span>
<span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Failure</span>: <span class="hljs-type">Result</span></span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">WeirdResult</span>: <span class="hljs-type">Success</span>, <span class="hljs-type">Failure // 😨</span></span>
</code></pre>
<pre><code class="lang-kotlin"><span class="hljs-keyword">sealed</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Result</span></span>
<span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Success</span>: <span class="hljs-type">Result</span></span>
<span class="hljs-keyword">abstract</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Failure</span>: <span class="hljs-type">Result</span></span>

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SuccessImpl</span>: <span class="hljs-type">Success</span></span>() <span class="hljs-comment">// Cannot also implement Failure()!</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">FailureImpl</span>: <span class="hljs-type">Failure</span></span>() <span class="hljs-comment">// Cannot also implemente Success()!</span>
</code></pre>
<h1 id="heading-wrap-up-step-by-step-the-recipe">Wrap up: Step by Step, The recipe</h1>
<ul>
<li><p><strong>Step 1: Analyse your class hierarchy</strong></p>
<p>  Use some code analysis tool of your choice <em>(I recommend IntelliJ)</em> and get some insight into the structure of the abstract class hierarchy:</p>
<ul>
<li><p>Look for 'all implementations of this given abstract class'</p>
</li>
<li><p>Try to find some <em>structure</em> in the hierarchy.</p>
</li>
</ul>
</li>
<li><p><strong>Step 2: Take the abstract base class and convert it to a non-abstract class</strong></p>
<ul>
<li><p>Drop the <code>abstract</code> keyword in the class declaration</p>
</li>
<li><p>Replace all abstract functions with interfaces</p>
</li>
<li><p>Inject implementation of those interfaces in the constructor of the class</p>
</li>
</ul>
</li>
<li><p><strong>Step 3: Implement interfaces for the previously analysed use-cases</strong></p>
<ul>
<li><p>Go to direct subclasses of the recently converted abstract class and try to convert the <code>override fun</code> functions into implementations of the new interfaces</p>
</li>
<li><p>If the subclass was also declared abstract, then convert those abstract functions into interfaces again</p>
</li>
</ul>
</li>
<li><p><strong>Step 4: Repeat until all interfaces are fully implemented</strong></p>
<p>  Recursively apply Step 3, work your way up to the actual final implementations, copying code from the <code>override fun</code> functions into implementations of composable interfaces.</p>
</li>
<li><p><strong>Step 5: Refine the new created interfaces and composable implementations</strong></p>
<p>  Stir, Stir Stir 🧑‍🍳, the recently created interfaces and implementations need some refinements. Find good names, find good abstractions, put some love into the recently refactored code!</p>
</li>
<li><p><strong>Step 6: Declare the final entities</strong></p>
<p>  Provide factory functions to allow easy constructions of the 'production' instances. The code should feel declarative and 'composable'.</p>
</li>
</ul>
]]></content:encoded></item></channel></rss>