<?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: Davor Hrg</title>
    <description>The latest articles on DEV Community by Davor Hrg (@hrgdavor).</description>
    <link>https://dev.to/hrgdavor</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1260848%2F59d9df0e-a6de-4e7c-8e3d-cd0db7b7233a.jpeg</url>
      <title>DEV Community: Davor Hrg</title>
      <link>https://dev.to/hrgdavor</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hrgdavor"/>
    <language>en</language>
    <item>
      <title>Why Why Why Wyhash could be a valuable tool in your toolbox</title>
      <dc:creator>Davor Hrg</dc:creator>
      <pubDate>Sun, 22 Mar 2026 10:39:09 +0000</pubDate>
      <link>https://dev.to/hrgdavor/why-why-why-wyhash-could-be-a-valuable-tool-in-your-toolbox-3mhm</link>
      <guid>https://dev.to/hrgdavor/why-why-why-wyhash-could-be-a-valuable-tool-in-your-toolbox-3mhm</guid>
      <description>&lt;p&gt;I recently went down the rabbit hole of non-cryptographic hash functions and emerged with a new favorite: &lt;strong&gt;Wyhash&lt;/strong&gt;. It's not just another hash algorithm; it's a tiny beast that has found its way into modern high-performance environments like Zig and Bun.&lt;/p&gt;

&lt;p&gt;Here is why I think it deserves a spot in your developer toolbox.&lt;/p&gt;

&lt;p&gt;Of &lt;a href="https://dev.to/hrgdavor/steal-back-from-ai-stealbackfromai-eod"&gt;series &lt;/a&gt; of &lt;a href="https://dev.to/t/stealbackfromai"&gt;#stealbackfromai&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Need for Speed on Modern Hardware
&lt;/h2&gt;

&lt;p&gt;We are living in a 64-bit world, yet many legacy hash functions (like MurmurHash3) were designed when 32-bit operations were king. Wyhash is unapologetically modern. &lt;/p&gt;

&lt;p&gt;It is designed efficiently for 64-bit processors, leveraging native &lt;strong&gt;64x64 -&amp;gt; 128-bit multiplication&lt;/strong&gt;. This allows it to process data in larger chunks and achieve incredibly high throughput without needing complex, platform-specific SIMD instructions (like AVX or NEON). It’s portable speed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quality You Can Trust
&lt;/h2&gt;

&lt;p&gt;Speed is nothing without quality. "Fast" hash functions often fail to pass stringent statistical tests, leading to collisions and poor distribution in hash maps.&lt;/p&gt;

&lt;p&gt;Wyhash passes &lt;strong&gt;Smhasher&lt;/strong&gt;, the gold standard test suite for non-cryptographic hash functions. It produced no bias and no collisions in the entire battery of tests. This makes it safe for hash tables, bloom filters, and other probabilistic data structures where collision resistance matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Simplicity is the Ultimate Sophistication
&lt;/h2&gt;

&lt;p&gt;The algorithm is surprisingly simple. The core implementation fits on a single screen. It uses a "multiply and mix" (MUM) strategy:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; Initialize state with a secret and seed.&lt;/li&gt;
&lt;li&gt; Consume input in 48-byte blocks.&lt;/li&gt;
&lt;li&gt; Mix the state using 128-bit multiplication products XORed into 64-bit results.&lt;/li&gt;
&lt;li&gt; Finalize with a last round of mixing.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's it. No complex S-boxes, no massive lookup tables.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learning Java 18+ along the way
&lt;/h2&gt;

&lt;p&gt;One of the hurdles in porting Wyhash to Java is that it relies heavily on &lt;strong&gt;unsigned&lt;/strong&gt; 128-bit math. Java, historically, has been strictly signed.&lt;/p&gt;

&lt;p&gt;In the past, we had to use "tricks" to emulate unsigned multiplication, often involving multiple operations and careful bit-shifting.&lt;/p&gt;

&lt;p&gt;However, while implementing this, found out that &lt;strong&gt;Java 18&lt;/strong&gt; introduced &lt;code&gt;Math.unsignedMultiplyHigh(long x, long y)&lt;/code&gt;. This intrinsic method maps directly to the hardware instructions (like &lt;code&gt;MUL&lt;/code&gt; on x86_64) that produce the upper 64 bits of a 128-bit product. &lt;/p&gt;

&lt;h2&gt;
  
  
  The Power of the Default Seed
&lt;/h2&gt;

&lt;p&gt;We often think of seeds as something that &lt;em&gt;must&lt;/em&gt; be random to prevent HashDoS attacks. And for hash maps receiving untrusted input, that is true.&lt;/p&gt;

&lt;p&gt;But Wyhash could serve another powerful purpose: &lt;strong&gt;Compatibility&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;By using the standard "default secret" (originating from the C++ implementation) and a fixed seed (like &lt;code&gt;0&lt;/code&gt;), Wyhash becomes a deterministic, cross-language checksum.&lt;/p&gt;

&lt;p&gt;You can hash a file in &lt;strong&gt;Zig&lt;/strong&gt;, send it over the wire, and verify it in &lt;strong&gt;Java&lt;/strong&gt; or &lt;strong&gt;JavaScript (Bun)&lt;/strong&gt; using the exact same algorithm. This is incredibly to me personally, and you may have totally different requirements. Sadly it is not given that same variant is implemented in different languages. So if you are aiming for compatibility you yourself must make sure same variant is used.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Note on Versions and Compatibility
&lt;/h2&gt;

&lt;p&gt;Wyhash has evolved through several versions (v1, v2, v3/final3, v4). While the algorithm's core principles remain the same, different versions produce different hash outputs for the same input. From what I was able to research, and my own preferences &lt;code&gt;v3/final3&lt;/code&gt; looks to be the way to go.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Java Port &amp;amp; Zig&lt;/strong&gt;: &lt;a href="https://github.com/hrgdavor/wyhash" rel="noopener noreferrer"&gt;This Java implementation&lt;/a&gt; is a direct port of the version used in the &lt;strong&gt;Zig standard library&lt;/strong&gt; (&lt;code&gt;std.hash.Wyhash&lt;/code&gt;), effectively the &lt;code&gt;final3&lt;/code&gt; (v3) variant. This ensures binary compatibility with Zig applications.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Bun&lt;/strong&gt;: Bun.hash is directly compatible with Zig's std.hash.Wyhash. Because Bun is written in Zig, its Bun.hash function is a direct binding to the Zig standard library's implementation. &lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Go 1.17+&lt;/strong&gt;: Not compatible. Uses a "Wyhash-inspired" fallback implementation for its map hashing. It is &lt;strong&gt;not&lt;/strong&gt; a direct port and will not be binary compatible with standard Wyhash implementations.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;@pencroff-lab/wyhash-ts&lt;/strong&gt;: provides a specific &lt;code&gt;wyhash_bun&lt;/code&gt; function to explicitly guarantee this Zig-compatible behavior in other Node.js environments. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So, for cross-language checksums, ensure both sides are speaking the same "dialect" (version) of Wyhash and using the same seed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The "Frozen" Problem&lt;/strong&gt;: Zig values stability in its standard library. Because std.hash is often used for persistent data (like hash maps or disk-backed caches), the Zig team is extremely hesitant to update the implementation to the latest C version (v4.2). Doing so would change the hash output for the same input/seed, silently corrupting data for anyone relying on consistent hashes across Zig versions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a Standalone Implementation?
&lt;/h2&gt;

