<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: CharlieTap</title>
    <description>The latest articles on DEV Community by CharlieTap (@charlietap).</description>
    <link>https://dev.to/charlietap</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F804180%2F4d718bc1-81a7-427b-b522-c49cb635a754.png</url>
      <title>DEV Community: CharlieTap</title>
      <link>https://dev.to/charlietap</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/charlietap"/>
    <language>en</language>
    <item>
      <title>Interfacing with Wasm from Kotlin</title>
      <dc:creator>CharlieTap</dc:creator>
      <pubDate>Sat, 08 Nov 2025 16:52:41 +0000</pubDate>
      <link>https://dev.to/charlietap/interfacing-with-wasm-from-kotlin-2k2c</link>
      <guid>https://dev.to/charlietap/interfacing-with-wasm-from-kotlin-2k2c</guid>
      <description>&lt;p&gt;You may have heard you can compile Kotlin → Wasm but did you know you can call Wasm from Kotlin?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Why would you want to do that?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It may sound odd but theres genuine reasons why you would want to do this. &lt;/p&gt;

&lt;p&gt;WebAssembly is a compilation target of almost every single language you can think of, Kotlin, Swift, Go, Rust, C, C* etc etc etc the list goes on. If you can interface with Webassembly, you have the potential to access libraries from any of these languages! &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff9vm1x8gylqw2mgnmysm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff9vm1x8gylqw2mgnmysm.png" alt=" " width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fancy working on an AI project but you're stuck because machine learning engineers cannot write anything else but Python? No problem compile Pytorch to as Wasm module and you're off to the races... Okay thats not actually possible yet but it will be soon!&lt;/p&gt;

&lt;p&gt;Programming language ecosystems for the longest time have been siloed, preventing not just the sharing of code, but also knowledge and ideas. Wasm presents the opportunity to change that. In the not too distant future we will have the equivalent of npm but for wasm modules, a dependency registry which is contributed to by every programming language ecosystem in the world. Fast, memory safe libraries written in Rust, high level ergonomic libraries written in you're favourite scripting language and everything in between.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;But isn't Web-Assembly for the ... Web?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Not really no, whilst it got its start as a solution in the browser WebAssembly is simply the specification of a virtual machine, you can implement that virtual machine wherever you like. Today we're going to take a look at a WebAssembly virtual machine built on top of Kotlin Multiplatform, which runs everywhere Kotlin Multiplatform does.&lt;/p&gt;

&lt;p&gt;The library in question is one I've been working on slowly for the past 2 years, its called &lt;a href="https://github.com/CharlieTap/chasm" rel="noopener noreferrer"&gt;chasm&lt;/a&gt; and it makes working with Wasm incredibly simple. &lt;/p&gt;

&lt;p&gt;Lets dive in &lt;/p&gt;




&lt;h3&gt;
  
  
  First things first we need some Wasm
&lt;/h3&gt;

&lt;p&gt;As mentioned Wasm is the compilation target of many languages, but rather than setup a language, its toolchain and compile a program were going to use a Wasm module I've prepared for us.&lt;/p&gt;

&lt;p&gt;You can download it &lt;a href="https://raw.githubusercontent.com/CharlieTap/interfacing-with-wasm-from-kotlin/main/add.wasm" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;add.wasm&lt;/strong&gt; is an incredibly simple wasm module which exports one function 'add', that takes two integers and you guessed it, adds them.&lt;/p&gt;

&lt;p&gt;Go ahead and put &lt;code&gt;add.wasm&lt;/code&gt; somewhere your build can see it (e.g. &lt;code&gt;src/main/resources/add.wasm&lt;/code&gt; or &lt;code&gt;src/commonMain/resources/add.wasm&lt;/code&gt;).&lt;/p&gt;




&lt;h3&gt;
  
  
  Generating our interface
&lt;/h3&gt;

&lt;p&gt;Chasm is at its heart a Wasm virtual machine, virtual machines tend to have low level apis and really aren't beginner friendly. For this reason Chasm also ships a Gradle plugin, the plugin simplifies things by generating a typesafe Kotlin interface which hides the lower level virtual machine api.&lt;/p&gt;

&lt;p&gt;Add the following config to your build.gradle file and run a Gradle sync&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// build.gradle.kts&lt;/span&gt;
&lt;span class="nf"&gt;plugins&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// your usual plugin(s), e.g. jvm, android, kmp&lt;/span&gt;
    &lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"io.github.charlietap.chasm.gradle"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="s"&gt;"2.0.0-1.2.0"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;chasm&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;modules&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Adder"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;binary&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;projectDirectory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"src/main/resources/add.wasm"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;packageName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"com.example.wasm"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On sync (or build), the plugin decodes your Wasm module and inspects its public exports. Using the type information available its able to synthesise a Kotlin interface and implementation for you to use.&lt;/p&gt;

&lt;p&gt;If you take a look in your modules build directory you should find the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;com.example.wasm&lt;/span&gt;

&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;Adder&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;internal&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AdderImpl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;binary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ByteArray&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="c1"&gt;//omitted for brevity&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Adder&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that the concrete implementation accepts the Wasm binary at runtime. Although that might seem counterintuitive since the plugin is already configured with a binary but it’s intentional. This design lets you swap implementations at runtime: you can ship Adder v1 with your project and later download v2 to update the behaviour in-process... Yes this is legal on Android and IOS because Chasm is an interpreter.&lt;/p&gt;

&lt;p&gt;Thats it! You have everything you need and you can now instantiate your AdderImpl and call it like any other Kotlin class.&lt;/p&gt;




&lt;h3&gt;
  
  
  Whats happening under the hood
&lt;/h3&gt;

&lt;p&gt;Before we dive further into the intricacies and gotchas, I want to explain a little bit about how this plugin runs your code on each platform.&lt;/p&gt;

&lt;p&gt;It helps to distinguish between Chasm’s virtual machine API and the code generation this plugin produces. Chasm is a Kotlin Multiplatform (KMP) project that runs on the JVM and most native targets, but it doesn’t target the web directly. Browsers already provide a WebAssembly VM that unlike Chasm can JIT and AOT-compile Wasm, delivering near-native performance.&lt;/p&gt;

&lt;p&gt;To take advantage of that, the Gradle plugin generates code against an abstract VM interface. On the JVM and native targets it uses Chasm (an interpreter). On JavaScript targets it delegates to the browser’s builtin Wasm runtime (V8, SpiderMonkey, or JavaScriptCore), inheriting their JIT/AOT compilation and performance.&lt;/p&gt;




&lt;h2&gt;
  
  
  Gotchas, gotchas gotchas
&lt;/h2&gt;

&lt;p&gt;It’d be great if everything were as simple as the Adder example, but WebAssembly is still evolving. We’re not yet at the point where you can grab any off the shelf library and interface with it from Kotlin without friction. In the next section, we’ll walk through the pitfalls you’re likely to hit and how the Wasm roadmap aims to address them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lets talk Strings
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmr5go8qb8pm4w9mifzz5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmr5go8qb8pm4w9mifzz5.png" alt=" " width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Sooner or later you’ll want to call a function that takes or returns a &lt;code&gt;String&lt;/code&gt;. Here’s the catch: core WebAssembly (Wasm) has no built in string type. Different languages represent strings differently (think &lt;code&gt;&amp;amp;str&lt;/code&gt; vs &lt;code&gt;String&lt;/code&gt; in Rust, plus OS and FFI-oriented variants), so most current approaches settle on “just bytes”: UTF-8 encoded data living in linear memory.&lt;/p&gt;

&lt;p&gt;Compilers work around this in a few common ways, typically by writing the string into module memory and returning a pointer (and sometimes a length). It’s easier to see by example.&lt;/p&gt;

&lt;p&gt;Say you had a function like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;username&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"foo"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some compilers will emit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(func (export "username") (result i32 i32))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s two &lt;code&gt;i32&lt;/code&gt;s: the first is a pointer to the string in memory, the second is its length.&lt;/p&gt;

&lt;p&gt;Others will emit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(func (export "username") (result i32))
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So how do you know where the string ends? It depends on the calling convention. Some toolchains use null-terminated strings; others encode the length at (or just before) the pointer. The important bit is: you must know your compiler’s string encoding strategy.&lt;/p&gt;

&lt;p&gt;The good news: Chasm has support for all of these different encoding strategies. Even if the Wasm interface uses integers under the hood, Chasm can still generate a Kotlin interface that deals in &lt;code&gt;String&lt;/code&gt;. You just need to steer the config with the right encoding hint.&lt;/p&gt;

&lt;p&gt;Assume the compiler null-terminates strings and returns a single integer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nf"&gt;chasm&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;modules&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Foo"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;binary&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;projectDirectory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"src/commonMain/resources/foo.wasm"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;packageName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"com.foo.bar"&lt;/span&gt;
            &lt;span class="nf"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"username"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;stringReturnType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;StringEncodingStrategy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;NULL_TERMINATED&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;So for &lt;code&gt;String&lt;/code&gt; parameters we would just add &lt;code&gt;stringParam()&lt;/code&gt;?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Yes... but there’s one more piece. When you pass a &lt;code&gt;String&lt;/code&gt;, Chasm has to write it into the VM’s memory. To do that safely, it needs to coordinate with your Wasm module’s allocator. If your toolchain exports allocator functions, Chasm can use them.&lt;/p&gt;

&lt;p&gt;Assuming null-terminated strings and that your module exports the classic &lt;code&gt;malloc&lt;/code&gt;/&lt;code&gt;free&lt;/code&gt;, you can wire up an allocator like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;truncate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nf"&gt;chasm&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;modules&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Truncator"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;binary&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;projectDirectory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"src/commonMain/resources/truncate.wasm"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;packageName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"com.foo.bar"&lt;/span&gt;
            &lt;span class="n"&gt;allocator&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ExportedAllocator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"malloc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"free"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"truncate"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nf"&gt;stringParam&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;StringEncodingStrategy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;NULL_TERMINATED&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nf"&gt;stringReturnType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;StringEncodingStrategy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;NULL_TERMINATED&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you want to see working examples of multiple string encodings, the example project has a &lt;a href="https://github.com/CharlieTap/chasm/blob/main/example/consumer-jvm-test/build.gradle.kts" rel="noopener noreferrer"&gt;particular module&lt;/a&gt; which demonstrates this.&lt;/p&gt;

&lt;p&gt;This isn’t ideal, you can see I'm jumping through hoops to make all this work. It forces callers to understand compiler conventions, and it doesn’t work if the compiler doesn’t expose the needed behaviours (Kotlin itself is not capable of any of the above). Things get even trickier for functions that return classes/structs or other aggregate types; there’s no portable way to express those over the core ABI.&lt;/p&gt;

&lt;p&gt;However... as mentioned before Wasm is an evolving spec and for the problems highlighted there is light at the end of the tunnel. The upcoming &lt;strong&gt;&lt;a href="https://component-model.bytecodealliance.org/" rel="noopener noreferrer"&gt;Wasm Component Model&lt;/a&gt;&lt;/strong&gt; proposal looks to address all of these issues.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Component Model
&lt;/h2&gt;

&lt;p&gt;The Component Model is a clever proposal. It tackles the awkward reality that modules compiled from different languages have differing binary interfaces and yet we still want them to interoperate. You can’t just bolt a string type onto core Wasm and call it done; that wouldn’t help the thousands of existing modules already in the wild. So the proposal comes at the problem from two angles:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Add a language-neutral type system&lt;/strong&gt;&lt;br&gt;
This is WIT (WebAssembly Interface Types) an IDL and type system for describing functions and rich data (string, list, records, variants, result, etc.). With WIT, everyone agrees on what crosses the boundary, independent of any one compiler’s ABI.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Wrap existing modules in a "component"&lt;/strong&gt;&lt;br&gt;
A component is a new concept to WebAssembly, its essentially a wrapper around a core Wasm module that exposes WIT defined imports/exports. The component acts as a translator speaking WIT to the outside world, and internally lifting and lowering types from modules differing binary interfaces&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The Component Model is currently a Stage 1 WebAssembly proposal. It’s been actively developed for the last few years and is inching toward standardisation. When a formal spec lands, I'll be promptly updating Chasm...&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;So once the Component Model ships, will everything “just work”?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Kind of... but not by itself. The Component Model standardises type boundaries. At the start of this post we spoke about ubiquitous programming components, true universal binaries and for that to be a reality we need to talk about syscalls and operating systems.&lt;/p&gt;




&lt;h2&gt;
  
  
  Syscalls, WASI and the elusive universal binary
&lt;/h2&gt;

&lt;p&gt;WebAssembly is a specification of an abstract virtual machine, but it has no notion of an operating system. Instead the specification details a system of imports and exports. You can for example import functions from the host system, it's through this mechanism Wasm can import all the operating system functionality it needs.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Right... but how do modules know what to imports to call?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;They target WASI (the WebAssembly System Interface), which is an entirely separate specification which standardises a set of OS-like capabilities as well known imports. In simple terms: the module asks for functions defined by WASI, and the host provides implementations for the current platform.&lt;/p&gt;

&lt;p&gt;WASI is nascent in comparison to Wasm, there have been three iterations of it so far, preview 1,2 and 3 and you can infer from the naming this is not the endgame.&lt;/p&gt;

&lt;p&gt;Preview 1 is the broadly supported baseline that works with today’s stable Wasm runtimes (Wasm 3.0). It covers file I/O, clocks, random, etc., but it lacks networking.&lt;/p&gt;

&lt;p&gt;Preview 2 and 3 are largely complete but they unfortunately depend on types from The Component Model...&lt;/p&gt;

&lt;p&gt;Chasm supports WASI Preview 1 today via the excellent &lt;a href="https://github.com/illarionov/wasi-emscripten-host" rel="noopener noreferrer"&gt;wasi-emscripten-host&lt;/a&gt; but you have to wire the imports yourself. I will automate this once we get something complete that works with stable Wasm but for now I'm busy working on some other "things" I think will have more impact.&lt;/p&gt;




&lt;h2&gt;
  
  
  Closing Thoughts
&lt;/h2&gt;

