<?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: Shai Almog</title>
    <description>The latest articles on DEV Community by Shai Almog (@codenameone).</description>
    <link>https://dev.to/codenameone</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F417973%2Fa7b22dd9-5565-48f5-bfab-7e5035b3888f.png</url>
      <title>DEV Community: Shai Almog</title>
      <link>https://dev.to/codenameone</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/codenameone"/>
    <language>en</language>
    <item>
      <title>Smaller Simpler Maven Projects</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Thu, 22 Oct 2026 17:57:54 +0000</pubDate>
      <link>https://dev.to/codenameone/smaller-simpler-maven-projects-4ca5</link>
      <guid>https://dev.to/codenameone/smaller-simpler-maven-projects-4ca5</guid>
      <description>&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3pv7v3woi0q42qnuclsi.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3pv7v3woi0q42qnuclsi.jpg" alt="A smaller Maven project grows platform modules as needed" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Opening a new application should show you the application. A directory full of platform modules makes you learn the build's structure before you have written a screen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.codenameone.com/blog/gradle-smaller-projects/" rel="noopener noreferrer"&gt;Last week we added smaller Gradle projects&lt;/a&gt;. Maven now starts small too. The application archetype defaults to the root POM and &lt;code&gt;common&lt;/code&gt;, with no platform modules. You can still run the simulator and build the target apps. This closes our &lt;a href="https://www.codenameone.com/blog/android-apps-beyond-android/" rel="noopener noreferrer"&gt;weekly release series&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The default tree matches the first task
&lt;/h2&gt;

&lt;p&gt;The relevant part of a new project looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-app/
    pom.xml
    common/
        pom.xml
        codenameone_settings.properties
        src/main/java/
        src/main/resources/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a shortened tree, not a count of every generated resource or launcher script. The structural count is &lt;strong&gt;two POMs&lt;/strong&gt;. Source, CSS and resources remain where an existing Maven user expects them.&lt;/p&gt;

&lt;p&gt;When a platform has no module, &lt;code&gt;common&lt;/code&gt; handles its build. These commands keep their meaning:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mvn cn1:run
mvn package &lt;span class="nt"&gt;-Dcodename1&lt;/span&gt;.platform&lt;span class="o"&gt;=&lt;/span&gt;android
mvn package &lt;span class="nt"&gt;-Pexecutable-jar&lt;/span&gt; &lt;span class="nt"&gt;-Dcodename1&lt;/span&gt;.platform&lt;span class="o"&gt;=&lt;/span&gt;javase
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The desktop executable JAR goes under &lt;code&gt;common/target&lt;/code&gt; in this layout. Platform modules become useful when a target needs independent dependencies or plugins, rather than because every possible target needs a directory on day one.&lt;/p&gt;

&lt;h2&gt;
  
  
  A native implementation does not need another POM
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;mvn cn1:generate-native-interfaces&lt;/code&gt; still creates platform-specific source directories. An Android implementation can live under &lt;code&gt;android/src/main/java&lt;/code&gt;, and an iOS implementation under &lt;code&gt;ios/src/main/objectivec&lt;/code&gt;, without either directory becoming a Maven module.&lt;/p&gt;

&lt;p&gt;The build treats a platform as a separate module when &lt;code&gt;&amp;lt;platform&amp;gt;/pom.xml&lt;/code&gt; exists. Native source directories alone do not make that decision. JavaSE implementations are compiled into &lt;code&gt;common/target/cn1-javase/classes&lt;/code&gt;, separate from application classes, so they are not accidentally uploaded into a device build.&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%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgICBDb21tb25bQ29tbW9uIGFwcGxpY2F0aW9uIG1vZHVsZV0gLS0-IEFuZHJvaWRbQW5kcm9pZCBidWlsZF0KICAgIENvbW1vbiAtLT4gSU9TW2lPUyBidWlsZF0KICAgIENvbW1vbiAtLT4gRGVza3RvcFtEZXNrdG9wIGJ1aWxkXQogICAgTmF0aXZlW05hdGl2ZSBzb3VyY2UgZGlyZWN0b3JpZXNdIC0tPiBDb21tb24KICAgIFNlcGFyYXRlW09wdGlvbmFsIHBsYXRmb3JtIFBPTV0gLS0-IEN1c3RvbVtTZXBhcmF0ZSBwbGF0Zm9ybSBjb25maWd1cmF0aW9uXQ%3D%3D%3Ftype%3Dpng%26bgColor%3Dffffff" 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%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgICBDb21tb25bQ29tbW9uIGFwcGxpY2F0aW9uIG1vZHVsZV0gLS0-IEFuZHJvaWRbQW5kcm9pZCBidWlsZF0KICAgIENvbW1vbiAtLT4gSU9TW2lPUyBidWlsZF0KICAgIENvbW1vbiAtLT4gRGVza3RvcFtEZXNrdG9wIGJ1aWxkXQogICAgTmF0aXZlW05hdGl2ZSBzb3VyY2UgZGlyZWN0b3JpZXNdIC0tPiBDb21tb24KICAgIFNlcGFyYXRlW09wdGlvbmFsIHBsYXRmb3JtIFBPTV0gLS0-IEN1c3RvbVtTZXBhcmF0ZSBwbGF0Zm9ybSBjb25maWd1cmF0aW9uXQ%3D%3D%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="769" height="302"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This also keeps the Android migration introduced on Friday approachable. Imported Android sources live under &lt;code&gt;common/src/main/android&lt;/code&gt;; your initial port need not begin by learning every target module's POM.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ask for the structure you need
&lt;/h2&gt;

&lt;p&gt;The archetype's &lt;code&gt;platformModules&lt;/code&gt; property defaults to &lt;code&gt;none&lt;/code&gt;. &lt;code&gt;all&lt;/code&gt; restores the full platform layout, and a comma-separated value such as &lt;code&gt;javase,android&lt;/code&gt; creates only those modules. Its &lt;code&gt;projectType&lt;/code&gt; defaults to &lt;code&gt;app&lt;/code&gt;; &lt;code&gt;app-with-backend&lt;/code&gt; adds the server, and &lt;code&gt;backend-only&lt;/code&gt; creates a standalone server project.&lt;/p&gt;

&lt;p&gt;For example, these are &lt;strong&gt;options to append to your normal archetype generation command&lt;/strong&gt;, not commands on their own:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-DplatformModules=javase,android
-DprojectType=app-with-backend
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A backend-only project has its POM and &lt;code&gt;src/main/java&lt;/code&gt; at the root. It needs no empty client beside it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./mvnw cn1:backend
./mvnw cn1:backend-package
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The minimal default applies to newly generated projects using the updated archetype. It does not delete modules from an existing checkout. Before simplifying an older project, check for platform-specific build configuration rather than assuming its POMs are empty scaffolding.&lt;/p&gt;

&lt;h2&gt;
  
  
  Less structure to explain and inspect
&lt;/h2&gt;

&lt;p&gt;I still prefer Maven's explicit XML for build configuration. I also prefer having less of it to read. Those preferences can coexist.&lt;/p&gt;

&lt;p&gt;A smaller project tree helps a new contributor find the code that matters. It also leaves fewer generated configuration files for an LLM to misread when asked to change one screen. That is a reduction in surrounding material, not a guarantee that generated code is correct. Review the diff and run the relevant application behavior just as before.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/codenameone/CodenameOne/pull/5937" rel="noopener noreferrer"&gt;PR #5937&lt;/a&gt; introduced these project shapes. The current &lt;a href="https://www.codenameone.com/developer-guide/#maven-minimal-layout" rel="noopener noreferrer"&gt;archetype reference&lt;/a&gt; defines how target selection and native directories work. Start with the default, then add a platform module when you have a concrete setting to put in it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discussion
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;How many of your platform POMs contain configuration you actually maintain?&lt;/em&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>PGO for Faster Apps and Generational/Adaptive GC</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Wed, 21 Oct 2026 18:01:09 +0000</pubDate>
      <link>https://dev.to/codenameone/pgo-for-faster-apps-and-generationaladaptive-gc-122e</link>
      <guid>https://dev.to/codenameone/pgo-for-faster-apps-and-generationaladaptive-gc-122e</guid>
      <description>&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwgzcsgl3lu1w3embar88.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwgzcsgl3lu1w3embar88.jpg" alt="Profile-guided builds and adaptive collection reduce native application work" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your native compiler can optimize an app more effectively when it knows which code actually runs. Profile-guided optimization, or PGO, gives it that information. The build service runs an instrumented version of your application, records its execution and uses the profile to compile the binary your users receive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PGO is now a Pro and Enterprise cloud-build feature for iOS, native macOS and native Linux.&lt;/strong&gt; You enable it with a build hint; the service handles the extra compilation and training. It is a way to improve native performance without rewriting application logic or managing a profiling pipeline yourself.&lt;/p&gt;

&lt;p&gt;This release also adds adaptive generational garbage collection to ParparVM. We will start with PGO and how to use it, then look at the GC changes and the memory cost behind their CPU improvements. This continues our &lt;a href="https://www.codenameone.com/blog/android-apps-beyond-android/" rel="noopener noreferrer"&gt;weekly release series&lt;/a&gt; and &lt;a href="https://www.codenameone.com/blog/parparvm-four-byte-header/" rel="noopener noreferrer"&gt;last week's object-layout work&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let your app guide the compiler
&lt;/h2&gt;

&lt;p&gt;An ordinary optimizing compiler has to estimate which branches are common and which functions are worth inlining. PGO replaces some of those estimates with counts from an actual run. Frequently executed code can be placed together and optimized for speed, while code outside those paths can stay compact.&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%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgICBTb3VyY2VbQXBwbGljYXRpb24gYW5kIHJ1bnRpbWUgY29kZV0gLS0-IEluc3RydW1lbnRbQ29tcGlsZSB3aXRoIGNvdW50ZXJzXQogICAgSW5zdHJ1bWVudCAtLT4gVHJhaW5bUnVuIHRoZSBhcHAgb24gdGhlIGJ1aWxkIHNlcnZlcl0KICAgIFRyYWluIC0tPiBQcm9maWxlW0Z1bmN0aW9uIGFuZCBicmFuY2ggY291bnRzXQogICAgUHJvZmlsZSAtLT4gQ29tcGlsZVtDb21waWxlIGFnYWluIHVzaW5nIHRoZSBwcm9maWxlXQogICAgQ29tcGlsZSAtLT4gRGVsaXZlcltEZWxpdmVyIG9wdGltaXplZCBuYXRpdmUgYXBwXQ%3D%3D%3Ftype%3Dpng%26bgColor%3Dffffff" 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%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgICBTb3VyY2VbQXBwbGljYXRpb24gYW5kIHJ1bnRpbWUgY29kZV0gLS0-IEluc3RydW1lbnRbQ29tcGlsZSB3aXRoIGNvdW50ZXJzXQogICAgSW5zdHJ1bWVudCAtLT4gVHJhaW5bUnVuIHRoZSBhcHAgb24gdGhlIGJ1aWxkIHNlcnZlcl0KICAgIFRyYWluIC0tPiBQcm9maWxlW0Z1bmN0aW9uIGFuZCBicmFuY2ggY291bnRzXQogICAgUHJvZmlsZSAtLT4gQ29tcGlsZVtDb21waWxlIGFnYWluIHVzaW5nIHRoZSBwcm9maWxlXQogICAgQ29tcGlsZSAtLT4gRGVsaXZlcltEZWxpdmVyIG9wdGltaXplZCBuYXRpdmUgYXBwXQ%3D%3D%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="1781" height="94"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The profile applies to translated Java, Codename One's runtime and your native sources. The instrumented binary stays on the build server. Your users receive the second build, without the training counters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Turn on PGO for your target
&lt;/h2&gt;

&lt;p&gt;Add the relevant hint to &lt;code&gt;codenameone_settings.properties&lt;/code&gt;. This example enables it for iOS and sets a 90-second training period:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;codename1.arg.ios.pgo&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;codename1.arg.pgo.trainingSeconds&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;90&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each target has its own switch:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Build hint&lt;/th&gt;
&lt;th&gt;Target&lt;/th&gt;
&lt;th&gt;Training environment&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;codename1.arg.ios.pgo=true&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;iOS device app&lt;/td&gt;
&lt;td&gt;Temporary iOS simulator on the build server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;codename1.arg.macos.pgo=true&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Native macOS app&lt;/td&gt;
&lt;td&gt;Build server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;codename1.arg.linux.pgo=true&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Native Linux app&lt;/td&gt;
&lt;td&gt;Build server with a virtual display&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The training period defaults to 60 seconds. Values below 10 or above 300 are brought into that range. Increasing it gives the application longer to exercise its startup work; it does not create user interactions.&lt;/p&gt;

&lt;p&gt;If you already subscribe to Pro or Enterprise, enable the hint and submit a supported cloud build. PGO is included in those plans. You can &lt;a href="https://www.codenameone.com/pricing/" rel="noopener noreferrer"&gt;compare plans here&lt;/a&gt;. These hints belong to the cloud build service; local builds do not consume them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Give the training run useful work
&lt;/h2&gt;

&lt;p&gt;The app starts from a clean state and runs unattended. Resource loading, initial layout and painting can contribute to the profile, as can animations, timers and network work that start automatically. Repeated work is particularly useful because the compiler can identify the paths that dominate execution.&lt;/p&gt;

&lt;p&gt;Nobody signs in or taps a button during training. If the app stops at a login screen, the profile describes that screen. Code behind it remains correct, but the compiler has no evidence that it is an important execution path. Before enabling PGO, run the app from a clean installation and check what it does without input.&lt;/p&gt;

&lt;p&gt;PGO adds build time because it requires two compilations and a training run. Start with a normal Release build and record the time for a representative task. Repeat that task with the profile-guided build on the same device. Check memory and the result of the task as well as elapsed time. The gain depends on how much of your workload the training run reached; we are not assigning a universal speedup percentage to it.&lt;/p&gt;

&lt;p&gt;A requested profile-guided build either produces that binary or fails with a reason. An empty profile, a crash during training or an incompatible build mode does not silently produce an ordinary build. The &lt;a href="https://www.codenameone.com/developer-guide/performance/" rel="noopener noreferrer"&gt;performance guide&lt;/a&gt; lists the settings, training rules and incompatible configurations, including debug and simulator-only modes. &lt;a href="https://github.com/codenameone/CodenameOne/pull/5945" rel="noopener noreferrer"&gt;PR #5945&lt;/a&gt; introduced the build support.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adaptive GC reduces the work left to run
&lt;/h2&gt;

&lt;p&gt;PGO improves the generated native code. The collector changes reduce how much work that code needs to do. In our allocation workload, the concurrent collector spent time tracing fresh objects and sweeping their slots even when almost none survived.&lt;/p&gt;

&lt;p&gt;ParparVM now has an adaptive generational phase. Holding more memory can reduce collection frequency, but it can also make the next full trace or transition much worse. The standard Codename One benchmarks use &lt;strong&gt;no PGO&lt;/strong&gt;, so the following GC results measure the runtime changes separately.&lt;/p&gt;

&lt;h2&gt;
  
  
  Collect survivors instead of revisiting every allocation
&lt;/h2&gt;

&lt;p&gt;A generational collector concentrates frequent work on young objects, based on the observation that many die quickly. References from older objects to young ones must still be tracked, and surviving objects cannot simply disappear when a young page is reset.&lt;/p&gt;

&lt;p&gt;Our hybrid chooses between the existing concurrent path and stop-the-world generational cycles. On eligible multicore processes, it enters after repeated low-survival cycles with allocation demand and a concurrent collector busy for at least half the interval between cycles. &lt;strong&gt;Low available memory is an exit condition&lt;/strong&gt;, not the trigger to enter.&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%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IFRECiAgICBDb25jdXJyZW50W0NvbmN1cnJlbnQgY29sbGVjdGlvbl0gLS0-IENoZWNre0xvdyBzdXJ2aXZhbCBhbmQgaGlnaCBjb2xsZWN0b3IgZHV0eT99CiAgICBDaGVjayAtLT58UmVwZWF0ZWQsIGVsaWdpYmxlIHRocmVhZHMsIG1lbW9yeSBhdmFpbGFibGV8IE1ham9yW1N0b3AtdGhlLXdvcmxkIG1ham9yXQogICAgQ2hlY2sgLS0-fE5vfCBDb25jdXJyZW50CiAgICBNYWpvciAtLT4gTWlub3JbWW91bmctZ2VuZXJhdGlvbiBtaW5vciBjeWNsZXNdCiAgICBNaW5vciAtLT4gRXhpdHtIaWdoIHN1cnZpdmFsLCBsb25nIG1ham9yIG1hcmssIHF1aWV0IG9yIGxvdyBtZW1vcnk_fQogICAgRXhpdCAtLT58WWVzfCBDb25jdXJyZW50CiAgICBFeGl0IC0tPnxOb3wgTWlub3I%3D%3Ftype%3Dpng%26bgColor%3Dffffff" 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%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IFRECiAgICBDb25jdXJyZW50W0NvbmN1cnJlbnQgY29sbGVjdGlvbl0gLS0-IENoZWNre0xvdyBzdXJ2aXZhbCBhbmQgaGlnaCBjb2xsZWN0b3IgZHV0eT99CiAgICBDaGVjayAtLT58UmVwZWF0ZWQsIGVsaWdpYmxlIHRocmVhZHMsIG1lbW9yeSBhdmFpbGFibGV8IE1ham9yW1N0b3AtdGhlLXdvcmxkIG1ham9yXQogICAgQ2hlY2sgLS0-fE5vfCBDb25jdXJyZW50CiAgICBNYWpvciAtLT4gTWlub3JbWW91bmctZ2VuZXJhdGlvbiBtaW5vciBjeWNsZXNdCiAgICBNaW5vciAtLT4gRXhpdHtIaWdoIHN1cnZpdmFsLCBsb25nIG1ham9yIG1hcmssIHF1aWV0IG9yIGxvdyBtZW1vcnk_fQogICAgRXhpdCAtLT58WWVzfCBDb25jdXJyZW50CiAgICBFeGl0IC0tPnxOb3wgTWlub3I%3D%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="468" height="1054"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;An all-dead young page can be reset in constant time. The allocator's bump path is inline again, and accounting moves from each object to each page where possible. Those changes remove work rather than merely asking another CPU to do it.&lt;/p&gt;

&lt;p&gt;A process that has created virtual threads stays concurrent. A registered native thread that the collector cannot hold also prevents these stop-the-world cycles. Pausing only some allocating threads is not a valid stop-the-world algorithm. The &lt;a href="https://github.com/codenameone/CodenameOne/blob/ebe0e64be3/vm/ByteCodeTranslator/src/cn1_globals.m" rel="noopener noreferrer"&gt;runtime implementation&lt;/a&gt; enforces those boundaries.&lt;/p&gt;

&lt;h2&gt;
  
  
  More memory can buy fewer collections
&lt;/h2&gt;

&lt;p&gt;The young-generation budget responds to collection duty. Two successive minors consuming more than 5% of their intervals can grow it; below 1%, it can shrink. Growth is bounded, including by a fraction of host memory. Requiring two busy minors avoids treating one collection delayed by the OS scheduler as proof that the application needs a larger heap.&lt;/p&gt;

&lt;p&gt;A larger young generation lets more short-lived objects die before the next collection. That can save CPU. It also leaves more memory resident, and increases the amount of work a later phase may inherit. You have to measure both sides.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.codenameone.com/blog/release-2026-10-09/gc-budget.svg" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fblog%2Frelease-2026-10-09%2Fgc-budget.svg" alt="Conceptual relationship between collection frequency, memory budget and later tracing cost" width="720" height="345"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Conceptual diagram, not benchmark data. Increasing a budget can reduce frequent collection work while increasing retained memory and the cost of a later scan. The actual curves depend on survival and reference patterns.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;One failed transition made this concrete. Deferring the exit decision until the next cycle handed as much as 128 MB of young objects to a concurrent collection. Its fresh-object protection retained them, while application threads kept allocating behind it. The engineering record reports a roughly 500 ms cycle and a page-heap count rising from 3,446 to 10,538 in the affected stress test. The exit decision now runs at the end of the stopped cycle, before application threads resume.&lt;/p&gt;

&lt;p&gt;Another attempted concurrent major promoted about 6.4 million dead objects into the old generation. The next minor then spent 260 to 305 ms examining the remembered set. Those are failure observations from the &lt;a href="https://github.com/codenameone/CodenameOne/blob/ebe0e64be3/vm/CLAUDE.md" rel="noopener noreferrer"&gt;GC engineering record&lt;/a&gt;, not advertised pause bounds for the corrected collector.&lt;/p&gt;