&lt;p&gt;Initially, I looked at existing libraries like &lt;strong&gt;Hash4j&lt;/strong&gt;. It's a fantastic library, but it's a large dependency if you &lt;em&gt;only&lt;/em&gt; need one algorithm.&lt;/p&gt;

&lt;p&gt;Sometimes, the best dependency is the one you don't add.&lt;/p&gt;

&lt;p&gt;Wyhash represents a sweet spot in modern engineering: it's fast, simple, high-quality, and portable. If you need a hash function for your next tool, give it a look.&lt;/p&gt;

</description>
      <category>stealbackfromai</category>
      <category>java</category>
      <category>zig</category>
    </item>
    <item>
      <title>scrollbar-gutter to the rescue</title>
      <dc:creator>Davor Hrg</dc:creator>
      <pubDate>Fri, 06 Mar 2026 10:09:17 +0000</pubDate>
      <link>https://dev.to/hrgdavor/scrollbar-gutter-to-the-rescue-30hm</link>
      <guid>https://dev.to/hrgdavor/scrollbar-gutter-to-the-rescue-30hm</guid>
      <description>&lt;p&gt;As of 2026, scrollbar customization has reached a high level of cross-browser stability through standard CSS properties, specifically addressing preventing content width changes (layout shifts).&lt;/p&gt;

&lt;p&gt;Of &lt;a href="https://dev.to/hrgdavor/steal-back-from-ai-stealbackfromai-eod"&gt;series &lt;/a&gt; of &lt;a href="https://dev.to/t/stealbackfromai"&gt;#stealbackfromai&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Preventing Content Width Changes
&lt;/h2&gt;

&lt;p&gt;The most effective way to ensure a scrollbar does not change content width is the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/scrollbar-gutter" rel="noopener noreferrer"&gt;&lt;strong&gt;scrollbar-gutter&lt;/strong&gt;&lt;/a&gt; property, which is now widely supported in modern browsers.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;scrollbar-gutter: stable;&lt;/code&gt;&lt;/strong&gt;: This reserves space for the scrollbar even when content doesn't overflow. This prevents the "jumping" effect where content shifts left once a scrollbar appears.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integrating with Padding&lt;/strong&gt;: When no scrollbar is present, the reserved gutter is painted as an extension of your existing padding. This allows you to define a consistent layout where the scrollbar simply "fills in" the space you've already allocated.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Centering Content&lt;/strong&gt;: You can use &lt;code&gt;scrollbar-gutter: stable both-edges;&lt;/code&gt; to reserve space on both sides of a container, keeping centered content perfectly aligned regardless of scroll state.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Floating/Overlay Behavior
&lt;/h2&gt;

&lt;p&gt;"floating" scrollbars are typically handled as &lt;strong&gt;overlay scrollbars&lt;/strong&gt; by the operating system (standard on macOS and mobile). &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Native Overlay&lt;/strong&gt;: If the OS uses overlay scrollbars, they naturally float on top of content without affecting layout width.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Standard Styling&lt;/strong&gt;: You can now use standard properties like &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/scrollbar-width" rel="noopener noreferrer"&gt;&lt;strong&gt;scrollbar-width&lt;/strong&gt;&lt;/a&gt; (values: &lt;code&gt;auto&lt;/code&gt;, &lt;code&gt;thin&lt;/code&gt;, &lt;code&gt;none&lt;/code&gt;) and &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/scrollbar-color" rel="noopener noreferrer"&gt;&lt;strong&gt;scrollbar-color&lt;/strong&gt;&lt;/a&gt; to adjust look and feel across Chrome, Firefox, and Safari without relying solely on old &lt;code&gt;-webkit-&lt;/code&gt; prefixes. &lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Implementation Summary
&lt;/h2&gt;

&lt;p&gt;To achieve a look where the scrollbar doesn't shift content and fits within your padding:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.container&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;overflow-y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c"&gt;/* Reserves the space so content width stays fixed */&lt;/span&gt;
  &lt;span class="py"&gt;scrollbar-gutter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;stable&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c"&gt;/* Modern styling for the scrollbar itself */&lt;/span&gt;
  &lt;span class="py"&gt;scrollbar-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;thin&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;scrollbar-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#888&lt;/span&gt; &lt;span class="nb"&gt;transparent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To implement a layout where the scrollbar occupies the "padding" area without shifting content, you can combine &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;scrollbar-gutter: stable&lt;/code&gt;&lt;/strong&gt; with CSS variables to dynamically manage your container's internal spacing.&lt;/p&gt;

&lt;p&gt;Modern Implementation with CSS Variables&lt;/p&gt;

&lt;p&gt;This approach uses a variable for padding that accounts for the scrollbar space, ensuring your content remains visually centered or aligned as if the scrollbar were "floating" in that reserved gutter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* Define base padding for your layout */&lt;/span&gt;
  &lt;span class="py"&gt;--site-padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c"&gt;/* Use a variable for scrollbar color for easy theme switching */&lt;/span&gt;
  &lt;span class="py"&gt;--sb-thumb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#888&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--sb-track&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;transparent&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.scroll-container&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* 1. Layout Stability */&lt;/span&gt;
  &lt;span class="nl"&gt;overflow-y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c"&gt;/* Always reserves space for the scrollbar so content doesn't jump */&lt;/span&gt;
  &lt;span class="py"&gt;scrollbar-gutter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;stable&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 

  &lt;span class="c"&gt;/* 2. Visual Customization (2026 Standards) */&lt;/span&gt;
  &lt;span class="py"&gt;scrollbar-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;thin&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* Options: auto, thin, none */&lt;/span&gt;
  &lt;span class="py"&gt;scrollbar-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--sb-thumb&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--sb-track&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c"&gt;/* 3. Padding Integration */&lt;/span&gt;
  &lt;span class="c"&gt;/* Content is padded by --site-padding, and the gutter 
     sits alongside it without pushing the content in further */&lt;/span&gt;
  &lt;span class="py"&gt;padding-inline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--site-padding&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c"&gt;/* Optional: Mirror the gutter on the left for perfect centering */&lt;/span&gt;
  &lt;span class="c"&gt;/* scrollbar-gutter: stable both-edges; */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c"&gt;/* Hover effect to make the "floating" bar more interactive */&lt;/span&gt;