&lt;p&gt;Despite all the rough edges and limitations, I think we’re trending in the right direction. Chasm makes interacting with the Wasm of today feel seamless. It’s Kotlin Multiplatform from the ground up, you can write one module and run it across web, iOS, and Android whilst programming against the same consistent interface.&lt;/p&gt;

&lt;p&gt;For the more curious you can check out the Kotlin Multiplatform example project &lt;a href="https://github.com/CharlieTap/chasm/tree/main/example" rel="noopener noreferrer"&gt;here&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>webassembly</category>
      <category>android</category>
    </item>
    <item>
      <title>Free android lint perf</title>
      <dc:creator>CharlieTap</dc:creator>
      <pubDate>Wed, 27 Aug 2025 18:12:42 +0000</pubDate>
      <link>https://dev.to/charlietap/free-android-lint-perf-103p</link>
      <guid>https://dev.to/charlietap/free-android-lint-perf-103p</guid>
      <description>&lt;p&gt;Want your Android lint check to run a little faster? Have an Android project which doesn't support Android Wear (WearOS) devices?&lt;/p&gt;

&lt;p&gt;Add the following to your lint.xml&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;issue id="InvalidWearFeatureAttribute" severity="ignore" /&amp;gt;
    &amp;lt;issue id="SquareAndRoundTilePreviews" severity="ignore" /&amp;gt;
    &amp;lt;issue id="TilePreviewImageFormat" severity="ignore" /&amp;gt;
    &amp;lt;issue id="TileProviderPermissions" severity="ignore" /&amp;gt;
    &amp;lt;issue id="WatchFaceEditor" severity="ignore" /&amp;gt;
    &amp;lt;issue id="WatchFaceForAndroidX" severity="ignore" /&amp;gt;
    &amp;lt;issue id="WatchFaceFormatDeclaresHasNoCode" severity="ignore" /&amp;gt;
    &amp;lt;issue id="WatchFaceFormatMissingVersion" severity="ignore" /&amp;gt;
    &amp;lt;issue id="WatchFaceFormatInvalidVersion" severity="ignore" /&amp;gt;
    &amp;lt;issue id="WearableActionDuplicate" severity="ignore" /&amp;gt;
    &amp;lt;issue id="WearableConfigurationAction" severity="ignore" /&amp;gt;
    &amp;lt;issue id="WearStandaloneAppFlag" severity="ignore" /&amp;gt;
    &amp;lt;issue id="WearSplashScreen" severity="ignore" /&amp;gt;
    &amp;lt;issue id="WearRecents" severity="ignore" /&amp;gt;
    &amp;lt;issue id="WearPasswordInput" severity="ignore" /&amp;gt;
    &amp;lt;issue id="WearMaterialTheme" severity="ignore" /&amp;gt;
    &amp;lt;issue id="WearBackNavigation" severity="ignore" /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;What the helly?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Whilst profiling the lint task on an Android project I noticed that around 5% of overall time taken could be attributed to Android Wear checks. This was weird as the project didn't have support for Android Wear!&lt;/p&gt;

&lt;p&gt;The longest running Wear related lint detector was the following&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WearDetector        2846 ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looking up the &lt;a href="https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-main:lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/WearDetector.kt" rel="noopener noreferrer"&gt;source&lt;/a&gt; for this you can see it's a base class which other detectors can inherit from.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-main:lint/libs/lint-checks/src/main/java/com/android/tools/lint/checks/WearMaterialThemeDetector.kt" rel="noopener noreferrer"&gt;Some detectors&lt;/a&gt; correctly use the base class to only register interest in ast symbols if wear is enabled. However most don't, and that means these detectors run even on non wear projects.&lt;/p&gt;

&lt;p&gt;WearDetector itself parses the merged manifest associated with your projects in order to determine if they support wear devices. However this call is not cached, meaning every implementor of WearDetector which calls the function &lt;code&gt;isWearProject&lt;/code&gt; causes the linter to reparse the manifest.&lt;/p&gt;

&lt;p&gt;In order to avoid a detector running you must ignore all the checks they register, ignoring one is not sufficient. The above xml does just that for android wear related checks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;WearDetector        0 ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're wondering about the profiler I used, it's &lt;a href="https://github.com/google/android-lint-performance-probe" rel="noopener noreferrer"&gt;here&lt;/a&gt;. It's not very well maintained tbh and I had to rewrite parts of the project to make it work. It also depends on a proprietary JVM profiler which costs money... but theres a trial if you're keen&lt;/p&gt;

</description>
      <category>android</category>
      <category>cicd</category>
      <category>performance</category>
    </item>
    <item>
      <title>Emulating classes with functions in Kotlin for maximum performance 🚀</title>
      <dc:creator>CharlieTap</dc:creator>
      <pubDate>Mon, 07 Oct 2024 04:59:46 +0000</pubDate>
      <link>https://dev.to/charlietap/emulating-classes-with-functions-in-kotlin-for-maximum-performance-4fo1</link>
      <guid>https://dev.to/charlietap/emulating-classes-with-functions-in-kotlin-for-maximum-performance-4fo1</guid>
      <description>&lt;p&gt;If you're building quality software in Kotlin, you'll undoubtedly be using classes and dependency injection to ensure your code is testable.&lt;/p&gt;

&lt;p&gt;And you'd be correct to do so! In fact, 99.9999% of the time I would say that's always the right approach to take. But sometimes it isn't... You see, there are certain drawbacks to this style of programming, more specifically drawbacks surrounding the maximum performance of your programs.&lt;/p&gt;

&lt;p&gt;Let’s take a look&lt;/p&gt;

&lt;p&gt;Generally, if you’re following the Single Responsibility Principle, you’ll have one class for every ‘behavior.’ Your programs become deeply nested graphs of classes composed of one another. But classes are not zero-cost abstractions; they introduce overhead, and as you’ll see later, this overhead translates to a significant number of instructions beyond the functions the classes expose. Worse still, they require allocation, which can mean a system call, increased GC pressure, and potentially an O(N) search across the heap to find an appropriate spot for their associated data.&lt;/p&gt;

&lt;p&gt;It gets worse if your class extends an interface, this guarantees dynamic dispatch. You can think of this as another layer of indirection, it involves chasing a pointer through a v-table just so you can address the code you want to run.&lt;/p&gt;

&lt;p&gt;You pay these aforementioned costs for every class you create, so this effect is compounding! &lt;/p&gt;

&lt;p&gt;There's a lot more to touch on here surrounding CPU architecture changing a lot since the &lt;em&gt;naissance&lt;/em&gt; of object oriented programming and Java but I want to keep us on track. &lt;/p&gt;

&lt;p&gt;Now don't panic! I said you're almost certainly doing the right thing and I mean it. In most everyday applications these overheads are in the nano/microsecond range. This delay is likely imperceivable to your customers and certainly not something you should be sacrificing the ergonomics of you codebase for!&lt;/p&gt;

&lt;p&gt;But the project I've been working on for the past year is bit different... &lt;/p&gt;

&lt;p&gt;Introducing &lt;a href="https://github.com/CharlieTap/chasm" rel="noopener noreferrer"&gt;Chasm&lt;/a&gt;. A WebAssembly interpreter built on top of Kotlin Multiplatform&lt;/p&gt;

&lt;p&gt;Now chasm is not any old software project, its a web assembly interpreter, and being an interpreter its goal is to translate the instructions it receives into the minimal amount of instructions for the underlying cpu.&lt;/p&gt;

&lt;p&gt;For example in web assembly we have an instruction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;i32.add
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and if the interpreter was doing the absolute minimum we would see something like this on ARM.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;add w0, w1, w2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Theres actually a little bit more to this as web assembly is a stack machine and we would have to pop the operands from the stack but its easier to explain like this.&lt;/p&gt;

&lt;p&gt;You can imagine at some point all interpreters boil down to a loop executing an instruction.&lt;/p&gt;

&lt;p&gt;We’re going to walk through a contrived example where we have classes and interfaces that aim to simply add two numbers. Though this is a bit silly, we should be easily able to identify the add instruction and thus recognise pretty much everything else as overhead.&lt;/p&gt;

&lt;p&gt;Let's start with a class that's responsible for doing the adding:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;Adder&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AdderImpl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Adder&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now, let’s do some composition using dependency injection, as we normally would when writing modern Kotlin, to construct another class.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;AddInstructionExecutor&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AddInstructionExecutorImpl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;adder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Adder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;AddInstructionExecutor&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;adder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally lets add a main function where we construct and invoke the executor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AddInstructionExecutorImpl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;AdderImpl&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're going to use Romain Guys fantastic &lt;a href="https://github.com/romainguy/kotlin-explorer" rel="noopener noreferrer"&gt;Kotlin-Explorer&lt;/a&gt; project to inspect the generated assembly. Please give it a star as it's an awesome bit of kit!&lt;/p&gt;

&lt;p&gt;So first the AdderImpl &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff0wz21rnfd2azz1vne4m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff0wz21rnfd2azz1vne4m.png" alt=" " width="423" height="163"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Okay not too bad, just the add instruction alongside returns for the initializer and add function. And now for the AdderInstructionExecutorImpl&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbre4w989w9wh6ftlie18.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbre4w989w9wh6ftlie18.png" alt=" " width="423" height="521"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Oof 7 instructions and 1 branch in the initializer, and 20 instructions for the add function... let's see how that affects main&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe3dwxmbxm8vq3ul3uw10.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe3dwxmbxm8vq3ul3uw10.png" alt=" " width="423" height="681"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;47 instructions and 8 branches! Thats the cost we're paying for our abstraction and we're only going through two layers of indirection. You can imagine how this scales within deep dependency graphs.&lt;/p&gt;

&lt;p&gt;So the question is can we avoid this? maintain some abstraction and keep our code testable? &lt;/p&gt;

&lt;p&gt;The answer is yes! kinda... there's some tradeoffs ... lets take a look&lt;/p&gt;

&lt;p&gt;Adder implementation and interface&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;typealias&lt;/span&gt; &lt;span class="nc"&gt;FastAdder&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;

&lt;span class="k"&gt;inline&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;FastAdder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;+&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first thing you'll notice is that we are no longer using an interface, instead we opt for a typealias. Alongside this we have swapped the class out with an inline function. If you have a keen eye theres already a tradeoff here, without a class or interface we've lost a concrete type, any function which takes two ints and returns on could satisfy our alias. &lt;/p&gt;

&lt;p&gt;So does it make any real difference?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6pchc66lpc85e5klx74p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6pchc66lpc85e5klx74p.png" alt=" " width="423" height="74"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We've saved an instruction! Thats definitely worth compromising our type system 🤣 Hold on we're getting there...&lt;/p&gt;

&lt;p&gt;AdderInstructionExecutor implementation and interface&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;typealias&lt;/span&gt; &lt;span class="nc"&gt;FastAddInstructionExecutor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;

&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;FastAddInstructionExecutor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAddInstructionExecutor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nc"&gt;FastAdder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;internal&lt;/span&gt; &lt;span class="k"&gt;inline&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;FastAddInstructionExecutor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;adder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;FastAdder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;adder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So again a typealias rather than the interface but this time we have two functions oddly? the second of which is inlined? What's happening here. Well.. This is very manual dependency injection, the first function is the one you would call, without dependencies and also the function you would use in places where the typealias is expected. The second function is the actual implementation, all the dependencies are extracted into parameters so its entirely testable. It's very important for the second function to be inline, as you'll see from the following assembly&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcf73ew4frh0qmssec1gx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcf73ew4frh0qmssec1gx.png" alt=" " width="652" height="250"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As you can see, the function call is actually identical to FastAdderImpl, the compiler has simply inlined it and it does nothing extra. The second function however has a lot more going on, this is because the compiler is unaware of what function will be given at runtime and thus have to deal with invoking the function reference. Fortunately this second function is unused by our program.&lt;/p&gt;

&lt;p&gt;Finally lets turn our attention to the main function, you can probably see where this is going...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;fastMain&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAddInstructionExecutor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffznqplm0t6qxrlo8utq1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffznqplm0t6qxrlo8utq1.png" alt=" " width="423" height="74"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Just 2 instructions! And ultimately this makes sense, everything else is just abstraction that we've added to make our program easier to maintain.&lt;/p&gt;

&lt;p&gt;Cool! So we're able to emulate classes, perform dependency injection, and create abstractions using functions whilst achieving maximum performance. But it could get better right? After all we have the following tradeoffs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We've lost the unique type of the class and interface&lt;/li&gt;
&lt;li&gt;We are hardcoding our dependencies, potentially in multiple places&lt;/li&gt;
&lt;li&gt;We use two functions to emulate dependency injection, this a bit of  a song and dance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, the ergonomics of the above approach may change in the future due to forthcoming updates to the Kotlin language. For example&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Context receivers&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Context receivers allow us to separate the "context" of a function call from its arguments. Theoretically if the compiler can resolve those dependencies and they are immutable then it may be able to provide the same inlining we see in the above approach. This is very hand wavy and probably doesn't work but kinda like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@JvmInline&lt;/span&gt;
&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ExecutorContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;adder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;FastAdder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ExecutorContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;inline&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;FastAddInstructionExecutor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;adder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Anyway, I hope this breakdown brought you something new! I appreciate you're unlikely to be doing this in your day job, but think of it as another tool in your tool belt.&lt;/p&gt;

&lt;p&gt;I wrote this post on a flight from London to Tokyo, with spotty WiFi and not a lot of sleep, you'll have to forgive any typos. If you like this style of content give it a like and if you're super kind give my &lt;a href="https://github.com/CharlieTap/chasm" rel="noopener noreferrer"&gt;repository&lt;/a&gt; a star as its goes a long way! I'll have a blog post out in the not to distant future talking more about the interpreter itself and how it might change the way you build your applications&lt;/p&gt;

&lt;p&gt;Charlie&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>performance</category>
      <category>assembly</category>
      <category>android</category>
    </item>
    <item>
      <title>Making my concurrent algorithm 6000% better 🚀</title>
      <dc:creator>CharlieTap</dc:creator>
      <pubDate>Sun, 29 Oct 2023 20:11:11 +0000</pubDate>
      <link>https://dev.to/charlietap/making-my-concurrent-algorithm-6000-better-24oo</link>
      <guid>https://dev.to/charlietap/making-my-concurrent-algorithm-6000-better-24oo</guid>
      <description>&lt;p&gt;Would you trade a little bit of memory for a whole lot of concurrency?&lt;/p&gt;