&lt;h2&gt;
  
  
  Read time and memory together
&lt;/h2&gt;

&lt;p&gt;The committed calibration change for &lt;a href="https://github.com/codenameone/CodenameOne/blob/6516c1ca47/vm/selfhost/perf-baseline/pr/5940.json" rel="noopener noreferrer"&gt;PR #5940&lt;/a&gt; gives a useful view of the trade. These are historical gate baselines for &lt;code&gt;objectAllocation&lt;/code&gt;, with JDK 25 equal to 1. Lower is better in both columns.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Runner&lt;/th&gt;
&lt;th&gt;Time ratio, before → after&lt;/th&gt;
&lt;th&gt;Peak memory ratio, before → after&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;macOS ARM64, three-CPU runner&lt;/td&gt;
&lt;td&gt;3.696 → 1.106&lt;/td&gt;
&lt;td&gt;0.481 → 0.937&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Linux x64, AMD EPYC 7763&lt;/td&gt;
&lt;td&gt;5.011 → 1.649&lt;/td&gt;
&lt;td&gt;0.370 → 0.370&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Linux ARM64, Neoverse N2&lt;/td&gt;
&lt;td&gt;3.043 → 3.043&lt;/td&gt;
&lt;td&gt;0.239 → 0.378&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Windows x64, AMD family 25/model 1&lt;/td&gt;
&lt;td&gt;4.932 → 1.559&lt;/td&gt;
&lt;td&gt;0.400 → 0.386&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://www.codenameone.com/blog/release-2026-10-09/gc-ratios.svg" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fblog%2Frelease-2026-10-09%2Fgc-ratios.svg" alt="Before and after calibration ratios for allocation time and peak memory, with JDK 25 at one" width="944" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The macOS row is the clearest cost: much less allocation time, almost twice the previous memory ratio. Linux ARM64 records more memory with its time baseline unchanged. A value unchanged in the overlay can be a carried-forward baseline; it is not a fresh measurement of zero change. The ratios still show ParparVM slower than JDK 25 on this allocation workload after the improvement.&lt;/p&gt;

&lt;p&gt;These are calibration records, &lt;strong&gt;not a new controlled before/after experiment&lt;/strong&gt;. Most selected rows record one calibration run; the Windows row records three. They have no confidence intervals, and later master baselines include further recalibrations. The &lt;a href="https://www.codenameone.com/blog/release-2026-10-09/gc-allocation-baselines.csv" rel="noopener noreferrer"&gt;data CSV&lt;/a&gt; and &lt;a href="https://www.codenameone.com/blog/release-2026-10-09/gc-baseline-overlay.json" rel="noopener noreferrer"&gt;original overlay&lt;/a&gt; retain that context. Compare within a runner row; do not rank different machines from these ratios.&lt;/p&gt;

&lt;p&gt;The allocation workload is deliberately narrow. It tells us about allocation and collection behavior for that survival pattern, not frame latency, network throughput or every application. A backend using virtual threads also follows the concurrent path, so an allocation microbenchmark's hybrid gain cannot be assigned to that backend.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make a speed gain part of the next baseline
&lt;/h2&gt;

&lt;p&gt;The performance gate runs paired ParparVM and JDK 25 workloads on Linux x64 and ARM64, Windows x64 and ARM64, and macOS ARM64. It alternates which runtime goes first, verifies output and records median ratios. CPU-specific baselines keep one runner model from silently standing in for another.&lt;/p&gt;

&lt;p&gt;For workload timings, the gate uses the fastest measured repetition inside each process; JVM startup is not charged to the workload. Memory is a process peak: footprint on macOS, RSS on Linux and working set on Windows. Those are related measurements, not identical operating-system counters.&lt;/p&gt;

&lt;p&gt;A change outside the permitted tolerance fails in either direction. A large improvement needs a reviewed baseline update too, otherwise a later regression could give it back while remaining below the old limit. Each PR owns an overlay, records a reason for an intentional change and names the value it replaces. Merged overlays are folded without changing the resolved baseline.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Run locally on the appropriate platform and inspect both metrics.&lt;/span&gt;
vm/selfhost/ci-perf-gate.sh run macos-arm64 /tmp/cn1-perf
vm/selfhost/ci-perf-gate.sh verdict /tmp/cn1-perf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This comparative gate covers the five host OS/architecture combinations above. It is not a JDK comparison running on an iPhone, Android device or browser. Those ports have their own behavior and screenshot coverage. The &lt;a href="https://www.codenameone.com/port-status/" rel="noopener noreferrer"&gt;port status page&lt;/a&gt; exposes the distinction, and &lt;a href="https://github.com/codenameone/CodenameOne/pull/5930" rel="noopener noreferrer"&gt;PR #5930&lt;/a&gt; explains the baseline workflow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the gain you measured
&lt;/h2&gt;

&lt;p&gt;PGO and adaptive collection improve different parts of execution. Compare a profile-guided build with a regular build using the same application workflow, and keep the time and memory results together. For compiler and runtime changes, the performance gate gives the next release a baseline to preserve.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discussion
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Which part of your app would benefit most from a profile-guided build: startup, rendering, or repeated background work?&lt;/em&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>Try Real Java in Your Browser, Compiler Errors Included</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Tue, 20 Oct 2026 18:39:41 +0000</pubDate>
      <link>https://dev.to/codenameone/try-real-java-in-your-browser-compiler-errors-included-2pfl</link>
      <guid>https://dev.to/codenameone/try-real-java-in-your-browser-compiler-errors-included-2pfl</guid>
      <description>&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq1foe61wfenkt05ewgej.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fq1foe61wfenkt05ewgej.jpg" alt="Java source becomes bytecode and a live browser preview" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A playground should tell you whether your Java works. A Java-like interpreter can instead teach you which Java it happens to understand. That difference becomes annoying as soon as you paste a record, a lambda or an example with a tricky generic type.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We rebuilt the &lt;a href="https://www.codenameone.com/playground/" rel="noopener noreferrer"&gt;Codename One Playground&lt;/a&gt; around our in-tree Java compiler. Your code becomes class files, then the ParparVM translator turns those classes into JavaScript inside the page. The running app loads them and updates the preview. Compilation does not send your source to a server.&lt;/p&gt;

&lt;h2&gt;
  
  
  A compiler running inside its own output
&lt;/h2&gt;

&lt;p&gt;Both the compiler and translator are written in Java. ParparVM translates them into the JavaScript bundle you load when you open the Playground. That bundle can then compile and translate more Java: the code you type.&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%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IFRECiAgICBFZGl0W0VkaXQgSmF2YSBzb3VyY2VdIC0tPiBXYWl0WzMwMCBtcyBhZnRlciB0eXBpbmcgc3RvcHNdCiAgICBXYWl0IC0tPiBDb21waWxlcltJbi1wYWdlIEphdmEgY29tcGlsZXJdCiAgICBBUElbQVBJIHNpZ25hdHVyZSBzdHVic10gLS0-IENvbXBpbGVyCiAgICBDb21waWxlciAtLT4gQnl0ZWNvZGVbQ2xhc3MgZmlsZXNdCiAgICBCeXRlY29kZSAtLT4gVHJhbnNsYXRvcltJbmNyZW1lbnRhbCBQYXJwYXJWTSB0cmFuc2xhdG9yXQogICAgVHJhbnNsYXRvciAtLT4gTG9hZGVyW0xvYWQgSmF2YVNjcmlwdCBjbGFzcyBkZWZpbml0aW9uc10KICAgIExvYWRlciAtLT4gUHJldmlld1tSdW4gYW5kIHVwZGF0ZSBwcmV2aWV3XQogICAgQ29tcGlsZXIgLS0-IEVycm9yc1tMaW5lIGFuZCBjb2x1bW4gZGlhZ25vc3RpY3Nd%3Ftype%3Dpng%26bgColor%3Dffffff" 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%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IFRECiAgICBFZGl0W0VkaXQgSmF2YSBzb3VyY2VdIC0tPiBXYWl0WzMwMCBtcyBhZnRlciB0eXBpbmcgc3RvcHNdCiAgICBXYWl0IC0tPiBDb21waWxlcltJbi1wYWdlIEphdmEgY29tcGlsZXJdCiAgICBBUElbQVBJIHNpZ25hdHVyZSBzdHVic10gLS0-IENvbXBpbGVyCiAgICBDb21waWxlciAtLT4gQnl0ZWNvZGVbQ2xhc3MgZmlsZXNdCiAgICBCeXRlY29kZSAtLT4gVHJhbnNsYXRvcltJbmNyZW1lbnRhbCBQYXJwYXJWTSB0cmFuc2xhdG9yXQogICAgVHJhbnNsYXRvciAtLT4gTG9hZGVyW0xvYWQgSmF2YVNjcmlwdCBjbGFzcyBkZWZpbml0aW9uc10KICAgIExvYWRlciAtLT4gUHJldmlld1tSdW4gYW5kIHVwZGF0ZSBwcmV2aWV3XQogICAgQ29tcGlsZXIgLS0-IEVycm9yc1tMaW5lIGFuZCBjb2x1bW4gZGlhZ25vc3RpY3Nd%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="523" height="766"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is our &lt;code&gt;com.codename1.tools.javac&lt;/code&gt; implementation, not the OpenJDK compiler copied into the page. It targets javac's language rules and diagnostics. The &lt;a href="https://github.com/codenameone/CodenameOne/tree/ebe0e64be3/vm/JavaCompiler/tests" rel="noopener noreferrer"&gt;compiler conformance tests&lt;/a&gt; compare program output and negative cases with the reference compiler.&lt;/p&gt;

&lt;p&gt;That distinction matters when reporting a bug. A familiar error message is useful, but agreement on a test corpus does not prove every possible Java program behaves identically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Paste a record and change it
&lt;/h2&gt;

&lt;p&gt;Open the Playground and replace the Java pane with this script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;Item&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt;

&lt;span class="nc"&gt;Item&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Item&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Orders"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;Form&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Java in the browser"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BoxLayout&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BoxLayout&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Y_AXIS&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="nc"&gt;Label&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Label&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;": "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="n"&gt;button&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Show the next count"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addActionListener&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setText&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&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;item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)));&lt;/span&gt;
&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addAll&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;label&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;show&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Playground supplies the usual UI imports and supports script-shaped input. Change the constructor's &lt;code&gt;3&lt;/code&gt; to &lt;code&gt;"three"&lt;/code&gt;. That is a type error, and the editor can point to it before the app runs. Restore the number and click the button to exercise a listener compiled from your code.&lt;/p&gt;

&lt;p&gt;Records, switch expressions, pattern matching and lambdas go through the compiler's type checking and lowering. A record gets explicit methods in bytecode. The translator handles the resulting classes against the API the page actually contains.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.codenameone.com/blog/release-2026-10-09/playground-record.png" rel="noopener noreferrer"&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F3hum7sgirtpmniyn154x.png" alt="The article record example running in the browser Playground after the button updates Orders to four" width="800" height="511"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Captured from the public Playground on October 8. Clicking the compiled listener changed the label to &lt;code&gt;Orders: 4&lt;/code&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.codenameone.com/blog/release-2026-10-09/playground-type-error.png" rel="noopener noreferrer"&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8go8cooay833amqh9id5.png" alt="The Playground reports that the Item constructor requires an int after three is supplied as a string" width="800" height="511"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The deliberate mistake reports the constructor mismatch at line 3, column 13. The preview does not run the invalid program.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the API open without keeping everything
&lt;/h2&gt;

&lt;p&gt;A normal app has a known set of reachable classes. The translator removes unused code and can turn some virtual calls into direct calls. The Playground cannot assume that tomorrow's source will only call methods today's bundle already uses.&lt;/p&gt;

&lt;p&gt;Its host bundle therefore keeps selected API packages open. Those methods retain predictable names and support subclasses arriving later. A small stub library supplies signatures, generic information and constants to the compiler without supplying a second copy of the implementations.&lt;/p&gt;

&lt;p&gt;The compiler, translator and platform internals stay closed to user code, so they can still be reduced and renamed. Incremental translation only emits the new user classes. A later run replaces those definitions without rebuilding the whole application bundle.&lt;/p&gt;

&lt;h2&gt;
  
  
  Removing the interpreter removed other machinery
&lt;/h2&gt;

&lt;p&gt;BeanShell needed access bridges to call framework code in an environment without its usual reflection support. The old generated access adapters occupied 23,827,851 bytes across 161 Java files, before counting their central registry. The new pipeline removes that registry, BeanShell and its listener and lambda bridges.&lt;/p&gt;

&lt;p&gt;That source-file saving is not a browser download measurement. The compiler and translator also add code, and compression changes the transfer size. The repository includes &lt;code&gt;compare-javascript-bundles.sh&lt;/code&gt; to compare actual artifacts; use the same build configuration and compression for both. The Playground console reports compile, load and run times so a smaller bundle and a faster edit cycle can be measured separately.&lt;/p&gt;

&lt;p&gt;For the example above, one Chromium run on the local Mac reported &lt;strong&gt;1,247 ms compile, 505 ms load and 8 ms run&lt;/strong&gt;. The invalid version reported &lt;strong&gt;841 ms compile&lt;/strong&gt; and no load or run. Those are single observations from the deployed Playground, not a warmed benchmark or a before/after speedup. They make the phases visible without attributing an unmeasured gain to the rewrite.&lt;/p&gt;

&lt;h2&gt;
  
  
  An unfinished edit must be valid input to the tool
&lt;/h2&gt;

&lt;p&gt;A compiler in an editor sees broken programs most of the time: half a string literal, an incomplete method and a generic type with its closing bracket still missing. The conformance suite includes program prefixes so these produce diagnostics rather than exceptions or hangs.&lt;/p&gt;

&lt;p&gt;Other gates compare the self-hosted translator's output with the JVM translator byte for byte. Browser tests run samples, edit code, rerun it, navigate forms and provoke listener exceptions. That last case matters: an exception swallowed after a successful compile makes the preview look frozen while the actual failure disappears.&lt;/p&gt;

&lt;p&gt;There are limits. The page exposes a supported API, not the whole JDK. Annotation processing and Java modules are absent; native method declarations are rejected. Some library surfaces exist but remain unsupported at runtime, including the browser regex implementation documented in the guide. BeanShell-only share links can now fail compilation.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.codenameone.com/developer-guide/java-in-the-browser/" rel="noopener noreferrer"&gt;Java in the browser chapter&lt;/a&gt; explains the pipeline and gates. &lt;a href="https://github.com/codenameone/CodenameOne/pull/5938" rel="noopener noreferrer"&gt;PR #5938&lt;/a&gt; began this rebuild. Try a small part of your next screen in the &lt;a href="https://www.codenameone.com/playground/" rel="noopener noreferrer"&gt;live Playground&lt;/a&gt;, including a deliberate mistake, and see whether the feedback tells you what you need.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discussion
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Which Java construct do you reach for first when checking whether a playground runs the language you use?&lt;/em&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>How Do You Test Spring Compatibility?</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Mon, 19 Oct 2026 19:54:58 +0000</pubDate>
      <link>https://dev.to/codenameone/how-do-you-test-spring-compatibility-1n7m</link>
      <guid>https://dev.to/codenameone/how-do-you-test-spring-compatibility-1n7m</guid>
      <description>&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr8ef8jw3w2jixet0nbtn.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr8ef8jw3w2jixet0nbtn.jpg" alt="The same service contract is checked on the JVM and native runtime" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Codename One backend is not Spring compatible. We use similar syntax to ease migration and porting, but familiar annotations do not tell you whether the ported application behaves the same way. Tests do.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Write those tests in the original Spring application first. Capture the behavior your clients depend on, get the tests passing, then bring the same assertions to Codename One. Here, compatibility means that your application's behavior survives the move. It does not mean that Codename One implements the Spring framework.&lt;/p&gt;

&lt;p&gt;The new testing API gives you familiar request builders and assertions for that job. You can run them against the port on the JVM, then check the compiled native backend too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Establish the behavior in Spring first
&lt;/h2&gt;

&lt;p&gt;Start with an endpoint whose contract you already know. For a Spring application that exposes &lt;code&gt;/greet/{name}&lt;/code&gt;, this test records the successful response and what happens for an unknown route:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.beans.factory.annotation.Autowired&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.boot.test.context.SpringBootTest&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.springframework.test.web.servlet.MockMvc&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;org.junit.jupiter.api.Test&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;springframework&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;servlet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;request&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;MockMvcRequestBuilders&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="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;springframework&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;servlet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;MockMvcResultMatchers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;jsonPath&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;static&lt;/span&gt; &lt;span class="n"&gt;org&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;springframework&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;web&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;servlet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;result&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;MockMvcResultMatchers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@SpringBootTest&lt;/span&gt;
&lt;span class="nd"&gt;@AutoConfigureMockMvc&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;GreetingApiTest&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Autowired&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;MockMvc&lt;/span&gt; &lt;span class="n"&gt;mvc&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="nd"&gt;@Test&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;greetsByName&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;mvc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;perform&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/greet/{name}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Ada"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;andExpect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;isOk&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
                &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;andExpect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jsonPath&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"$.greeting"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello, Ada"&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Test&lt;/span&gt;
    &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;unknownRoutesAreNotFound&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;mvc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;perform&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/nowhere"&lt;/span&gt;&lt;span class="o"&gt;)).&lt;/span&gt;&lt;span class="na"&gt;andExpect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;isNotFound&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 example uses the Spring Boot 3 test imports. Run it against the original application before porting. If the application protects these routes, configure the test identity to match its access rules rather than disabling security to get a green result.&lt;/p&gt;

&lt;p&gt;For a real service, go beyond the successful response. Test validation errors, missing records and denied access. For a write endpoint, verify the stored state as well as the response. Those expectations define what the port must preserve.&lt;/p&gt;

&lt;h2&gt;
  
  
  Port the test setup, keep the assertions
&lt;/h2&gt;