&lt;span class="nc"&gt;.scroll-container&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--sb-thumb&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#555&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  demos
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://codepen.io/Connum/pen/XWqxXzw" rel="noopener noreferrer"&gt;CodePen by Connum&lt;/a&gt; &lt;strong&gt;Comprehensive Toggle Demo&lt;/strong&gt;: This  allows you to toggle &lt;code&gt;scrollbar-gutter: stable&lt;/code&gt; and &lt;code&gt;both-edges&lt;/code&gt; on and off to see exactly how it prevents layout shifts.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://codepen.io/waxolunist/embed/ExweBMz?" rel="noopener noreferrer"&gt;CodePen by waxolunist&lt;/a&gt;  &lt;strong&gt;Centered Content Demo&lt;/strong&gt;: This shows how to use &lt;code&gt;stable both-edges&lt;/code&gt; to keep content perfectly centered within its padding, even when a scrollbar is added.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://defensivecss.dev/tip/scrollbar-gutter/" rel="noopener noreferrer"&gt;interactive demo on Defensive CSS&lt;/a&gt; shows how this works with your padding specifically, which focuses on using this property to keep UI "polished" and prevent content from bumping into the scrollbar.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Recap
&lt;/h2&gt;

&lt;p&gt;In these examples, the magic happens with these three lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.container&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;overflow-y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;          &lt;span class="c"&gt;/* Only show scrollbar if needed */&lt;/span&gt;
  &lt;span class="py"&gt;scrollbar-gutter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;stable&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c"&gt;/* Reserve the space to prevent width change */&lt;/span&gt;
  &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;20px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;             &lt;span class="c"&gt;/* Scrollbar sits in its gutter, NOT on your padding */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  how does this compare to scroll: always
&lt;/h1&gt;

&lt;p&gt;While &lt;code&gt;overflow-y: scroll&lt;/code&gt; (always) and &lt;code&gt;scrollbar-gutter: stable&lt;/code&gt; both prevent layout shifts, they differ significantly in visual "clutter" and how they handle different operating systems.&lt;/p&gt;

&lt;p&gt;Comparison Table&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;&lt;code&gt;overflow-y: scroll&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;scrollbar-gutter: stable&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Visibility&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Always shows the scrollbar track and thumb, even if content doesn't overflow.&lt;/td&gt;
&lt;td&gt;Only shows the thumb when content overflows, but always reserves the space.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Visual Look&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Can look "broken" or cluttered on short pages due to the empty track.&lt;/td&gt;
&lt;td&gt;Keeps the layout clean; the reserved space is invisible (matches background) until needed.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Layout Shift&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Prevents shifts because the scrollbar is permanent.&lt;/td&gt;
&lt;td&gt;Prevents shifts by "locking" the content width regardless of overflow state.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Overlay SB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Forces a gutter even on systems that use floating/overlay scrollbars (like macOS).&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Smart:&lt;/strong&gt; On overlay systems, it reserves zero space because the scrollbar floats on top anyway.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The main drawback of the old &lt;code&gt;overflow-y: scroll&lt;/code&gt; trick is that it forces an "ugly" empty scrollbar track on every page.&lt;/p&gt;