&lt;p&gt;This is the concept behind a concurrent data structure I've been implementing in my &lt;a href="https://github.com/CharlieTap/cachemap" rel="noopener noreferrer"&gt;Kotlin Multiplatform library&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The data structure is known as LeftRight and it's here to give you an alternative to those pesky locks that everyone wraps around shared mutable state.&lt;/p&gt;

&lt;p&gt;I'll explain&lt;/p&gt;

&lt;h2&gt;
  
  
  What is LeftRight?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F15woh53eniulrbungjgp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F15woh53eniulrbungjgp.png" alt=" " width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;LeftRight is a concurrency primitive, it allows you turn any mutable data structure into concurrent version of itself.&lt;/p&gt;

&lt;p&gt;For example... Have a mutable set you need to be thread safe?&lt;/p&gt;

&lt;p&gt;Voilà&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;sharedSet&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LeftRight&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;MutableSet&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="err"&gt;mutable&lt;/span&gt;&lt;span class="nc"&gt;SetOf&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or more likely you have mutable HashMap ...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;sharedMap&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LeftRight&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;MutableMap&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="err"&gt;mutable&lt;/span&gt;&lt;span class="nc"&gt;MapOf&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or any other data structure you may want to share, LeftRight is generic over T and just requires a constructor for T to work. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Thats cool but why would I bring in your library as another dependency to my project when I can just wrap my data in a lock?&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Well you'd be saying goodbye all of your concurrent performance if you did that. Locks don't scale. LeftRight doesn't use locks, at least not for coordinating reads and writes.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;How can your library be safely exposing state whilst allowing reads and writes to access the same memory address, thats impossible?&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You're right thats impossible...&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;What???&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Maybe the picture above can give you a clue... &lt;/p&gt;

&lt;p&gt;Nope? Okay...&lt;/p&gt;

&lt;h2&gt;
  
  
  How do we LeftRight?
&lt;/h2&gt;

&lt;p&gt;At its core LeftRight is incredibly simple, it consists of two copies of a given data structure and an atomic pointer. The atomic pointer works like a traffic warden at a fork in the road, directing read traffic to one data structure and write traffic to the other.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;So we have 2x the memory footprint?&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Probably not no, Kotlin is a pointer heavy language, data structures tend to store references to objects rather than the objects themselves. So what we end up with is just two times the amount of pointers.&lt;/p&gt;

&lt;p&gt;The beauty of LeftRight lies in its ability to allow reads to progress without using locks or even asking them to wait. And this isn't an easy feat!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The problem&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Imagine we have a &lt;code&gt;LeftRight&amp;lt;MutableSet&amp;gt;&lt;/code&gt;, this means we will have two MutableSets internally, we'll call them L and R for short. Initially the atomic pointer is directing writes to L and reads to R.&lt;/p&gt;

&lt;p&gt;A write comes in and we update L.&lt;/p&gt;

&lt;p&gt;Now we somehow need to let readers know about the change, so we redirect the pointer such that L is now accepting reads and R is now accepting writes.&lt;/p&gt;

&lt;p&gt;But we haven't applied the change from before to R yet, so no problem right we just apply it to R and we're good?&lt;/p&gt;

&lt;p&gt;Nope, this isn't safe, because we don't know if readers have departed from R yet. Maybe they read the pointer just before we redirected it and are now about to read from the side we also are about to write to ... Somehow we need to signal to writes that reads have departed without using traditional lock/wait based solutions like mutexes or semaphores.&lt;/p&gt;

&lt;p&gt;LeftRight has a very clever solution to this problem, the algorithm goes something like this:&lt;/p&gt;

&lt;p&gt;We create a global array of counters (these are just 32 bit Ints)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;counters&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;arrayOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;maxReaderParallelism&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a read comes in:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We increment our counter&lt;/li&gt;
&lt;li&gt;We read from the data structure designated by the atomic pointer&lt;/li&gt;
&lt;li&gt;We increment our counter again&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Given each reader thread has its own index into the global array and thus its own private counter, we can infer that there is only ever one thread writing to the counter. If there is only one writer then we do not need any locks or waits. In fact the counters needn't even be atomic as a CAS loop would never fail, we do however need to mark them as volatile to ensure all threads see the events in the order they happened and that the compiler doesn't rearrange our code in weird and wonderful ways.&lt;/p&gt;

&lt;p&gt;For writes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We perform the write on the data structure designated by the atomic pointer&lt;/li&gt;
&lt;li&gt;We switch the atomic pointer so now reads will see this update&lt;/li&gt;
&lt;li&gt;Now we need to wait for readers to depart from the old read side (the new write side)&lt;/li&gt;
&lt;li&gt;We perform the write again on the new write side of the data structure&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So how do we determine that readers have departed?&lt;/p&gt;

&lt;p&gt;The is where the counters come in and the magic happens. Remember reads increment the counter twice, once before and once after a read. When the counter is odd, we know a reader is actively in the map, but when its even we know its finished its work and the following read will have to go through the atomic pointer again. So, we filter out all the counters that are odd and then we loop on them, waiting for them to change, not necessarily become even, but change. Any change means that the counter will have moved onto the new write side of the data structure.&lt;/p&gt;

&lt;p&gt;Whats with the weird sizing on the counter array?&lt;/p&gt;

&lt;p&gt;The size of this array dictates the amount of reader threads left right supports, this is one of the constraints on my implementation. If we allowed the array to grow or be resized then we would have wrap it in a lock. Instead we attempt to anticipate the upper bound on threads in your program, we do this using the same logic Kotlin coroutines does for thread pool sizing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why LR?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F93thytet1g9iqp7pyxf4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F93thytet1g9iqp7pyxf4.png" alt=" " width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I created LR for two reasons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;There are no Kotlin Multiplatform concurrent datastructures&lt;/li&gt;
&lt;li&gt;Locks are not a suitable replacement in 2023&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Kotlin Multiplatform is still in its infancy, at present there are no Kotlin Multiplatform concurrent data structures. You can't just simply just reach for ConcurrentHashMap like you could when you were working within the JVM ecosystem, everything depends on what targets your project supports. &lt;/p&gt;

&lt;p&gt;In the absence of the wealth of JVM based libraries, I see more and more people reaching for locks. Whilst this may be perfectly acceptable for a given use case, I don't believe generally replacing concurrent data structures with locks is a smart move in 2023.&lt;/p&gt;

&lt;p&gt;Locks strip our multicore CPUs of their parallelism super powers through contention, and contention isn't something thats going to go away, quite the opposite actually it's going to augment. Computers are gaining more cores year on year, high end consumer chips now have up to 24 cores, that will shortly rise to 32 and within the next decade we will undoubtedly see core counts that breach the 100 mark. Do you really want 99 cores waiting on your mutex? We'll see the performance ramifications of this later on.&lt;/p&gt;

&lt;p&gt;LeftRight gives you a scalable alternative to locks and positions itself generic concurrent data structure with more than acceptable performance. &lt;/p&gt;

&lt;p&gt;LeftRight is not a direct competitor to the highest performing concurrent data structures, like ConcurrentHashMap for example. ConcurrentHashMap is a bespoke concurrent HashMap implementation, its not a wrapper around a HashMap, it &lt;strong&gt;is&lt;/strong&gt; the HashMap. LeftRight however is a generic wrapper around a data structure, as always with indirection we incur the cost of chasing pointers. LeftRight is also entirely ignorant to the workings of the thing its wraps, it knows just two invariants, that a reads will occur and that writes will occur. For this reason it's unable to provide domain specific optimisations. &lt;/p&gt;

&lt;h2&gt;
  
  
  Less talk more benchmarks
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F799hgnk0jud3blp00kma.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F799hgnk0jud3blp00kma.png" alt=" " width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Time to put my money where my mouth is, I've been moaning a fair bit about locks, lets dive into some benchmarks.&lt;/p&gt;

&lt;p&gt;We're going to compare:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Javas ConcurrentHashMap (The gold standard 👑)&lt;/li&gt;
&lt;li&gt;LeftRight (The new kid 🤠)&lt;/li&gt;
&lt;li&gt;ReentrantReadWriteLock (The best locks have to offer 🔒)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I've been generous here, rather than use a standard mutex I've gone with a read write mutex in order to really get the best out of locks.&lt;/p&gt;

&lt;p&gt;The below benchmarks are all run on an Apple M1 Max, the benchmark suite can be found in the repo &lt;a href="https://github.com/CharlieTap/cachemap/tree/main/benchmark" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  ReentrantReadWriteLock
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Single Threaded&lt;/th&gt;
&lt;th&gt;Multi Threaded&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;read throughput&lt;/td&gt;
&lt;td&gt;0.0069 ops/ns&lt;/td&gt;
&lt;td&gt;0.0018 ops/ns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;read avg time&lt;/td&gt;
&lt;td&gt;14 ns/op&lt;/td&gt;
&lt;td&gt;5,783 ns/op&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;write throughput&lt;/td&gt;
&lt;td&gt;0.0789 ops/ns&lt;/td&gt;
&lt;td&gt;0.0056 ops/ns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;write avg time&lt;/td&gt;
&lt;td&gt;13 ns/op&lt;/td&gt;
&lt;td&gt;227 ns/op&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Single threaded benchmarks look pretty good as you would expect of a lock under no contention but it's not looking so great for the multithreaded side of things. The average time of a read is 400x slower and overall throughput around 6x less when accessing the hashmap from multiple threads.  &lt;/p&gt;

&lt;h3&gt;
  
  
  LeftRight
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Single Threaded&lt;/th&gt;
&lt;th&gt;Multi Threaded&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;read throughput&lt;/td&gt;
&lt;td&gt;0.0808 ops/ns&lt;/td&gt;
&lt;td&gt;0.6449 ops/ns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;read avg time&lt;/td&gt;
&lt;td&gt;12 ns/op&lt;/td&gt;
&lt;td&gt;16 ns/op&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;write throughput&lt;/td&gt;
&lt;td&gt;0.0381 ops/ns&lt;/td&gt;
&lt;td&gt;0.0324 ops/ns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;write avg time&lt;/td&gt;
&lt;td&gt;28 ns/op&lt;/td&gt;
&lt;td&gt;437 ns/op&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;LeftRight shows a remarkable improvement in both single threaded and multi threaded benchmarks. More importantly the multithreaded throughput of reads is showing an 8x linear improvement which checks out given my CPU has 8 performance cores. Writes are as expected given they are serialised with a mutex.&lt;/p&gt;

&lt;h3&gt;
  
  
  ConcurrentHashMap
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Single Threaded&lt;/th&gt;
&lt;th&gt;Multi Threaded&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;read throughput&lt;/td&gt;
&lt;td&gt;0.1406&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;read avg time&lt;/td&gt;
&lt;td&gt;7 ns/op&lt;/td&gt;
&lt;td&gt;9 ns/op&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;write throughput&lt;/td&gt;
&lt;td&gt;0.0093&lt;/td&gt;
&lt;td&gt;0.0073&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;write avg time&lt;/td&gt;
&lt;td&gt;10 ns/op&lt;/td&gt;
&lt;td&gt;1,398 ns/op&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;As expected reads are blazingly fast and ahead of both the previous two implementations, but interestingly LR comes within 80-90% which is better than we could have hoped for a data structure with inherently more indirection. Writes seem to struggle but this is likely a quirk a of the benchmark writing to the same key and thus the same bin (ConcurrentHashMap uses bin level locking), in reality writes would have a much fairer distribution and I would expect Concurrent HashMap to come out on top.&lt;/p&gt;

&lt;p&gt;Here's a graph depicting all the above courtesy of ChatGPT &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvg0wx2ztwi1c68hop32s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvg0wx2ztwi1c68hop32s.png" alt=" " width="800" height="1068"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What you came here for
&lt;/h2&gt;

&lt;p&gt;LeftRight is remarkably performant for a generic concurrency primitive. But things weren't always this way... lets take a look at an earlier implementations benchmark&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Single Threaded&lt;/th&gt;
&lt;th&gt;Multi Threaded&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;read throughput&lt;/td&gt;
&lt;td&gt;0.0716 ops/ns&lt;/td&gt;
&lt;td&gt;0.0107 ops/ns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;read avg time&lt;/td&gt;
&lt;td&gt;14 ns/op&lt;/td&gt;
&lt;td&gt;534 ns/op&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;write throughput&lt;/td&gt;
&lt;td&gt;0.0368 ops/ns&lt;/td&gt;
&lt;td&gt;0.0376 ops/ns&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;write avg time&lt;/td&gt;
&lt;td&gt;27 ns/op&lt;/td&gt;
&lt;td&gt;442 ns/op&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;You can see from the above, the performance takes a nose dive with the introduction of more threads/cpu cores. I expected this to be the case for mutations, after all we have a single writer policy which is enforced using lock.&lt;/p&gt;

&lt;p&gt;But for reads this didn't make sense at all, not only is the average time of a read 50x slower but the throughout is 7x less??? I'm using a lock-free/wait-free algorithm for reads! Wtf??? Theres no shared memory and nothing for threads to contend on???&lt;/p&gt;

&lt;p&gt;Well ...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F35a59jj6lrffxeovkydj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F35a59jj6lrffxeovkydj.png" alt=" " width="680" height="680"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It transpires that it's not enough to have threads guard their own private memory when accessing shared resources. Due to the nature of modern CPU cache architectures, even adjacent memory locations can be a source of contention.&lt;/p&gt;

&lt;p&gt;It's like contention through osmosis 😭 😭&lt;/p&gt;

&lt;p&gt;This phenomenon, known as false sharing, occurs when multiple threads, running on different cores, access different variables that reside on the same cache line. Even though they're not directly sharing data, the mere fact that their data resides closely in memory means they effectively compete for the same cache line. This can cause performance degradation as threads constantly invalidate each other's cache lines, even if they're not accessing shared data directly.&lt;/p&gt;