&lt;p&gt;For the Codename One version, change the framework imports and test annotations. The request inputs and expected results above stay the same:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.test.web.servlet.MockMvc;
&lt;/span&gt;&lt;span class="gi"&gt;+import com.codename1.backend.annotations.Autowired;
+import com.codename1.backend.test.BackendTest;
+import com.codename1.backend.test.MockMvc;
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
-import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
&lt;/span&gt;&lt;span class="gi"&gt;+import static com.codename1.backend.test.MockMvcRequestBuilders.get;
+import static com.codename1.backend.test.MockMvcResultMatchers.jsonPath;
+import static com.codename1.backend.test.MockMvcResultMatchers.status;
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;-@SpringBootTest
-@AutoConfigureMockMvc
&lt;/span&gt;&lt;span class="gi"&gt;+@BackendTest
&lt;/span&gt; class GreetingApiTest {
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;@BackendTest&lt;/code&gt; starts the module's application and injects test fields from its generated wiring. &lt;code&gt;MockMvc&lt;/code&gt; passes requests through routing, sessions, scoped beans and error handling inside the process. The matching Codename One controller, service and test are in the &lt;a href="https://github.com/codenameone/CodenameOne/tree/ebe0e64be3/docs/demos/backend/src/main/java/com/codenameone/developerguide/backend/testing" rel="noopener noreferrer"&gt;compiled guide examples&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;New backend templates include the test setup. Existing modules need the test dependency and annotation-processing goals in the &lt;a href="https://www.codenameone.com/developer-guide/backend-testing/" rel="noopener noreferrer"&gt;testing guide&lt;/a&gt;. Keep the same fixtures and expected values on both sides. Changing an assertion merely to accept the port's output hides the difference you were trying to detect.&lt;/p&gt;

&lt;p&gt;These import changes work for this example. A larger suite can use Spring test features that Codename One does not implement. Port that setup explicitly, and keep track of any case that cannot run yet. Passing the tests demonstrates the behavior they cover, not every possible interaction in the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decide what the test needs to cross
&lt;/h2&gt;

&lt;p&gt;Use &lt;code&gt;MockMvc&lt;/code&gt; for request and response behavior without sending that request through a socket. Use &lt;code&gt;TestRestTemplate&lt;/code&gt; when the HTTP transport is part of the contract. Both work with the test application's beans.&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%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgICBUZXN0c1tTYW1lIHJlcXVlc3QgaW5wdXRzIGFuZCBleHBlY3RlZCByZXN1bHRzXSAtLT4gU3ByaW5nW09yaWdpbmFsIFNwcmluZyBhcHBsaWNhdGlvbl0KICAgIFRlc3RzIC0tPiBQb3J0W1BvcnRlZCBDb2RlbmFtZSBPbmUgdGVzdCBzZXR1cF0KICAgIFBvcnQgLS0-IEpWTVtKVk0gYmFja2VuZF0KICAgIFBvcnQgLS0-IE5hdGl2ZVtDb21waWxlZCBuYXRpdmUgYmFja2VuZF0KICAgIFNwcmluZyAtLT4gQ29udHJhY3RbVmVyaWZ5IHN0YXR1cywgYm9keSBhbmQgc3RvcmVkIHN0YXRlXQogICAgSlZNIC0tPiBDb250cmFjdAogICAgTmF0aXZlIC0tPiBDb250cmFjdA%3D%3D%3Ftype%3Dpng%26bgColor%3Dffffff" 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%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgICBUZXN0c1tTYW1lIHJlcXVlc3QgaW5wdXRzIGFuZCBleHBlY3RlZCByZXN1bHRzXSAtLT4gU3ByaW5nW09yaWdpbmFsIFNwcmluZyBhcHBsaWNhdGlvbl0KICAgIFRlc3RzIC0tPiBQb3J0W1BvcnRlZCBDb2RlbmFtZSBPbmUgdGVzdCBzZXR1cF0KICAgIFBvcnQgLS0-IEpWTVtKVk0gYmFja2VuZF0KICAgIFBvcnQgLS0-IE5hdGl2ZVtDb21waWxlZCBuYXRpdmUgYmFja2VuZF0KICAgIFNwcmluZyAtLT4gQ29udHJhY3RbVmVyaWZ5IHN0YXR1cywgYm9keSBhbmQgc3RvcmVkIHN0YXRlXQogICAgSlZNIC0tPiBDb250cmFjdAogICAgTmF0aXZlIC0tPiBDb250cmFjdA%3D%3D%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="1193" height="278"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A test can add a &lt;code&gt;@TestConfiguration&lt;/code&gt; and substitute a &lt;code&gt;@Primary&lt;/code&gt; bean. That is useful when you need deterministic behavior from a dependency while still testing the controller and generated wiring. It also works in the compiled runner.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;@MockitoBean&lt;/code&gt; is available on the JVM with Mockito on the test classpath. It cannot follow you into a native binary because Mockito creates classes at runtime. Prefer an explicit test implementation when the same scenario needs to run in both environments.&lt;/p&gt;

&lt;p&gt;Security tests can use &lt;code&gt;@WithMockUser&lt;/code&gt; or request post-processors. A mocked JWT identity tests authorization after authentication; it does not test signature verification or the token exchange. Keep a real-client integration test for those boundaries, as &lt;a href="https://www.codenameone.com/blog/sign-in-client-server-contract/" rel="noopener noreferrer"&gt;Saturday's article&lt;/a&gt; describes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run the same supported tests after compilation
&lt;/h2&gt;

&lt;p&gt;In a project with a backend module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mvn &lt;span class="nt"&gt;-pl&lt;/span&gt; backend &lt;span class="nt"&gt;-Dcodename1&lt;/span&gt;.platform&lt;span class="o"&gt;=&lt;/span&gt;backend &lt;span class="nb"&gt;test
&lt;/span&gt;mvn &lt;span class="nt"&gt;-pl&lt;/span&gt; backend &lt;span class="nt"&gt;-Dcodename1&lt;/span&gt;.platform&lt;span class="o"&gt;=&lt;/span&gt;backend &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-Dcn1&lt;/span&gt;.backend.compiledTests&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-Dcn1&lt;/span&gt;.backend.compiledTests.strict&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second command runs Surefire, generates direct test calls and builds a native test binary with the application's generated wiring and native code. It writes &lt;code&gt;TEST-&amp;lt;class&amp;gt;-compiled.xml&lt;/code&gt; reports beside the JVM reports. This is a ParparVM test binary; it does not embed JUnit's reflection-based runner.&lt;/p&gt;

&lt;p&gt;Strict mode matters when porting a suite. Without it, Mockito-dependent classes can be excluded or reported skipped. Strict mode makes those unsupported cases fail the build so an apparently successful native run does not hide the tests you meant to keep.&lt;/p&gt;

&lt;p&gt;The native path needs the backend toolchain, including clang and the OpenSSL, libcurl and nghttp2 headers. It builds on Linux and macOS. Windows has no native backend runner. Run native tests locally as part of assessing your migration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Account for differences in the test framework
&lt;/h2&gt;

&lt;p&gt;There are no &lt;code&gt;@WebMvcTest&lt;/code&gt; slices; each test runs the application. The generated test application excludes management endpoints, MCP and OpenTelemetry exporters even if production configuration enables them. Check those integrations separately.&lt;/p&gt;

&lt;p&gt;The compiled runner supports a defined JUnit 5 subset. Nested test classes, Kotlin tests and some selectors cannot simply be carried over. JSONPath supports common property and index operations, not every filter or function. &lt;code&gt;MOCK&lt;/code&gt; and &lt;code&gt;NONE&lt;/code&gt; still start a loopback listener for the server's runtime tasks, even though &lt;code&gt;MockMvc.perform&lt;/code&gt; dispatches in-process.&lt;/p&gt;

&lt;p&gt;Keep the Spring run as your reference while you port. Run the same scenarios against the JVM backend, investigate differences, then run the compiled path and inspect both failures and skips. Replace unsupported test machinery with explicit fixtures where practical. A test that never ran cannot tell you whether the migration preserved its behavior.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/codenameone/CodenameOne/pull/5932" rel="noopener noreferrer"&gt;PR #5932&lt;/a&gt; introduced the framework, and current master adds the security test support. Start with the endpoint whose behavior would be most expensive to get wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discussion
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Which behavior would you test first before porting your Spring application?&lt;/em&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>Update DB Schema in Production with Flyway like Syntax</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Sun, 18 Oct 2026 16:43:55 +0000</pubDate>
      <link>https://dev.to/codenameone/update-db-schema-in-production-with-flyway-like-syntax-2lj6</link>
      <guid>https://dev.to/codenameone/update-db-schema-in-production-with-flyway-like-syntax-2lj6</guid>
      <description>&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyo299xcyvek7d71kuc38.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyo299xcyvek7d71kuc38.jpg" alt="Versioned database changes bring old installations forward" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The database on your laptop has today's schema. The database on a customer's phone might be six releases old. Both have to work after the next update, and deleting the customer's data is not a migration strategy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Codename One now uses the same migration engine for client databases and the native backend. It follows Flyway-style versioned scripts and history, with build-time registration so the scripts travel inside the app or server. This continues our &lt;a href="https://www.codenameone.com/blog/android-apps-beyond-android/" rel="noopener noreferrer"&gt;weekly release series&lt;/a&gt; and supplies the tables behind &lt;a href="https://www.codenameone.com/blog/sign-in-client-server-contract/" rel="noopener noreferrer"&gt;yesterday's security stack&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Give each schema change a place in history
&lt;/h2&gt;

&lt;p&gt;Client scripts live in &lt;code&gt;common/src/main/db/migration&lt;/code&gt;. Backend scripts live in &lt;code&gt;src/main/resources/db/migration&lt;/code&gt; inside the backend module. For a notes application, the first script might be:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- V1__create_note.sql&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;note&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;INTEGER&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;note_body&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;note&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A later release adds a timestamp through another file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- V2__add_note_created.sql&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;note&lt;/span&gt; &lt;span class="k"&gt;ADD&lt;/span&gt; &lt;span class="k"&gt;COLUMN&lt;/span&gt; &lt;span class="n"&gt;created&lt;/span&gt; &lt;span class="nb"&gt;BIGINT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;note&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;created&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;created&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The double underscore separates the version from the description. Versions sort numerically, so &lt;code&gt;2.10&lt;/code&gt; comes after &lt;code&gt;2.9&lt;/code&gt;. Never edit a versioned SQL script that has shipped. Its recorded checksum lets the engine detect that two installations would otherwise have different definitions of the same version.&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%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSOwogICAgZnJlc2hbIk5ldyBkYXRhYmFzZSJdIC0tPiB2MVsiQXBwbHkgVjEiXTsKICAgIHYxIC0tPiB2MlsiQXBwbHkgVjIiXTsKICAgIGV4aXN0aW5nWyJEYXRhYmFzZSBhbHJlYWR5IGF0IFYxIl0gLS0-IHYyOwogICAgdjIgLS0-IHJlYWR5WyJDdXJyZW50IHNjaGVtYSJdOwogICAgcmVhZHkgLS0-IHJlb3BlblsiVmFsaWRhdGUgaGlzdG9yeSBvbiBuZXh0IG9wZW4iXTs%3D%3Ftype%3Dpng%26bgColor%3Dffffff" 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%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSOwogICAgZnJlc2hbIk5ldyBkYXRhYmFzZSJdIC0tPiB2MVsiQXBwbHkgVjEiXTsKICAgIHYxIC0tPiB2MlsiQXBwbHkgVjIiXTsKICAgIGV4aXN0aW5nWyJEYXRhYmFzZSBhbHJlYWR5IGF0IFYxIl0gLS0-IHYyOwogICAgdjIgLS0-IHJlYWR5WyJDdXJyZW50IHNjaGVtYSJdOwogICAgcmVhZHkgLS0-IHJlb3BlblsiVmFsaWRhdGUgaGlzdG9yeSBvbiBuZXh0IG9wZW4iXTs%3D%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="1163" height="174"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Skipping an app release is now ordinary input to the migration engine. It applies the missing versions in order. A repeatable &lt;code&gt;R__description.sql&lt;/code&gt; runs after versioned migrations and again when its checksum changes, which suits a view definition. The build catches duplicate versions, malformed filenames and empty scripts before packaging.&lt;/p&gt;

&lt;h2&gt;
  
  
  Upgrade before the app starts using the database
&lt;/h2&gt;

&lt;p&gt;For the client, migrate immediately after opening the database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;Database&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Display&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInstance&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;openOrCreate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"notes.db"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;Migrations&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;migrate&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The imports are &lt;code&gt;com.codename1.db.Database&lt;/code&gt;, &lt;code&gt;com.codename1.db.Migrations&lt;/code&gt; and &lt;code&gt;com.codename1.ui.Display&lt;/code&gt;. Run database work on its owning thread. If a migration moves substantial data, do it off the event dispatch thread and show progress before opening the normal UI.&lt;/p&gt;

&lt;p&gt;The annotation ORM's &lt;code&gt;EntityManager.open("notes.db")&lt;/code&gt; performs migration for you. The build compiles scripts into generated code, so there is no folder of migration resources for an iOS package to discover at runtime.&lt;/p&gt;

&lt;p&gt;An older binary also needs protection from newer data. By default, a future schema causes migration to refuse before changing it. The &lt;a href="https://www.codenameone.com/developer-guide/#client-schema-migrations" rel="noopener noreferrer"&gt;client guide&lt;/a&gt; shows how to handle &lt;code&gt;MigrationException.FUTURE_SCHEMA&lt;/code&gt; and ask the user to update. This is safer than hoping an old query happens to survive a downgrade.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make server startup an ordering guarantee
&lt;/h2&gt;

&lt;p&gt;The backend applies pending migrations before opening its entity manager or accepting application requests. Its database lock coordinates instances starting against the same database. A successful transactional migration records its history row with the schema change.&lt;/p&gt;

&lt;p&gt;Use the Maven goals to inspect and validate a deployment before starting it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mvn &lt;span class="nt"&gt;-pl&lt;/span&gt; backend &lt;span class="nt"&gt;-Dcodename1&lt;/span&gt;.platform&lt;span class="o"&gt;=&lt;/span&gt;backend cn1:migrate-info
mvn &lt;span class="nt"&gt;-pl&lt;/span&gt; backend &lt;span class="nt"&gt;-Dcodename1&lt;/span&gt;.platform&lt;span class="o"&gt;=&lt;/span&gt;backend cn1:migrate-validate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://www.codenameone.com/developer-guide/backend-data/#backend-schema-migrations" rel="noopener noreferrer"&gt;server migration guide&lt;/a&gt; also covers applying and repairing migrations. &lt;code&gt;migrate-info&lt;/code&gt; reads history without changing it. Keep connection settings pointed at the intended environment before running a mutating goal.&lt;/p&gt;

&lt;p&gt;SQL common to SQLite, PostgreSQL and MySQL can live directly in &lt;code&gt;db/migration&lt;/code&gt;. Engine-specific variants belong under &lt;code&gt;sqlite&lt;/code&gt;, &lt;code&gt;postgresql&lt;/code&gt; or &lt;code&gt;mysql&lt;/code&gt;, using the same migration filename. MariaDB uses the MySQL variant. Shared machinery does not make different database dialects interchangeable.&lt;/p&gt;

&lt;h2&gt;
  
  
  The failure case determines the recovery plan
&lt;/h2&gt;

&lt;p&gt;SQLite and PostgreSQL can roll a transactional migration back with its history update. MySQL and MariaDB can commit DDL statements as they execute. A failed script there can leave part of its schema change behind. The engine records failure and refuses to proceed; inspect and correct the database before repairing history.&lt;/p&gt;

&lt;p&gt;A SQLite script marked &lt;code&gt;executeInTransaction=false&lt;/code&gt; has another boundary: the normal transaction cannot serialize several starting processes across the whole script. Run such a migration from one process before starting the rest. Do not apply the normal locking guarantee to that exception.&lt;/p&gt;

&lt;p&gt;A populated database without migration history is also refused by default. Adopt it by explicitly recording a baseline for the version it already implements. A baseline records your assertion about the schema; it does not inspect every column and prove the assertion correct.&lt;/p&gt;

&lt;p&gt;The security library registers its own named migration set and separate history table, then runs before application migrations. That lets your scripts depend on its tables without mixing your application's version numbers with the library's.&lt;/p&gt;

&lt;p&gt;There are no undo scripts. Write a forward repair migration, and keep tested backups for changes that cannot be reversed from the remaining data. Java migrations have no automatic checksum, so changing an already-applied Java migration will not get the same SQL checksum protection.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test the upgrade your customers will take
&lt;/h2&gt;

&lt;p&gt;A fresh-database test only proves the easiest path. Keep fixtures for an older supported schema, upgrade them to current, then reopen and verify that nothing applies twice. Add a newer-than-app fixture to test refusal. For important data, assert the values after migration as well as the table shape.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/codenameone/CodenameOne/pull/5961" rel="noopener noreferrer"&gt;implementation in PR #5961&lt;/a&gt; provides the engine. Your release history provides the cases that matter. Tomorrow's testing article shows how to keep backend behavior under test while changing runtimes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discussion
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;How far back does your oldest supported installation's database go?&lt;/em&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>Easy Full Stack Authentication and Login in the Style of Spring</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Sat, 17 Oct 2026 16:27:03 +0000</pubDate>
      <link>https://dev.to/codenameone/easy-full-stack-authentication-and-login-in-the-style-of-spring-455a</link>
      <guid>https://dev.to/codenameone/easy-full-stack-authentication-and-login-in-the-style-of-spring-455a</guid>
      <description>&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm5ubq0mt0u45k3gx0y2g.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fm5ubq0mt0u45k3gx0y2g.jpg" alt="A client session connects to server access rules and verification" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Three requests leave your app just as its access token expires. If each refreshes independently, the first exchange can invalidate the refresh token the other two are about to use. A login feature becomes a race between requests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This week's security work joins the Codename One client and backend around those transitions. The client can coordinate renewal, the server can rotate tokens, and tests can verify what happens after a real request is refused.&lt;/p&gt;

&lt;h2&gt;
  
  
  Put the access rule beside the route
&lt;/h2&gt;

&lt;p&gt;The backend has Spring-style security chains under &lt;code&gt;com.codename1.backend.security&lt;/code&gt;. A chain chooses which requests it handles, then authenticates and authorizes them. This excerpt from the &lt;a href="https://github.com/codenameone/CodenameOne/blob/ebe0e64be3/docs/demos/backend/src/main/java/com/codenameone/developerguide/backend/security/SecurityConfig.java" rel="noopener noreferrer"&gt;security guide example&lt;/a&gt; protects an API using JWT scopes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Bean&lt;/span&gt;
&lt;span class="nd"&gt;@Order&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nc"&gt;SecurityFilterChain&lt;/span&gt; &lt;span class="nf"&gt;api&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpSecurity&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;securityMatcher&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/**"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;authorizeHttpRequests&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;requestMatchers&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/orders/**"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;hasAuthority&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SCOPE_orders:read"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;anyRequest&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;authenticated&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sessionManagement&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sessionCreationPolicy&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SessionCreationPolicy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;STATELESS&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;oauth2ResourceServer&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oauth2&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;oauth2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;jwt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Customizer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withDefaults&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&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;Configure the trusted issuer and the audience your API expects. The guide's configuration keys include &lt;code&gt;cn1.security.oauth2.resourceserver.jwt.issuer-uri&lt;/code&gt; and &lt;code&gt;cn1.security.oauth2.resourceserver.jwt.audiences&lt;/code&gt;. Audience checking is optional unless configured, so set it deliberately when several services share an issuer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A backend with no &lt;code&gt;SecurityFilterChain&lt;/code&gt; bean has no security layer.&lt;/strong&gt; Adding a dependency does not secure the routes. A stateless chain also has no CSRF filter unless you request one. Session-based browser applications have a different threat model and need the corresponding session and CSRF configuration.&lt;/p&gt;

&lt;p&gt;Method rules such as &lt;code&gt;@PreAuthorize&lt;/code&gt; are compiled into checks. They can apply to self-calls and private methods because the build weaves them into the method, rather than relying on calls passing through a runtime proxy. The expression language is a supported subset; ACLs, &lt;code&gt;@PostAuthorize&lt;/code&gt; and arbitrary expression evaluation are outside it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Give the client one owner for renewal
&lt;/h2&gt;

&lt;p&gt;Register an installed app as a public OAuth client with an exact redirect URI and PKCE. A client secret embedded in an app would not remain secret. After configuring &lt;code&gt;OidcClient&lt;/code&gt;, install an authorizer for the API base URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;OidcRequestAuthorizer&lt;/span&gt; &lt;span class="n"&gt;authorizer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;OidcRequestAuthorizer&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;install&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://api.example.com/v1"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;authorizer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addSignInRequiredListener&lt;/span&gt;&lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;showSignIn&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="n"&gt;authorizer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;load&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;ready&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;saved&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&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;saved&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;showSignIn&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;showNotes&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 is an application excerpt: &lt;code&gt;client&lt;/code&gt; is configured with your provider and token store; &lt;code&gt;showSignIn()&lt;/code&gt; and &lt;code&gt;showNotes()&lt;/code&gt; are your UI methods. Use &lt;code&gt;SecureStorageTokenStore&lt;/code&gt; for platform-protected persistence. Plain storage fallback is an explicit option, not something the app should silently enable to make an error disappear.&lt;/p&gt;

&lt;p&gt;The authorizer matches the scheme, host and port, then whole path segments. &lt;code&gt;/v1&lt;/code&gt; includes &lt;code&gt;/v1/notes&lt;/code&gt;, but not &lt;code&gt;/v10&lt;/code&gt;. It covers ordinary &lt;code&gt;ConnectionRequest&lt;/code&gt; calls, the REST builder and generated clients through the network layer.&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%2Fmermaid.ink%2Fimg%2Fc2VxdWVuY2VEaWFncmFtCiAgICBwYXJ0aWNpcGFudCBBcHAgYXMgQXBwIHJlcXVlc3RzCiAgICBwYXJ0aWNpcGFudCBBdXRoIGFzIFJlcXVlc3QgYXV0aG9yaXplcgogICAgcGFydGljaXBhbnQgU2VydmVyIGFzIEJhY2tlbmQKICAgIEFwcC0-PkF1dGg6IFF1ZXVlIHNldmVyYWwgcmVxdWVzdHMKICAgIEF1dGgtPj5TZXJ2ZXI6IE9uZSByZWZyZXNoIGV4Y2hhbmdlCiAgICBTZXJ2ZXItLT4-QXV0aDogTmV3IGFjY2VzcyBhbmQgcmVmcmVzaCB0b2tlbnMKICAgIEF1dGgtPj5BdXRoOiBQZXJzaXN0IHRva2VucwogICAgQXV0aC0-PlNlcnZlcjogU2VuZCB3YWl0aW5nIHJlcXVlc3RzCiAgICBTZXJ2ZXItLT4-QXBwOiBEZWxpdmVyIHJlc3BvbnNlcw%3D%3D%3Ftype%3Dpng%26bgColor%3Dffffff" 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%2Fmermaid.ink%2Fimg%2Fc2VxdWVuY2VEaWFncmFtCiAgICBwYXJ0aWNpcGFudCBBcHAgYXMgQXBwIHJlcXVlc3RzCiAgICBwYXJ0aWNpcGFudCBBdXRoIGFzIFJlcXVlc3QgYXV0aG9yaXplcgogICAgcGFydGljaXBhbnQgU2VydmVyIGFzIEJhY2tlbmQKICAgIEFwcC0-PkF1dGg6IFF1ZXVlIHNldmVyYWwgcmVxdWVzdHMKICAgIEF1dGgtPj5TZXJ2ZXI6IE9uZSByZWZyZXNoIGV4Y2hhbmdlCiAgICBTZXJ2ZXItLT4-QXV0aDogTmV3IGFjY2VzcyBhbmQgcmVmcmVzaCB0b2tlbnMKICAgIEF1dGgtPj5BdXRoOiBQZXJzaXN0IHRva2VucwogICAgQXV0aC0-PlNlcnZlcjogU2VuZCB3YWl0aW5nIHJlcXVlc3RzCiAgICBTZXJ2ZXItLT4-QXBwOiBEZWxpdmVyIHJlc3BvbnNlcw%3D%3D%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="734" height="473"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Renewal starts before expiry by default. Requests queued during the exchange wait for its result. If a token-bearing request receives &lt;code&gt;401&lt;/code&gt;, the authorizer can refresh and retry it once; the caller receives the eventual response. A refused refresh ends the session, clears stored tokens and calls the sign-in listener. If your request writes a body from a stream, verify that your implementation can supply it again before depending on a retry.&lt;/p&gt;

&lt;p&gt;For redirects followed by Codename One, the token is removed outside the registered origin or path. The current iOS port lets the system follow redirects before application code sees them. Keep protected API redirects within hosts you control; do not infer that every transport has the same redirect interception point.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choose the state your deployment can share
&lt;/h2&gt;

&lt;p&gt;The backend includes form login, HTTP Basic, API keys, JWT resource servers, OAuth login and authorization-server support. MFA includes TOTP and recovery codes; passkey endpoints use WebAuthn. These are separate mechanisms with separate configuration, not one switch that makes every deployment secure.&lt;/p&gt;

&lt;p&gt;Several instances cannot share an in-memory token repository or rate limiter. Declare the database-backed stores when that is your deployment shape. &lt;code&gt;cn1.security.schema.enabled=true&lt;/code&gt; belongs in the module's build-time &lt;code&gt;application.properties&lt;/code&gt;; it registers the security migrations. It creates tables, but &lt;strong&gt;does not select JDBC stores for you&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Refresh tokens and one-use codes must be consumed atomically. Otherwise two instances can both accept the same secret. The JDBC implementations use conditional database operations and their affected-row counts for those decisions. TOTP secrets are sealed under the configured encryption key; password hashes and one-use recovery codes have their own storage rules.&lt;/p&gt;

&lt;p&gt;The packaged server links the mechanisms its chains reach. A token-verifying API need not carry login pages and a password store. That is a useful consequence of explicit configuration and dead-code removal, although it does not reduce the need to review the rules you enabled.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test the failure after the successful login
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://github.com/codenameone/CodenameOne/tree/ebe0e64be3/scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone/tests/backend" rel="noopener noreferrer"&gt;end-to-end client tests&lt;/a&gt; connect to the HelloCodenameOne backend. &lt;code&gt;BackendTokenRefreshTest&lt;/code&gt; deliberately replaces an access token with one the server did not sign. It checks that the generated client receives one successful response after renewal, that the refresh token rotates, and that reusing a spent refresh token ends the session.&lt;/p&gt;

&lt;p&gt;That proves more than a mock that always supplies an authenticated user. It exercises the client's networking and token state together with the server's token endpoint and protected route.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Test layer&lt;/th&gt;
&lt;th&gt;What it can establish&lt;/th&gt;
&lt;th&gt;What it does not establish&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Backend &lt;code&gt;MockMvc&lt;/code&gt; and mock identities&lt;/td&gt;
&lt;td&gt;Route and method authorization decisions&lt;/td&gt;
&lt;td&gt;A provider login or signature verification when identity is injected&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Native client integration tests&lt;/td&gt;
&lt;td&gt;Requests, token exchange, renewal and denial against the backend&lt;/td&gt;
&lt;td&gt;Every external identity provider or device sign-in surface&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Browser-specific tests&lt;/td&gt;
&lt;td&gt;Browser networking and credential APIs, including the virtual-authenticator vault test&lt;/td&gt;
&lt;td&gt;Passing the native credential/redirect harness unchanged&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Port test matrix&lt;/td&gt;
&lt;td&gt;The declared cases on each supported target&lt;/td&gt;
&lt;td&gt;Every security mechanism on every port&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The credential/redirect tests explicitly report a browser skip because the browser owns those cookies and redirects. A green port suite must be read with those skips. WebAuthn PRF bridge coverage uses Chromium's virtual authenticator; it is not a claim that every hardware authenticator supports that extension.&lt;/p&gt;

&lt;p&gt;SAML, LDAP, opaque-token introspection and several authorization-server extensions are outside this release. The &lt;a href="https://www.codenameone.com/developer-guide/backend-security/" rel="noopener noreferrer"&gt;backend security guide&lt;/a&gt; and &lt;a href="https://www.codenameone.com/developer-guide/authentication-and-identity/" rel="noopener noreferrer"&gt;client identity guide&lt;/a&gt; specify the supported surface. &lt;a href="https://github.com/codenameone/CodenameOne/pull/5961" rel="noopener noreferrer"&gt;PR #5961&lt;/a&gt; connects those pieces; tomorrow we will look at the migrations underneath their persistent stores.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discussion
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Which failure is hardest to reproduce in your sign-in flow: renewal, revocation, or returning from the provider?&lt;/em&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>Android on iOS, Web &amp; Desktop Efficiently and for Free</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Fri, 16 Oct 2026 17:23:44 +0000</pubDate>
      <link>https://dev.to/codenameone/android-on-ios-web-desktop-efficiently-and-for-free-3fp6</link>
      <guid>https://dev.to/codenameone/android-on-ios-web-desktop-efficiently-and-for-free-3fp6</guid>
      <description>&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhlv47k8of2vr6we1fz0e.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fhlv47k8of2vr6we1fz0e.jpg" alt="Android application screens reach phones, desktop and the browser" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We are not the first to bring Android to iOS. Earlier projects brought Android's runtime along with the application. Dalvik was built for Android, including a just-in-time compiler (JIT) that ordinary iOS apps are &lt;a href="https://support.apple.com/guide/security/security-of-runtime-process-sec15bfe098e/web" rel="noopener noreferrer"&gt;not permitted to use&lt;/a&gt;. Disabling it removes an important source of &lt;a href="https://android-developers.googleblog.com/2010/05/android-22-and-developers-goodies.html" rel="noopener noreferrer"&gt;Dalvik's performance&lt;/a&gt;. Dalvik was designed around interpretation and JIT compilation, not ahead-of-time compilation (AOT), so taking away the JIT leaves an interpreter rather than an optimized native app.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Our approach is different. We compile the application into native code for iOS and remove the code it does not use. It becomes a proper iOS application, with access to OS capabilities through Codename One APIs and native interfaces. You get native execution, a UI you control and a path to improve the application after the initial port.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You can now compile classic Android apps into native Codename One apps.&lt;/strong&gt; Activities, XML layouts and AndroidX code work on iOS, desktop and the web through the compatibility layer. Each imported view is a Codename One component underneath, so you can replace screens individually and mix the two APIs as you migrate.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.codenameone.com/android-compatibility-demo/" rel="noopener noreferrer"&gt;Try the imported gallery in your browser&lt;/a&gt;. The same gallery produces an &lt;strong&gt;8.73 MB compressed iOS app bundle&lt;/strong&gt;, including the compatibility code it uses and the runtime. We built both versions locally; the table below separates the compressed bundle from its unpacked size.&lt;/p&gt;

&lt;p&gt;The framework and compatibility layer are &lt;a href="https://www.codenameone.com/faq/" rel="noopener noreferrer"&gt;open source and free for commercial use&lt;/a&gt;. You can build locally with your own tools. The optional cloud build service has &lt;a href="https://www.codenameone.com/pricing/" rel="noopener noreferrer"&gt;free and paid plans&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Coming This Week
&lt;/h2&gt;

&lt;p&gt;Today we cover the Android migration path, with real gallery screens and the build pipeline. This week's follow-ups cover full stack authentication, database upgrades, testing and the tools that make these applications practical to build:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Saturday, October 10:&lt;/strong&gt; &lt;a href="https://www.codenameone.com/blog/sign-in-client-server-contract/" rel="noopener noreferrer"&gt;Easy Full Stack Authentication and Login in the Style of Spring&lt;/a&gt;. Token renewal, access rules and tests that cross the network.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sunday, October 11:&lt;/strong&gt; &lt;a href="https://www.codenameone.com/blog/database-upgrades-without-lost-data/" rel="noopener noreferrer"&gt;Update DB Schema in Production with Flyway like Syntax&lt;/a&gt;. Ordered migrations for devices that skip releases and servers that share a database.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monday, October 12:&lt;/strong&gt; &lt;a href="https://www.codenameone.com/blog/spring-tests-native-backend/" rel="noopener noreferrer"&gt;How Do You Test Spring Compatibility?&lt;/a&gt;. Write behavior tests in Spring, then keep the same assertions when you port to Codename One.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tuesday, October 13:&lt;/strong&gt; &lt;a href="https://www.codenameone.com/blog/playground-real-java-compiler/" rel="noopener noreferrer"&gt;Try Real Java in Your Browser, Compiler Errors Included&lt;/a&gt;. The compiler and translator now run inside the Playground page.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wednesday, October 14:&lt;/strong&gt; &lt;a href="https://www.codenameone.com/blog/gc-memory-budget-performance/" rel="noopener noreferrer"&gt;PGO for Faster Apps and Generational/Adaptive GC&lt;/a&gt;. Profile-guided native builds for Pro and Enterprise, plus adaptive GC and performance gates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Thursday, October 15:&lt;/strong&gt; &lt;a href="https://www.codenameone.com/blog/maven-projects-with-less-scaffolding/" rel="noopener noreferrer"&gt;Smaller Simpler Maven Projects&lt;/a&gt;. Minimal projects are now the default, with platform modules added when you need them.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Keep the app working while you change it
&lt;/h2&gt;

&lt;p&gt;A migration usually has an awkward middle. The original application still needs releases while its replacement slowly acquires features. For a small team, that can mean maintaining two incomplete products.&lt;/p&gt;

&lt;p&gt;The compatibility layer lets you start with the existing application structure. From a Codename One Maven project, import an Android Studio module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mvn cn1:import-android-project &lt;span class="nt"&gt;-Dcn1&lt;/span&gt;.android.import&lt;span class="o"&gt;=&lt;/span&gt;/path/to/MyAndroidApp
mvn cn1:run
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The importer copies the module's &lt;code&gt;src/main&lt;/code&gt;, reconciles its namespace with the manifest and reports which Gradle dependencies the compatibility runtime covers. Read that report before deciding the app is portable. A successful import is the start of testing, not a certificate for every screen.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.codenameone.com/developer-guide/android-interop/" rel="noopener noreferrer"&gt;Android interop guide&lt;/a&gt; covers manual import, Kotlin sources and the supported APIs. Use a Codename One release containing this change, or build current master while it is being released.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you are looking at
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.codenameone.com/developer-guide/img/android-interop-main.png" rel="noopener noreferrer"&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7kfjbzrtbockse4nt6sx.png" alt="The gallery launcher built with the Android framework on the left and Codename One on the right" width="736" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These are captures of the same gallery sources on the same Android 16 emulator, at 720 by 1600 pixels and 320 dpi. The left app uses the Android framework. The right app uses the compatibility layer. They are &lt;strong&gt;both Android captures&lt;/strong&gt;; this comparison measures the rendering difference, not iOS behavior.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.codenameone.com/developer-guide/img/android-interop-material.png" rel="noopener noreferrer"&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8h925jdmek40gd8c63kv.png" alt="Material buttons, inputs, chips and cards from the same sources in the Android and Codename One builds" width="736" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are visible differences. The Codename One Android build uses a narrower bold face, draws under the status bar differently and has its own Material shadows. Custom rendering gives you control over those pixels across platforms; it does not make every widget pixel-identical to Android.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try the Android gallery in your browser
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://www.codenameone.com/android-compatibility-demo/" rel="noopener noreferrer"&gt;Open the interactive Android gallery&lt;/a&gt;, or &lt;a href="https://www.codenameone.com/blog/release-2026-10-09/android-gallery-source.zip" rel="noopener noreferrer"&gt;download its source project&lt;/a&gt;. Tap &lt;strong&gt;Count&lt;/strong&gt;, open &lt;strong&gt;Details&lt;/strong&gt;, then use the overflow menu to try the other screens. This is the imported Android sample compiled to JavaScript, not a video or a recreation in HTML.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.codenameone.com/blog/release-2026-10-09/android-browser-main.png" rel="noopener noreferrer"&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu45zydzmp3uqvfaur0r7.png" alt="The Android gallery running in Chromium at a phone-sized browser viewport after a counter tap" width="440" height="850"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The browser build at a 440 by 850 viewport. The counter and activity navigation run through the compatibility layer.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Is it Efficient?
&lt;/h2&gt;

&lt;p&gt;We built the gallery from master revision &lt;code&gt;ebe0e64be3&lt;/code&gt; as an unsigned ARM64 Release device app with Xcode 27.0, then enabled release symbol stripping. It contains the sample's activities and resources, the reachable compatibility code, Codename One and the native runtime.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Artifact&lt;/th&gt;
&lt;th&gt;Size, 1 MB = 1,048,576 bytes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;ARM64 native executable, stripped&lt;/td&gt;
&lt;td&gt;19.19 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;App bundle, sum of file sizes&lt;/td&gt;
&lt;td&gt;22.33 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ZIP of that unsigned app bundle&lt;/td&gt;
&lt;td&gt;8.73 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The build relocated &lt;strong&gt;1,316 compatibility classes&lt;/strong&gt; and emitted &lt;strong&gt;862 compatibility translation units&lt;/strong&gt; for this app. Across the whole application, its method-removal pass discarded &lt;strong&gt;24,616 methods&lt;/strong&gt;, and field elimination removed &lt;strong&gt;220 never-read instance fields&lt;/strong&gt;. Those counts describe different stages; they are not a measurement of bytes saved by the Android layer alone.&lt;/p&gt;

&lt;p&gt;The ZIP is a compression measurement, &lt;strong&gt;not an IPA, an App Store download estimate or an installed-device measurement&lt;/strong&gt;. Signing and distribution change packaging. We built the device binary locally but did not run it on a physical iPhone. The &lt;a href="https://www.codenameone.com/blog/release-2026-10-09/android-ios-size.json" rel="noopener noreferrer"&gt;measurement record&lt;/a&gt; includes exact byte counts, configuration and the executable hash. The &lt;a href="https://www.codenameone.com/blog/release-2026-10-09/README.txt" rel="noopener noreferrer"&gt;reproduction notes&lt;/a&gt; describe the build.&lt;/p&gt;

&lt;p&gt;This is the gallery's footprint, not a minimum hello-world figure. It demonstrates what the retained Android-compatible surface costs in a real package without claiming that the package contains the whole Android OS.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Android code reaches an iPhone
&lt;/h2&gt;

&lt;p&gt;A runtime-container approach preserves an Android execution environment around the app. Our build resolves the supported APIs into the application's own code. There is no Dalvik or ART runtime bundled with this layer.&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%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IFRECiAgICBTb3VyY2VbQW5kcm9pZCBzb3VyY2UgYW5kIHJlc291cmNlc10gLS0-IFJlc291cmNlc1tDb21waWxlIFhNTCwgUiBjbGFzc2VzIGFuZCBmYWN0b3JpZXNdCiAgICBSZXNvdXJjZXMgLS0-IENvbXBpbGVbQ29tcGlsZSBhZ2FpbnN0IEFuZHJvaWQgY29tcGF0aWJpbGl0eSBBUEldCiAgICBDb21waWxlIC0tPiBSZW1hcFtSZWxvY2F0ZSBBbmRyb2lkIHBhY2thZ2UgcmVmZXJlbmNlc10KICAgIFJlbWFwIC0tPiBMaW5rW0tlZXAgcmVhY2hhYmxlIGFwcGxpY2F0aW9uIGFuZCBydW50aW1lIGNvZGVdCiAgICBMaW5rIC0tPiBOYXRpdmVbUGFycGFyVk0gbmF0aXZlIG91dHB1dCBmb3IgaU9TXQogICAgTGluayAtLT4gV2ViW0phdmFTY3JpcHQgYnJvd3NlciBhcHBsaWNhdGlvbl0KICAgIExpbmsgLS0-IERlc2t0b3BbQ29kZW5hbWUgT25lIGRlc2t0b3AgdGFyZ2V0c10%3D%3Ftype%3Dpng%26bgColor%3Dffffff" 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%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IFRECiAgICBTb3VyY2VbQW5kcm9pZCBzb3VyY2UgYW5kIHJlc291cmNlc10gLS0-IFJlc291cmNlc1tDb21waWxlIFhNTCwgUiBjbGFzc2VzIGFuZCBmYWN0b3JpZXNdCiAgICBSZXNvdXJjZXMgLS0-IENvbXBpbGVbQ29tcGlsZSBhZ2FpbnN0IEFuZHJvaWQgY29tcGF0aWJpbGl0eSBBUEldCiAgICBDb21waWxlIC0tPiBSZW1hcFtSZWxvY2F0ZSBBbmRyb2lkIHBhY2thZ2UgcmVmZXJlbmNlc10KICAgIFJlbWFwIC0tPiBMaW5rW0tlZXAgcmVhY2hhYmxlIGFwcGxpY2F0aW9uIGFuZCBydW50aW1lIGNvZGVdCiAgICBMaW5rIC0tPiBOYXRpdmVbUGFycGFyVk0gbmF0aXZlIG91dHB1dCBmb3IgaU9TXQogICAgTGluayAtLT4gV2ViW0phdmFTY3JpcHQgYnJvd3NlciBhcHBsaWNhdGlvbl0KICAgIExpbmsgLS0-IERlc2t0b3BbQ29kZW5hbWUgT25lIGRlc2t0b3AgdGFyZ2V0c10%3D%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="896" height="734"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The resource compiler generates constructors for activities and layout classes. Inflation can call those constructors directly, without discovering classes by reflection. A later pass moves &lt;code&gt;android.*&lt;/code&gt;, AndroidX and Material references under the compatibility namespace. Even the Android target uses that relocated implementation, avoiding a collision with the OS classes.&lt;/p&gt;

&lt;p&gt;ParparVM can then remove unreachable classes and methods from the native build. Supporting an API in the library does not mean every application must retain every implementation. Resources, reachable screens and native libraries still cost space. This is why an app measurement matters more than the size of the compatibility JAR.&lt;/p&gt;

&lt;p&gt;The result is an application you can develop beyond its Android origins. The compatibility layer gets the existing screens running; Codename One APIs and native interfaces give you somewhere to go next.&lt;/p&gt;

&lt;h2&gt;
  
  
  Replace one screen, keep the next
&lt;/h2&gt;

&lt;p&gt;The following excerpt from the &lt;a href="https://github.com/codenameone/CodenameOne/blob/ebe0e64be3/docs/demos/android-interop/src/main/java/com/codenameone/developerguide/androidinterop/EmbedAndroidView.java" rel="noopener noreferrer"&gt;compiled interop example&lt;/a&gt; puts an Android custom view inside a Codename One form. &lt;code&gt;androidContext&lt;/code&gt; is the installed Android runtime's context, and &lt;code&gt;MyChartView&lt;/code&gt; is the example's &lt;code&gt;android.view.View&lt;/code&gt; subclass:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;android&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;view&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;View&lt;/span&gt; &lt;span class="n"&gt;chart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MyChartView&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;androidContext&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;add&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;BorderLayout&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;CENTER&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;chart&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getPeer&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reverse direction matters too. A Codename One app can install an imported module and start one of its activities. Here &lt;code&gt;SettingsActivity&lt;/code&gt; belongs to that module:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;AndroidRuntime&lt;/span&gt; &lt;span class="n"&gt;rt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AndroidRuntime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInstance&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;rt&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;rt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;AndroidRuntime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;install&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;codename1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;generated&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="na"&gt;AndroidAppImpl&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="nc"&gt;Context&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getApplication&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="nc"&gt;Intent&lt;/span&gt; &lt;span class="n"&gt;intent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Intent&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;SettingsActivity&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;intent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addFlags&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Intent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;FLAG_ACTIVITY_NEW_TASK&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;startActivity&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;intent&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the final activity finishes, the Codename One form that opened it returns. You can build new navigation in Codename One while keeping an imported screen for settings or data entry. Shared application logic can call Codename One APIs directly as you remove dependencies on Android-specific behavior.&lt;/p&gt;

&lt;p&gt;Start with one complete user journey. Check its inputs, back navigation and persistence on each target. Then choose the next screen based on product value. The migration can stop at a useful intermediate state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Find the boundary before it finds you
&lt;/h2&gt;

&lt;p&gt;Import requires the application's source. APK execution and Compose UI are outside this layer.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Parcel&lt;/code&gt; is an in-process value container here; it does not reproduce Android IPC or serialization snapshots. SQLite uses one connection, so concurrent database work needs care. Reflection-based class loading cannot be carried onto iOS. &lt;code&gt;MotionLayout&lt;/code&gt; and &lt;code&gt;CoordinatorLayout&lt;/code&gt; are outside the supported surface, and some Material details differ.&lt;/p&gt;

&lt;p&gt;Those are reasons to read the &lt;a href="https://www.codenameone.com/developer-guide/android-interop/" rel="noopener noreferrer"&gt;limitations&lt;/a&gt;, not reasons to rewrite every supported screen. The gallery and port tests give you concrete starting coverage. Your dependencies and workflows determine the rest. The implementation is in &lt;a href="https://github.com/codenameone/CodenameOne/pull/5941" rel="noopener noreferrer"&gt;PR #5941&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  More This Week
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Easy Full Stack Authentication and Login in the Style of Spring
&lt;/h3&gt;

&lt;p&gt;Last week's &lt;a href="https://www.codenameone.com/blog/java-server-work-before-startup/" rel="noopener noreferrer"&gt;native backend&lt;/a&gt; gave us the application structure. This week adds a security stack that connects server access rules to the app making the requests. It includes form login, JWT resource servers and OAuth, with MFA and passkey support for applications that need them.&lt;/p&gt;

&lt;p&gt;If you have used Spring Security, the configuration will look familiar. This excerpt from the &lt;a href="https://github.com/codenameone/CodenameOne/blob/ebe0e64be3/docs/demos/backend/src/main/java/com/codenameone/developerguide/backend/security/SecurityConfig.java" rel="noopener noreferrer"&gt;security example&lt;/a&gt; defines a stateless API and requires a scope for reading orders:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Bean&lt;/span&gt;
&lt;span class="nd"&gt;@Order&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nc"&gt;SecurityFilterChain&lt;/span&gt; &lt;span class="nf"&gt;api&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;HttpSecurity&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;securityMatcher&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/**"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;authorizeHttpRequests&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;auth&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;requestMatchers&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/api/orders/**"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;hasAuthority&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SCOPE_orders:read"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;anyRequest&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;authenticated&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sessionManagement&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sessionCreationPolicy&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SessionCreationPolicy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;STATELESS&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
        &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;oauth2ResourceServer&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;oauth2&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;oauth2&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;jwt&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Customizer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;withDefaults&lt;/span&gt;&lt;span class="o"&gt;()));&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;build&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;On the client, &lt;code&gt;OidcRequestAuthorizer&lt;/code&gt; attaches credentials to requests for your API and coordinates token renewal. If several requests need a fresh token at once, they wait for one refresh exchange. A rejected refresh clears the session and returns control to your sign-in UI.&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%2Fmermaid.ink%2Fimg%2Fc2VxdWVuY2VEaWFncmFtCiAgICBwYXJ0aWNpcGFudCBBcHAgYXMgQ2xpZW50IHJlcXVlc3RzCiAgICBwYXJ0aWNpcGFudCBBdXRoIGFzIFJlcXVlc3QgYXV0aG9yaXplcgogICAgcGFydGljaXBhbnQgQVBJIGFzIEJhY2tlbmQKICAgIEFwcC0-PkF1dGg6IFJlcXVlc3RzIG5lZWQgYSBmcmVzaCB0b2tlbgogICAgQXV0aC0-PkFQSTogT25lIHJlZnJlc2ggZXhjaGFuZ2UKICAgIEFQSS0tPj5BdXRoOiBSb3RhdGVkIHRva2VucwogICAgQXV0aC0-PkFQSTogU2VuZCB3YWl0aW5nIHJlcXVlc3RzCiAgICBBUEktLT4-QXBwOiBQcm90ZWN0ZWQgcmVzcG9uc2Vz%3Ftype%3Dpng%26bgColor%3Dffffff" 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%2Fmermaid.ink%2Fimg%2Fc2VxdWVuY2VEaWFncmFtCiAgICBwYXJ0aWNpcGFudCBBcHAgYXMgQ2xpZW50IHJlcXVlc3RzCiAgICBwYXJ0aWNpcGFudCBBdXRoIGFzIFJlcXVlc3QgYXV0aG9yaXplcgogICAgcGFydGljaXBhbnQgQVBJIGFzIEJhY2tlbmQKICAgIEFwcC0-PkF1dGg6IFJlcXVlc3RzIG5lZWQgYSBmcmVzaCB0b2tlbgogICAgQXV0aC0-PkFQSTogT25lIHJlZnJlc2ggZXhjaGFuZ2UKICAgIEFQSS0tPj5BdXRoOiBSb3RhdGVkIHRva2VucwogICAgQXV0aC0-PkFQSTogU2VuZCB3YWl0aW5nIHJlcXVlc3RzCiAgICBBUEktLT4-QXBwOiBQcm90ZWN0ZWQgcmVzcG9uc2Vz%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="709" height="399"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We test that connection end to end. One test supplies an invalid access token, checks that renewal recovers the request, then tries to reuse a spent refresh token and expects the session to end. The client networking code and backend both participate. Browser credentials and redirects have separate test boundaries, which Saturday's article explains alongside the native port coverage.&lt;/p&gt;

&lt;p&gt;Saturday's &lt;a href="https://www.codenameone.com/blog/sign-in-client-server-contract/" rel="noopener noreferrer"&gt;authentication deep dive&lt;/a&gt; covers the client setup, persistent server stores and failure cases. The &lt;a href="https://www.codenameone.com/developer-guide/backend-security/" rel="noopener noreferrer"&gt;backend security guide&lt;/a&gt; and &lt;a href="https://www.codenameone.com/developer-guide/authentication-and-identity/" rel="noopener noreferrer"&gt;client identity guide&lt;/a&gt; have the configuration details.&lt;/p&gt;

&lt;h3&gt;
  
  
  Update DB Schema in Production with Flyway like Syntax
&lt;/h3&gt;

&lt;p&gt;The backend's security stores need database tables, and those tables need to evolve after deployment. Your app has the same problem: a customer who skips three releases still needs the right local database when they return.&lt;/p&gt;

&lt;p&gt;We now have a shared migration engine for the client and server, using familiar Flyway-style filenames. Keep the original schema in &lt;code&gt;V1__create_note.sql&lt;/code&gt;, then add a new file for the next change:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- V2__add_note_created.sql&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;note&lt;/span&gt; &lt;span class="k"&gt;ADD&lt;/span&gt; &lt;span class="k"&gt;COLUMN&lt;/span&gt; &lt;span class="n"&gt;created&lt;/span&gt; &lt;span class="nb"&gt;BIGINT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="n"&gt;note&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;created&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;created&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the client, put these files in &lt;code&gt;common/src/main/db/migration&lt;/code&gt; and call &lt;code&gt;Migrations.migrate(db)&lt;/code&gt; after opening the database. The annotation ORM does that when it opens the entity manager. On the server, scripts go in the backend module's &lt;code&gt;src/main/resources/db/migration&lt;/code&gt; and run before the application accepts requests.&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%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgICBGcmVzaFtOZXcgaW5zdGFsbGF0aW9uXSAtLT4gVjFbQXBwbHkgVjFdCiAgICBWMSAtLT4gVjJbQXBwbHkgVjJdCiAgICBFeGlzdGluZ1tFeGlzdGluZyBkYXRhYmFzZSBhdCBWMV0gLS0-IFYyCiAgICBWMiAtLT4gUmVhZHlbT3BlbiB0aGUgYXBwbGljYXRpb25d%3Ftype%3Dpng%26bgColor%3Dffffff" 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%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgICBGcmVzaFtOZXcgaW5zdGFsbGF0aW9uXSAtLT4gVjFbQXBwbHkgVjFdCiAgICBWMSAtLT4gVjJbQXBwbHkgVjJdCiAgICBFeGlzdGluZ1tFeGlzdGluZyBkYXRhYmFzZSBhdCBWMV0gLS0-IFYyCiAgICBWMiAtLT4gUmVhZHlbT3BlbiB0aGUgYXBwbGljYXRpb25d%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="897" height="174"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;History and checksums let the engine detect changed scripts. Repeatable migrations handle objects such as views, and a database newer than the application is refused before old code changes its data. For servers, a database lock coordinates instances that start together.&lt;/p&gt;

&lt;p&gt;Sunday's &lt;a href="https://www.codenameone.com/blog/database-upgrades-without-lost-data/" rel="noopener noreferrer"&gt;database migration article&lt;/a&gt; covers baselines, skipped releases and recovery after a failed migration. It also explains why a MySQL schema change and a transactional PostgreSQL migration need different recovery plans. The &lt;a href="https://www.codenameone.com/developer-guide/backend-data/#backend-schema-migrations" rel="noopener noreferrer"&gt;server migration reference&lt;/a&gt; includes Maven commands for inspecting and validating history before deployment.&lt;/p&gt;

&lt;h3&gt;
  
  
  How Do You Test Spring Compatibility?
&lt;/h3&gt;

&lt;p&gt;The Codename One backend uses Spring-style syntax to ease porting, but it is not Spring compatible. How do you know whether the port behaves like the original? Write tests in your Spring application first, then run the same requests and assertions against the Codename One implementation.&lt;/p&gt;

&lt;p&gt;The backend now includes &lt;code&gt;@BackendTest&lt;/code&gt;, dependency injection and a &lt;code&gt;MockMvc&lt;/code&gt; API for carrying those checks across. Change the framework imports and test setup while keeping the behavior you expect. In both applications, the request assertion can look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Autowired&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;MockMvc&lt;/span&gt; &lt;span class="n"&gt;mvc&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@Test&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;greetsByName&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;mvc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;perform&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/greet/{name}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Ada"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;andExpect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;isOk&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt;
            &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;andExpect&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jsonPath&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"$.greeting"&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello, Ada"&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;Run it in Spring before the port, then in Codename One after adapting the test setup. A changed status or response is something to investigate, not a reason to rewrite the expected value. Add denied requests and state-changing operations so the suite covers more than the successful response. &lt;code&gt;TestRestTemplate&lt;/code&gt; can also cross a real HTTP connection when transport behavior is part of the contract.&lt;/p&gt;

&lt;p&gt;The next step is running the tests against compiled native code. The build generates direct test calls and produces reports beside the JVM results. Strict mode makes unsupported test cases fail rather than disappear into a green run. Mockito remains a JVM option; explicit test implementations can run in both environments.&lt;/p&gt;

&lt;p&gt;Monday's &lt;a href="https://www.codenameone.com/blog/spring-tests-native-backend/" rel="noopener noreferrer"&gt;testing article&lt;/a&gt; starts with the Spring test, shows the Codename One changes and finishes with local native test commands. The &lt;a href="https://www.codenameone.com/developer-guide/backend-testing/" rel="noopener noreferrer"&gt;testing guide&lt;/a&gt; documents the supported APIs and the setup for an existing backend module.&lt;/p&gt;

&lt;h3&gt;
  
  
  Try Real Java in Your Browser, Compiler Errors Included
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://www.codenameone.com/playground/" rel="noopener noreferrer"&gt;Playground&lt;/a&gt; now compiles Java as you type. We replaced BeanShell with our Java compiler and the ParparVM translator, both running inside the browser. Source becomes bytecode, the translator produces JavaScript, and the preview loads the new classes. Your source stays in the page.&lt;/p&gt;

&lt;p&gt;That means records and lambdas go through actual type checking. Here is part of the example in Tuesday's post:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;Item&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt;

&lt;span class="nc"&gt;Item&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Item&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Orders"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;Label&lt;/span&gt; &lt;span class="n"&gt;label&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Label&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;": "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Change &lt;code&gt;3&lt;/code&gt; to &lt;code&gt;"three"&lt;/code&gt; and the compiler points to the constructor mismatch. Fix it and the preview can run again. The screenshot below shows that deliberate mistake in the public Playground:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.codenameone.com/blog/release-2026-10-09/playground-type-error.png" rel="noopener noreferrer"&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8go8cooay833amqh9id5.png" alt="The Playground catches a String passed to a record constructor that expects an int" width="800" height="511"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The rebuild also removes 161 generated BeanShell adapter source files, totaling about 23.8 MB. That is removed source code, not a browser download measurement. Incremental translation emits the new user classes without rebuilding the entire host application.&lt;/p&gt;

&lt;p&gt;Tuesday's &lt;a href="https://www.codenameone.com/blog/playground-real-java-compiler/" rel="noopener noreferrer"&gt;Playground article&lt;/a&gt; gives you the full interactive example and explains how a Java compiler can run inside its own translated output. You can &lt;a href="https://www.codenameone.com/playground/" rel="noopener noreferrer"&gt;try it now&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  PGO for Faster Apps and Generational/Adaptive GC
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Profile-guided optimization (PGO) is now available on Pro and Enterprise cloud builds.&lt;/strong&gt; The build service runs your application, records which code executes, then compiles it again using that profile. The compiler can make better decisions about inlining, branch layout and where to place frequently used functions. Your users get the optimized native binary.&lt;/p&gt;

&lt;p&gt;This works for iOS, native macOS and native Linux. The profile applies to translated Java, the Codename One runtime and your native sources. For an iOS build, enable it in &lt;code&gt;codenameone_settings.properties&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;codename1.arg.ios.pgo&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;codename1.arg.pgo.trainingSeconds&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;90&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The service handles both compilations and the training run. If you already have Pro or Enterprise, you can enable it in your next supported cloud build. If you are choosing a plan, PGO is now another reason to &lt;a href="https://www.codenameone.com/pricing/" rel="noopener noreferrer"&gt;look at Pro&lt;/a&gt;: the build service can optimize using execution data from your own app.&lt;/p&gt;

&lt;p&gt;Training is unattended, so the best candidates do representative work from a clean launch. Code behind a login or button may never be reached. Wednesday's article shows how the training works and how to compare the result against your ordinary build.&lt;/p&gt;

&lt;p&gt;ParparVM now switches between concurrent collection and a generational phase when allocation behavior makes that worthwhile. If almost every new object dies quickly, resetting young pages avoids tracing and sweeping those objects individually. The collector can spend less CPU keeping track of garbage.&lt;/p&gt;

&lt;p&gt;Giving that phase more memory can reduce collection frequency. It can also make the next full collection worse. During development, one bad transition retained enough young objects to push a stress test's page count from 3,446 to 10,538. Deciding when to leave the generational phase mattered as much as deciding when to enter it. Low available memory now sends the collector back to the concurrent path.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.codenameone.com/blog/release-2026-10-09/gc-ratios.svg" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fwww.codenameone.com%2Fblog%2Frelease-2026-10-09%2Fgc-ratios.svg" alt="Allocation time and peak memory calibration ratios before and after the GC change" width="944" height="412"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Historical allocation-workload calibration ratios, with JDK 25 equal to 1. Lower is better. These are gate records, not a new controlled benchmark.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The macOS record shows the trade clearly: its time ratio falls from 3.696 to 1.106, while its peak memory ratio rises from 0.481 to 0.937. That is a substantial CPU improvement with a substantial memory cost. It also explains why our performance gate tracks both metrics across its five desktop and server OS/architecture combinations. Mobile and browser behavior have separate port coverage.&lt;/p&gt;

&lt;p&gt;These GC records use no PGO. They measure separate runtime work that benefits ordinary builds too. Wednesday's &lt;a href="https://www.codenameone.com/blog/gc-memory-budget-performance/" rel="noopener noreferrer"&gt;PGO and adaptive GC article&lt;/a&gt; leads with the premium build feature, then explores the collector's memory budget, transition failures and regression gate.&lt;/p&gt;

&lt;h3&gt;
  
  
  Smaller Simpler Maven Projects
&lt;/h3&gt;

&lt;p&gt;Last week we introduced minimal Gradle projects. Maven now gets the same treatment, and the smaller layout is the default for new apps. You start with two POMs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-app/
    pom.xml
    common/
        pom.xml
        codenameone_settings.properties
        src/main/java/
        src/main/resources/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The simulator and platform builds still work from that project. Native implementations can live in directories such as &lt;code&gt;ios/src/main/objectivec&lt;/code&gt; without needing another Maven module. Add a platform POM when you need separate dependencies or build configuration for that target.&lt;/p&gt;

&lt;p&gt;The archetype can generate selected platform modules, all of them, or none. It also has &lt;code&gt;app-with-backend&lt;/code&gt; and &lt;code&gt;backend-only&lt;/code&gt; project types, so a standalone server does not need an empty client module next to it. Existing projects keep their structure.&lt;/p&gt;

&lt;p&gt;Thursday's &lt;a href="https://www.codenameone.com/blog/maven-projects-with-less-scaffolding/" rel="noopener noreferrer"&gt;Maven article&lt;/a&gt; shows the commands, native source locations and optional layouts. The &lt;a href="https://www.codenameone.com/developer-guide/#maven-minimal-layout" rel="noopener noreferrer"&gt;archetype reference&lt;/a&gt; has the configuration details.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Word
&lt;/h2&gt;

&lt;p&gt;This week marks a major milestone for Codename One. For years, we ran on Android. Now we have flipped the script: Android runs on Codename One. The same application can reach iOS, the web and desktop, and its next version can grow beyond the Android APIs it started with. A lot more is coming in this area.&lt;/p&gt;

&lt;p&gt;The backend has made a major leap since last week too. With full stack authentication, database migrations and Spring-style testing in place, we now have a substantial backend implementation nearing beta. You can build the client and server together, then test the connection between them with the same tools you use to build the applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discussion
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Which screen would you move first if your Android app could reach iOS or the browser before the rewrite was finished?&lt;/em&gt;&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>Don't Order Fish on Monday. Don't Release on Friday</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Thu, 15 Oct 2026 17:45:58 +0000</pubDate>
      <link>https://dev.to/codenameone/dont-order-fish-on-monday-dont-release-on-friday-me0</link>
      <guid>https://dev.to/codenameone/dont-order-fish-on-monday-dont-release-on-friday-me0</guid>
      <description>&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc6us5ylpxqjuhdxo6fsy.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc6us5ylpxqjuhdxo6fsy.jpg" alt="A release gate catches a failure before App Store submission" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Anthony Bourdain's famous warning about Monday fish was a warning about the supply chain behind the menu. In his &lt;a href="https://www.newyorker.com/magazine/1999/04/19/dont-eat-before-reading-this" rel="noopener noreferrer"&gt;1999 essay&lt;/a&gt;, he described seafood bought for the weekend still being served on Monday. Knowing how the kitchen worked changed what he would order.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Developers have a similar rule: never release on Friday. If something breaks, you have just made plans for everyone's weekend. It is excellent advice that we have ignored for years.&lt;/p&gt;

&lt;p&gt;We settled on Friday releases to avoid dropping a framework update into the middle of our customers' release week. The idea was to use the quieter window to test and repair our release before it collided with theirs. That assumes a lot about when other people ship. It has also cost me plenty of weekends.&lt;/p&gt;

&lt;p&gt;This time the problems consumed much of the week. There were regressions around the optional Xcode 27 path, push, and windows. Some overlapped, so a fix could make the original symptom disappear while the underlying problem resurfaced elsewhere. That is exhausting for us, and it is worse for the customer trying to finish an app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Catch a rejection your device tests can miss
&lt;/h2&gt;

&lt;p&gt;One failure was especially instructive. Apps using particular crypto APIs could compile, link and run, then fail App Store Connect submission because the binary imported non-public symbols. Most apps did not enable the affected code, so most users and our usual tests never encountered it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;_CCCryptorGCMAddAAD
_CCCryptorGCMAddIV
_CCCryptorGCMFinal
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The SDK's libraries exported these symbols. The linker could resolve them. But they were not declared in the public headers. A successful link did not establish that they were public APIs suitable for submission.&lt;/p&gt;

&lt;p&gt;Our tests had another blind spot: the calls sat behind an optional feature gate that the sample app did not normally enable. Testing the same sample more often would not make that branch appear.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/codenameone/CodenameOne/blob/master/scripts/check-ios-private-api.py" rel="noopener noreferrer"&gt;new checker source&lt;/a&gt; preserves the rejection and the causal chain. It is a more useful record than a claim that we “added more tests.”&lt;/p&gt;

&lt;h2&gt;
  
  
  How your build could enable a feature you never requested
&lt;/h2&gt;

&lt;p&gt;The builder enabled a feature by replacing a preprocessor definition. A plain string replacement for one name also matched the start of a longer name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;#define CN1_INCLUDE_CRYPTO
#define CN1_INCLUDE_CRYPTO_GCM
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enabling general crypto could therefore enable the GCM path too. The customer did not have to request that optional path to compile its imports.&lt;/p&gt;

&lt;p&gt;The fix makes the builder match the whole definition name. A focused regression test guards that behavior. The GCM implementation itself was rewritten using public CommonCrypto operations and a C implementation of GHASH, the authentication calculation. The authenticated-decryption path verifies the tag before releasing decrypted output.&lt;/p&gt;

&lt;p&gt;A separate sweep found &lt;code&gt;sqlite3_key&lt;/code&gt; and &lt;code&gt;sqlite3_rekey&lt;/code&gt; imports that were valid for the bundled cipher engine but not public declarations in Apple's SQLite headers. Those imports now depend on the bundled engine defining them. One rejected upload exposed more than one place where “the symbol links” had stood in for “the symbol is public.”&lt;/p&gt;

&lt;h2&gt;
  
  
  How we will catch this class of failure before it reaches you
&lt;/h2&gt;

&lt;p&gt;A regression test for the three names would catch those three names returning. We also need a check for the broader mistake: importing an SDK symbol that has no public declaration.&lt;/p&gt;

&lt;p&gt;The new gate asks that question in two ways:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Check&lt;/th&gt;
&lt;th&gt;What it sees&lt;/th&gt;
&lt;th&gt;Why it is needed&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Source mode&lt;/td&gt;
&lt;td&gt;Native port sources with feature gates enabled and optional-engine variants&lt;/td&gt;
&lt;td&gt;The ordinary sample does not exercise every feature combination&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Binary mode&lt;/td&gt;
&lt;td&gt;Imports in a linked Release device app and the covered bundled components&lt;/td&gt;
&lt;td&gt;The final link can contain code beyond the port source sweep&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%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IFRECiAgICBTb3VyY2VbTmF0aXZlIHNvdXJjZXMgYW5kIGZlYXR1cmUgdmFyaWFudHNdIC0tPiBDb21waWxlW0NvbXBpbGUgdGhlIGNvdmVyZWQgY29uZmlndXJhdGlvbnNdCiAgICBDb21waWxlIC0tPiBJbXBvcnRzW0NvbGxlY3QgU0RLIGltcG9ydHNdCiAgICBCaW5hcnlbTGlua2VkIFJlbGVhc2UgZGV2aWNlIGFwcF0gLS0-IEltcG9ydHMKICAgIFNES1tQdWJsaWMgU0RLIGhlYWRlcnNdIC0tPiBDb21wYXJlW0NvbXBhcmUgZXhwb3J0ZWQgbmFtZXMgd2l0aCBkZWNsYXJhdGlvbnNdCiAgICBJbXBvcnRzIC0tPiBDb21wYXJlCiAgICBDb21wYXJlIC0tPiBGYWlsW0ZhaWwgb24gdW5kZWNsYXJlZCBpbXBvcnRlZCBzeW1ib2xzXQogICAgUHJvYmVbS25vd24gcHJpdmF0ZSBhbmQgcHVibGljIGNvbnRyb2wgc3ltYm9sc10gLS0-IFZlcmlmeVtWZXJpZnkgdGhlIGNoZWNrZXIgY2FuIGRpc3Rpbmd1aXNoIHRoZW1dCiAgICBWZXJpZnkgLS0-IENvbXBhcmU%3D%3Ftype%3Dpng%26bgColor%3Dffffff" 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%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IFRECiAgICBTb3VyY2VbTmF0aXZlIHNvdXJjZXMgYW5kIGZlYXR1cmUgdmFyaWFudHNdIC0tPiBDb21waWxlW0NvbXBpbGUgdGhlIGNvdmVyZWQgY29uZmlndXJhdGlvbnNdCiAgICBDb21waWxlIC0tPiBJbXBvcnRzW0NvbGxlY3QgU0RLIGltcG9ydHNdCiAgICBCaW5hcnlbTGlua2VkIFJlbGVhc2UgZGV2aWNlIGFwcF0gLS0-IEltcG9ydHMKICAgIFNES1tQdWJsaWMgU0RLIGhlYWRlcnNdIC0tPiBDb21wYXJlW0NvbXBhcmUgZXhwb3J0ZWQgbmFtZXMgd2l0aCBkZWNsYXJhdGlvbnNdCiAgICBJbXBvcnRzIC0tPiBDb21wYXJlCiAgICBDb21wYXJlIC0tPiBGYWlsW0ZhaWwgb24gdW5kZWNsYXJlZCBpbXBvcnRlZCBzeW1ib2xzXQogICAgUHJvYmVbS25vd24gcHJpdmF0ZSBhbmQgcHVibGljIGNvbnRyb2wgc3ltYm9sc10gLS0-IFZlcmlmeVtWZXJpZnkgdGhlIGNoZWNrZXIgY2FuIGRpc3Rpbmd1aXNoIHRoZW1dCiAgICBWZXJpZnkgLS0-IENvbXBhcmU%3D%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="960" height="606"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The checker uses SDK export information to identify the library symbols and scans public headers with comments removed. A name mentioned in a comment is not accepted as a declaration. It also exercises known private and public controls before trusting a clean result. A check that can never report a failure is a very comforting way to learn nothing.&lt;/p&gt;

&lt;p&gt;For a local inspection of a linked app, the script exposes a binary mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 scripts/check-ios-private-api.py &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--binary&lt;/span&gt; /path/to/Release-iphoneos/MyApp.app &lt;span class="nt"&gt;--sdk&lt;/span&gt; iphoneos
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Replace the example path with your Release device artifact; use &lt;code&gt;--developer-dir&lt;/code&gt; if the SDK belongs to a different Xcode installation. The repository wires source mode and Release binary mode into its iOS checks. &lt;a href="https://github.com/codenameone/CodenameOne/pull/5923" rel="noopener noreferrer"&gt;PR #5923&lt;/a&gt; contains the implementation and the builder repair.&lt;/p&gt;

&lt;h2&gt;
  
  
  Know what your submission check covers
&lt;/h2&gt;

&lt;p&gt;This is an imported-symbol check for the covered C and Objective-C surface. It skips Swift and C++ mangled names that cannot be matched by this header scan, and it does not inspect private Objective-C selectors. The binary check in the repository runs against the covered sample and bundled components, not every arbitrary dependency a customer might add.&lt;/p&gt;

&lt;p&gt;It also cannot predict every App Store review decision. Entitlements, privacy declarations and application behavior are different questions. Checking imports catches one specific reason for rejection before you upload.&lt;/p&gt;

&lt;p&gt;Review those remaining checks separately when preparing your submission.&lt;/p&gt;

&lt;h2&gt;
  
  
  We owe you more reliable releases
&lt;/h2&gt;

&lt;p&gt;This week was hard. I spent too much of it chasing regressions, and some of you spent time proving to us that a fix had not really fixed your problem. That is time you should have been able to spend on your own apps. Our release process needs to do better.&lt;/p&gt;

&lt;p&gt;Changing the day we ship would not repair the gaps that let these failures through. Our plan is to strengthen the gates around the things we deliver: exercise optional configurations, check the final artifacts, and test rules that cover a whole class of mistakes. We still need the small regression test for each bug. We also need to ask why that bug could reach a customer while the checks were green.&lt;/p&gt;

&lt;p&gt;The private API gate is one concrete step in that plan. It checks feature combinations our regular sample missed, examines a linked Release app, and uses known control symbols to verify that the checker can actually fail. The wider work is ongoing; one new gate does not solve every regression involving Xcode, push, or windows.&lt;/p&gt;

&lt;p&gt;I cannot promise next week will be quiet. I can promise that a difficult week should leave us with stronger release checks, not just a longer list of closed bugs. As that coverage grows, fewer failures should depend on a customer finding the right combination of APIs for us.&lt;/p&gt;

&lt;p&gt;This closes the &lt;a href="https://www.codenameone.com/blog/java-server-work-before-startup/" rel="noopener noreferrer"&gt;weekly series&lt;/a&gt;. I am excited about the backend and the UI improvements, but you should be able to try them without wondering what else an update will break. That is the release quality we need to earn.&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>Do You Want Your Web App to Feel Like a Native App?</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Wed, 14 Oct 2026 17:59:45 +0000</pubDate>
      <link>https://dev.to/codenameone/do-you-want-your-web-app-to-feel-like-a-native-app-124k</link>
      <guid>https://dev.to/codenameone/do-you-want-your-web-app-to-feel-like-a-native-app-124k</guid>
      <description>&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxx99u1p3nrfn930ujvkr.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxx99u1p3nrfn930ujvkr.jpg" alt="Desktop appearance with a menu bar above the browser app" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can give your Codename One web app the same desktop theme family as your users' operating system: Aqua on macOS, Fluent on Windows, or Adwaita on Linux. Menus can sit above the app in HTML, with commands such as File and View visible instead of tucked into a mobile overflow menu.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That gives you a browser version of your app that fits more comfortably on a desktop. The JavaScript port now supports both choices, and the same theme work reaches the Certificate Wizard you use to sign your apps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow the operating system, or choose a theme
&lt;/h2&gt;

&lt;p&gt;Set these in the Settings app's &lt;strong&gt;Build Hints&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;nativeTheme&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;native&lt;/span&gt;
&lt;span class="py"&gt;javascript.titleBar&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;html&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;code&gt;codenameone_settings.properties&lt;/code&gt;, use &lt;code&gt;codename1.arg.nativeTheme&lt;/code&gt; and &lt;code&gt;codename1.arg.javascript.titleBar&lt;/code&gt; instead. Desktop browsers select Fluent for Windows, Aqua for macOS, and Adwaita for Linux and the remaining desktop systems. Phones and tablets keep the mobile theme selection. The browser detection treats an iPad as a tablet even when it reports a Mac browser.&lt;/p&gt;

&lt;p&gt;These are Codename One themes drawing the controls. They are not the operating system's native widget toolkit embedded in the page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.codenameone.com/blog/desktop-theme-collage.jpg" rel="noopener noreferrer"&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvu9cxn910p18s3jilc3z.jpg" alt="The same Settings form in the macOS Aqua, Windows Fluent and Linux Adwaita theme families" width="800" height="1162"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;The Settings app demonstrates the three desktop theme families now available to browser apps. Click to compare the controls at full size.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You can pin a desktop theme when the application's design calls for it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;javascript.desktopTheme&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;fluent&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The other explicit choices are &lt;code&gt;aqua&lt;/code&gt;, &lt;code&gt;adwaita&lt;/code&gt; and &lt;code&gt;none&lt;/code&gt;. &lt;code&gt;none&lt;/code&gt; keeps mobile themes on desktop browsers. Leave the value at &lt;code&gt;auto&lt;/code&gt; to follow the operating system. The broader &lt;code&gt;nativeTheme=modern&lt;/code&gt; setting has different behavior: it selects the modern mobile families, including on desktop browsers. Use &lt;code&gt;native&lt;/code&gt; when desktop family selection is what you want.&lt;/p&gt;

&lt;h2&gt;
  
  
  Put commands above the app
&lt;/h2&gt;

&lt;p&gt;The default &lt;code&gt;javascript.titleBar=toolbar&lt;/code&gt; keeps the themed Toolbar inside the app. With &lt;code&gt;html&lt;/code&gt;, the page supplies title and menu bars instead. The title follows the current form; commands group into menus using the same &lt;code&gt;Command.setDesktopMenu()&lt;/code&gt; hint used on the desktop ports.&lt;/p&gt;

&lt;p&gt;For example, inside an initialized app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;codename1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ui&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Form&lt;/span&gt; &lt;span class="n"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;codename1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ui&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Form&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Notes"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;codename1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ui&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Command&lt;/span&gt; &lt;span class="n"&gt;refresh&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;codename1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ui&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;Command&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Refresh"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;actionPerformed&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;codename1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ui&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;events&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ActionEvent&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setTitle&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Notes refreshed"&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;refresh&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setDesktopMenu&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"View"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getToolbar&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;addCommandToOverflowMenu&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;refresh&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;form&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;show&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Commands without a menu hint go into a &lt;strong&gt;Commands&lt;/strong&gt; menu. The HTML bars follow the selected appearance and the browser's light or dark preference. They do not have fake minimize, maximize or close buttons: the page does not own the browser window. The browser tab title remains the application's display name.&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%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IFRECiAgICBDb21tYW5kc1tUb29sYmFyIGNvbW1hbmRzIGFuZCBtZW51IGhpbnRzXSAtLT4gTW9kZXtUaXRsZSBiYXIgc2V0dGluZ30KICAgIE1vZGUgLS0-fHRvb2xiYXJ8IEluc2lkZVtUaGVtZWQgVG9vbGJhciBpbnNpZGUgdGhlIGFwcF0KICAgIE1vZGUgLS0-fGh0bWx8IE91dHNpZGVbSFRNTCB0aXRsZSBhbmQgbWVudSBhYm92ZSB0aGUgYXBwXQogICAgT3V0c2lkZSAtLT4gQWN0aW9uW0Rpc3BhdGNoIHRvIHRoZSBzYW1lIENvbW1hbmRdCiAgICBJbnNpZGUgLS0-IEFjdGlvbg%3D%3D%3Ftype%3Dpng%26bgColor%3Dffffff" 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%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IFRECiAgICBDb21tYW5kc1tUb29sYmFyIGNvbW1hbmRzIGFuZCBtZW51IGhpbnRzXSAtLT4gTW9kZXtUaXRsZSBiYXIgc2V0dGluZ30KICAgIE1vZGUgLS0-fHRvb2xiYXJ8IEluc2lkZVtUaGVtZWQgVG9vbGJhciBpbnNpZGUgdGhlIGFwcF0KICAgIE1vZGUgLS0-fGh0bWx8IE91dHNpZGVbSFRNTCB0aXRsZSBhbmQgbWVudSBhYm92ZSB0aGUgYXBwXQogICAgT3V0c2lkZSAtLT4gQWN0aW9uW0Rpc3BhdGNoIHRvIHRoZSBzYW1lIENvbW1hbmRdCiAgICBJbnNpZGUgLS0-IEFjdGlvbg%3D%3D%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="586" height="587"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The source is in &lt;a href="https://github.com/codenameone/CodenameOne/blob/master/Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/JavaScriptDesktopChrome.java" rel="noopener noreferrer"&gt;JavaScriptDesktopChrome&lt;/a&gt;, with build-hint and page behavior documented in the &lt;a href="https://www.codenameone.com/developer-guide/working-with-javascript/" rel="noopener noreferrer"&gt;JavaScript guide&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep navigation inside your browser app
&lt;/h2&gt;

&lt;p&gt;We do not support additional native &lt;code&gt;Window&lt;/code&gt; surfaces in the JavaScript port. Popup blockers and the browser's window ownership make that a different promise from drawing a menu inside the current page. &lt;strong&gt;Windows browsers are supported; extra OS windows are the unsupported feature.&lt;/strong&gt; Dialogs and navigation within the app remain the appropriate model.&lt;/p&gt;

&lt;p&gt;There are renderer limits too. For example, the full lifted-lens optics from &lt;a href="https://www.codenameone.com/blog/ios27-glass-from-measurements/" rel="noopener noreferrer"&gt;Sunday's iOS article&lt;/a&gt; are not implemented in the browser. Try the actual controls and effects your app uses rather than assuming a theme name proves complete renderer parity.&lt;/p&gt;

&lt;p&gt;The build prunes theme resources it can prove the app will not reach. If your code constructs resource names dynamically, set &lt;code&gt;javascript.pruneThemes=false&lt;/code&gt;; otherwise a theme loaded only through that constructed name may be missing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use the same desktop styling when you sign your app
&lt;/h2&gt;

&lt;p&gt;A gallery of buttons cannot tell us whether an application works comfortably with a desktop theme. The Certificate Wizard has longer forms, tables, dialogs and instructions that people must actually read to finish a task.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/codenameone/CodenameOne/pull/5916" rel="noopener noreferrer"&gt;PR #5916&lt;/a&gt; adopts the native desktop themes there, continuing the work we did in Settings. Its current source uses &lt;code&gt;@DesktopBuild(themeMode = "native", titleBar = DesktopTitleBar.NATIVE, ...)&lt;/code&gt;, with interactive scrollbars. Fluent, Aqua and Adwaita now provide the surrounding control styles instead of a separate wizard-specific desktop imitation.&lt;/p&gt;

&lt;p&gt;The same change fixes handling of an App ID's team prefix. That is a separate functional repair, not a consequence of adopting a theme. The wizard's &lt;a href="https://github.com/codenameone/CodenameOne/tree/master/scripts/certificatewizard" rel="noopener noreferrer"&gt;current source&lt;/a&gt; keeps both changes visible.&lt;/p&gt;

&lt;p&gt;Try the browser theme with a form that has real commands and enough content to scroll. Switch appearances, open its menus and check keyboard navigation. That will teach you more than another screenshot of an isolated button.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.codenameone.com/blog/java-server-work-before-startup/" rel="noopener noreferrer"&gt;weekly overview&lt;/a&gt; links the rest of the release. It also includes this week's smaller map-label improvement.&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>Connect Your App to Your Enterprise Traces</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Tue, 13 Oct 2026 19:51:36 +0000</pubDate>
      <link>https://dev.to/codenameone/connect-your-app-to-your-enterprise-traces-2n9m</link>
      <guid>https://dev.to/codenameone/connect-your-app-to-your-enterprise-traces-2n9m</guid>
      <description>&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpcw7yqg82ynsl59ydq3x.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpcw7yqg82ynsl59ydq3x.jpg" alt="One trace connects the client app, native backend and enterprise services" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your customer taps “Place order.” The request crosses your app, an API gateway, a native backend, a payment service and a database. Your operations team needs to see that path in the same tracing system it uses for the rest of the company. Neither the client app nor a compiled Java server should disappear from that picture.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I wrote a book on debugging, so I am probably more opinionated about this than most people: in 2026, production diagnosis should not depend on somebody searching a pile of logs with &lt;code&gt;grep&lt;/code&gt; and guessing which lines belong together.&lt;/p&gt;

&lt;p&gt;Logs still matter. At enterprise scale, though, you need to connect work across machines, teams and runtimes, then see whether a failure is isolated or spreading. On a JVM, Java agents can instrument much of that work for us. Once our backend is translated to C and compiled into a native executable, the usual JVM instrumentation hook is gone.&lt;/p&gt;

&lt;p&gt;We have to carry that visibility into the binary. &lt;a href="https://www.codenameone.com/blog/follow-a-tap-with-opentelemetry/" rel="noopener noreferrer"&gt;OpenTelemetry support&lt;/a&gt; started that work last week. The &lt;a href="https://github.com/codenameone/CodenameOne/pull/5908" rel="noopener noreferrer"&gt;expanded backend API&lt;/a&gt; adds metrics and management alongside it. This article follows what happens after you turn them on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Follow an operation across your systems
&lt;/h2&gt;

&lt;p&gt;Observability is the ability to understand a system's internal behavior from the evidence it emits. OpenTelemetry, usually shortened to OTel, defines APIs, data conventions and transport for producing that evidence. It is not itself the screen where you investigate an incident; a collector and an observability backend handle that part. The &lt;a href="https://opentelemetry.io/docs/concepts/observability-primer/" rel="noopener noreferrer"&gt;OTel primer&lt;/a&gt; provides the broader model.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Question&lt;/th&gt;
&lt;th&gt;Useful evidence&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Why did this request take two seconds?&lt;/td&gt;
&lt;td&gt;A trace connecting its operations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Is database waiting increasing across the service?&lt;/td&gt;
&lt;td&gt;Metrics over time&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;What did the application decide at the failure?&lt;/td&gt;
&lt;td&gt;Logs and events with context&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A span records one timed operation. Related spans form a trace. Metrics aggregate behavior across operations: a count, a current value or a distribution such as request duration. Neither replaces the other. A single trace can explain a slow query without telling you how often it occurs.&lt;/p&gt;

&lt;p&gt;Our integration exports traces and metrics. It does not turn every application log into an OTLP log record.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add tracing to your native executable
&lt;/h2&gt;

&lt;p&gt;Put this in the backend's &lt;code&gt;application.properties&lt;/code&gt; before building:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;cn1.otel.enabled&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or use the backend's &lt;code&gt;@OpenTelemetry&lt;/code&gt; annotation. The generator installs the tracer, making its implementation reachable to the translator. Without that build-time opt-in, the unused tracing code can be left out. Setting an environment variable after deploying a binary that omitted the tracer cannot add it back.&lt;/p&gt;

&lt;p&gt;Once enabled, the runtime records an incoming request span named for its route template. A &lt;code&gt;Web&lt;/code&gt; call creates a client span and propagates W3C trace headers. A database statement creates another client span. Incoming &lt;code&gt;traceparent&lt;/code&gt; connects the request to its caller.&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%2Fmermaid.ink%2Fimg%2Fc2VxdWVuY2VEaWFncmFtCiAgICBwYXJ0aWNpcGFudCBBcHAgYXMgQ04xIGNsaWVudCBhcHAKICAgIHBhcnRpY2lwYW50IE5hdGl2ZSBhcyBDTjEgbmF0aXZlIGJhY2tlbmQKICAgIHBhcnRpY2lwYW50IFNlcnZpY2UgYXMgRXhpc3RpbmcgZW50ZXJwcmlzZSBzZXJ2aWNlCiAgICBwYXJ0aWNpcGFudCBEQiBhcyBEYXRhYmFzZQogICAgcGFydGljaXBhbnQgQ29sbGVjdG9yIGFzIEVudGVycHJpc2UgT1RlbCBjb2xsZWN0b3IKICAgIEFwcC0-Pk5hdGl2ZTogT3JkZXIgcmVxdWVzdCArIHRyYWNlcGFyZW50CiAgICBOYXRpdmUtPj5TZXJ2aWNlOiBIVFRQIGNhbGwgKyB0cmFjZXBhcmVudAogICAgU2VydmljZS0-PkRCOiBTUUwgc3BhbiB1bmRlciB0aGUgc2FtZSB0cmFjZQogICAgREItLT4-U2VydmljZTogUmVzdWx0CiAgICBTZXJ2aWNlLS0-Pk5hdGl2ZTogUmVzdWx0CiAgICBOYXRpdmUtLT4-QXBwOiBSZXNwb25zZQogICAgQXBwLT4-TmF0aXZlOiBCYXRjaGVkIGNsaWVudCBzcGFucyB2aWEgcmVsYXkKICAgIE5hdGl2ZS0-PkNvbGxlY3RvcjogQ2xpZW50IHNwYW5zICsgc2VydmVyIHNwYW5zICsgbWV0cmljcwogICAgU2VydmljZS0-PkNvbGxlY3RvcjogRXhpc3Rpbmcgc2VydmljZSB0ZWxlbWV0cnk%3D%3Ftype%3Dpng%26bgColor%3Dffffff" 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%2Fmermaid.ink%2Fimg%2Fc2VxdWVuY2VEaWFncmFtCiAgICBwYXJ0aWNpcGFudCBBcHAgYXMgQ04xIGNsaWVudCBhcHAKICAgIHBhcnRpY2lwYW50IE5hdGl2ZSBhcyBDTjEgbmF0aXZlIGJhY2tlbmQKICAgIHBhcnRpY2lwYW50IFNlcnZpY2UgYXMgRXhpc3RpbmcgZW50ZXJwcmlzZSBzZXJ2aWNlCiAgICBwYXJ0aWNpcGFudCBEQiBhcyBEYXRhYmFzZQogICAgcGFydGljaXBhbnQgQ29sbGVjdG9yIGFzIEVudGVycHJpc2UgT1RlbCBjb2xsZWN0b3IKICAgIEFwcC0-Pk5hdGl2ZTogT3JkZXIgcmVxdWVzdCArIHRyYWNlcGFyZW50CiAgICBOYXRpdmUtPj5TZXJ2aWNlOiBIVFRQIGNhbGwgKyB0cmFjZXBhcmVudAogICAgU2VydmljZS0-PkRCOiBTUUwgc3BhbiB1bmRlciB0aGUgc2FtZSB0cmFjZQogICAgREItLT4-U2VydmljZTogUmVzdWx0CiAgICBTZXJ2aWNlLS0-Pk5hdGl2ZTogUmVzdWx0CiAgICBOYXRpdmUtLT4-QXBwOiBSZXNwb25zZQogICAgQXBwLT4-TmF0aXZlOiBCYXRjaGVkIGNsaWVudCBzcGFucyB2aWEgcmVsYXkKICAgIE5hdGl2ZS0-PkNvbGxlY3RvcjogQ2xpZW50IHNwYW5zICsgc2VydmVyIHNwYW5zICsgbWV0cmljcwogICAgU2VydmljZS0-PkNvbGxlY3RvcjogRXhpc3Rpbmcgc2VydmljZSB0ZWxlbWV0cnk%3D%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="1237" height="581"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The native backend propagates context on outbound &lt;code&gt;Web&lt;/code&gt; calls. Each receiving service must accept and continue that context. OTel-compatible instrumentation lets those services participate regardless of their implementation language.&lt;/p&gt;

&lt;h2&gt;
  
  
  Include the client, not just the servers
&lt;/h2&gt;

&lt;p&gt;The client annotation belongs on your app's main class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.codename1.annotations.OpenTelemetry&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.codename1.system.Lifecycle&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@OpenTelemetry&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;relay&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"https://api.example.com"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;relayToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"replace-with-your-relay-token"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;serviceName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"orders-app"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;sampleRatio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;requireAnalyticsConsent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OrdersApp&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Lifecycle&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;Replace the URL with your backend and the token placeholder with your relay token. Set the backend's &lt;code&gt;RELAY_TOKEN&lt;/code&gt; environment variable to that same value. Enable its relay as shown below, and wire the app's analytics-consent flow before collecting telemetry. The client instruments supported app operations and &lt;code&gt;ConnectionRequest&lt;/code&gt; calls, including outgoing trace context. It sends its recorded spans through the backend rather than carrying your collector credentials.&lt;/p&gt;

&lt;p&gt;For browser apps, context propagation is limited to the relay host and configured &lt;code&gt;propagateTo&lt;/code&gt; hosts. A cross-origin service must allow the tracing headers in CORS. The &lt;a href="https://www.codenameone.com/blog/follow-a-tap-with-opentelemetry/" rel="noopener noreferrer"&gt;client tracing walkthrough&lt;/a&gt; covers consent, propagation and the supported span types.&lt;/p&gt;

&lt;p&gt;That connects the experience on the device to the work behind it. A server-only trace cannot show the client work before the request or after the response. You can investigate that larger path in your enterprise tracing system instead of keeping a separate, disconnected account of what the app did.&lt;/p&gt;

&lt;p&gt;Database spans record SQL as supplied, with placeholders intact; bound parameters are not recorded. If an application concatenates a secret into SQL, the resulting text already contains it. &lt;code&gt;cn1.otel.attributes.exclude=db.query.text&lt;/code&gt; can exclude that attribute. Parameterized SQL remains the better habit.&lt;/p&gt;

&lt;p&gt;WebSocket lifetime is outside this automatic request-span model. An hours-long connection with thousands of messages is not one useful HTTP request duration, and its upgrade is handled before this tracing path.&lt;/p&gt;

&lt;h2&gt;
  
  
  Send it to the system your team already uses
&lt;/h2&gt;

&lt;p&gt;For an enabled build, configure a collector at runtime:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OTEL_SERVICE_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;orders-native
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OTEL_EXPORTER_OTLP_ENDPOINT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://127.0.0.1:4318
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OTEL_TRACES_SAMPLER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;parentbased_traceidratio
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;OTEL_TRACES_SAMPLER_ARG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0.1
./build/Orders
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here &lt;code&gt;Orders&lt;/code&gt; is the packaged application's executable. The ratio samples a tenth of newly rooted traces; the parent-based policy respects an upstream sampling decision.&lt;/p&gt;

&lt;p&gt;The exporter uses OTLP over HTTP, with protobuf by default and JSON as an option. It does not implement OTLP/gRPC. It appends &lt;code&gt;/v1/traces&lt;/code&gt; or &lt;code&gt;/v1/metrics&lt;/code&gt; to the base endpoint unless you supply a signal-specific endpoint.&lt;/p&gt;

&lt;p&gt;Dynatrace can receive that OTLP data. For traces, its &lt;a href="https://docs.dynatrace.com/docs/ingest-from/opentelemetry/otlp-api" rel="noopener noreferrer"&gt;documented ingest endpoint&lt;/a&gt; is configured through &lt;code&gt;OTEL_EXPORTER_OTLP_TRACES_ENDPOINT&lt;/code&gt;, with an ingest credential in &lt;code&gt;OTEL_EXPORTER_OTLP_HEADERS&lt;/code&gt;. This uses explicit telemetry export; the translated server no longer has the JVM hooks used by Java instrumentation agents.&lt;/p&gt;

&lt;p&gt;A dedicated exporter thread batches spans. If the collector is slow or unavailable, request handling does not wait indefinitely for it. A bounded queue eventually drops spans and records the drops. That preserves the service at the cost of incomplete telemetry, which is itself a condition to monitor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Track latency without a metric for every URL
&lt;/h2&gt;

&lt;p&gt;The server records request duration, active requests, open connections, database pool counts and task queue depth. Scheduled jobs report duration and outcome. The default metric export interval is one minute, using cumulative temporality.&lt;/p&gt;

&lt;p&gt;Route labels use &lt;code&gt;/orders/{id}&lt;/code&gt;, not &lt;code&gt;/orders/81379&lt;/code&gt;. Putting each order ID into a metric label would create a new time series for every order. Templates keep the series count bounded by the API's shape.&lt;/p&gt;

&lt;p&gt;The application can instrument a method too:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;example&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.codename1.backend.annotations.Counted&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.codename1.backend.annotations.Component&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.codename1.backend.annotations.Timed&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@Component&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;QuoteService&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@Timed&lt;/span&gt;
    &lt;span class="nd"&gt;@Counted&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;quoteCents&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="o"&gt;)&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;quantity&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;IllegalArgumentException&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"quantity must be positive"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;quantity&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;250&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 deliberately small example creates a duration histogram and call counters, including a failure counter. The default instrument names derive from the class and method. The build weaves the measurement into the method, so a call through &lt;code&gt;this&lt;/code&gt; is measured too. For a real quote service, validate upper bounds and apply the application's monetary rules.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;@ManagedResource&lt;/code&gt;, &lt;code&gt;@ManagedAttribute&lt;/code&gt; and &lt;code&gt;@ManagedOperation&lt;/code&gt; offer a JMX-inspired model for current values and operator actions. Numeric managed getters become gauges. The management transport is the backend's own HTTP API; this is not a JVM JMX server hidden inside the native executable. Follow the &lt;a href="https://www.codenameone.com/developer-guide/backend-observability/" rel="noopener noreferrer"&gt;management configuration&lt;/a&gt; before exposing operational actions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let an agent check the running application
&lt;/h2&gt;

&lt;p&gt;MCP, the Model Context Protocol, gives a development tool a structured way to ask the server questions. The backend serves tools for listing routes, inspecting beans, reading recent requests and querying the database. After editing a handler, you can call it and inspect the row it wrote from your development tool.&lt;/p&gt;

&lt;p&gt;A practical loop is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Start the backend on its development profile.&lt;/li&gt;
&lt;li&gt;Inspect &lt;code&gt;backend_routes&lt;/code&gt;, &lt;code&gt;backend_beans&lt;/code&gt; and &lt;code&gt;backend_schema&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Make the edit, then stop and restart the server. There is no hot reload.&lt;/li&gt;
&lt;li&gt;Exercise the handler with &lt;code&gt;backend_call&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Check &lt;code&gt;backend_requests&lt;/code&gt;, &lt;code&gt;backend_sql&lt;/code&gt; and &lt;code&gt;backend_metrics&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The default SQL tool uses a database-enforced read-only transaction unless its caller explicitly requests write access. An application can publish its own methods with &lt;code&gt;@McpTool&lt;/code&gt;; the build derives JSON Schema from their supported parameter types and calls them directly.&lt;/p&gt;

&lt;p&gt;For a tool of your own, here is a complete bean using the generated dispatch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;package&lt;/span&gt; &lt;span class="nn"&gt;example&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.codename1.backend.annotations.McpParam&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.codename1.backend.annotations.McpTool&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.codename1.backend.annotations.Component&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;@Component&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;QuoteTools&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;QuoteService&lt;/span&gt; &lt;span class="n"&gt;quotes&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nf"&gt;QuoteTools&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;QuoteService&lt;/span&gt; &lt;span class="n"&gt;quotes&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;quotes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;quotes&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@McpTool&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Returns a quote in cents for a positive quantity."&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;quote&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@McpParam&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"quantity"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;quantity&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;quotes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;quoteCents&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;quantity&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;It reuses &lt;code&gt;QuoteService&lt;/code&gt; above. The description tells an agent when the tool is relevant, and &lt;code&gt;@McpParam&lt;/code&gt; supplies a stable argument name independent of compiler parameter metadata. Runtime authentication still has to be configured before serving it outside development.&lt;/p&gt;

&lt;p&gt;Development tools are omitted by &lt;code&gt;backendPackage&lt;/code&gt; / &lt;code&gt;cn1:backend-package&lt;/code&gt; unless deliberately enabled. Outside development, the MCP endpoint requires a bearer token. A tokenless development server is restricted to loopback. Origin checks also matter when a browser can reach that machine. The &lt;a href="https://www.codenameone.com/developer-guide/backend-mcp/" rel="noopener noreferrer"&gt;MCP chapter&lt;/a&gt; spells out these boundaries.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep collector credentials off the phone
&lt;/h2&gt;

&lt;p&gt;An app can send its own spans through the backend relay. The relay validates the OTLP/JSON payload, applies size and span-count limits, then queues it for export using credentials held by the server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;cn1.otel.relay&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;span class="py"&gt;cn1.otel.relay.token&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;${RELAY_TOKEN}&lt;/span&gt;
&lt;span class="py"&gt;cn1.otel.relay.corsOrigin&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;https://app.example.com&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The relay requires the client's &lt;code&gt;relayToken&lt;/code&gt; to match &lt;code&gt;RELAY_TOKEN&lt;/code&gt;; a missing or different token returns HTTP 401. The CORS setting is for a web app on another origin. An app-carried relay token is not an unextractable secret; the collector's ingest credentials should remain on the backend. A full queue returns 503 so the client can back off.&lt;/p&gt;

&lt;p&gt;Start with one real app action that crosses the native backend and an existing enterprise service. Verify that its spans share a trace ID in your collector, then check consent, sampling, queue drops and access to the relay before expanding collection. The goal is to make the app and its native server observable parts of the system your operations team already runs.&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>Do You Prefer Gradle?</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Mon, 12 Oct 2026 21:43:37 +0000</pubDate>
      <link>https://dev.to/codenameone/do-you-prefer-gradle-46ko</link>
      <guid>https://dev.to/codenameone/do-you-prefer-gradle-46ko</guid>
      <description>&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F064mchl5qz593z09q3d1.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F064mchl5qz593z09q3d1.jpg" alt="A smaller project tree with an optional Gradle build" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can now choose Gradle for a Codename One project. Start with fewer files, add platform directories when you need native code, or create a standalone backend without a mobile app attached. A smaller project is easier to browse and leaves less scaffolding competing for space in an LLM's context.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I like Maven XML. I know it is verbose. I also spent enough time at Sun Microsystems staring at deeply scripted, heavily patched &lt;code&gt;make&lt;/code&gt; files to develop a lasting suspicion of clever build systems.&lt;/p&gt;

&lt;p&gt;Someone starts with a reasonable shortcut. Someone else needs one more condition. A few years later the build has its own undocumented programming language and nobody wants to touch it. I would rather a build system be strict and limited. Give people enough power and, with the best intentions, they build a Frankenstein build.&lt;/p&gt;

&lt;p&gt;That is a personal preference. Plenty of developers I respect love Gradle, or at least appreciate what it lets them do. This week we added &lt;a href="https://github.com/codenameone/CodenameOne/pull/5922" rel="noopener noreferrer"&gt;optional Gradle support&lt;/a&gt; because it lets us offer something useful: a smaller Codename One project, including a standalone backend with no client scaffolding.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start with fewer files
&lt;/h2&gt;

&lt;p&gt;The Maven app layout has a root POM and platform modules ready for native code. That makes the structure explicit, but a new app often has no code in most of those places.&lt;/p&gt;

&lt;p&gt;A Gradle app starts with the sources and configuration it needs. Native directories appear when you generate native interfaces. A backend subproject appears when you add a backend.&lt;/p&gt;

&lt;p&gt;For a concrete example, we converted the checked-in HelloCodenameOne sample with the current converter:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Project files&lt;/th&gt;
&lt;th&gt;Maven sample&lt;/th&gt;
&lt;th&gt;Converted Gradle sample&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Total files, including app sources and resources&lt;/td&gt;
&lt;td&gt;358&lt;/td&gt;
&lt;td&gt;305&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Maven POMs&lt;/td&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gradle Kotlin configuration files&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That removes &lt;strong&gt;53 files, about 15% of the tree&lt;/strong&gt;. We counted the tracked sample before conversion and all files in the converter's new directory afterward, excluding build output and caches. This is the existing demonstration app, with its resources and Kotlin source, rather than a comparison of two different Hello World programs.&lt;/p&gt;

&lt;p&gt;The generated &lt;code&gt;settings.gradle.kts&lt;/code&gt; is small enough to show in full, with its explanatory comments removed:&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;pluginManagement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;repositories&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;maven&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://repo.codenameone.com/maven2"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;gradlePluginPortal&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;plugins&lt;/span&gt; &lt;span class="p"&gt;{&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;"com.codenameone"&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;"7.0.274"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;rootProject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"HelloCodenameOne"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The example selects release &lt;code&gt;7.0.274&lt;/code&gt;, which includes Gradle support. The app's own dependencies and Kotlin plugin go in &lt;code&gt;build.gradle.kts&lt;/code&gt;; the framework's platform builders come from the Codename One plugin.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MyApp/
  settings.gradle.kts
  gradle.properties
  gradlew
  gradle/wrapper/
  codenameone_settings.properties
  icon.png
  src/main/java/
  src/main/css/theme.css
  src/main/resources/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add &lt;code&gt;build.gradle.kts&lt;/code&gt; when you have dependencies or build configuration of your own. Kotlin source belongs in &lt;code&gt;src/main/kotlin&lt;/code&gt; when the project applies the Kotlin plugin.&lt;/p&gt;

&lt;p&gt;That smaller tree helps a human understand an unfamiliar project. It can also reduce the irrelevant files an agent sees when building its context. The file reduction is measurable; the context saved depends on which files your agent reads.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the same builds when you switch
&lt;/h2&gt;

&lt;p&gt;We did not implement a second set of platform builders. The Gradle plugin calls the shared engine used by Maven. That includes upload staging, local builders, CSS compilation and bytecode compliance checks.&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%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IFRECiAgICBNYXZlbltNYXZlbiBwbHVnaW5dIC0tPiBFbmdpbmVbU2hhcmVkIENvZGVuYW1lIE9uZSBidWlsZCBlbmdpbmVdCiAgICBHcmFkbGVbR3JhZGxlIHBsdWdpbl0gLS0-IEVuZ2luZQogICAgRW5naW5lIC0tPiBDU1NbQ29tcGlsZSBDU1MgYW5kIGNoZWNrIGJ5dGVjb2RlXQogICAgRW5naW5lIC0tPiBMb2NhbFtHZW5lcmF0ZSBsb2NhbCBwbGF0Zm9ybSBwcm9qZWN0c10KICAgIEVuZ2luZSAtLT4gVXBsb2FkW1N0YWdlIGNsb3VkIGJ1aWxkIHVwbG9hZF0%3D%3Ftype%3Dpng%26bgColor%3Dffffff" 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%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IFRECiAgICBNYXZlbltNYXZlbiBwbHVnaW5dIC0tPiBFbmdpbmVbU2hhcmVkIENvZGVuYW1lIE9uZSBidWlsZCBlbmdpbmVdCiAgICBHcmFkbGVbR3JhZGxlIHBsdWdpbl0gLS0-IEVuZ2luZQogICAgRW5naW5lIC0tPiBDU1NbQ29tcGlsZSBDU1MgYW5kIGNoZWNrIGJ5dGVjb2RlXQogICAgRW5naW5lIC0tPiBMb2NhbFtHZW5lcmF0ZSBsb2NhbCBwbGF0Zm9ybSBwcm9qZWN0c10KICAgIEVuZ2luZSAtLT4gVXBsb2FkW1N0YWdlIGNsb3VkIGJ1aWxkIHVwbG9hZF0%3D%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="871" height="326"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From a generated Gradle app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./gradlew run
./gradlew cn1Test
./gradlew buildJavascriptLocal
./gradlew buildIosXcodeProject &lt;span class="nt"&gt;-Popen&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;run&lt;/code&gt; starts the simulator. &lt;code&gt;cn1Test&lt;/code&gt; uses the simulator test runner. The last two commands build the JavaScript port locally and generate an Xcode project. Tasks that submit cloud builds are available too, with the same service requirements as Maven.&lt;/p&gt;

&lt;p&gt;There are limits. Running Gradle requires JDK 17 or newer and Gradle 8.5 or newer; the generated wrapper supplies Gradle. These projects target Java 17. They do not support legacy &lt;code&gt;.cn1lib&lt;/code&gt; files or the old resource Designer task. The OpenAPI, gRPC and GraphQL generators and on-device debugging helpers still have Maven goals without equivalent Gradle tasks. The &lt;a href="https://www.codenameone.com/developer-guide/gradle-project-workflow/" rel="noopener noreferrer"&gt;workflow chapter&lt;/a&gt; lists the supported surface.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build a standalone Java backend
&lt;/h2&gt;

&lt;p&gt;In the &lt;a href="https://www.codenameone.com/initializr/" rel="noopener noreferrer"&gt;Initializr&lt;/a&gt;, choose &lt;strong&gt;Gradle&lt;/strong&gt; as the build tool and &lt;strong&gt;Backend only&lt;/strong&gt; as the project type. The server lives at the root, with its Java source and application properties. There is no client theme, simulator module or collection of empty native directories.&lt;/p&gt;

&lt;p&gt;For a generated project named &lt;code&gt;MyService&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;CN1_PROFILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dev ./gradlew runBackend
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That runs the development server on the JVM. Stop it before changing the backend code; this is a restart workflow, not hot reload. To build and run the native executable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./gradlew backendPackage
&lt;span class="nv"&gt;CN1_PROFILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dev &lt;span class="nv"&gt;PORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;9000 ./build/MyService
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The development profile uses the generated in-memory SQLite configuration. Without it, the starter reads its production settings and expects &lt;code&gt;DATABASE_URL&lt;/code&gt;. The executable's name follows the project name.&lt;/p&gt;

&lt;p&gt;An existing Gradle app can gain a server with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./gradlew addBackend
&lt;span class="nv"&gt;CN1_PROFILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;dev ./gradlew :backend:runBackend
./gradlew :backend:backendPackage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I hope people will try the backend on its own. It is still experimental, but a small independent service can reveal useful constraints without requiring a mobile application first. The &lt;a href="https://www.codenameone.com/blog/java-server-work-before-startup/" rel="noopener noreferrer"&gt;weekly overview&lt;/a&gt; shows the Spring-style programming model it now supports.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try Gradle alongside your Maven project
&lt;/h2&gt;

&lt;p&gt;The converter leaves the original project in place and writes a sibling project. Replace &lt;code&gt;VERSION&lt;/code&gt; with the Codename One plugin release containing this feature:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mvn com.codenameone:codenameone-maven-plugin:VERSION:convert-to-gradle &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-Dcn1&lt;/span&gt;.sourceProject&lt;span class="o"&gt;=&lt;/span&gt;/path/to/MyApp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-Dcn1&lt;/span&gt;.outputDir&lt;span class="o"&gt;=&lt;/span&gt;/path/to/MyApp-gradle
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The destination must be empty or absent. The conversion carries sources, resources, CSS and platform implementations into the new layout, and raises the application's bytecode target to Java 17. A customized backend comes across; the untouched backend skeleton is optional. Legacy &lt;code&gt;.cn1lib&lt;/code&gt; dependencies stop conversion with their names rather than silently disappearing.&lt;/p&gt;

&lt;p&gt;Open the converted project and run its tests before switching the team's build. Your old checkout remains available for comparison.&lt;/p&gt;

&lt;h2&gt;
  
  
  Find the API for the code you are writing
&lt;/h2&gt;

&lt;p&gt;Last week we split the Developer Guide into chapters. Before that, we rebuilt the Javadocs. A backend-only project made the next problem obvious: a client API page is the wrong answer when you are writing a server.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/codenameone/CodenameOne/pull/5918" rel="noopener noreferrer"&gt;PR #5918&lt;/a&gt; separates the &lt;a href="https://www.codenameone.com/javadoc/" rel="noopener noreferrer"&gt;client API&lt;/a&gt; from the &lt;a href="https://www.codenameone.com/backend/javadoc/" rel="noopener noreferrer"&gt;backend API&lt;/a&gt;. The references label their audience and link shared classes to their counterpart. Backend classes belong in the backend reference; a UI implementation package does not belong there just because it exists in the repository.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What you are writing&lt;/th&gt;
&lt;th&gt;Reference to start with&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;A form, component or device integration&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.codenameone.com/javadoc/" rel="noopener noreferrer"&gt;Client Javadocs&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A controller, database service or scheduled job&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.codenameone.com/backend/javadoc/" rel="noopener noreferrer"&gt;Backend Javadocs&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shared models and mapping code&lt;/td&gt;
&lt;td&gt;The shared class page, with its counterpart link&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The distinction helps readers and code-generation tools for the same reason: it reduces the chance of choosing an API that exists in the wrong runtime. Both references are checked with &lt;code&gt;doclint&lt;/code&gt;, so malformed documentation has a build-time consequence too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try Gradle 9 for your Android build
&lt;/h2&gt;

&lt;p&gt;The optional Gradle project layout and the Gradle version used inside a generated Android project are different choices. You can keep your Codename One app on Maven and still test the newer Android builder.&lt;/p&gt;

&lt;p&gt;In Settings, under &lt;strong&gt;Build Hints&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;android.gradleVersion&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;9&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or in &lt;code&gt;codenameone_settings.properties&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;codename1.arg.android.gradleVersion&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;9&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The current builder pairs Gradle 9.8.0 with Android Gradle Plugin 9.4.1. It adjusts the generated project for removed DSL elements and AGP's built-in Kotlin support. This selects the experimental Gradle 9 / Android Gradle Plugin 9 path from &lt;a href="https://github.com/codenameone/CodenameOne/pull/5919" rel="noopener noreferrer"&gt;PR #5919&lt;/a&gt;. It is opt-in. Much of the surrounding tooling and library configuration still assumes Gradle 8. Test your actual native dependencies and generated Android project before adopting it; a successful empty app is not the same test.&lt;/p&gt;

&lt;p&gt;I expect to keep using Maven for plenty of projects. Supporting Gradle gives teams another way into the same build machinery, and gives a small server a project tree that looks like a small server.&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>Can You Tell the Difference Between These iOS Tab Bars?</title>
      <dc:creator>Shai Almog</dc:creator>
      <pubDate>Sun, 11 Oct 2026 18:18:59 +0000</pubDate>
      <link>https://dev.to/codenameone/can-you-tell-the-difference-between-these-ios-tab-bars-dmg</link>
      <guid>https://dev.to/codenameone/can-you-tell-the-difference-between-these-ios-tab-bars-dmg</guid>
      <description>&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2For66yn54wvjp1pxy4kjd.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2For66yn54wvjp1pxy4kjd.jpg" alt="UIKit and Codename One tab bars from the simulator comparison" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Watch how the two tab bars respond to the recorded taps, holds, drags and changes of direction. Can you tell which one is UIKit and which one is drawn by Codename One?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is Codename One?&lt;/strong&gt; Codename One is an open-source framework for building native iOS, Android, desktop, and web apps from a single Java or Kotlin codebase. Learn more at &lt;a href="https://www.codenameone.com/" rel="noopener noreferrer"&gt;codenameone.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;That is a more useful test than a still image. Your users feel the delay before a selection moves, the way the glass follows a finger and the settling motion after release. A tab bar can have the right colors and still feel wrong in your hand.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.codenameone.com/blog/ios-27-glass-you-can-test/" rel="noopener noreferrer"&gt;Last week's iOS 27 theme&lt;/a&gt; was close, but I still wasn't happy with those interactions. Getting closer meant recording UIKit's presentation layers during real touches, fitting the motion to spring equations and reconstructing the glass effects. Here is the result, followed by the measurements and formulas behind it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Watch the same gestures twice
&lt;/h2&gt;



&lt;a href="https://www.codenameone.com/blog/ios27-measured-glass/light.mp4" rel="noopener noreferrer"&gt;Download the light comparison video&lt;/a&gt;.


&lt;p&gt;&lt;em&gt;UIKit is on top and Codename One below. These committed iOS 27 simulator recordings use the same touch sequence: six taps, a held press, a tap on the selected tab, a slow drag and a flick. Each gesture occupies a 4.2-second slot aligned on the touch.&lt;/em&gt;&lt;/p&gt;



&lt;a href="https://www.codenameone.com/blog/ios27-measured-glass/dark-tabs.mp4" rel="noopener noreferrer"&gt;Download the dark comparison video&lt;/a&gt;.


&lt;p&gt;&lt;em&gt;The dark appearance exposes the edge, vibrancy and refraction differences more clearly. UIKit is above Codename One in both simulator recordings.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/codenameone/CodenameOne/blob/master/scripts/record-ios-tabs-side-by-side.sh" rel="noopener noreferrer"&gt;recording script&lt;/a&gt; and &lt;a href="https://github.com/codenameone/CodenameOne/blob/master/docs/videos/README.md" rel="noopener noreferrer"&gt;video notes&lt;/a&gt; make the provenance inspectable. The remaining differences are part of the comparison: the lifted lens, its rounded ends and the saturation still deserve work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why your tab bar can look right and feel wrong
&lt;/h2&gt;

&lt;p&gt;Setting the selected tab in code did not produce the complete native interaction. UIKit performs that motion for genuine touches on a controller-managed bar. The probe therefore uses XCUITest taps, holds, and drags.&lt;/p&gt;

&lt;p&gt;There was another complication: UIKit did not attach a simple &lt;code&gt;CAAnimation&lt;/code&gt; containing the whole trajectory. It updated layer values frame by frame. The useful record was the presentation geometry at each display refresh: position, bounds, transform, opacity and the touch timestamps that explained them.&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%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgICBUb3VjaFtSZWFsIFhDVUlUZXN0IGdlc3R1cmVdIC0tPiBVSUtpdFtOYXRpdmUgdGFiIGNvbnRyb2xsZXJdCiAgICBVSUtpdCAtLT4gTG9nW1Blci1mcmFtZSBwcmVzZW50YXRpb24gZ2VvbWV0cnldCiAgICBMb2cgLS0-IEZpdFtGaXQgc3ByaW5ncyBhbmQgcmV0YWluZWQgY3VydmVzXQogICAgRml0IC0tPiBKYXZhW0phdmEgbW90aW9uIG1vZGVsXQogICAgTG9nIC0tPiBGaXh0dXJlW1JlY29yZGVkIGZpeHR1cmVzXQogICAgSmF2YSAtLT4gQ2hlY2tbUmVwbGF5IGFuZCBjb21wYXJlXQogICAgRml4dHVyZSAtLT4gQ2hlY2s%3D%3Ftype%3Dpng%26bgColor%3Dffffff" 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%2Fmermaid.ink%2Fimg%2FZmxvd2NoYXJ0IExSCiAgICBUb3VjaFtSZWFsIFhDVUlUZXN0IGdlc3R1cmVdIC0tPiBVSUtpdFtOYXRpdmUgdGFiIGNvbnRyb2xsZXJdCiAgICBVSUtpdCAtLT4gTG9nW1Blci1mcmFtZSBwcmVzZW50YXRpb24gZ2VvbWV0cnldCiAgICBMb2cgLS0-IEZpdFtGaXQgc3ByaW5ncyBhbmQgcmV0YWluZWQgY3VydmVzXQogICAgRml0IC0tPiBKYXZhW0phdmEgbW90aW9uIG1vZGVsXQogICAgTG9nIC0tPiBGaXh0dXJlW1JlY29yZGVkIGZpeHR1cmVzXQogICAgSmF2YSAtLT4gQ2hlY2tbUmVwbGF5IGFuZCBjb21wYXJlXQogICAgRml4dHVyZSAtLT4gQ2hlY2s%3D%3Ftype%3Dpng%26bgColor%3Dffffff" alt="Diagram" width="1613" height="186"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Capturing an image on every frame changes the timing enough to contaminate the measurement. The probe separates geometry logging from optional frame snapshots. Snapshot runs are useful for comparing the lens at the same motion state, not for fitting its timing. Likewise, lossless screenshots over known backdrops reveal the blur; compressed video smears the very edges we need to inspect.&lt;/p&gt;

&lt;p&gt;Those constraints are documented in the &lt;a href="https://github.com/codenameone/CodenameOne/tree/master/scripts/fidelity-app/ios-native-ref/motion-probe" rel="noopener noreferrer"&gt;native probe&lt;/a&gt;. They explain why “looks close in a recording” was not enough.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make taps, holds and drags respond differently
&lt;/h2&gt;

&lt;p&gt;For a fixed target, a damped spring follows this equation:&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%2Fwww.codenameone.com%2Fblog%2Fruntime-diagrams%2Fdamped-spring.svg" 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%2Fwww.codenameone.com%2Fblog%2Fruntime-diagrams%2Fdamped-spring.svg" alt="Damped spring equation: acceleration plus damping times velocity plus restoring force equals zero" width="431" height="73"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here &lt;code&gt;x&lt;/code&gt; is position, &lt;code&gt;xₜ&lt;/code&gt; is the target, &lt;code&gt;ω&lt;/code&gt; controls the natural frequency and &lt;code&gt;ζ&lt;/code&gt; the damping. Dots denote derivatives with respect to time. A critically damped spring has &lt;code&gt;zeta = 1&lt;/code&gt;; lower damping allows an overshoot. The implementation uses the measured constants rather than retuning the entire animation by eye:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Channel&lt;/th&gt;
&lt;th&gt;Model in the current source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Selection travel&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;omega = 2*pi/0.4&lt;/code&gt;, &lt;code&gt;zeta = 0.85&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lens lift&lt;/td&gt;
&lt;td&gt;Critical damping, &lt;code&gt;omega = 2*pi/0.25&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Platter return&lt;/td&gt;
&lt;td&gt;Critical damping, &lt;code&gt;omega = 2*pi/0.4&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Whole-bar press growth&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;omega = 17.6&lt;/code&gt;, &lt;code&gt;zeta = 0.59&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lens following a drag&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;omega = 32&lt;/code&gt;, &lt;code&gt;zeta = 0.90&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The travel stops once the remaining distance after the overshoot is below about 0.195 points. The drag and press models include a measured 20 ms touch lag. These small details are visible because your finger gives the animation a moving reference point.&lt;/p&gt;

&lt;p&gt;The current &lt;a href="https://github.com/codenameone/CodenameOne/blob/master/CodenameOne/src/com/codename1/ui/TabGlassMotion.java" rel="noopener noreferrer"&gt;TabGlassMotion&lt;/a&gt; and &lt;a href="https://github.com/codenameone/CodenameOne/blob/master/CodenameOne/src/com/codename1/ui/TabGlassGesture.java" rel="noopener noreferrer"&gt;TabGlassGesture&lt;/a&gt; contain the formulas, retained tables and stopping rules. The regeneration tool checks the closed forms against captures and rewrites the channels that remain tabulated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Let users drag across tabs without selecting each one
&lt;/h2&gt;

&lt;p&gt;During a drag the lens follows the finger on its own spring, so it can lag behind. On release, UIKit selects the tab under the finger, not the one nearest the lagging lens. The lens then settles toward that tab from its current position and velocity.&lt;/p&gt;

&lt;p&gt;Resetting velocity at that handoff creates a visible interruption. Snapping to the nearest lens position after a fast flick creates another discontinuity. A press on the already selected tab must also lift and pulse without inventing travel to another tab. A held press has to keep its lifted state until release rather than play a fixed-duration tap and finish underneath your finger.&lt;/p&gt;

&lt;p&gt;The release adds a small wobble. Our model uses a common starting point for it even though the captures show slightly different onset times across gesture types. That approximation is one reason to keep the full gesture suite alongside the short demo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep icons legible as the backdrop changes
&lt;/h2&gt;

&lt;p&gt;A vibrant tab icon is not simply painted with an alpha color. Its result depends on the glass behind it. The probe reads UIKit's color matrices across tint sweeps, and &lt;code&gt;VibrancyMatrix&lt;/code&gt; reconstructs the transform.&lt;/p&gt;

&lt;p&gt;For a non-gray dark tint vector &lt;code&gt;t&lt;/code&gt;, the measured form is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;e = 0.5 * min(t) / max(t)
M = e*I + (0.5 - e) * (ones * transpose(t)) / dot(t,t)
offset = t
output = M * backdrop + offset
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;I&lt;/code&gt; is the identity matrix and &lt;code&gt;ones&lt;/code&gt; is a column of three ones. Gray tints have their own formula, so this expression is not a universal branch to paste over the implementation. Light appearance uses a different form. The &lt;a href="https://github.com/codenameone/CodenameOne/blob/master/CodenameOne/src/com/codename1/ui/plaf/VibrancyMatrix.java" rel="noopener noreferrer"&gt;vibrancy source&lt;/a&gt; and recorded fixture hold those cases together.&lt;/p&gt;

&lt;p&gt;The selection platter also transforms the bar's existing glass. &lt;code&gt;Graphics.colorMatrixRegion&lt;/code&gt; applies that operation within a shape and optional mask. The lifted lens needs a separate operation: refraction around a circular bevel, color dispersion, an outline, rim light and shadow. &lt;code&gt;glassLensRegion&lt;/code&gt; supplies that rendering path.&lt;/p&gt;

&lt;p&gt;This is why adjusting the opacity of a rounded rectangle could only take us so far. The effect changes the content underneath it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Keep the glass animation on the GPU
&lt;/h2&gt;

&lt;p&gt;A more accurate motion model is wasted if the renderer misses the frames that distinguish it. The iOS bar glass now runs on the GPU instead of passing through a CPU blur with GPU synchronization around it. Mutable images reuse their stencil texture. The tab animation avoids asking for a second whole-component repaint, and the lens scratch texture is released after use.&lt;/p&gt;

&lt;p&gt;The portable color-matrix operation reaches the JavaScript port too. The browser lens still lacks the full optics implementation. A shared API does not mean every rendering backend implements every effect today.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choose the appearance, then test your screen
&lt;/h2&gt;

&lt;p&gt;In the Settings app's &lt;strong&gt;Build Hints&lt;/strong&gt;, select:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;ios.themeMode&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;modern&lt;/span&gt;
&lt;span class="py"&gt;ios.themeGeneration&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;27&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the cloud toolchain, &lt;code&gt;ios.xcode_version=27&lt;/code&gt; is a separate setting. In &lt;code&gt;codenameone_settings.properties&lt;/code&gt;, prefix build-hint keys with &lt;code&gt;codename1.arg.&lt;/code&gt;. A local Xcode build uses the Xcode installation selected on your Mac.&lt;/p&gt;

&lt;p&gt;Codename One draws these controls itself. The theme and its motion ship with the app, so adopting this appearance remains your decision. That control also gives us the responsibility to measure the behavior we reproduce.&lt;/p&gt;

&lt;p&gt;Try the animation over a photograph, a scrolling list and a plain dark background. Hold it. Drag past the middle and release. The &lt;a href="https://github.com/codenameone/CodenameOne/pull/5906" rel="noopener noreferrer"&gt;implementation in PR #5906&lt;/a&gt; and the fixtures behind it make those interactions repeatable instead of leaving fidelity to memory.&lt;/p&gt;

</description>
      <category>java</category>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
    </item>
  </channel>
</rss>