&lt;p&gt;With &lt;code&gt;scrollbar-gutter: stable&lt;/code&gt;, the browser reserves the exact width of a scrollbar as a "blank" gutter.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;When content is short:&lt;/strong&gt; You see your background color in that gutter, but no scrollbar track.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;When content overflows:&lt;/strong&gt; The scrollbar thumb simply appears in that reserved space.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;With Overlay Scrollbars:&lt;/strong&gt; It does nothing on mobile or macOS (where scrollbars don't take up space), whereas &lt;code&gt;overflow: scroll&lt;/code&gt; might force an unnecessary layout change&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Reset
&lt;/h2&gt;

&lt;p&gt;Many modern "resets" now use this combination for a polished feel:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nt"&gt;html&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c"&gt;/* Only scroll if needed */&lt;/span&gt;
  &lt;span class="nl"&gt;overflow-y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
  &lt;span class="c"&gt;/* Fallback for older Safari versions */&lt;/span&gt;
  &lt;span class="nl"&gt;overflow-y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;scroll&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
  &lt;span class="c"&gt;/* Modern standard to prevent jumping */&lt;/span&gt;
  &lt;span class="py"&gt;scrollbar-gutter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;stable&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  More
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://caniuse.com/mdn-css_properties_scrollbar-gutter" rel="noopener noreferrer"&gt;caniuse&lt;/a&gt; scrollbar-gutter&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/scrollbar-gutter" rel="noopener noreferrer"&gt;MDN Web Docs&lt;/a&gt; scrollbar-gutter&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://css-tricks.com/almanac/properties/s/scrollbar-gutter/" rel="noopener noreferrer"&gt;CSS-Tricks Guide&lt;/a&gt; scrollbar-gutter&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://gomakethings.com/prevent-scrollbar-jump/" rel="noopener noreferrer"&gt;Go Make Things&lt;/a&gt; Prevent scrollbar jump&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>stealbackfromai</category>
      <category>css</category>
    </item>
    <item>
      <title>Java record compact constructors (steal back from ai)</title>
      <dc:creator>Davor Hrg</dc:creator>
      <pubDate>Wed, 25 Feb 2026 09:49:29 +0000</pubDate>
      <link>https://dev.to/hrgdavor/java-record-compact-constructors-steal-back-from-ai-4d8a</link>
      <guid>https://dev.to/hrgdavor/java-record-compact-constructors-steal-back-from-ai-4d8a</guid>
      <description>&lt;p&gt;First in the &lt;a href="https://dev.to/hrgdavor/steal-back-from-ai-stealbackfromai-eod"&gt;series &lt;/a&gt; of &lt;a href="https://dev.to/t/stealbackfromai"&gt;#stealbackfromai&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  you need validation for record or default values ?
&lt;/h1&gt;

&lt;p&gt;I was silly trying this to define default values in a record:&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="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;SomeCounts&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;UUID&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;v2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;v3&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
    &lt;span class="nc"&gt;SomeCounts&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;UUID&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;v2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;v2&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="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;v1&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="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;v2&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="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;v2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
                &lt;span class="n"&gt;v3&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="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;v3&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 does not compile (Canonical constructor cannot delegate to another constructor). Turns out there is nicer and cleaner way already provided called compact constructor.&lt;/p&gt;

&lt;h1&gt;
  
  
  Compact constructors
&lt;/h1&gt;

&lt;p&gt;Compact constructors in Java records provide a concise way to customize the canonical constructor (the default one matching the record's components) without repeating parameter names.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Syntax Basics&lt;/strong&gt;&lt;br&gt;
Declare it immediately after the record header, using just the record name in braces—no parameter list needed:&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="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;SomeCounts&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;UUID&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;v2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;v3&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;SomeCounts&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// id = id; //do not do this, just set values you want to change&lt;/span&gt;
        &lt;span class="n"&gt;v1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;v1&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="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;v2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;v2&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="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;v2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;v3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;v3&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="mi"&gt;0&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;v3&lt;/span&gt;
        &lt;span class="c1"&gt;// Validation example: &lt;/span&gt;
        &lt;span class="c1"&gt;//if (id == null) throw new IllegalArgumentException();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The compiler implicitly supplies parameters matching the fields and handles field assignments &lt;em&gt;after&lt;/em&gt; your body executes.&lt;sup id="fnref1"&gt;1&lt;/sup&gt;&lt;sup id="fnref2"&gt;2&lt;/sup&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Execution Flow
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Your compact body runs first (for validation/normalization).&lt;/li&gt;
&lt;li&gt;Compiler auto-assigns parameters to &lt;code&gt;final&lt;/code&gt; fields.&lt;/li&gt;
&lt;li&gt;Superclass &lt;code&gt;Record&lt;/code&gt; constructor completes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;You can modify parameters (like null checks), throw exceptions, or print logs, but &lt;em&gt;cannot&lt;/em&gt; assign fields directly (&lt;code&gt;this.v1 = ...&lt;/code&gt; fails compilation).&lt;sup id="fnref3"&gt;3&lt;/sup&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Benefits
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero boilerplate&lt;/strong&gt;: No need to write &lt;code&gt;this.field = field;&lt;/code&gt; everywhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Immutable safety&lt;/strong&gt;: Fields stay &lt;code&gt;final&lt;/code&gt;; changes only affect incoming parameters before final assignment.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Canonical only&lt;/strong&gt;: Applies to the main constructor; doesn't affect static factories or other overloads.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Common Patterns
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Use Case&lt;/th&gt;
&lt;th&gt;Example Code&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Null-to-default&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;v1 = (v1 != null) ? v1 : 0L;&lt;/code&gt; &lt;sup id="fnref4"&gt;4&lt;/sup&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Range validation&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;if (v3 &amp;lt; 0) throw new IllegalArgumentException();&lt;/code&gt; &lt;sup id="fnref5"&gt;5&lt;/sup&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Normalization&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;id = id.toString().toLowerCase();&lt;/code&gt; &lt;sup id="fnref5"&gt;5&lt;/sup&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This keeps your record cleaner while ensuring numeric fields are never null post-construction.&lt;sup id="fnref1"&gt;1&lt;/sup&gt;&lt;sup id="fnref6"&gt;6&lt;/sup&gt;&lt;sup id="fnref7"&gt;7&lt;/sup&gt;&lt;sup id="fnref8"&gt;8&lt;/sup&gt;&lt;sup id="fnref9"&gt;9&lt;/sup&gt;&lt;sup id="fnref10"&gt;10&lt;/sup&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Do not set all fields
&lt;/h2&gt;

&lt;p&gt;How Assignment Works&lt;br&gt;
The compact constructor modifies local parameter variables, not fields directly. After your body executes, the compiler implicitly assigns each parameter to its corresponding final field, so those values that you do not change are set(I was defensive and setting all of them, but even).&lt;/p&gt;

&lt;p&gt;If you declare an empty compact constructor, it will work just fine, no worries like for java beans.&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="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;SomeCounts&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="no"&gt;UUID&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;v1&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;v2&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;v3&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;SomeCounts&lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// all values will be set, do not worry&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;ol&gt;

&lt;li id="fn1"&gt;
&lt;p&gt;&lt;a href="https://blogs.oracle.com/javamagazine/post/java-record-compact-canonical-constructor" rel="noopener noreferrer"&gt;https://blogs.oracle.com/javamagazine/post/java-record-compact-canonical-constructor&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn2"&gt;
&lt;p&gt;&lt;a href="https://docs.oracle.com/en/java/javase/16/language/records.html" rel="noopener noreferrer"&gt;https://docs.oracle.com/en/java/javase/16/language/records.html&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn3"&gt;
&lt;p&gt;&lt;a href="https://stackoverflow.com/questions/74083123/java-record-compact-constructor-bytecode" rel="noopener noreferrer"&gt;https://stackoverflow.com/questions/74083123/java-record-compact-constructor-bytecode&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn4"&gt;
&lt;p&gt;&lt;a href="https://xebia.com/blog/how-to-use-java-records/" rel="noopener noreferrer"&gt;https://xebia.com/blog/how-to-use-java-records/&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn5"&gt;
&lt;p&gt;&lt;a href="https://mikemybytes.com/2022/02/16/java-records-and-compact-constructors/" rel="noopener noreferrer"&gt;https://mikemybytes.com/2022/02/16/java-records-and-compact-constructors/&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn6"&gt;
&lt;p&gt;&lt;a href="https://www.reddit.com/r/java/comments/stte7o/java_records_compact_constructors/" rel="noopener noreferrer"&gt;https://www.reddit.com/r/java/comments/stte7o/java_records_compact_constructors/&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn7"&gt;
&lt;p&gt;&lt;a href="https://stackoverflow.com/questions/77582274/throw-exception-in-records-compact-constructor" rel="noopener noreferrer"&gt;https://stackoverflow.com/questions/77582274/throw-exception-in-records-compact-constructor&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn8"&gt;
&lt;p&gt;&lt;a href="https://www.baeldung.com/java-records-custom-constructor" rel="noopener noreferrer"&gt;https://www.baeldung.com/java-records-custom-constructor&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn9"&gt;
&lt;p&gt;&lt;a href="https://coderanch.com/t/754107/certification/understand-Records-comapact-constructor-overloaded" rel="noopener noreferrer"&gt;https://coderanch.com/t/754107/certification/understand-Records-comapact-constructor-overloaded&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;li id="fn10"&gt;
&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=i0p1B7L3FW8" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=i0p1B7L3FW8&lt;/a&gt; ↩&lt;/p&gt;
&lt;/li&gt;

&lt;/ol&gt;

</description>
      <category>stealbackfromai</category>
    </item>
    <item>
      <title>"Steal" back from AI #stealbackfromai</title>
      <dc:creator>Davor Hrg</dc:creator>
      <pubDate>Wed, 25 Feb 2026 09:31:36 +0000</pubDate>
      <link>https://dev.to/hrgdavor/steal-back-from-ai-stealbackfromai-eod</link>
      <guid>https://dev.to/hrgdavor/steal-back-from-ai-stealbackfromai-eod</guid>
      <description>&lt;h1&gt;
  
  
  What?
&lt;/h1&gt;

&lt;p&gt;I am making tiny an effort, to share summaries from things I find useful, that google and others stole from stackoverflow and all of us. It may be recursive or pointless, but I am doing it anyway. &lt;/p&gt;

&lt;p&gt;Also, if anyone is interested, can comment on each of those, maybe fix some mistakes or provide different opinion, or whatever.&lt;/p&gt;

&lt;p&gt;No commitment, just an experiment. Also, could be useful to me in the future, ... who knows.&lt;/p&gt;

&lt;p&gt;There will likely be somewhat embarasing things, I should have known much before, but hey, shit happens.&lt;/p&gt;

&lt;h1&gt;
  
  
  They are pisseed
&lt;/h1&gt;

&lt;p&gt;My thing here is in no way as consequential as destilling, but hope never dies that I can make some tiny difference.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=W9WB2xbM5sc" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=W9WB2xbM5sc&lt;/a&gt;&lt;/p&gt;

</description>
      <category>stealbackfromai</category>
    </item>
    <item>
      <title>REST is often THE WRONG CHOICE, it is a lazy default for too many, and I am sick of it.</title>
      <dc:creator>Davor Hrg</dc:creator>
      <pubDate>Sun, 21 Dec 2025 17:48:05 +0000</pubDate>
      <link>https://dev.to/hrgdavor/rest-is-often-the-wrong-choice-it-is-a-lazy-default-for-too-many-and-i-am-sick-of-it-4c8k</link>
      <guid>https://dev.to/hrgdavor/rest-is-often-the-wrong-choice-it-is-a-lazy-default-for-too-many-and-i-am-sick-of-it-4c8k</guid>
      <description>&lt;p&gt;TL;DR: Rant: When the frontend simply calls backend methods(it's RPC), REST is the wrong choice there. It’s your decision if you still want to use it, sure it gets the job done, but it’s not really ok when forced on those who don’t need or want it. You should at least know JSON-RPC exists (and has actual good spec, unlike REST).&lt;/p&gt;

&lt;p&gt;Thumbnail from &lt;a href="https://youtu.be/pKZw1im9yt8?t=14" rel="noopener noreferrer"&gt;The Family Guy&lt;/a&gt; :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;dick, you ever wonder what's outside those walls ?&lt;/li&gt;
&lt;li&gt;now, that's dangerous thinking Paul, you better stick to your work&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why I rant about it
&lt;/h2&gt;

&lt;p&gt;This wouldn’t be as bad if it weren’t affecting the freedom to choose the best fit for a specific project in practice. The problem with trendy tech is that it trickles down, so it’s shoved down our throats regardless of whether it fits or not. All kinds of "experts" who influence the choice of technology in specifications, auditing, and management go along with those trends, because deviating would make them look less "expertly."... Also AI loves statistically average slop. &lt;/p&gt;

&lt;h2&gt;
  
  
  Scope
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;This text applies to use cases where two sets of code communicate through method calls&lt;/strong&gt;. A persistent backend that exposes API methods to perform some work, and a frontend that uses a wrapper—such as Axios to turn the API into convenient methods for components to call. Using REST as the communication layer in this scenario is downright disgusting.  REST may be better fit for PHP depending on how URLs are routed, or whatever else.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I prefer making single-page applications and do not care even slightly for SSE. I’ve done my share of PHP programming, experienced some horrible things in Java EE, ASP and .NET, and I’m never going back to that.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why REST is wrong here
&lt;/h2&gt;

&lt;p&gt;On both sides of the communication, you have code that is neatly defined as a method or an interface. It has a method name, some parameters, a return value, and it can throw an exception (or, if you prefer, return a union type with an error). &lt;/p&gt;

&lt;p&gt;Instead of providing a clear and simple mapping, you are using REST, and it introduces an unnecessary layer of noise:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Different HTTP methods&lt;/li&gt;
&lt;li&gt;Different content types in responses, especially in case of errors&lt;/li&gt;
&lt;li&gt;Sometimes parameters are passed in the URL&lt;/li&gt;
&lt;li&gt;Sometimes they are passed in the request body&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;This gives me flashbacks to SOAP, where values could be passed either in tags or in attributes, creating a mess when you try to map everything.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Look at this simple example in Java&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;@GetMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/users/{userId}/orders"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Order&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getUserOrders&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="nd"&gt;@PathVariable&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;       &lt;span class="c1"&gt;// From path: /users/123/orders&lt;/span&gt;
    &lt;span class="nd"&gt;@RequestParam&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;  &lt;span class="c1"&gt;// ?status=PENDING&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;orderRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findByUserIdAndStatus&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userId&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="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It may not look too bad because people are used to it. But there is so much pointless noise here.&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;@Rpc&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Order&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getUserOrders&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;userId&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;status&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;orderRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;findByUserIdAndStatus&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;userId&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="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  R: But I like REST
&lt;/h2&gt;

&lt;p&gt;Why are you reading this? Just move along.&lt;/p&gt;

&lt;h2&gt;
  
  
  R: But REST is better because of X...
&lt;/h2&gt;

&lt;p&gt;Why are you still here? GTFO.&lt;/p&gt;

&lt;h2&gt;
  
  
  There are better ways
&lt;/h2&gt;

&lt;p&gt;Anything consistent is better. I’m sure there are great options like gRPC, but it’s easy to build your own. And if you’re used to sending JSON around, it will probably end up looking like &lt;a href="https://www.jsonrpc.org/specification" rel="noopener noreferrer"&gt;JSON-RPC&lt;/a&gt; .&lt;/p&gt;

&lt;h2&gt;
  
  
  R: But I like shiny generated Swagger/OpenApi
&lt;/h2&gt;

&lt;p&gt;You can still generate that easily without the REST mess. I have written code to generate API swagger few times, and it is not rocket science. You should be ashamed If you can't use some Java reflection to do the same.&lt;/p&gt;

&lt;h2&gt;
  
  
  R: Are you sending HTTP 200 for errors too ?
&lt;/h2&gt;

&lt;p&gt;By default yes, because I do not care, HTTP is just one of possible transport layers. Errors are communicated inside the JSONRPC protocol. Why do you care? If really needed it is rather simple to also send different HTTP codes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding security to routes
&lt;/h2&gt;

&lt;p&gt;I would not do it by default. It is trivial to do, just enforce method names are part of the URL (even if it means they are redundantly in the RPC payload). &lt;/p&gt;

&lt;p&gt;On the other hand it is actually not a bad idea to add method name to url in a way it can be ignored by the backend, as it helps you see method names when inspecting in browser (instead a slew of requests to /rpc).&lt;/p&gt;

&lt;h2&gt;
  
  
  Compact API on JS side
&lt;/h2&gt;

&lt;p&gt;Mapping API methods can be done in type safe way without bloating the bundle with a simple proxy and types declaration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;makeRpcHandler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;method&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nx"&gt;reject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// call api method here and resolve / reject&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="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;receiver&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;prop&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;makeRpcHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prop&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="cm"&gt;/** @typedef { import('./MyApiInterface.js').MyApi } MyApi */&lt;/span&gt;
&lt;span class="cm"&gt;/** @type {MyApi} */&lt;/span&gt; 
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MyApi&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;Proxy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the &lt;code&gt;MyApiInterface.ts&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;SearchParam&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;order&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;Book&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;
  &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;SearchResult&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;
  &lt;span class="na"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;
  &lt;span class="na"&gt;rowcount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;MyApi&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;bookSearch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;search&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SearchParam&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SearchResult&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Book&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Later in your code, just import MyApi and use with all of the types visible in your editor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;MyApi&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./MyApi.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;someThing&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;
  &lt;span class="c1"&gt;// IDE will know result is SearchResult&amp;lt;Book&amp;gt;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;MyApi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bookSearch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;:[...]})&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that you have a single proxy that routes methods, and type safety is achieved through type definitions that will not be part of the bundle. Therefore, whether you have one method or one hundred, the bundled code for the bridge remains the same size.&lt;/p&gt;

&lt;h2&gt;
  
  
  More details
&lt;/h2&gt;

&lt;p&gt;How about NO. The point here is that you think about it a bit and decide if you still think REST is so great, and then accept the consequences of your decision. Code examples here are not a full solution, just to explain a bit deeper, what things could be.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you think SOAP is terrible but REST is great.
&lt;/h2&gt;

&lt;p&gt;I have news for you. SOAP is objectively much better technology than REST, but that also makes it complicated, and then sprinkle complication by big companies(MS, IBM, Oracle) and SOAP becomes even bigger pain in the ass to use as a developer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enshittification
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Enshittification" rel="noopener noreferrer"&gt;https://en.wikipedia.org/wiki/Enshittification&lt;/a&gt; is really nice term that I would like to draw some parallels here. REST by itself is not the sole issue here, it is part of a pattern. &lt;/p&gt;

&lt;p&gt;I can not speak about other languages, but in Java this ties neatly into Spring. These systems are intentionally made complicated so a ecosystem of consultants earn money. You are lured into using it by "Look how easy it is to make XY" but then for every little step you have to learn (or pay consultants) how to do it with Spring. Soon you are not coding, but configuring Spring. After you are hooked with "simple to start a project" you pay the price later (search: Spring cloud Gateway). &lt;/p&gt;

&lt;h2&gt;
  
  
  AI can help
&lt;/h2&gt;

&lt;p&gt;I strongly disagree here. AI will just make it worse. It will help produce more of the same bloated code that is in-line with short term corporate thinking, and will create a long term nightmare.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Hibernate HQL builder</title>
      <dc:creator>Davor Hrg</dc:creator>
      <pubDate>Wed, 05 Mar 2025 21:53:50 +0000</pubDate>
      <link>https://dev.to/hrgdavor/hibernate-hql-builder-55md</link>
      <guid>https://dev.to/hrgdavor/hibernate-hql-builder-55md</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;A single utility class for writing HQL in more readable way. Simplifies writing multiline queries, and queries that have optional parts depending on parameter values (like API adding extra filter by date if supplied date != null).&lt;/p&gt;

&lt;h2&gt;
  
  
  The utility
&lt;/h2&gt;

&lt;p&gt;For now, this not published as a maven library. &lt;strong&gt;Just copy&lt;/strong&gt; the utility class &lt;code&gt;HqlBuilder.java&lt;/code&gt; from &lt;a href="https://github.com/hrgdavor/hql-query-builder" rel="noopener noreferrer"&gt;hql-query-builder&lt;/a&gt;  to your project. If you like it, but not 100%, just tweak it to your own coding style.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rant:&lt;/strong&gt; *I both love and hate(more) Hibernate. I try to avoid it, but that is not always possible. Technologically Hibernate is great , and has sooo many features. For the same reason it is also is a huge bloat, slows down start-up, does freaky bytecode manipulation, and is pain in the ass so many times. Takes a ton of time to learn and obscures learning actual SQL. *&lt;/p&gt;

&lt;h2&gt;
  
  
  Basic comparison and sample use
&lt;/h2&gt;

&lt;p&gt;Standard way to write &lt;strong&gt;HQL&lt;/strong&gt; is ok for smaller queries, but gets ugly very quickly.&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;Query&lt;/span&gt; &lt;span class="n"&gt;q&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;.&lt;/span&gt;&lt;span class="na"&gt;createQuery&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
  &lt;span class="s"&gt;"SELECT id FROM Adddress WHERE userId = :userId AND houseNo &amp;gt; :houseNo"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setParameter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"userId"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;q&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setParameter&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"houseNo"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2L&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This style is also supported by &lt;code&gt;HqlBuilder&lt;/code&gt; (but it allows much more)&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;hb&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;HqlBuilder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
  &lt;span class="s"&gt;"SELECT id FROM Adddress WHERE userId = :userId AND houseNo &amp;gt; :houseNo"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;hb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;p&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"userId"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;hb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;p&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"houseNo"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2L&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above query can be written 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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;hb&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;HqlBuilder&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;hb&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="s"&gt;"SELECT id"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;hb&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="s"&gt;"FROM Adddress"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;hb&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="s"&gt;"WHERE"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;hb&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="s"&gt;"  userId = :userId"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;hb&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="s"&gt;"  AND houseNo &amp;gt; :houseNo"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2L&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;in a way that allows more formatting options for readability, and also placing parameter values close to where they are used in the query itself. &lt;/p&gt;

&lt;p&gt;Each call to &lt;code&gt;add&lt;/code&gt; puts a newline before each line(except the first), so, for the code above, &lt;strong&gt;HQL&lt;/strong&gt; will look like this:&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="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Adddress&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;
  &lt;span class="n"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;userId&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;houseNo&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;houseNo&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Debug and logging
&lt;/h2&gt;

&lt;p&gt;You can get the query that is going into hibernate by calling &lt;code&gt;hb.getQueryString()&lt;/code&gt; and you will get the query above.&lt;/p&gt;

&lt;p&gt;You can also dump what query with values looks like with &lt;code&gt;hb.toString()&lt;/code&gt;. Not actual SQL that will go into database, just an approximation where parameter placeholders are replaced with values(NULL for null values, and for other &lt;code&gt;.toString()&lt;/code&gt;)&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="no"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="n"&gt;street&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;
&lt;span class="no"&gt;FROM&lt;/span&gt; &lt;span class="nc"&gt;Adddress&lt;/span&gt;
&lt;span class="no"&gt;WHERE&lt;/span&gt;
  &lt;span class="n"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;
  &lt;span class="no"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;houseNo&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can improve this output by implementing more logic into &lt;code&gt;valueToString&lt;/code&gt; method.&lt;/p&gt;

&lt;h2&gt;
  
  
  Executing the query using Hibernate
&lt;/h2&gt;

&lt;p&gt;When you finish building the desired HQL, just call &lt;code&gt;.build(session)&lt;/code&gt; or &lt;code&gt;.build(session, resultType)&lt;/code&gt; to create a Hibernate Query object (that you can then use to list results)&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;hb&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="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Long&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="na"&gt;list&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  More details on .add
&lt;/h2&gt;

&lt;p&gt;The builder can be constructed empty, or with parameters (it is just alias to &lt;code&gt;.add&lt;/code&gt;)&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;hb&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;HqlBuilder&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;hb&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="s"&gt;"SELECT * FROM User"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// is same as&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;hb&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;HqlBuilder&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SELECT * FROM User"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, the further examples we will be for &lt;code&gt;.add&lt;/code&gt; method&lt;/p&gt;

&lt;p&gt;Add just a new query part (with or without parameter placeholders)&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;hb&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="s"&gt;"FROM User"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add query part with parameters and values&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;hb&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="s"&gt;"WHERE userId = :userId"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you are providing values for parameters, you must match count of parameters in query part. Either provide all parameter values, or provide none (and define them later using &lt;code&gt;.p&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Add a new query part with parameter placeholders, and provide parameter values separately)&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;hb&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="s"&gt;"  AND city = :city"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;hb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;p&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"city"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Hibernate HQL works nice with records
&lt;/h2&gt;

&lt;p&gt;As you can give a record to Hibernate Query to fill it with query results, this gives a nice way of working with selected data (this is part of Hibernate, not a feature added by this utility).&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="c1"&gt;// Address entity has many more fields, here we define those that we plan to select&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;record&lt;/span&gt; &lt;span class="nf"&gt;AddressPart&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;id&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;street&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;city&lt;/span&gt;&lt;span class="o"&gt;){};&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;AddressPart&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getUserAddresses&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
  &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;hb&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;HqlBuilder&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;hb&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="s"&gt;"SELECT id,street,city"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;hb&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="s"&gt;"FROM Adddress"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;hb&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="s"&gt;"WHERE"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;hb&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="s"&gt;"  userId = :userId"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

  &lt;span class="nc"&gt;Session&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;...;&lt;/span&gt;&lt;span class="c1"&gt;// obtain hibernate session in your app&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;hb&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="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;AddressPart&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="na"&gt;list&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;h2&gt;
  
  
  More examples
&lt;/h2&gt;

&lt;p&gt;A query that has optional name filter. If name is not provided, name filter is not added to query.&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="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getUsers&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;var&lt;/span&gt; &lt;span class="n"&gt;hb&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;HqlBuilder&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;hb&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="s"&gt;"FROM User"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;&lt;span class="c1"&gt;// HQL when selecting whole entities only needs FROM&lt;/span&gt;
  &lt;span class="n"&gt;hb&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="s"&gt;"WHERE"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;hb&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="s"&gt;"  deleted = :deleted"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&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;name&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;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="o"&gt;()){&lt;/span&gt;
  &lt;span class="n"&gt;hb&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="s"&gt;"  name LIKE :name"&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;name&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="nc"&gt;Session&lt;/span&gt; &lt;span class="n"&gt;session&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;...;&lt;/span&gt;&lt;span class="c1"&gt;// obtain hibernate session in your app&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;hb&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="n"&gt;session&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;User&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="na"&gt;list&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;h2&gt;
  
  
  Dilemma 1: parameters without name
&lt;/h2&gt;

&lt;p&gt;Not sure if it s good or bad to use the builder like this, but I could not help but to allow this use-case. If you just use &lt;code&gt;:&lt;/code&gt; without defining parameter name, then values are required at the &lt;code&gt;.add&lt;/code&gt; method call.&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;hb&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;HqlBuilder&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;hb&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="s"&gt;"SELECT id,street,city"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;hb&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="s"&gt;"FROM Adddress"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;hb&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="s"&gt;"WHERE"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;hb&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="s"&gt;"  userId = :"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;hb&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="s"&gt;"  AND houseNo &amp;gt; :"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2L&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the code above, HQL will look like this:&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="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;street&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;city&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Adddress&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;
  &lt;span class="n"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;_param_1&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;houseNo&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;_param_2&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Dilemma 2: conditional code formatting
&lt;/h2&gt;

&lt;p&gt;I would like to keep consistent visual formatting of indent in conditional statements. Example of such is:&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;hb&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;HqlBuilder&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;hb&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="s"&gt;"FROM User"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;&lt;span class="c1"&gt;// HQL when selecting whole entities only needs FROM&lt;/span&gt;
  &lt;span class="n"&gt;hb&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="s"&gt;"WHERE"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;hb&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="s"&gt;"  deleted = :deleted"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&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;name&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;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="o"&gt;()){&lt;/span&gt;
    &lt;span class="n"&gt;hb&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="s"&gt;"  name LIKE :name"&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;name&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that &lt;code&gt;if&lt;/code&gt; statement moves/confuses the indent of the HQL slightly. This is not a huge issue, but it bothers me. I have not found perfect solution, but am playing with few ideas.&lt;/p&gt;

&lt;p&gt;One idea is to move java code indent back:&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;hb&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;HqlBuilder&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;hb&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="s"&gt;"FROM User"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;&lt;span class="no"&gt;FROM&lt;/span&gt;
  &lt;span class="n"&gt;hb&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="s"&gt;"WHERE"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;hb&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="s"&gt;"  deleted = :deleted"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&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;name&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;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="o"&gt;()){&lt;/span&gt;
  &lt;span class="n"&gt;hb&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="s"&gt;"  name LIKE :name"&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;name&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another idea is utility function in the builder (but it works only for one line of HQL)&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="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;hb&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;HqlBuilder&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;hb&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="s"&gt;"FROM User"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;hb&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="s"&gt;"WHERE"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;hb&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="s"&gt;"  deleted = :deleted"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
  &lt;span class="n"&gt;hb&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="n"&gt;name&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;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="o"&gt;(),&lt;/span&gt;
      &lt;span class="s"&gt;"  name LIKE :name"&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;name&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;... Edit: addNextIf (it was &lt;strong&gt;very error prone&lt;/strong&gt;) is replaced with overload to add with first argument as boolean condition for when to add the part or not. &lt;/p&gt;

</description>
      <category>java</category>
      <category>hibernate</category>
      <category>sql</category>
      <category>hql</category>
    </item>
    <item>
      <title>Java livereload</title>
      <dc:creator>Davor Hrg</dc:creator>
      <pubDate>Wed, 22 Jan 2025 14:00:22 +0000</pubDate>
      <link>https://dev.to/hrgdavor/java-livereload-4j5i</link>
      <guid>https://dev.to/hrgdavor/java-livereload-4j5i</guid>
      <description>&lt;h2&gt;
  
  
  Intro
&lt;/h2&gt;

&lt;p&gt;Aim of this article is to help you reduce delay between writing code and seeing the results in Java.&lt;/p&gt;

&lt;p&gt;Background: I have been coding Java in backend and JavaScript in fronted for ages, and have grown accustomed to test things in JavaScript with livereload, but was not doing enough of that in Java.&lt;/p&gt;

&lt;p&gt;Disclaimer: I am not a fan of TDD, and amount of test I write depends on time I got and also on my mood at the time. Even if you do not write many tests you can see how playing with some pieces of code and seeing results immediately can be useful.&lt;/p&gt;

&lt;h2&gt;
  
  
  How ?
&lt;/h2&gt;

&lt;p&gt;Steps are really simple.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;run in DEBUG mode&lt;/li&gt;
&lt;li&gt;collect list of files to watch&lt;/li&gt;
&lt;li&gt;run a thread to watch for changes&lt;/li&gt;
&lt;li&gt;wait for a bit (100ms is reasonable) for hot-code-replace to kick in&lt;/li&gt;
&lt;li&gt;re-run your code&lt;/li&gt;
&lt;li&gt;continue looping until files change
&lt;/li&gt;
&lt;/ul&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;hr.hrg.livetest4j&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;java.io.File&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;BasicExample&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;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&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;Thread&lt;/span&gt;&lt;span class="o"&gt;(()-&amp;gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;File&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="o"&gt;=&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;File&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"target/test-classes/hr/hrg/livetest4j/BasicExample.class"&lt;/span&gt;&lt;span class="o"&gt;)};&lt;/span&gt;
            &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;lastMod&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;while&lt;/span&gt;&lt;span class="o"&gt;(!&lt;/span&gt;&lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;interrupted&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;mod&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;lastMod&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
                    &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;File&lt;/span&gt; &lt;span class="nl"&gt;file:&lt;/span&gt;&lt;span class="n"&gt;files&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;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;exists&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                            &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;err&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"file not found "&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getAbsolutePath&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
                            &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
                        &lt;span class="o"&gt;}&lt;/span&gt;
                        &lt;span class="n"&gt;mod&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Math&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;max&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mod&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;file&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lastModified&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;mod&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;lastMod&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                        &lt;span class="c1"&gt;// wait a bit more for hot-code-replace to kick in&lt;/span&gt;
                        &lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sleep&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                        &lt;span class="n"&gt;test&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;&lt;span class="c1"&gt;// run this on change&lt;/span&gt;
                    &lt;span class="o"&gt;}&lt;/span&gt;
                    &lt;span class="n"&gt;lastMod&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mod&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
                    &lt;span class="nc"&gt;Thread&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sleep&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;InterruptedException&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                    &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;printStackTrace&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
                &lt;span class="o"&gt;}&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}).&lt;/span&gt;&lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="o"&gt;(){&lt;/span&gt;
        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello basic 1"&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;Location of class files may be different depending on your IDE setup, and common ones are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;target/classes&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;target/test-classes&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;bin&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  LiveTest4j utility
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/hrgdavor/livetest4j" rel="noopener noreferrer"&gt;https://github.com/hrgdavor/livetest4j&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For now I do not plan to package it as maven dependency. It is simple enough to copy the Utility class from the project into your own project with other test classes.&lt;/p&gt;

&lt;p&gt;It started rather simple like the basic example above, but I have added few nice things so it is more usable in general.&lt;/p&gt;

&lt;p&gt;After you copy LiveTest4j.java to your project create a new file and simply use it 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="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;LiveTestSimple&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;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&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="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;LiveTest4j&lt;/span&gt;&lt;span class="o"&gt;(()-&amp;gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello live 1"&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="na"&gt;run&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;Run you class in debug mode, change something , save and watch the magic happen!&lt;/p&gt;

&lt;p&gt;The utility has some nice things&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it can extract class info from lambda to automatically add class file to watch list&lt;/li&gt;
&lt;li&gt;uses few common paths to search for generated class (and you can add your own)&lt;/li&gt;
&lt;li&gt;has debug mode detection to warn you if you mistakenly run it normally&lt;/li&gt;
&lt;li&gt;has an annotation to declare dependencies (classes,files) to add to watch list &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This can be very useful for specific use-cases, and if you like the benefits you can go further&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;run a specialised agent that allows more advanced code replace (built-in code reload is limited to method bodies)&lt;/li&gt;
&lt;li&gt;setup live reload in Spring&lt;/li&gt;
&lt;li&gt;use a commercial solution like JRebel&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Feedback
&lt;/h2&gt;

&lt;p&gt;All feedback how to improve the utility are welcome, also further resources on this topic as well.&lt;/p&gt;

&lt;p&gt;I tested this only in eclipse, so it would be great you can try it in your favourite editor and see if advanced things like debug mode detection works.&lt;/p&gt;

</description>
      <category>java</category>
      <category>livereload</category>
      <category>testing</category>
    </item>
  </channel>
</rss>