&lt;p&gt;Honestly I just keep coming back to this quote over and over again in my career...&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;There are only two hard things in Computer Science: cache invalidation and naming things.&lt;br&gt;
-- Phil Karlton&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So how do we fix this?&lt;/p&gt;

&lt;p&gt;The problem stems from the original implementation of my global counter array&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;globalEpochArray&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AtomicInt&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the arrays allocated, it will more than likely allocate those 64 AtomicInt objects next to one another on the heap.&lt;/p&gt;

&lt;p&gt;AtomicInt is just a wrapper around a 4 byte integer, and every class has a 12 byte (on a 64 bit JVM) object header, making one allocation of AtomicInt take 16 bytes of space.&lt;/p&gt;

&lt;p&gt;So we now have a 16 * 64 byte block of contiguous memory allocated on the heap.&lt;/p&gt;

&lt;p&gt;When thread 1 loads the first counter (which should be just [0 - 16] bytes) it in fact loads an entire cache line worth of memory, on my laptop (M1 Max) an L1 cache line is 128 bytes. So now the core running thread one has the first [0 - 128] bytes and thus the first 8 counters inside of its cache line. &lt;/p&gt;

&lt;p&gt;When thread 2 tries to load the second counter it does the same, now thread 2 has counters 2 through to 10 in its cache line.&lt;/p&gt;

&lt;p&gt;Now what happens when thread 1 mutates its counter? &lt;/p&gt;

&lt;p&gt;It invalidates the cache line of up to 8 other cores, and when poor thread two comes round to trying to change its counter, it now has to load it from RAM again.&lt;/p&gt;

&lt;p&gt;To give a rough idea of impact heres the load times between L1 CPU cache and RAM:&lt;/p&gt;

&lt;p&gt;L1 Cache Lookup: 0.5 to 1 nanoseconds.&lt;br&gt;
RAM Lookup: 50 to 100 nanoseconds&lt;/p&gt;

&lt;p&gt;This explains my average read time went from 14ns to 534ns&lt;/p&gt;

&lt;p&gt;So how do stop this? &lt;/p&gt;

&lt;p&gt;With this abomination&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PaddedVolatileInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;initialValue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Volatile&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="nd"&gt;@Volatile&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;p2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="nd"&gt;@Volatile&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;p3&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="nd"&gt;@Volatile&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;p4&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="nd"&gt;@Volatile&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;p5&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="nd"&gt;@Volatile&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;p6&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="nd"&gt;@Volatile&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;p7&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="nd"&gt;@Volatile&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;p8&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="nd"&gt;@Volatile&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;p9&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="nd"&gt;@Volatile&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;p10&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="nd"&gt;@Volatile&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;p11&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="nd"&gt;@Volatile&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;p12&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="nd"&gt;@Volatile&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;p13&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="nd"&gt;@Volatile&lt;/span&gt; &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;p14&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

    &lt;span class="nd"&gt;@Volatile&lt;/span&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;initialValue&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Essentially we pad our counter object such that fits perfectly inside an L1 cache line.&lt;/p&gt;

&lt;p&gt;12 byte header + (14 * 8) + 4 = 128 bytes&lt;/p&gt;

&lt;p&gt;This is really isn't an ideal solution, padding should be dynamic such that on cpus with smaller cache lines are accommodated for but well we don't have this level of control from Kotlin.&lt;/p&gt;

&lt;p&gt;But with that change our multithreaded throughput and average time fall dramatically.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Before (Multi Threaded)&lt;/th&gt;
&lt;th&gt;After (Multi Threaded)&lt;/th&gt;
&lt;th&gt;% Change&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;read throughput&lt;/td&gt;
&lt;td&gt;0.0107 ops/ns&lt;/td&gt;
&lt;td&gt;0.6449 ops/ns&lt;/td&gt;
&lt;td&gt;+5927.10%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;read avg time&lt;/td&gt;
&lt;td&gt;534 ns/op&lt;/td&gt;
&lt;td&gt;16 ns/op&lt;/td&gt;
&lt;td&gt;-97.00%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;write throughput&lt;/td&gt;
&lt;td&gt;0.0376 ops/ns&lt;/td&gt;
&lt;td&gt;0.0324 ops/ns&lt;/td&gt;
&lt;td&gt;-13.83%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;write avg time&lt;/td&gt;
&lt;td&gt;442 ns/op&lt;/td&gt;
&lt;td&gt;437 ns/op&lt;/td&gt;
&lt;td&gt;-1.13%&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The value of throughput now grows linearly with the amount of cores available, thus you would see different increases/decreases depending on the machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rapping up
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb3j9dacrlp8bar2ama4q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb3j9dacrlp8bar2ama4q.png" alt=" " width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can find my repository containing LeftRight &lt;a href="https://github.com/CharlieTap/cachemap" rel="noopener noreferrer"&gt;here&lt;/a&gt;, you'll notice that the repo is actually called cachemap. That's because the repo contains two libraries, LeftRight and CacheMap, the latter is essentially just a batteries included &lt;code&gt;LeftRight&amp;lt;MutableMap&amp;gt;&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Both LeftRight and CacheMap are experimental at the moment so please proceed with caution despite the positive looking benchmarks. Be extra careful with the suspending variants as these haven't yet been benchmarked nor optimised. &lt;/p&gt;

&lt;p&gt;Theres lots of optimisations to be made, writes suck more than need to at the moment. I have a rough design of a write batching algorithm which should amortise the cost of the lengthy writer wait step.&lt;/p&gt;

&lt;p&gt;I'm currently in the process of publishing the artifacts in Maven Central (the process is not fun 🤯), hopefully by the time you read this I will have instructions on the repo of how to pull the each artifact.&lt;/p&gt;

&lt;h3&gt;
  
  
  Acknowledgements
&lt;/h3&gt;

&lt;p&gt;I learned of LeftRight in a &lt;a href="https://www.youtube.com/watch?v=eLNAMEoKAAc" rel="noopener noreferrer"&gt;youtube stream from Jon Gjengset&lt;/a&gt; a while back, Jon is in my opinion the best comp sci content creator out there. Please go like and subscribe to his channel if you're keen on learning! &lt;/p&gt;

&lt;p&gt;You can also find what I believe to be the first paper on the primitive &lt;a href="https://hal.science/hal-01207881/document" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;All Illustrations were created by incredible artist that is Dalle 3&lt;/p&gt;

</description>
    </item>
    <item>
      <title>A Blazing Fast 🔥 Kotlin Native Cross Compilation Environment</title>
      <dc:creator>CharlieTap</dc:creator>
      <pubDate>Thu, 11 May 2023 19:13:59 +0000</pubDate>
      <link>https://dev.to/charlietap/blazing-fast-kotlin-native-cross-compilation-environment-3dn</link>
      <guid>https://dev.to/charlietap/blazing-fast-kotlin-native-cross-compilation-environment-3dn</guid>
      <description>&lt;p&gt;This post was written over the course of one evening, it's a brain-dump of information I've accumulated recently. You can expect the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Me continuously going on a tangent and failing 🤣&lt;/li&gt;
&lt;li&gt;A full development environment for cross compiling Kotlin Native 🔥🔥🔥&lt;/li&gt;
&lt;li&gt;Some real bleeding edge Docker techniques 🔪&lt;/li&gt;
&lt;li&gt;Remote development inside a container from Intelli 🤯 &lt;/li&gt;
&lt;li&gt;Emojis 🤓 🤖 🦄
&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Lets set the scene
&lt;/h1&gt;

&lt;p&gt;&lt;br&gt;
As part of an upcoming project involving io_uring (a Linux system IO library) I needed to create an environment capable of compiling a Kotlin Native application on my shiny Apple Silicon machine. Given my OS is macos and I'm trying to develop an application for Linux I understood this would require some form of cross compilation.&lt;/p&gt;

&lt;p&gt;So I took a look at the Kotlin Official Site and found this snippet&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;While cross-platform compilation is possible, which means using one platform to compile for a different one, in this Kotlin case we'll be targeting the same platform we're compiling on.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Easy peasy, so I set up my application and configured one target for my native application as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nf"&gt;linuxArm64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"native"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're just going to build for now, obviously the binary produced couldn't run on a mac.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./gradlew linkDebugExecutableNative
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And .... nothing, this is going to be a theme throughout the course of this blog post. If you run this on any mac you will receive no artifacts what so ever. I wasted an hour or so trying different permutations before realising that whilst cross compilation of some sort may be possible in Kotlin, mac to linux right now isn't happening.&lt;/p&gt;

&lt;h1&gt;
  
  
  To VM or not to VM
&lt;/h1&gt;

&lt;p&gt;&lt;br&gt;
Given we clearly aren't capable of compiling and running this application on our own machine we're going to need a linux environment. This is where Docker comes in, with Docker we can spin up containers with whatever OS we want, additionally those containers can also target different cpu architectures (Why would I wanna do that ... you'll see).&lt;/p&gt;

&lt;p&gt;My reason for reaching for Docker over spinning up a VM (yes i know Docker for mac using a vm behind the scenes) is because Docker makes it easy to try different OS's quickly. Additionally Docker allows me to mount my filesystem into the containers filesystem which I hoped would allow me to develop on my machine and just have the build and run of my application happen inside the container.&lt;/p&gt;

&lt;p&gt;So my first decision, what base image should I go for. In the interest of speed I took a look at what Gradle themselves had built on their Dockerhub repo. I figured this would at least mean I wouldn't have to worry about configuring both Gradle and the JDK and I could just focus on the libs I need for my application. If you've worked in this space before you'll know that images extend other images and typically you end up with the following set of operating systems to choose from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;scratch&lt;/strong&gt; (this is no OS so not useful)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;alpine&lt;/strong&gt; (this uses musl c and is very small)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;debian&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;ubuntu&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I like small containers and Gradle had an alpine based image with the JDK 17 built in so I went for this. It's worth noting I'm on ARM machine and therefore this will pull an ARM container by default. &lt;/p&gt;

&lt;p&gt;Before I setup a full environment I wrote a quick Dockerfile to validate it built my "Hello World" linuxArm64 application correctly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; gradle:8.1.1-jdk17-alpine&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;apk update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    apk add &lt;span class="nt"&gt;--no-cache&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    build-base &lt;span class="se"&gt;\
&lt;/span&gt;    liburing-dev

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . /app&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;./gradlew &lt;span class="nt"&gt;--stacktrace&lt;/span&gt; linkDebugExecutableNative


&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["build/bin/native/DebugExecutable/koru.kexe"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lets try to build that&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;--platform&lt;/span&gt; linux/amd64 &lt;span class="nt"&gt;-t&lt;/span&gt; koru-dev &lt;span class="nt"&gt;-f&lt;/span&gt; Dockerfile  &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But ... no bueno. It &lt;a href="https://youtrack.jetbrains.com/issue/KT-36871/Support-Aarch64-Linux-as-a-host-for-the-Kotlin-Native" rel="noopener noreferrer"&gt;turns out&lt;/a&gt; that you cannot compile Kotlin native applications on a linux arm host.&lt;/p&gt;

&lt;p&gt;Okay no problem we change the Dockerfile a little&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; -platform=linux/amd64 gradle:8.1.1-jdk17-alpine&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We change our Kotlin Native target also&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nf"&gt;linuxX64&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"native"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Boom! Another crash 😔 &lt;/p&gt;

&lt;p&gt;This time the culprit is root of the problem is &lt;a href="https://youtrack.jetbrains.com/issue/IDEA-304440/Cannot-run-program-java-failed-to-exec-spawn-helper-exit-value-1#focus=Comments-27-6736675.0-0" rel="noopener noreferrer"&gt;JDK 17&lt;/a&gt;. I'll admit I didn't fight this one too much and just downgraded to JDK 11.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; -platform=linux/amd64 gradle:8.1.1-jdk11-alpine&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;💥 💥 💥 &lt;/p&gt;

&lt;p&gt;Still nothing, it turns out that decision to use a container based on musl c came back to bite me. The Kotlin native toolchain uses glibc in places, meaning a musl c based container would be missing a bunch of necessary libs.&lt;/p&gt;

&lt;p&gt;I had a quick stab at adding some compat&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;RUN &lt;/span&gt;apk update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    apk add &lt;span class="nt"&gt;--no-cache&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    build-base &lt;span class="se"&gt;\
&lt;/span&gt;    liburing-dev &lt;span class="se"&gt;\
&lt;/span&gt;    gcompat &lt;span class="se"&gt;\
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;💥 💥 💥 &lt;/p&gt;

&lt;p&gt;Same result, I iterated some more and actually got pretty close to making this work (I actually think its possible) but eventually chose to cut my losses and went with an ubuntu base image.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; --platform=linux/amd64 gradle:8.1.1-jdk11&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅&lt;/p&gt;

&lt;p&gt;And with that I had some thing build! Albeit slowly, but building! Does its run?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-ti&lt;/span&gt; &lt;span class="nt"&gt;--platform&lt;/span&gt; linux/amd64 koru-dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅&lt;/p&gt;

&lt;p&gt;The beautiful "Hello World" graces my terminal&lt;br&gt;
&lt;/p&gt;

&lt;h1&gt;
  
  
  &lt;em&gt;The time you enjoy wasting is not wasted time&lt;/em&gt;
&lt;/h1&gt;

&lt;p&gt;&lt;br&gt;
Okay its slow but building the source into a container image was never the original plan, the original plan was to mount my filesystem at runtime and have the build/execution happen when the container runs. &lt;/p&gt;

&lt;p&gt;Given I'd already wasted a ton of time on this surely it wasn't time for another risk right? Well I'm stupid so thats what I did 🤣.&lt;/p&gt;

&lt;p&gt;You see I've worked with containers for a long time, and the pattern I planned to use of mounting the filesystem at runtime has some flaws. You see working this way you end up with slightly different containers between dev and your other environments (where the binary itself is baked in) and I'd recently &lt;a href="https://d0nut.hashnode.dev/entering-the-garden-of-ferris?ref=twitter-share" rel="noopener noreferrer"&gt;learned about a way&lt;/a&gt; to potentially get great dev environment performance whilst creating an image I could use agnostic of environment.&lt;/p&gt;

&lt;p&gt;Lets give it a go&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt;  --platform=linux/amd64 gradle:8.1.1-jdk11&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    build-essential &lt;span class="se"&gt;\
&lt;/span&gt;    liburing-dev


&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . /app&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nt"&gt;--mount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;cache,target&lt;span class="o"&gt;=&lt;/span&gt;/app/.gradle,rw &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nt"&gt;--mount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;cache,target&lt;span class="o"&gt;=&lt;/span&gt;/app/bin/build,rw &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nt"&gt;--mount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;cache,target&lt;span class="o"&gt;=&lt;/span&gt;/home/gradle/.gradle,rw &lt;span class="se"&gt;\
&lt;/span&gt;    gradle &lt;span class="nt"&gt;--stacktrace&lt;/span&gt; linkDebugExecutableNative


&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nt"&gt;--mount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;cache,target&lt;span class="o"&gt;=&lt;/span&gt;/app/bin/build,rw &lt;span class="se"&gt;\
&lt;/span&gt;    find /app/bin/build/ &lt;span class="se"&gt;\
&lt;/span&gt;        &lt;span class="nt"&gt;-maxdepth&lt;/span&gt; 4 &lt;span class="se"&gt;\
&lt;/span&gt;        &lt;span class="nt"&gt;-type&lt;/span&gt; f &lt;span class="nt"&gt;-executable&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;        &lt;span class="nt"&gt;-exec&lt;/span&gt; &lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt; /usr/local/bin &lt;span class="se"&gt;\;&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["build/bin/native/DebugExecutable/koru.kexe"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The new lines add cache volumes during the build phase of the container. So our first run came out at around 1 minute 30, and the second ... 1 minute. That isn't half bad. But I noticed in the logs that the Kotlin Native compiler Konan is being downloaded every time. After some fiddling I found a solution&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# For some reason konan seems to wipe the $HOME/.konan folder between builds&lt;/span&gt;
&lt;span class="c"&gt;# By defining this env variable konan will write cache out to a folder we are already using for caching&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; KONAN_DATA_DIR=/home/gradle/.gradle&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ 🚀&lt;/p&gt;

&lt;p&gt;45 Seconds! To build that is, a second or so to run. That's pretty fast, but remember this is Hello World and 45 seconds is still a poor development cycle. A large portion of the build time can be attributed to the Gradle daemon warming up and bootstrapping a JDK, something that wouldn't be an issue if we were running back to back builds on a running container...&lt;/p&gt;

&lt;p&gt;I had to compare it to the OG solution, for science! worst case we can use these image build time optimisations in our prod container.&lt;/p&gt;

&lt;p&gt;So what does the other solution look like?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt;  --platform=linux/amd64 gradle:8.1.1-jdk11&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; KONAN_DATA_DIR=/home/gradle/.gradle&lt;/span&gt;

&lt;span class="c"&gt;# /home/gradle/.gradle is already declared a volume in the base image&lt;/span&gt;
&lt;span class="c"&gt;#VOLUME /app/bin/build&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nt"&gt;--mount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;cache,target&lt;span class="o"&gt;=&lt;/span&gt;/var/cache/apt &lt;span class="se"&gt;\
&lt;/span&gt;    apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    build-essential &lt;span class="se"&gt;\
&lt;/span&gt;    liburing-dev

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["gradle", "-t", "runDebugExecutableNative", "--debug"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However we will be doing more than just building and running, we'll also have to mount our local filesystem into the container. The container won't look to run our program and exit either, instead it will build our project continuously, recompiling and rerunning tasks every time the files change&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;--platform&lt;/span&gt; linux/amd64 &lt;span class="nt"&gt;-t&lt;/span&gt; koru-dev &lt;span class="nt"&gt;-f&lt;/span&gt; Dockerfile  &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-ti&lt;/span&gt; &lt;span class="nt"&gt;--platform&lt;/span&gt; linux/amd64 &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;:/app"&lt;/span&gt; koru-dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;💥 💥 💥 &lt;/p&gt;

&lt;p&gt;Back to things breaking 😭, and its this line&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["gradle", "-t", "runDebugExecutableNative", "--debug"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The short of it that is that is the command that gets run when the container starts and more specifically runs a Gradle task every time your project files change. Unfortunately the file watching impl is broken (I never found a good reason as to why) and the this results in the task being run 1000 times per second. Okay Plan B, we will just launch the container with a shell and connect to the shell ourselves, issuing our own commands.&lt;/p&gt;

&lt;p&gt;A minor change&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["/bin/bash"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅&lt;/p&gt;

&lt;p&gt;We're back in action. I connect to the shell and issue my Gradle command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./gradlew runDebugExecutableNative
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first run is slow, this is expected. The second however takes around 45 seconds... How could it be this bad given we have a warm Gradle daemon ready to go?&lt;/p&gt;

&lt;p&gt;It transpires both our container and emulated file system are running slow. My first tip of this post, enable Rosetta 2 translation for x86 images in the Docker settings!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fux2dawc62ufxamhxb5eu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fux2dawc62ufxamhxb5eu.png" alt=" " width="800" height="130"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Remember we're running x86 images now, Docker supports running Rosettas translation over them ahead of time to get them running natively 😍.&lt;/p&gt;

&lt;p&gt;Whilst you're in the settings I want you to also enable the Virtio FS which can be found under the General tab. This will give a speedier filesystem for our bind mounted project code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg9uvnf1j5zdna9k7vccr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg9uvnf1j5zdna9k7vccr.png" alt=" " width="800" height="62"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With those changes made, I restart Docker entirely and issue my command again inside of the running container&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./gradlew runDebugExecutableNative
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✅ 🚀🚀🚀&lt;/p&gt;

&lt;p&gt;19 Seconds !!! thats absolutely rapid and really no different than Kotlin Native builds on my own machine.&lt;/p&gt;

&lt;p&gt;This approach is definitively faster, it seems the Gradle Daemon being up and running makes all the difference.&lt;/p&gt;

&lt;p&gt;Thats a wrap? Right? Well ....&lt;/p&gt;

&lt;p&gt;If you're only using the standard native lib yes, but I need c interop for io uring, and it turns out Intellij is unable to use the bindings that get generated inside of the container despite the fact they share the same filesystem.&lt;/p&gt;

&lt;p&gt;😠😠😠🤬🤬🤬😭😭😭&lt;/p&gt;

&lt;h1&gt;
  
  
  Another tangent 📐
&lt;/h1&gt;

&lt;p&gt;&lt;br&gt;
It's okay, I can work with this... the container is working and I've invested a ton of time optimising it, so let's not throw that away. IntelliJ is struggling because its outside the container observing files being generated on a linux machine. &lt;/p&gt;

&lt;p&gt;What if we could get it on the inside? its possible for IDE to connect to remote environments and run a backend inside the container whilst the frontend runs on the host. You might have heard of Intelli J connecting to Github Codespaces or other remote development environments, why couldn't it connect to our docker container?&lt;/p&gt;

&lt;p&gt;Well it can!&lt;/p&gt;

&lt;p&gt;I make some changes once again to the cross compilation development container. This time adding an ssh server and making this the containers process.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt;  --platform=linux/amd64 gradle:8.1.1-jdk11&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; KONAN_DATA_DIR=/home/gradle/.gradle&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nt"&gt;--mount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;cache,target&lt;span class="o"&gt;=&lt;/span&gt;/var/cache/apt &lt;span class="se"&gt;\
&lt;/span&gt;    apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    apt-get &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    build-essential &lt;span class="se"&gt;\
&lt;/span&gt;    liburing-dev &lt;span class="se"&gt;\
&lt;/span&gt;    openssh-server &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;sudo&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;useradd &lt;span class="nt"&gt;-rm&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; /home/ubuntu &lt;span class="nt"&gt;-s&lt;/span&gt; /bin/bash &lt;span class="nt"&gt;-g&lt;/span&gt; root &lt;span class="nt"&gt;-G&lt;/span&gt; &lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; 1001 &lt;span class="nb"&gt;test&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'test:test'&lt;/span&gt; | chpasswd

&lt;span class="k"&gt;RUN &lt;/span&gt;service ssh start

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 22&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["/usr/sbin/sshd","-D"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I build and run the container one final time, do note how I'm now binding port 20 on my local to that of the container on the run command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;--platform&lt;/span&gt; linux/amd64 &lt;span class="nt"&gt;-t&lt;/span&gt; koru-dev &lt;span class="nt"&gt;-f&lt;/span&gt; Dockerfile  &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-ti&lt;/span&gt; &lt;span class="nt"&gt;--platform&lt;/span&gt; linux/amd64 &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;pwd&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;:/app"&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 22:22 koru-dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It started okay lets try and connect to it. Inside IntelliJ I  go to File &amp;gt; Remote Development...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjckcr9wlero8o207elyz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjckcr9wlero8o207elyz.png" alt=" " width="800" height="678"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enter the following credentials, remember we made a user called test with a password of test when we built the Docker container&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftq76mcs0x59g9c4hff4x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftq76mcs0x59g9c4hff4x.png" alt=" " width="800" height="678"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After what feels like an eternity I'm greeted with an IDE, with working intellisense on my C bindings !!!!!!!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvd4m31917vk6evwbdrwo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvd4m31917vk6evwbdrwo.png" alt=" " width="800" height="517"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thoughts
&lt;/h2&gt;

&lt;p&gt;Despite wasting a week trying to get this working, I have in the process learned a lot and opened up some opportunities for the future. &lt;/p&gt;

&lt;p&gt;I did take some time to tidy up after myself, I moved the cross compilation Dockerfile into a file called Dockerfile.dev and made a second file for production called Dockerfile which has all the optimisations I worked on originally. I also made a small Makefile to save myself writing the same docker commands over and over&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight make"&gt;&lt;code&gt;&lt;span class="nv"&gt;BINARY_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;koru.kexe
&lt;span class="nv"&gt;IMAGE_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;koru

&lt;span class="nv"&gt;BUILD_ARG_BINARY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;--build-arg&lt;/span&gt; &lt;span class="nv"&gt;BINARY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;$(&lt;/span&gt;BINARY_NAME&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nv"&gt;.DEFAULT_GOAL&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="nb"&gt;help&lt;/span&gt;
&lt;span class="nl"&gt;.PHONY&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;build build-release build-verbose debug run help&lt;/span&gt;

&lt;span class="nl"&gt;build&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    docker build &lt;span class="nt"&gt;--platform&lt;/span&gt; linux/amd64 &lt;span class="p"&gt;$(&lt;/span&gt;BUILD_ARG_BINARY&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;IMAGE_NAME&lt;span class="p"&gt;)&lt;/span&gt; .
&lt;span class="nl"&gt;build-dev&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    docker build &lt;span class="nt"&gt;--platform&lt;/span&gt; linux/amd64 &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;IMAGE_NAME&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nt"&gt;-dev&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; Dockerfile.dev .
&lt;span class="nl"&gt;build-release&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    docker build &lt;span class="nt"&gt;--platform&lt;/span&gt; linux/amd64 &lt;span class="p"&gt;$(&lt;/span&gt;BUILD_ARG_BINARY&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;--build-arg&lt;/span&gt; &lt;span class="nv"&gt;MODE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;release &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;IMAGE_NAME&lt;span class="p"&gt;)&lt;/span&gt; .
&lt;span class="nl"&gt;build-verbose&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    docker build &lt;span class="nt"&gt;--platform&lt;/span&gt; linux/amd64 &lt;span class="p"&gt;$(&lt;/span&gt;BUILD_ARG_BINARY&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="p"&gt;$(&lt;/span&gt;IMAGE_NAME&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nt"&gt;--progress&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;plain .
&lt;span class="nl"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;build&lt;/span&gt;
    docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-ti&lt;/span&gt; &lt;span class="nt"&gt;--platform&lt;/span&gt; linux/amd64 &lt;span class="p"&gt;$(&lt;/span&gt;IMAGE_NAME&lt;span class="p"&gt;)&lt;/span&gt; /bin/bash
&lt;span class="nl"&gt;run&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;build&lt;/span&gt;
    docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-ti&lt;/span&gt; &lt;span class="nt"&gt;--platform&lt;/span&gt; linux/amd64 &lt;span class="p"&gt;$(&lt;/span&gt;IMAGE_NAME&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nl"&gt;run-dev&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;build-dev&lt;/span&gt;
    docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-ti&lt;/span&gt; &lt;span class="nt"&gt;--platform&lt;/span&gt; linux/amd64 &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;$(&lt;/span&gt;&lt;span class="s2"&gt;CURDIR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;:/app"&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 22:22 &lt;span class="p"&gt;$(&lt;/span&gt;IMAGE_NAME&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nt"&gt;-dev&lt;/span&gt;
&lt;span class="nl"&gt;run-release&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;build-release&lt;/span&gt;
    docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;-ti&lt;/span&gt; &lt;span class="nt"&gt;--platform&lt;/span&gt; linux/amd64 &lt;span class="p"&gt;$(&lt;/span&gt;IMAGE_NAME&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nl"&gt;help&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Available targets:"&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  build         Build the Docker image in debug mode"&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  build-release Build the Docker image in release mode"&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  build-verbose Build the Docker image in debug mode with verbose output"&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  debug         Build (if necessary) and run the container attaching a bash shell"&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  run           Build (if necessary) and run the Docker container"&lt;/span&gt;
    &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  help          Display this help message"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this I just run the following to spin up the environment&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;make run-dev 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're interested in any of the above I've made the project I'm working on public &lt;a href="https://github.com/CharlieTap/koru" rel="noopener noreferrer"&gt;here&lt;/a&gt;. Just please bare in mind this repository is part of a larger project so things may change.&lt;/p&gt;

&lt;p&gt;I think thats a wrap, I hope you learned something, I sure did!&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>docker</category>
      <category>devops</category>
      <category>gradle</category>
    </item>
    <item>
      <title>Synking all the things with CRDTs: Local first development</title>
      <dc:creator>CharlieTap</dc:creator>
      <pubDate>Wed, 12 Apr 2023 09:39:20 +0000</pubDate>
      <link>https://dev.to/charlietap/synking-all-the-things-with-crdts-local-first-development-3241</link>
      <guid>https://dev.to/charlietap/synking-all-the-things-with-crdts-local-first-development-3241</guid>
      <description>&lt;p&gt;So you want to build a local first application? You want the application to work both offline and online seamlessly with no interruption to the end user? &lt;/p&gt;

&lt;p&gt;How hard could it be right?...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7iuedxigj9zfaeudhekf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7iuedxigj9zfaeudhekf.png" alt="A Dalle-2 generated image of a monkey trying to solve an impossible puzzle" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For the past few months I've been working on a library in this space, something quite novel that I think offers an avenue for simplifying local first development and the challenges it presents. But before I introduce Synk I think its worth exploring this problem space, taking a look at why its particularly tricky and what existing solutions are out there. This blog post should serve as a good primer for anyone looking to get into local first development and CRDTs.&lt;/p&gt;

&lt;p&gt;Local first applications work offline, they work in isolation, the application shouldn't need to be connected to the internet in order to function. From a technical perspective this is grand departure from traditional client server applications. &lt;/p&gt;

&lt;p&gt;In order to work detached from the network, the client device must be capable of computing all the states that would typically be derived by the server... thats a lot business logic on the client. So the clients FAT, so what? Well that aside you've just decided to create a distributed system and with that comes an entire field of computer science worth of problems!&lt;/p&gt;

&lt;p&gt;But wait how did this happen?&lt;/p&gt;

&lt;p&gt;Let me explain, in a traditional client server architecture, the server is gospel, the state it holds is the truth and entirety. We'll skip over the fact that most servers are a facade hiding a deeper distributed system, the net of distribution we cast extends only as far as the server infrastructure, not the clients themselves. Whereas in a distributed system, the totality of the state is not found in a single node (like the server) the state is the aggregation of the state found across all the nodes in the system. Now that the client application can work and evolve in isolation from the network (and importantly the centralised server state), we've undoubtedly waded into distributed waters.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Thats Cap! ...Theorem&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Okay so we're saying that local first applications are actually complex distributed systems, we should probably classify what type. If you ever had the pleasure sitting through a CS lecture on DS then you've probably come across CAP Theorem. It a nutshell it states that distributed systems can have at most two of the three following properties:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Consistency&lt;/td&gt;
&lt;td&gt;Every read receives the most recent write or an error.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Availability&lt;/td&gt;
&lt;td&gt;Every request receives a (non-error) response, without the guarantee that it contains the most recent write.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Partition tolerance&lt;/td&gt;
&lt;td&gt;The system continues to operate despite an arbitrary number of messages being dropped (or delayed) by the network between nodes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi0bbk7llo2frx5ac40bv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi0bbk7llo2frx5ac40bv.png" alt=" " width="200" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Having at most two of the above properties means we end up with systems that are either CA, CP or AP. Can you guess what property doesn't hold in local first applications? ...&lt;/p&gt;

&lt;p&gt;It's Consistency, we're unable to guarantee strong consistency because we want them to work offline, they can't possibly return the most recent write if they can't coordinate with other nodes in the system. Instead they are what we call "&lt;em&gt;Eventually Consistent&lt;/em&gt;", once they have reconnected and synchronised the latest changes. In my opinion dropping the C is a blessing, solutions that guarantee strong consistency involve complex consensus algorithms... If you've ever studied Paxos or Raft you'll know why I say this is good thing. &lt;/p&gt;

&lt;p&gt;Okay so we're AP, Local first applications are AP distributed systems, cool, well whats does that mean? Well its means we'll face a certain subset of problems and we can lean on a subset of solutions. Lets have a look at the type of problems we will face.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Problem&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Replication&lt;/td&gt;
&lt;td&gt;How do changes get propagated to other clients without being lost?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conflict resolution&lt;/td&gt;
&lt;td&gt;What happens when two devices change the same piece of data whilst offline? Who's change wins and why?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Causal Ordering&lt;/td&gt;
&lt;td&gt;Who changed what when exactly? Can you really be sure on a timeline of events? What if the local clocks on the clients are out of synk?.. oops I mean sync&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Spoiler! Synk concerns itself with the bottom two. After all it's quite likely you already have made up your mind on how you want propagate data, for example what transport (Http, RPC, Sockets) and serialization format you want to use in your application. So replication is on you... but we have solutions for the rest. It's about time I introduce one of the most common solutions in AP distributed systems and exactly the thing you'll need to replicate.&lt;/p&gt;

&lt;h2&gt;
  
  
  CRDTs
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;"It's Cee Arr Dee Tees not Serdts"&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;CRDTs also known as Conflict-free replicated data types are the mechanism we will use to achieve "Eventual Consistency" in our AP distributed system.&lt;/p&gt;

&lt;p&gt;In simple terms, they're data structures that cannot conflict, each (and there are many!) has an algorithm that lets them deterministically merge updates. For almost all basic data structures you can think of (maps, sets, lists etc) there will exist CRDT implementations, often with different quirks but all guaranteeing conflict free merging of updates.&lt;/p&gt;

&lt;p&gt;Now if we're to use proper Distributed systems terminology these updates which are sent between nodes to keep the system up to date are known as "Messages". CRDTs are categorised by whats contained in the messages they produce. &lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Behaviour&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;State&lt;/td&gt;
&lt;td&gt;State based CRDTs will send messages that include the entire current state of the CRDT. This can be thought of as being similar to a POST/PUT request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Operation&lt;/td&gt;
&lt;td&gt;Operation based CRDTs will send messages that include the operation and parameters that need to be applied. This can be thought of as being similar to an RPC call.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Delta&lt;/td&gt;
&lt;td&gt;Delta based CRDTs will send messages that contain only the values that have changed in the CRDT. This can be thought of as being similar to a PATCH request&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;And thats not all, CRDTS are &lt;strong&gt;ACID 2.0&lt;/strong&gt;. Another acronym if you hadn't guessed, let me break that down for you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Associative&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight mathematica"&gt;&lt;code&gt;&lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means it doesn't matter if you merge a into b or b into a its the same&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Commutative&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight mathematica"&gt;&lt;code&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means it doesn't matter how you group the merges&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Idempotent&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight mathematica"&gt;&lt;code&gt;&lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;b&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;a&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;b&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means you can merge the same CRDT multiple times but the result doesn't change&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Distributed&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight mathematica"&gt;&lt;code&gt;&lt;span class="o"&gt;???&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In all honesty this is here to make the acronym work 🤣&lt;/p&gt;

&lt;p&gt;The above properties are incredibly powerful and make the job of replication in AP systems far far simpler. It doesn't matter if you accidentally send the same CRDT twice, it doesn't matter if you send updates out of order, it doesn't matter if you merge client A's updates into client B's before merging client C's... The system will always reach the same deterministic result (Thats the whole eventually consistent thing I mentioned).&lt;/p&gt;

&lt;p&gt;Now you might be thinking, thats a lot of rules surely it's hard to create something that satisfies all those constraints. But honestly its simpler than you might think, I'm going to run you through the two Synk uses under the hood and you'll see what I mean.&lt;/p&gt;

&lt;p&gt;Alright first up my favourite, and arguably the simplest...&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Ladies and gentleman please welcome G-SET!!!&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1eb6exwylkz0zzluobjy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1eb6exwylkz0zzluobjy.png" alt="A Dalle-2 generated image of Ali G replicating himself" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A G-Set or Grow-only Set is simply a Set without the remove operation, meaning it grows and never shrinks. Lets have a look at its behaviour in psuedocode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;gset&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;GSet&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
&lt;span class="n"&gt;gset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"foo"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; 

&lt;span class="c1"&gt;// println(gset) &lt;/span&gt;
&lt;span class="c1"&gt;// [&lt;/span&gt;
&lt;span class="c1"&gt;//  “foo”,&lt;/span&gt;
&lt;span class="c1"&gt;// ]&lt;/span&gt;

&lt;span class="n"&gt;gset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"foo"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;gset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"bim"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;gset&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"bar"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// println(gset) &lt;/span&gt;
&lt;span class="c1"&gt;// [&lt;/span&gt;
&lt;span class="c1"&gt;//   “bar”,&lt;/span&gt;
&lt;span class="c1"&gt;//   “bim”,&lt;/span&gt;
&lt;span class="c1"&gt;//   “foo”,&lt;/span&gt;
&lt;span class="c1"&gt;// ]&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see from above it's associative and commutative because the set has some intrinsic ordering which makes it agnostic to insertion/grouping order. It's idempotent because being a set it disallows duplicates. A G-Set is ACID 2.0 compliant making it a verified CRDT... &lt;/p&gt;

&lt;p&gt;The elephant in the room at this point is that it doesn't seem particularly useful. But like all data structures in computer science CRDTs can and should be composed together to model larger structures. A G-set is a collection, so now we need the collection to hold something useful, another CRDT. &lt;/p&gt;

&lt;p&gt;Without further Ado I introduce LWW-Map&lt;/p&gt;

&lt;p&gt;It stands for "Last write wins map" and it's a key value map with a couple of stipulations. &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;The value has to be a tuple where one of the items is always a timestamp. On map insertion if the key already exists then the existing timestamp is compared with the new timestamp and the victor is the most recent.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;You can't delete entries, thats generally a recurring theme in CRDTs but well address that shortly. &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can get an idea of its behaviour from the following psuedocode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;map&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LWWMap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"foo"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Pair&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="nc"&gt;Ali&lt;/span&gt; &lt;span class="nc"&gt;G&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// println(map) &lt;/span&gt;
&lt;span class="c1"&gt;// {&lt;/span&gt;
&lt;span class="c1"&gt;//  “foo” : { “value”: “Ali G”, “ts”:123 },&lt;/span&gt;
&lt;span class="c1"&gt;// }&lt;/span&gt;

&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"foo"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Pair&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="nc"&gt;Ali&lt;/span&gt; &lt;span class="nc"&gt;G&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;da&lt;/span&gt; &lt;span class="n"&gt;best&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;234&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"foo"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Pair&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="nc"&gt;Ali&lt;/span&gt; &lt;span class="nc"&gt;G&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;da&lt;/span&gt; &lt;span class="n"&gt;best&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;234&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;map&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"foo"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Pair&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;“&lt;/span&gt;&lt;span class="nc"&gt;Ali&lt;/span&gt; &lt;span class="nc"&gt;G&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="n"&gt;not&lt;/span&gt; &lt;span class="n"&gt;da&lt;/span&gt; &lt;span class="n"&gt;best&lt;/span&gt;&lt;span class="err"&gt;”&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;112&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// println(map) &lt;/span&gt;
&lt;span class="c1"&gt;// {&lt;/span&gt;
&lt;span class="c1"&gt;//   “foo” : { “value”: “Ali G is da best”, “ts”:234}&lt;/span&gt;
&lt;span class="c1"&gt;// }&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Maps ultimately allows us to serialize structs/data classes, so now combining both a G-Set and an LWW Map we can model collections of classes.&lt;/p&gt;

&lt;p&gt;James Long took this observation further in his talk &lt;a href="https://www.youtube.com/watch?v=DEcwa68f-jY" rel="noopener noreferrer"&gt;CRDTs for mortals&lt;/a&gt;, and stated that we can actually make tables in databases behave like G-Sets of LWW-Maps with a few tweaks. Stay with me hear I appreciate this is a leap of faith but we'll run through it.&lt;/p&gt;

&lt;p&gt;Tables with primary keys can behave like G-Sets so long as we never remove entries, a table is a collection of rows after all. But what about modelling a row as an LWW Map? Let's take a look at an example&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;id&lt;/th&gt;
&lt;th&gt;fn&lt;/th&gt;
&lt;th&gt;ln&lt;/th&gt;
&lt;th&gt;age&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;bob&lt;/td&gt;
&lt;td&gt;smith&lt;/td&gt;
&lt;td&gt;35&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;jack&lt;/td&gt;
&lt;td&gt;jones&lt;/td&gt;
&lt;td&gt;28&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The rows in the table above couldn't possibly satisfy the conditions of an LWW-Map because they're missing the associated timestamp data. So let's add that&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;id&lt;/th&gt;
&lt;th&gt;fn&lt;/th&gt;
&lt;th&gt;fn_ts&lt;/th&gt;
&lt;th&gt;ln&lt;/th&gt;
&lt;th&gt;ln_ts&lt;/th&gt;
&lt;th&gt;age&lt;/th&gt;
&lt;th&gt;age_ts&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;bob&lt;/td&gt;
&lt;td&gt;12345&lt;/td&gt;
&lt;td&gt;smith&lt;/td&gt;
&lt;td&gt;12345&lt;/td&gt;
&lt;td&gt;35&lt;/td&gt;
&lt;td&gt;12345&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;jack&lt;/td&gt;
&lt;td&gt;12345&lt;/td&gt;
&lt;td&gt;jones&lt;/td&gt;
&lt;td&gt;12345&lt;/td&gt;
&lt;td&gt;28&lt;/td&gt;
&lt;td&gt;12345&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Notice the id in the table above is the only column without timestamp meta, this is because it functions as the key in the map and is immutable, immutable values cannot change and thus the meta isn't needed. For the rest, if we check the current timestamp on insertion and use this to determine the new value and timestamp then our row behaves as an LWW Map, turning our table into a CRDT.&lt;/p&gt;

&lt;p&gt;But I need to delete things? 😅&lt;/p&gt;

&lt;p&gt;I thought you'd say that, well I have a solution, its imperfect but it works.  &lt;/p&gt;

&lt;h3&gt;
  
  
  Tombstones
&lt;/h3&gt;

&lt;p&gt;Tombstones are also known as soft delete flags outside the realms of distributed systems. It's a reasonably simple concept, add a flag to signify whether a record has been deleted, use this same flag to ignore deleted records in subsequent queries.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;id&lt;/th&gt;
&lt;th&gt;fn&lt;/th&gt;
&lt;th&gt;fn_ts&lt;/th&gt;
&lt;th&gt;ln&lt;/th&gt;
&lt;th&gt;ln_ts&lt;/th&gt;
&lt;th&gt;age&lt;/th&gt;
&lt;th&gt;age_ts&lt;/th&gt;
&lt;th&gt;del&lt;/th&gt;
&lt;th&gt;del_ts&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;bob&lt;/td&gt;
&lt;td&gt;12345&lt;/td&gt;
&lt;td&gt;smith&lt;/td&gt;
&lt;td&gt;12345&lt;/td&gt;
&lt;td&gt;35&lt;/td&gt;
&lt;td&gt;12345&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;12345&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;jack&lt;/td&gt;
&lt;td&gt;12345&lt;/td&gt;
&lt;td&gt;jones&lt;/td&gt;
&lt;td&gt;12345&lt;/td&gt;
&lt;td&gt;28&lt;/td&gt;
&lt;td&gt;12345&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;12345&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;I've got 99 problems but a conflict ain't one&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;CRDTs are an awesome solution to a difficult problem, personally I find them to be the most natural solution in the  distributed systems domain, but I must admit I have problems with existing libraries that leverage them. &lt;/p&gt;

&lt;p&gt;CRDTs are usually implemented in two ways, either at the domain level, which requires objects to implement some mergeable interface, or at the data level, in the form of a very bespoke (often not general purpose) datastore. The common denominator an intrusive piece of technology which has a large impact on the shape and design of the application you are creating.&lt;/p&gt;

&lt;p&gt;What if this wasn't the case? What if we could have conflict resolution as a pluggable piece of software? Something we could bootstrap existing systems with to give them distributed powers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Synk
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;A Kotlin Multiplatform CRDT library for local first applications&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Synk is a state based CRDT library, it monitors state changes over time using a special type of &lt;a href="https://github.com/charlieTap/hlc" rel="noopener noreferrer"&gt;timestamp&lt;/a&gt; which is capable of tracking events in a distributed system. Synk maintains this data in its own persistent key value storage database locally on each client, &lt;/p&gt;

&lt;p&gt;It's important to understand Synk does not store your data, merely it stores timestamps associated with it. Remember that timestamp data we added to the table? Well Synk tracks that separately, leaving your data layer entirely untouched, allowing you to build apps with the same technologies you always have.&lt;/p&gt;

&lt;p&gt;At its core Synk is comprised of just two functions!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nc"&gt;Synk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;outbound&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;old&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;Whenever a new record/object is created or updated in your application locally, give Synk the latest version and the old version (if applicable) and Synk will return you a message. This message needs to be propagated to all other clients.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nc"&gt;Synk&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;inbound&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Message&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt; &lt;span class="n"&gt;old&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;T&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;When receiving a Message from another client application, inbound needs to be called. This function will perform conflict resolution for you and return an instance of your object ready to be persisted.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Synk is deliberately minimal, you give it objects and it gives you messages, propagate those messages to the right clients and your application will have no conflicts, it's that easy!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;A distributed database... but you bring the database&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I'm excited to see what people come up with using this technology, the innovation in the CRDT space right now is crazy. Ultimately I had an idea with this project of creating a minimal library which gives the tools to turn a local database into a distributed one, all whilst exposing some but not all of the distributed primitives. The result is something quite novel, I hope the idea of using CRDTs to resolve your conflicts has piqued your interest.&lt;/p&gt;

&lt;p&gt;There's a bunch of detail I chose to omit in this blog, more for brevity than anything else. However theres a lot more information on the repository if you're interested! I've learned so much building out this project, I hope this blog has taught you a thing or two yourself, if you have any questions feel free to comment.&lt;/p&gt;

&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;A huge inspiration in all the above was James Longs presentation &lt;a href="https://www.youtube.com/watch?v=DEcwa68f-jY" rel="noopener noreferrer"&gt;CRDTs for mortals&lt;/a&gt;, if you haven't seen it already go and watch its awesome&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;All images were generated by Dalle-2, you can see the prompts I used below&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Image&lt;/th&gt;
&lt;th&gt;Prompt&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fityoe5jt3av5tsl8rdf7.png" width="800" height="800"&gt;&lt;/td&gt;
&lt;td&gt;“An oil painting of 4 databases trying really hard to synchronise with each other”&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7iuedxigj9zfaeudhekf.png" width="800" height="800"&gt;&lt;/td&gt;
&lt;td&gt;“An oil painting of a monkey attempting to solve the most complex puzzle imaginable”&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1eb6exwylkz0zzluobjy.png" width="800" height="800"&gt;&lt;/td&gt;
&lt;td&gt;“Ali G replicating himself into multiple people as an oil painting”&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>distributedsystems</category>
      <category>localfirstdev</category>
      <category>kotlin</category>
      <category>android</category>
    </item>
    <item>
      <title>Making PR checks pretty by example</title>
      <dc:creator>CharlieTap</dc:creator>
      <pubDate>Mon, 14 Feb 2022 21:08:30 +0000</pubDate>
      <link>https://dev.to/charlietap/making-pr-checks-pretty-by-example-4fej</link>
      <guid>https://dev.to/charlietap/making-pr-checks-pretty-by-example-4fej</guid>
      <description>&lt;p&gt;If you've worked on a project with any form of continuous integration before, then you'll likely be familiar with the concept of checks that run when you submit your code to version control. &lt;/p&gt;

&lt;p&gt;For those that aren't aware, typically tasks are run in response to submission/updates of code, these tasks can run tests, linting checks and a plethora of other code verifications. In most setups, branches are usually protected by a pull review process, which runs checks on proposed code changes before they are reviewed and potentially merged into the codebase.&lt;/p&gt;

&lt;p&gt;In this post I'm going to walk you through setting up a simple continuous integration task using GitHub Actions, which runs the popular Ktlint library and adds the output back onto the Pull Request in a way that's easy to digest using a tool called ReviewDog.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ktlint meets Gradle
&lt;/h2&gt;




&lt;p&gt;Whilst it's possible to run Ktlint from CLI, I prefer to introduce it in the form of a Gradle task. Providing it in this way allows all the configuration (albeit none in this case) to rest within the project itself, guaranteeing you and your teammates will have the same output when running it. Another benefit is that you can hook this task into the existing android linting task, just saving you one less thing to run in your CLI.&lt;/p&gt;

&lt;p&gt;My task of choice:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/jeremymailen/kotlinter-gradle" rel="noopener noreferrer"&gt;kotlinter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This particular plugin is very performant, it uses gradles incremental build system, and parallelises the linting work using the Gradle Worker API, making it very fast if you have a multi module workspace. Ultimately these checks are run often, so saving 10-15 seconds adds up over the course of a year.&lt;/p&gt;

&lt;p&gt;I won't cover the setup here, you can follow the link above or even checkout my example application to support this blog post which is found at the bottom.&lt;/p&gt;

&lt;p&gt;Once you think you are setup, run the following task to verify:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;gradle lintKotlin&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If you have any errors appear, there exists another task which may be able to automatically correct them for you:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;gradle formatKotlin&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Be aware there are certain things this task will be unable to fix for you and therefore you'll have to rectify them yourself using the feedback you get.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making this task run on Pull Requests
&lt;/h2&gt;




&lt;p&gt;To run any workflow in GitHub Actions you simply place a yaml file in the folder .github/workflows relative to the project root with the instructions you want to run.&lt;/p&gt;

&lt;p&gt;For example we could put the following:&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;&lt;code&gt;.github/workflows/code-quality.yml&lt;/code&gt;&lt;/strong&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Code Quality&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ktlint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run ktlint&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Clone repo&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@master&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;fetch-depth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Ktlint&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gradlew lintKotlin&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This would suffice, in running the linter and providing a check on PR which would validate the code. However we want to do something which produces more meaningful feedback like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1bf5z8b51nw5t78nv6mi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1bf5z8b51nw5t78nv6mi.png" alt=" " width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;...&lt;/p&gt;

&lt;h2&gt;
  
  
  The pretty part ...
&lt;/h2&gt;




&lt;p&gt;So we know how to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Run ktlint with Gradle&lt;/li&gt;
&lt;li&gt;Run the Gradle task from CI&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now we need to figure out how to use review dog to comment on the pull request with the results of the gradle lint task.&lt;/p&gt;

&lt;p&gt;So it's important to understand the output of that linting task. By default, kotlinter will create report files, one for each module you run it on. You'll find these reports inside the modules generated build folder (i.e if you run it on app, you'll find the report in app/build/...). Whilst its possible to aggregate these files and pass their contents onto review dog, it also writes any errors it finds to stderr. With a little bit shell scripting you can tailor this output ready to be piped into ReviewDog and save yourself the task of aggregating all of the reports.&lt;/p&gt;

&lt;p&gt;So lets look at the shape we need the listing report to be in for review dog to accept it:&lt;/p&gt;

&lt;p&gt;"reviewdog accepts any compiler or linter result from stdin and parses it with scan-f like 'errorformat', which is the port of Vim's errorformat feature."&lt;/p&gt;

&lt;p&gt;In simple terms, each line we feed into standard input of review dog must have a particular format, we can dictate that format to some extent, but ultimately we have to conform to errorformat.&lt;/p&gt;

&lt;p&gt;For example if we tell review dog through the command line argument "efm" to expect the format:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;%f:%l:%c:%t: %m&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Review dog will the expect each line to be in the following format:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;{file}:{line number}:{column number}:{error type}: {message}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Thankfully the stderr from the lintKotlin task is largely already in this form. &lt;/p&gt;

&lt;p&gt;We just need to filter for the lines we care about&lt;/p&gt;

&lt;p&gt;&lt;code&gt;grep 'Lint error &amp;gt; '&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Tweak them to replace the text 'Lint error &amp;gt; ' with the error type which in this case can simply be 'e' for error. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;sed 's/ Lint error &amp;gt; /e: /g'&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You can read more about the types and meanings of the format substitutions &lt;a href="https://vim-jp.org/vimdoc-en/quickfix.html#error-file-format" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Et Voila&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;&lt;code&gt;.github/scripts/check.sh&lt;/code&gt;&lt;/strong&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;

./gradlew lintKotlin 2&amp;gt;&amp;amp;1 | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s1"&gt;'Lint error &amp;gt; '&lt;/span&gt; | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s/ Lint error &amp;gt; /e: /g'&lt;/span&gt; | reviewdog  &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"ktlint"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-reporter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;github-pr-review &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-efm&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"%f:%l:%c:%t: %m"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-level&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;error &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-filter-mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;nofilter &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-fail-on-error&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Something to be aware of here, if you want scripts to be executable when they are run inside of CI, you'll need to instruct git of the executable permission. I.e git update-index --chmod=+x .github/scripts/check.sh&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting it all together
&lt;/h2&gt;




&lt;p&gt;Lets update the yaml file from earlier to look like the following:&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;&lt;code&gt;.github/workflows/code-quality.yml&lt;/code&gt;&lt;/strong&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Code Quality&lt;/span&gt;
&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ktlint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run ktlint&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;

    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Clone repo&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@master&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;fetch-depth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install Review Dog&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;reviewdog/action-setup@v1&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;reviewdog_version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;latest&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Ktlint&lt;/span&gt;
        &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;REVIEWDOG_GITHUB_API_TOKEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.github_token }}&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;.github/scripts/check.sh&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without going into too much detail, we added a new job to install review dog, and another to run our bash script we wrote earlier (You don't have to configure the secret, this secret is included for you).&lt;/p&gt;

&lt;p&gt;I've put an example project &lt;a href="https://github.com/CharlieTap/ktlint-gradle-actions" rel="noopener noreferrer"&gt;here&lt;/a&gt; for anyone that gets stuck and wants to copy this across, you'll also see the PR mentioned in the post on this repo.&lt;/p&gt;

</description>
      <category>android</category>
      <category>devops</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Gradle versions catalog integration</title>
      <dc:creator>CharlieTap</dc:creator>
      <pubDate>Fri, 28 Jan 2022 18:57:05 +0000</pubDate>
      <link>https://dev.to/charlietap/gradle-versions-catalog-integration-27l0</link>
      <guid>https://dev.to/charlietap/gradle-versions-catalog-integration-27l0</guid>
      <description>&lt;p&gt;This post is a continuation from another, I'd advise reading that prior to tucking it this. You can find it &lt;a href="https://dev.to/charlietap/upgrading-to-gradles-modern-plugin-configuration-5h40"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A versions what?
&lt;/h2&gt;




&lt;p&gt;Gradles versions catalog is new feature in modern versions of Gradle that allows you to centralise your projects dependencies, plugins and their respective versions into a single location. (though its possible to have more than one).&lt;/p&gt;

&lt;p&gt;If you've worked with projects that have more than one module, you'll know the pain of trying to keep all of your dependencies in alignment across all of the modules. The solution prior to the versions catalog introduction, was to create files inside the special Gradle buildSrc folder which could then be referenced in your build.gradle files. Whilst there's nothing terribly wrong with this approach, Gradle advocates for the versions catalog, and it has some additional benefits which you can leverage in certain conditions.&lt;/p&gt;

&lt;p&gt;For example, imagine you work in a large team, on a big project, and you have several repositories which are essentially modules for the same application. Rather than having different catalogs in each repository, you could publish the catalog to a repository like Maven and consume it in each repository, ensuring that even separate repos have their dependencies aligned. You can read more about this &lt;a href="https://docs.gradle.org/current/userguide/platforms.html#sec:version-catalog-plugin" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Okay enough spiel, lets see it what it looks like
&lt;/h2&gt;




&lt;p&gt;The catalog is written in a config language called toml, I've put a cut down implementation below just to showcase the sections.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;&lt;code&gt;build.gradle&lt;/code&gt;&lt;/strong&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[versions]&lt;/span&gt;

&lt;span class="py"&gt;compileSdk&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"31"&lt;/span&gt;
&lt;span class="py"&gt;minSdk&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"23"&lt;/span&gt;
&lt;span class="py"&gt;targetSdk&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"31"&lt;/span&gt;

&lt;span class="py"&gt;appVersionCode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1"&lt;/span&gt;
&lt;span class="py"&gt;appVersionName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.0"&lt;/span&gt;
&lt;span class="py"&gt;applicationId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"com.x.y"&lt;/span&gt;

&lt;span class="py"&gt;kotlin&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.5.31"&lt;/span&gt;
&lt;span class="py"&gt;android-build-tools-plugin&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"7.1.0"&lt;/span&gt;

&lt;span class="py"&gt;hilt-core&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"2.40.5"&lt;/span&gt;
&lt;span class="py"&gt;hilt-integrations&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.0.0"&lt;/span&gt;

&lt;span class="nn"&gt;[plugins]&lt;/span&gt;

&lt;span class="py"&gt;plugin-kotlin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"org.jetbrains.kotlin.jvm"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;version.ref&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"kotlin"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="py"&gt;plugin-kotlin-android&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"org.jetbrains.kotlin.android"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;version.ref&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"kotlin"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="py"&gt;plugin-android&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"com.android.application"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;version.ref&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"android-build-tools-plugin"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="py"&gt;plugin-android-lib&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"com.android.library"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;version.ref&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"android-build-tools-plugin"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nn"&gt;[libraries]&lt;/span&gt;

&lt;span class="py"&gt;hilt-core&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;module&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"com.google.dagger:hilt-android"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;version.ref&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"hilt-core"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="py"&gt;hilt-compiler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;module&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"com.google.dagger:hilt-compiler"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;version.ref&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"hilt-core"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="py"&gt;hilt-integrations-work&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;module&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"androidx.hilt:hilt-work"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;version.ref&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"hilt-integrations"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="py"&gt;hilt-integrations-compiler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;module&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"androidx.hilt:hilt-compiler"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;version.ref&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"hilt-integrations"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="py"&gt;hilt-integrations-compose&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;module&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"androidx.hilt:hilt-navigation-compose"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;version.ref&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"hilt-compose"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="nn"&gt;[bundles]&lt;/span&gt;

&lt;span class="py"&gt;hilt-deps&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s"&gt;"hilt-core"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"hilt-integrations-work"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"hilt-integrations-compose"&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="py"&gt;hilt-compilers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="s"&gt;"hilt-compiler"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"hilt-integrations-compiler"&lt;/span&gt; &lt;span class="p"&gt;]&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Thats... different
&lt;/h2&gt;




&lt;p&gt;It is, but at a high level its very simple, there's four sections:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A section for declaring versions&lt;/li&gt;
&lt;li&gt;A section for declaring plugins&lt;/li&gt;
&lt;li&gt;A section for declaring libraries &lt;/li&gt;
&lt;li&gt;A section for declaring bundles (basically a logical grouping of libraries)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You place this file in your projects top level gradle folder and you should be ready to use it.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to reference the different sections
&lt;/h2&gt;




&lt;h3&gt;
  
  
  For plugins
&lt;/h3&gt;

&lt;p&gt;You can reference them when loading them into the classpath as discussed in the prior article.&lt;/p&gt;

&lt;p&gt;That looks like this:&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;&lt;code&gt;build.gradle&lt;/code&gt;&lt;/strong&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;plugins&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;alias&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;libs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;plugin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;kotlin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;android&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;apply&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember the apply false, you don't want to apply it here, it needs to be applied in each module you need it in respectively.&lt;/p&gt;

&lt;p&gt;Applying inside the module using the alias notation requires Gradle 7.4 which is currently at rc-01, if you're willing to upgrade early you can do this.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;&lt;code&gt;app/build.gradle&lt;/code&gt;&lt;/strong&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;plugins&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;alias&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;libs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;plugin&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;android&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; 
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The default is to apply, thus when not putting apply at all it will apply the plugin on that module.&lt;/p&gt;

&lt;p&gt;If you are not in a position to upgrade and for Gradle versions &amp;lt; 7.4, you can do this:&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;&lt;code&gt;app/build.gradle&lt;/code&gt;&lt;/strong&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;plugins&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="s2"&gt;"org.jetbrains.kotlin.android"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  For versions
&lt;/h3&gt;

&lt;p&gt;Individual versions can be referenced in your build.gradle files, it allows you to do things like this:&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;&lt;code&gt;app/build.gradle&lt;/code&gt;&lt;/strong&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;android&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="n"&gt;compileSdkVersion&lt;/span&gt; &lt;span class="n"&gt;libs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;versions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;compileSdk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;Integer&lt;/span&gt;

    &lt;span class="n"&gt;defaultConfig&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;applicationId&lt;/span&gt; &lt;span class="n"&gt;libs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;versions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;applicationId&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;minSdkVersion&lt;/span&gt; &lt;span class="n"&gt;libs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;versions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;minSdk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;Integer&lt;/span&gt;
        &lt;span class="n"&gt;targetSdkVersion&lt;/span&gt; &lt;span class="n"&gt;libs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;versions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;targetSdk&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;Integer&lt;/span&gt;
        &lt;span class="n"&gt;versionName&lt;/span&gt; &lt;span class="n"&gt;libs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;versions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;appVersionName&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;versionCode&lt;/span&gt; &lt;span class="n"&gt;libs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;versions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;appVersionCode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;Integer&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  For libraries
&lt;/h3&gt;

&lt;p&gt;For libraries its equally simple&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;&lt;code&gt;app/build.gradle&lt;/code&gt;&lt;/strong&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="n"&gt;libs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;material&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  For bundles
&lt;/h3&gt;

&lt;p&gt;Bundles provide a great way to group related dependencies and save you some line space, similarly to bundles they can be implemented like so:&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;&lt;code&gt;app/build.gradle&lt;/code&gt;&lt;/strong&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="n"&gt;libs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;bundles&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hilt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;deps&lt;/span&gt;
    &lt;span class="n"&gt;kapt&lt;/span&gt; &lt;span class="n"&gt;libs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;bundles&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hilt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;compilers&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Fin
&lt;/h2&gt;

&lt;p&gt;Next up I'll be looking at tidying up Gradle files using precompiled Gradle script plugins..&lt;/p&gt;

</description>
      <category>android</category>
      <category>gradle</category>
      <category>kotlin</category>
    </item>
    <item>
      <title>Upgrading to Gradles modern plugin configuration</title>
      <dc:creator>CharlieTap</dc:creator>
      <pubDate>Wed, 26 Jan 2022 21:40:37 +0000</pubDate>
      <link>https://dev.to/charlietap/upgrading-to-gradles-modern-plugin-configuration-5h40</link>
      <guid>https://dev.to/charlietap/upgrading-to-gradles-modern-plugin-configuration-5h40</guid>
      <description>&lt;p&gt;Android studio Bumblebee became stable this week and with it comes a bunch of new features and updates. One of which being that new projects created in Bumblebee have a modernised Gradle configuration. You can read about this &lt;a href="https://developer.android.com/studio/releases/gradle-plugin#settings-gradle" rel="noopener noreferrer"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Whilst this is great for new projects, it's important to keep legacy projects up to date, and migrating from old to new is great way to understand the new approach (I say "new" here, but the syntax has in fact been around for quite some now, it just hadn't been widely adopted).&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Upgrade?
&lt;/h3&gt;

&lt;p&gt;A quick scan of the Gradle docs goes on to explain the following:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgha5pckm4c2b0h2albci.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgha5pckm4c2b0h2albci.png" alt=" " width="800" height="264"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Alongside this, generally its good to keep your configuration compliant with the latest version of Gradle, it eases the migration path when moving between versions.&lt;/p&gt;

&lt;h3&gt;
  
  
  How did we used to do it again?
&lt;/h3&gt;

&lt;p&gt;Historically Android projects would use the buildscript DSL to load load plugins into the classpath, you would do this is your top level build.gradle file.&lt;/p&gt;

&lt;p&gt;It should look something like this:&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;&lt;code&gt;build.gradle&lt;/code&gt;&lt;/strong&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;buildscript&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="n"&gt;repositories&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;google&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;gradlePluginPortal&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;classpath&lt;/span&gt; &lt;span class="s1"&gt;'com.android.tools.build:gradle:x.y.z'&lt;/span&gt;
        &lt;span class="n"&gt;classpath&lt;/span&gt; &lt;span class="s2"&gt;"org.jetbrains.kotlin:kotlin-gradle-pluginx.y.z"&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Theres two things happening inside this block:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;It's declaring a set of repositories, these are repositories which will be searched when Gradle looks for the plugins you have declared. This is similar to the list of repositories you declare when you are configuration your dependencies, however these repositories will only be searched when Gradle looks for plugins.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It's declaring a set of plugins you wish to use in your project and loading a particular version for each into the classpath. &lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Applying the plugins in your application&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Inside your :app modules build.gradle you would use the now legacy &lt;em&gt;apply plugin&lt;/em&gt; syntax to enable the plugins on that particular module.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;&lt;code&gt;app/build.gradle&lt;/code&gt;&lt;/strong&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;
&lt;span class="n"&gt;apply&lt;/span&gt; &lt;span class="nl"&gt;plugin:&lt;/span&gt; &lt;span class="s1"&gt;'com.android.application'&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you've ever worked on projects that have more than one Gradle module you realise the benefits of the separation above as you'll be able to enable plugins on a module by module basis, and lock the plugin versions to a consistent version across all of your modules.&lt;/p&gt;

&lt;h3&gt;
  
  
  Okay but what does it look like now
&lt;/h3&gt;

&lt;p&gt;Well, the part where you declared the repositories for the plugins... that's moved, its now in your top level settings.gradle file&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;&lt;code&gt;settings.gradle&lt;/code&gt;&lt;/strong&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;
&lt;span class="n"&gt;pluginManagement&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;repositories&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;gradlePluginPortal&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;google&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;mavenCentral&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This makes more sense when you realise the repositories for your dependencies are also now moved the settings.gradle file&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;&lt;code&gt;settings.gradle&lt;/code&gt;&lt;/strong&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;
&lt;span class="n"&gt;dependencyResolutionManagement&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;repositoriesMode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;set&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RepositoriesMode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;FAIL_ON_PROJECT_REPOS&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;repositories&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;google&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;mavenCentral&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;maven&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="s1"&gt;'https://jitpack.io'&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Loading the plugins&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Loading the plugins (but not applying them), still takes place in the top level build.gradle file&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;&lt;code&gt;build.gradle&lt;/code&gt;&lt;/strong&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;plugins&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="s1"&gt;'com.android.application'&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="s1"&gt;'x.y.z'&lt;/span&gt; &lt;span class="n"&gt;apply&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="s1"&gt;'org.jetbrains.kotlin.android'&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="s1"&gt;'x.y.z'&lt;/span&gt; &lt;span class="n"&gt;apply&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Enabling the plugins&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This takes place in whatever module you want to apply the plugin on, for example:&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;&lt;code&gt;app/build.gradle&lt;/code&gt;&lt;/strong&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;
&lt;span class="n"&gt;plugins&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'com.android.application'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'org.jetbrains.kotlin.android'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Thats all?
&lt;/h3&gt;

&lt;p&gt;Well as with everything new, there's some gotchas here to be aware of. The most important of which is that not all plugins will work the new syntax.&lt;/p&gt;

&lt;p&gt;Notice how before you would say something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;apply&lt;/span&gt; &lt;span class="nl"&gt;plugin:&lt;/span&gt; &lt;span class="s1"&gt;'kotlin-android'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and now you say something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'org.jetbrains.kotlin.android'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The difference is the latter uses a particular id, and only plugins which are configured with &lt;a href="https://docs.gradle.org/current/userguide/plugins.html#sec:plugin_markers" rel="noopener noreferrer"&gt;Gradle Project Market Artifact&lt;/a&gt; will work out of the box with this syntax.&lt;/p&gt;

&lt;p&gt;In particular I have found one popular plugin at this time which is not compliant and that is the Dagger Hilt Plugin. &lt;br&gt;
You can track the support for it &lt;a href="https://github.com/google/dagger/issues/2774" rel="noopener noreferrer"&gt;here&lt;/a&gt;, it's quite likely by the time you read this post that it will already have been released.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is there any point if not all plugins work
&lt;/h3&gt;

&lt;p&gt;Well there's a workaround, and for the plugins you do configure, you'll theoretically optimise your build time by adopting those.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Workaround&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We'll use the case of Dagger as I imagine this to be the most common. For future proofing I have put the future syntax, commented out in the examples below.&lt;/p&gt;

&lt;p&gt;Put the following in your top level build.gradle&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;&lt;code&gt;build.gradle&lt;/code&gt;&lt;/strong&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;
&lt;span class="c1"&gt;// it will look like this once Dagger publishes the marker&lt;/span&gt;
&lt;span class="c1"&gt;// id 'com.google.dagger.hilt.android' version 'x.y.z' apply false&lt;/span&gt;

&lt;span class="c1"&gt;// for now&lt;/span&gt;
&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"dagger.hilt.android.plugin"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;apply&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then in your settings.gradle the following:&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;&lt;code&gt;settings.gradle&lt;/code&gt;&lt;/strong&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;
&lt;span class="n"&gt;pluginManagement&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;resolutionStrategy&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;eachPlugin&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt; &lt;span class="n"&gt;requested&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="s1"&gt;'dagger.hilt.android.plugin'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;useModule&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"com.google.dagger:hilt-android-gradle-plugin:x.y.z"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt; &lt;span class="n"&gt;management&lt;/span&gt; &lt;span class="n"&gt;config&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally apply the plugin in your app modules build.gradle&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;&lt;code&gt;app/build.gradle&lt;/code&gt;&lt;/strong&gt;
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;
&lt;span class="n"&gt;plugins&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="c1"&gt;//  id 'com.google.dagger.hilt.android' in the future&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="s1"&gt;'dagger.hilt.android.plugin'&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above solution should work for any legacy Gradle plugin which lacks the marker you need. &lt;/p&gt;

&lt;h3&gt;
  
  
  Cool, so anything else?
&lt;/h3&gt;

&lt;p&gt;This is the first in a three post series, the following two will cover:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Integration of the new plugin system and Gradles' new version catalog system, you can find it &lt;a href="https://dev.to/charlietap/gradle-versions-catalog-integration-27l0"&gt;here&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Creating your own pre compiled script plugins for tidying up your build.gradle config and give an example showcasing config for the popular the popular &lt;a href="https://github.com/ben-manes/gradle-versions-plugin" rel="noopener noreferrer"&gt;versions gradle plugin&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>android</category>
      <category>kotlin</category>
      <category>gradle</category>
    </item>
  </channel>
</rss>
