<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Towards Dev - Medium]]></title>
        <description><![CDATA[A publication for sharing projects, ideas, codes, and new theories. - Medium]]></description>
        <link>https://towardsdev.com?source=rss----a648dc4ecb66---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>Towards Dev - Medium</title>
            <link>https://towardsdev.com?source=rss----a648dc4ecb66---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 11 Oct 2026 05:44:24 GMT</lastBuildDate>
        <atom:link href="https://towardsdev.com/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[I Built a Mac App That Makes Markdown Look Effortless]]></title>
            <link>https://towardsdev.com/i-built-a-mac-app-that-makes-markdown-look-effortless-bdabd0702af3?source=rss----a648dc4ecb66---4</link>
            <guid isPermaLink="false">https://medium.com/p/bdabd0702af3</guid>
            <category><![CDATA[mac]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[writing]]></category>
            <category><![CDATA[markdown]]></category>
            <category><![CDATA[productivity]]></category>
            <dc:creator><![CDATA[Stéphane Paquet]]></dc:creator>
            <pubDate>Sat, 10 Oct 2026 21:01:01 GMT</pubDate>
            <atom:updated>2026-10-10T21:01:01.601Z</atom:updated>
            <content:encoded><![CDATA[<h4>One place to read, write and keep your Markdown files your AI writes.</h4><figure><img alt="Markify, a free Markdown app for the Mac." src="https://cdn-images-1.medium.com/max/1024/1*6W7Y18U-r5LU7cWuubnERA.jpeg" /><figcaption>Markify, a free Markdown app for the Mac.</figcaption></figure><p>Markdown quietly won.</p><p>It started as a way for programmers to write README files. Today it’s the language of notes apps, blogs, documentation sites, help centers and team wikis. And over the past year it became something else: the language AI speaks. Ask ChatGPT, Claude or Gemini a question and the answer comes back in Markdown. Coding agents write their plans, specs and reports in Markdown. Even the instructions we give them live in Markdown files.</p><p>Yet reading and writing Markdown still feels like reading the back of a sweater. Pound signs, asterisks, brackets, pipes. Most apps give you two options: stare at the symbols, or split your screen in half and watch a preview update next to the text you’re actually typing.</p><p>I wanted neither. So I built Markify.</p><h4>A page, not a code file</h4><p>Open a Markdown file in <strong>Markify</strong> and it looks like a finished document. Headings are headings. Checklists have real checkboxes. Tables are tables. Code is colored, math is typeset, diagrams are drawn. You write directly on that page, the way you would in Pages or Word.</p><figure><img alt="A Markdown file in Markify: tags, a note, a checklist and a table, ready to edit." src="https://cdn-images-1.medium.com/max/1024/1*grRUYY_rIIJf7q803e_6cg.png" /><figcaption>A Markdown file in Markify: tags, a note, a checklist and a table, ready to edit.</figcaption></figure><p>Caption: A Markdown file in Markify: tags, a note, a checklist and a table, ready to edit.</p><figure><img alt="The raw Markdown is always there when you want it, and out of the way when you don’t." src="https://cdn-images-1.medium.com/max/1024/1*VWHafVZNDtRW-WjENH44FA.png" /><figcaption>The raw Markdown is always there when you want it, and out of the way when you don’t.</figcaption></figure><p>The difference is what happens when you save. Markify doesn’t turn your work into a proprietary format or lock it inside a database. Your file stays a plain Markdown file, the same one your blog, your team, your Git repository or your AI tools already understand. If you ever want to see the raw text, it’s one click away. You just don’t have to.</p><figure><img alt="The raw Markdown is always there when you want it, and out of the way when you don’t." src="https://cdn-images-1.medium.com/max/1024/1*VWHafVZNDtRW-WjENH44FA.png" /><figcaption>The raw Markdown is always there when you want it, and out of the way when you don’t.</figcaption></figure><h4>Markdown from anywhere</h4><p>Not every Markdown file lives on your Mac. Documentation sits on GitHub. Someone sends you a link. A project you’re curious about has twenty pages of docs in a repository you’ll never clone.</p><p>Markify opens them all. Paste a link to any Markdown file on the web and it opens as a clean page. Paste the address of a GitHub or GitLab repository and you can browse its documentation like a little library. Private repositories work too. Whatever you open is your own copy: your edits never leave your Mac.</p><figure><img alt="Open a Markdown file or a whole GitHub or GitLab repository from a link." src="https://cdn-images-1.medium.com/max/952/1*LFPwMGTv-KkAocuguQ6ZhA.png" /><figcaption>Open a Markdown file or a whole GitHub or GitLab repository from a link.</figcaption></figure><p>And for the files already on your Mac, you don’t even need to open the app. Select one in Finder, press Space, and Quick Look shows it properly formatted, images, math and diagrams included.</p><h4>A home for what your AI writes</h4><p>This is the part that surprised me most while building Markify.</p><p>If you work with AI coding tools like Claude Code, Codex or OpenCode, you know the feeling. The agent finishes a task and leaves you a long report in the terminal: what it changed, what it tested, what it’s unsure about. Or it writes a plan you’re supposed to review before it starts. That’s real reading, and a terminal is a terrible place to do it.</p><p>With Markify, your agent can send its report straight to the app, where it opens as a proper document you can read, edit and keep. The same goes for the files that steer these tools, the AGENTS.md and CLAUDE.md files that teams now maintain as carefully as their code.</p><p>Markify also speaks Google’s Open Knowledge Format, a way of packaging knowledge as a folder of Markdown files so AI agents can use it. Open one and Markify understands how the pieces connect and points out anything that’s broken.</p><p>In the next update, Markify will also notice when another app changes a file you have open, whether that’s an agent, a sync service or a teammate’s commit. You choose to keep your version, reload theirs, or merge the two.</p><h4>Everything a document needs</h4><p>Tables, checklists, footnotes, highlighted notes and warnings, code with syntax colors, math, and diagrams that draw themselves from a few lines of text. Keep your notes together in a library that shows every Markdown file in a folder, one click away.</p><figure><img alt="The library keeps every note in your folder one click away." src="https://cdn-images-1.medium.com/max/1024/1*wU3vpgU5-kxU87F_ioqftQ.png" /><figcaption>The library keeps every note in your folder one click away.</figcaption></figure><figure><img alt="Code and math look right, and stay plain text underneath." src="https://cdn-images-1.medium.com/max/1024/1*wU3vpgU5-kxU87F_ioqftQ.png" /><figcaption>Code and math look right, and stay plain text underneath.</figcaption></figure><p>When it’s time to share with someone who doesn’t use Markdown, export to a web page or a PDF in a couple of clicks.</p><h4>Apple Intelligence, on your Mac</h4><p>Markify uses Apple Intelligence to proofread, rewrite, change the tone of a paragraph, summarize a long document, or suggest a title and tags. Every suggestion can be compared with your original, accepted or reverted. It all runs on your Mac.</p><figure><img alt="Rewrite a paragraph with Apple Intelligence, then compare, accept or revert." src="https://cdn-images-1.medium.com/max/1024/1*Adx7oXaXUrToGSVGNE0pcQ.png" /><figcaption>Rewrite a paragraph with Apple Intelligence, then compare, accept or revert.</figcaption></figure><h4>Made for the Mac, and only the Mac</h4><p>Markify isn’t a website wrapped in an app. It’s built with Apple’s own tools, so it feels at home next to Notes and Pages, and follows the Mac conventions you already know.</p><p>It’s also private by design. No account to create. No ads. No tracking of what you write. Your files stay wherever you save them.</p><h4>Try it</h4><p>Markify is free and runs on Apple silicon and Intel Macs with macOS 26 or later.</p><p>Download Markify: <a href="https://github.com/spaquet/markify/releases/latest">https://github.com/spaquet/markify/releases/latest</a><br>Learn more: <a href="https://spaquet.github.io/markify/">https://spaquet.github.io/markify/</a></p><p>The install guide walks you through it in a minute in case you encounter some MacOS Safe Guard issues: <a href="https://spaquet.github.io/markify/help/updates.html">https://spaquet.github.io/markify/help/updates.html</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=bdabd0702af3" width="1" height="1" alt=""><hr><p><a href="https://towardsdev.com/i-built-a-mac-app-that-makes-markdown-look-effortless-bdabd0702af3">I Built a Mac App That Makes Markdown Look Effortless</a> was originally published in <a href="https://towardsdev.com">Towards Dev</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Your Database Is Probably the Slowest Part of Your Application-Here’s Why]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://towardsdev.com/your-database-is-probably-the-slowest-part-of-your-application-heres-why-d90a7d0dfb73?source=rss----a648dc4ecb66---4"><img src="https://cdn-images-1.medium.com/max/1672/1*8cx_UeNotq006QlVtJ7y4g.png" width="1672"></a></p><p class="medium-feed-snippet">Your Spring Boot API takes 2 seconds to respond.</p><p class="medium-feed-link"><a href="https://towardsdev.com/your-database-is-probably-the-slowest-part-of-your-application-heres-why-d90a7d0dfb73?source=rss----a648dc4ecb66---4">Continue reading on Towards Dev »</a></p></div>]]></description>
            <link>https://towardsdev.com/your-database-is-probably-the-slowest-part-of-your-application-heres-why-d90a7d0dfb73?source=rss----a648dc4ecb66---4</link>
            <guid isPermaLink="false">https://medium.com/p/d90a7d0dfb73</guid>
            <category><![CDATA[spring-boot]]></category>
            <category><![CDATA[postgresql]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[database]]></category>
            <category><![CDATA[java]]></category>
            <dc:creator><![CDATA[Diya Satpute]]></dc:creator>
            <pubDate>Fri, 09 Oct 2026 20:01:01 GMT</pubDate>
            <atom:updated>2026-10-09T20:01:01.726Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Why I Send Chat Messages Through an API and Receive Updates Directly from Firebase]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://towardsdev.com/why-i-send-chat-messages-through-an-api-and-receive-updates-directly-from-firebase-ffb65e631bb1?source=rss----a648dc4ecb66---4"><img src="https://cdn-images-1.medium.com/max/1672/1*q6rzX1Q9-y4iHl6X62ox8A.png" width="1672"></a></p><p class="medium-feed-snippet">The tradeoffs behind using an API for writes and Firebase listeners for reads. For sharing text: Firebase lets us send and receive&#x2026;</p><p class="medium-feed-link"><a href="https://towardsdev.com/why-i-send-chat-messages-through-an-api-and-receive-updates-directly-from-firebase-ffb65e631bb1?source=rss----a648dc4ecb66---4">Continue reading on Towards Dev »</a></p></div>]]></description>
            <link>https://towardsdev.com/why-i-send-chat-messages-through-an-api-and-receive-updates-directly-from-firebase-ffb65e631bb1?source=rss----a648dc4ecb66---4</link>
            <guid isPermaLink="false">https://medium.com/p/ffb65e631bb1</guid>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[coding]]></category>
            <category><![CDATA[serverless]]></category>
            <category><![CDATA[software-architecture]]></category>
            <category><![CDATA[firebase]]></category>
            <dc:creator><![CDATA[Aziz Fikri Mahmudi]]></dc:creator>
            <pubDate>Fri, 09 Oct 2026 10:02:26 GMT</pubDate>
            <atom:updated>2026-10-09T10:02:25.186Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Building a Configurable Load Balancer in Java & Spring Boot — Weighted Round Robin]]></title>
            <link>https://towardsdev.com/building-a-configurable-load-balancer-in-java-spring-boot-weighted-round-robin-8af6e7b57a4e?source=rss----a648dc4ecb66---4</link>
            <guid isPermaLink="false">https://medium.com/p/8af6e7b57a4e</guid>
            <category><![CDATA[system-design-interview]]></category>
            <category><![CDATA[design-patterns]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[system-design-concepts]]></category>
            <dc:creator><![CDATA[Nakul Mitra]]></dc:creator>
            <pubDate>Fri, 09 Oct 2026 10:02:23 GMT</pubDate>
            <atom:updated>2026-10-09T10:02:22.090Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*w6oQ8vxnllSZFvaxWGtAow.png" /></figure><p>In the <a href="https://medium.com/towardsdev/connection-read-timeouts-in-a-custom-load-balancer-using-java-spring-boot-f8c85a0457db">previous articles</a> of this Load Balancer series, we built our own Load Balancer using Java and Spring Boot and gradually added features such as health checks, retry and fallback, circuit breakers, and connection/read timeouts.</p><p>So far, our Load Balancer has been using the <strong>Round Robin</strong> algorithm to select backend servers.</p><p>Round Robin is simple and works well when all backend servers have similar capabilities.</p><p>But what happens when our backend servers are not equally powerful?</p><p>For example:</p><pre>Server 1 → 8 CPU cores, 16 GB RAM<br>Server 2 → 2 CPU cores, 4 GB RAM</pre><p>Should both servers receive exactly the same amount of traffic?</p><p>Not necessarily.</p><p>This is where <strong>Weighted Round Robin</strong> can help.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fround-lake.dustinice.workers.dev%3A443%2Fhttps%2Fwww.youtube.com%2Fembed%2FVOOmHUJWHOo%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fround-lake.dustinice.workers.dev%3A443%2Fhttps%2Fwww.youtube.com%2Fwatch%3Fv%3DVOOmHUJWHOo&amp;image=https%3A%2F%2Fround-lake.dustinice.workers.dev%3A443%2Fhttps%2Fi.ytimg.com%2Fvi%2FVOOmHUJWHOo%2Fhqdefault.jpg&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/875d7ffe1ae69574c1816356dd25382d/href">https://medium.com/media/875d7ffe1ae69574c1816356dd25382d/href</a></iframe><p>In this article, we will understand how Weighted Round Robin works and how we can design our Load Balancer so that the routing algorithm itself becomes configurable.</p><p><a href="https://buymeacoffee.com/nakulmitra"><em>Buy Me a Coffee</em></a></p><h3>1. What is Round Robin?</h3><p>Before understanding Weighted Round Robin, let’s quickly look at the algorithm we already implemented.</p><p>Suppose we have two backend servers:</p><pre>Server 1 → localhost:8080<br>Server 2 → localhost:8081</pre><p>With Round Robin, requests are distributed sequentially:</p><pre>Request 1 → 8080<br>Request 2 → 8081<br>Request 3 → 8080<br>Request 4 → 8081<br>Request 5 → 8080</pre><p>So the pattern is:</p><pre>8080 → 8081 → 8080 → 8081 → ...</pre><p>The main characteristic of Round Robin is that every server gets approximately the same number of requests.</p><p>This is simple and works very well when the backend servers have similar capabilities.</p><p>But that assumption isn’t always true.</p><h3>2. The Problem with Normal Round Robin</h3><p>Imagine that we have the following two servers:</p><pre>Server 1<br>8 CPU cores<br>16 GB RAM<br><br>Server 2<br>2 CPU cores<br>4 GB RAM</pre><p>Server 1 is clearly more powerful.</p><p>However, with normal Round Robin, we still distribute traffic like this:</p><pre>8080<br>8081<br>8080<br>8081<br>8080<br>8081</pre><p>Both servers receive approximately 50% of the traffic.</p><p>This may not be the most efficient distribution.</p><p>If Server 1 can comfortably handle more traffic, we would ideally like to send more requests to it.</p><p>Instead of treating all servers equally, we can assign a <strong>weight</strong> to each server.</p><p>This leads us to Weighted Round Robin.</p><h3>3. What is Weighted Round Robin?</h3><p>Weighted Round Robin is an extension of the traditional Round Robin algorithm.</p><p>The key difference is:</p><blockquote><strong><em>Round Robin treats servers equally, while Weighted Round Robin distributes traffic according to the configured weight of each server.</em></strong></blockquote><p>For example:</p><pre>Server 8080 → Weight 3<br>Server 8081 → Weight 1</pre><p>The higher the weight, the more traffic that server should receive.</p><p>A simple request sequence could therefore be:</p><pre>8080<br>8080<br>8080<br>8081</pre><p>So the distribution ratio becomes:</p><pre>8080 : 8081 = 3 : 1</pre><p>Over a large number of requests, Server 8080 should receive approximately three times as much traffic as Server 8081.</p><h3>4. Understanding the Weight</h3><p>It’s important to understand that a weight does <strong>not</strong> mean the server can process exactly that number of requests.</p><p>For example:</p><pre>8080 → Weight 3<br>8081 → Weight 1</pre><p>doesn’t mean “<em>Send exactly 3 requests to 8080 and 1 request to 8081”.</em></p><p>Instead, it represents a <strong>relative traffic ratio</strong>.</p><p>For example, over 100 requests, we would approximately expect:</p><pre>8080 → 75 requests<br>8081 → 25 requests</pre><p>because the total weight is:</p><pre>3 + 1 = 4</pre><p>Therefore:</p><pre>8080 → 3/4 = 75%</pre><pre>8081 → 1/4 = 25%</pre><p>The exact sequence may vary depending on the implementation, but the goal is to maintain the configured ratio over time.</p><h3>5. Example with Three Servers</h3><p>Let’s make the example slightly more interesting.</p><p>Suppose we have:</p><pre>Server A → Weight 5<br>Server B → Weight 3<br>Server C → Weight 2</pre><p>The total weight is:</p><pre>5 + 3 + 2 = 10</pre><p>So approximately:</p><pre>Server A → 50%<br>Server B → 30%<br>Server C → 20%</pre><p>This gives us much more control over how traffic is distributed.</p><p>For example, if Server A is significantly more powerful than Server C, we can give it a higher weight.</p><h3>6. Where Can Weighted Round Robin Be Useful?</h3><p>Weighted Round Robin can be useful in several situations.</p><h3>Different server capacities</h3><p>Suppose we have:</p><pre>Server A → 8 CPU cores<br>Server B → 4 CPU cores<br>Server C → 2 CPU cores</pre><p>We could configure:</p><pre>Server A → Weight 4<br>Server B → Weight 2<br>Server C → Weight 1</pre><p>This allows the more powerful servers to receive more traffic.</p><h3>Different instance sizes</h3><p>In cloud environments, we may have backend instances with different resource configurations.</p><p>A larger instance can be assigned a higher weight.</p><h3>Gradually introducing a new server</h3><p>Suppose our existing production server has:</p><pre>Server A → Weight 9</pre><p>and we introduce:</p><pre>Server B → Weight 1</pre><p>Server B will initially receive a much smaller percentage of traffic.</p><p>This can be useful when gradually introducing additional capacity.</p><h3>7. How Can We Implement Weighted Round Robin?</h3><p>For our custom Load Balancer, we are going to keep the implementation simple.</p><p>Suppose we have:</p><pre>8080 → Weight 3<br>8081 → Weight 1</pre><p>We can conceptually create a weighted sequence:</p><pre>8080<br>8080<br>8080<br>8081</pre><p>Then we can use a counter to continuously move through this sequence.</p><p>For example:</p><pre>int index = Math.floorMod(<br>        counter.getAndIncrement(),<br>        weightedServers.size()<br>);</pre><p>This gives us a simple repeating weighted pattern.</p><p>So instead of:</p><pre>8080 → 8081 → 8080 → 8081</pre><p>we can get:</p><pre>8080 → 8080 → 8080 → 8081</pre><p>This is a straightforward way to demonstrate the concept in our custom Load Balancer.</p><blockquote><strong><em>Note:</em></strong><em> This is a simplified implementation for learning purposes. Production load balancers can use more sophisticated approaches such as Smooth Weighted Round Robin.</em></blockquote><h3>8. But There Is Another Problem</h3><p>Now that we have two algorithms:</p><pre>Round Robin<br>Weighted Round Robin</pre><p>we need to think about our application design.</p><p>Where should the routing algorithm live?</p><p>If we put both algorithms directly inside our LoadBalancerService, we could end up with something like:</p><pre>if (algorithm.equals(&quot;ROUND_ROBIN&quot;)) {<br>    // Round Robin logic<br>} else if (algorithm.equals(&quot;WEIGHTED_ROUND_ROBIN&quot;)) {<br>    // Weighted Round Robin logic<br>}</pre><p>This may work initially.</p><p>But what happens when we introduce:</p><pre>Least Connections<br>IP Hash<br>Random</pre><p>Our service will keep growing.</p><p>Every new algorithm will require changes to the existing service.</p><p>This is not the design we want.</p><h3>9. Separating Server Selection from Request Processing</h3><p>Our Load Balancer actually has multiple responsibilities.</p><p>For example:</p><pre>Health Check<br>Circuit Breaker<br>Retry<br>Timeout<br>Request Forwarding<br>Server Selection</pre><p>We can separate the <strong>server selection</strong> responsibility from the rest of the request-processing logic.</p><p>The Load Balancer should be responsible for things such as:</p><pre>Should I send this request?<br>Is the server healthy?<br>Should I retry?<br>Has the circuit opened?<br>Did the request timeout?</pre><p>But it doesn’t necessarily need to know <em>“How exactly should I select the next server?”</em></p><p>That can be delegated to a separate strategy.</p><h3>10. Using the Strategy Pattern</h3><p>For this, we can use the <strong>Strategy Pattern</strong>.</p><p>We create a common interface:</p><pre>public interface LoadBalancingStrategy {<br>  Server selectServer(<br>    List&lt;Server&gt; servers,<br>    Set&lt;String&gt; excludedServers<br>  );<br>}</pre><p>Now each algorithm implements this interface.</p><p>Our design becomes:</p><pre>LoadBalancingStrategy<br>        |<br>        +---- RoundRobinStrategy<br>        |<br>        +---- WeightedRoundRobinStrategy</pre><p>The main LoadBalancerService doesn&#39;t need to know the internal implementation of either algorithm.</p><p>It simply asks the strategy:</p><pre>&quot;Give me the next server.&quot;</pre><h3>11. RoundRobinStrategy</h3><p>Our existing Round Robin logic can now move into:</p><pre>RoundRobinStrategy</pre><p>This class is responsible only for Round Robin server selection.</p><p>For example:</p><pre>int index = Math.floorMod(<br>        counter.getAndIncrement(),<br>        servers.size()<br>);<br><br>return servers.get(index);</pre><p>The important thing is that this logic no longer needs to live inside the main Load Balancer service.</p><h3>12. WeightedRoundRobinStrategy</h3><p>Similarly, we create:</p><pre>WeightedRoundRobinStrategy</pre><p>This class implements the same interface:</p><pre>LoadBalancingStrategy</pre><p>but contains the Weighted Round Robin logic.</p><p>For example:</p><pre>8080 → Weight 3<br>8081 → Weight 1</pre><p>can produce:</p><pre>8080<br>8080<br>8080<br>8081</pre><p>The strategy then selects servers from this weighted sequence.</p><p>This gives us a clean separation between the two algorithms.</p><h3>13. Adding Weight to Our Server</h3><p>Our existing Server object needs to store the weight.</p><p>Conceptually, we add:</p><pre>private int weight;</pre><p>Now a server can be represented as:</p><pre>Server 8080 → weight = 3<br>Server 8081 → weight = 1</pre><p>The WeightedRoundRobinStrategy can use this value while selecting the next server.</p><h3>14. Making the Algorithm Configurable</h3><p>Now comes one of the most important improvements.</p><p>I don’t want to modify Java code whenever I want to change the routing algorithm.</p><p>Instead, we can configure it using application.properties.</p><p>For example:</p><pre>load-balancer.algorithm=ROUND_ROBIN</pre><p>If we want Weighted Round Robin:</p><pre>load-balancer.algorithm=WEIGHTED_ROUND_ROBIN</pre><p>Now the routing algorithm becomes a configuration decision.</p><p>We don’t need to rewrite our LoadBalancerService.</p><h3>15. Algorithm Enum</h3><p>We can represent the supported algorithms using an enum:</p><pre>public enum LoadBalancingAlgorithm {<br>    ROUND_ROBIN,<br>    WEIGHTED_ROUND_ROBIN<br>}</pre><p>This gives us a clean and strongly typed representation of the available algorithms.</p><p>If we introduce another algorithm in the future, we can add another value.</p><h3>16. Strategy Factory</h3><p>Now we need something that can select the correct strategy based on the configuration.</p><p>For this, we can introduce:</p><pre>LoadBalancingStrategyFactory</pre><p>The factory reads the configured algorithm and returns the appropriate strategy.</p><p>For example:</p><pre>application.properties<br>        |<br>        ↓<br>ROUND_ROBIN<br>        |<br>        ↓<br>Strategy Factory<br>        |<br>        ↓<br>RoundRobinStrategy</pre><p>Or:</p><pre>application.properties<br>        |<br>        ↓<br>WEIGHTED_ROUND_ROBIN<br>        |<br>        ↓<br>Strategy Factory<br>        |<br>        ↓<br>WeightedRoundRobinStrategy</pre><p>Our LoadBalancerService only needs to work with:</p><pre>LoadBalancingStrategy</pre><p>It doesn’t need to know which implementation was selected.</p><h3>17. What About Retry and Circuit Breaker?</h3><p>This is particularly important because our Load Balancer already contains <strong>Retry and Circuit Breaker</strong> functionality.</p><p>Introducing multiple routing strategies should not break those features.</p><p>Suppose our first request is sent to:</p><pre>8080</pre><p>and the request fails.</p><p>Our retry mechanism should select another eligible server.</p><p>But the retry mechanism doesn’t need to know whether the Load Balancer is using:</p><pre>Round Robin</pre><p>or:</p><pre>Weighted Round Robin</pre><p>Instead, it asks the currently selected strategy for the next server.</p><p>The flow becomes:</p><pre>Request<br>   ↓<br>Load Balancer<br>   ↓<br>Select server using strategy<br>   ↓<br>Forward request<br>   ↓<br>Failure<br>   ↓<br>Retry<br>   ↓<br>Ask same strategy for another server<br>   ↓<br>Forward request</pre><p>This is an important architectural improvement.</p><p>The <strong>retry mechanism controls whether another attempt should happen</strong>, while the <strong>load-balancing strategy controls which server should be selected</strong>.</p><h3>18. Avoiding the Same Server During Retry</h3><p>Suppose we have:</p><pre>8080<br>8081</pre><p>The first attempt selects:</p><pre>8080</pre><p>but it fails.</p><p>We don’t want our retry to immediately select 8080 again.</p><p>Therefore, we maintain a set of attempted servers.</p><p>For example:</p><pre>Set&lt;String&gt; attemptedServers = new HashSet&lt;&gt;();</pre><p>After selecting 8080:</p><pre>attemptedServers.add(server.getUrl());</pre><p>The next strategy call can then exclude already attempted servers.</p><p>This allows both Round Robin and Weighted Round Robin to work with our existing Retry and Fallback functionality.</p><h3>19. Maximum Retry Attempts</h3><p>We also need to consider how many times we should retry.</p><p>For example:</p><pre>load-balancer.max-attempts=2</pre><p>What if only one server is available?</p><p>We shouldn’t call the same server twice just because the maximum attempts is two.</p><p>So we can calculate:</p><pre>int attempts = Math.min(<br>        maxAttempts,<br>        availableServers.size()<br>);</pre><p>If:</p><pre>maxAttempts = 2<br>availableServers = 1</pre><p>then:</p><pre>attempts = 1</pre><p>If:</p><pre>maxAttempts = 2<br>availableServers = 2</pre><p>then:</p><pre>attempts = 2</pre><p>This keeps our retry behavior simple and avoids unnecessary repeated requests to the same backend.</p><h3>20. Final Architecture</h3><p>After this improvement, our architecture looks like:</p><pre>                        Client<br>                          |<br>                          ↓<br>                LoadBalancerService<br>                          |<br>        +-----------------+----------------+<br>        |                 |                |<br>        ↓                 ↓                ↓<br>   Health Check      Circuit Breaker      Retry<br>                                             |<br>                                             ↓<br>                                  LoadBalancingStrategy<br>                                             |<br>                            +----------------+----------------+<br>                            |                                 |<br>                            ↓                                 ↓<br>                   RoundRobinStrategy             WeightedRoundRobinStrategy</pre><p>The algorithm is selected through:</p><pre>load-balancer.algorithm</pre><p>This means our core service no longer needs to contain algorithm-specific server-selection code.</p><h3>21. Why Is This Design Better?</h3><p>There are several benefits to this approach.</p><h3>1. Easy to switch algorithms</h3><p>We can change:</p><pre>load-balancer.algorithm=ROUND_ROBIN</pre><p>to:</p><pre>load-balancer.algorithm=WEIGHTED_ROUND_ROBIN</pre><p>without changing the core request-forwarding code.</p><h3>2. Separation of responsibilities</h3><p>The Load Balancer handles:</p><ul><li>Health checks</li><li>Circuit Breaker</li><li>Retry</li><li>Timeouts</li><li>Request forwarding</li></ul><p>The strategy handles:</p><ul><li>Server selection</li></ul><h3>3. Easier to extend</h3><p>If we want to introduce:</p><pre>Least Connections<br>IP Hash<br>Random</pre><p>we can implement another strategy rather than modifying the entire service.</p><h3>4. Existing features remain reusable</h3><p>Our Retry, Circuit Breaker, Health Check, and Timeout mechanisms don’t need to understand the internal details of the routing algorithm.</p><h3>22. A Note About Production Implementations</h3><p>Our goal in this series is to build a Load Balancer from scratch so that we can understand the underlying concepts.</p><p>Therefore, our Weighted Round Robin implementation is intentionally simple.</p><p>Real-world load balancers may use more sophisticated algorithms such as:</p><ul><li>Smooth Weighted Round Robin</li><li>Dynamic load-based routing</li><li>Least Connections</li><li>Latency-aware routing</li><li>Resource-aware routing</li></ul><p>A production implementation may also dynamically adjust weights based on real-time server metrics.</p><p>For example:</p><pre>CPU usage<br>Memory usage<br>Active connections<br>Response time</pre><p>These metrics can be used to make routing decisions more dynamic.</p><h3>23. Future Possibilities</h3><p>The Strategy Pattern gives us a foundation for adding more algorithms in future tutorials.</p><p>Our architecture can eventually become:</p><pre>LoadBalancingStrategy<br>        |<br>        +---- Round Robin<br>        |<br>        +---- Weighted Round Robin<br>        |<br>        +---- Least Connections<br>        |<br>        +---- IP Hash<br>        |<br>        +---- Random</pre><p>And the selection can continue to be controlled through configuration.</p><p>This makes our custom Load Balancer easier to evolve as we introduce more advanced concepts.</p><h3>Conclusion</h3><p>In this tutorial, we took our existing Round Robin Load Balancer and introduced <strong>Weighted Round Robin</strong>.</p><p>The main difference is simple:</p><p><strong>Round Robin:</strong></p><pre>Every server gets approximately equal traffic.</pre><p><strong>Weighted Round Robin:</strong></p><pre>Servers receive traffic according to their configured weights.</pre><p>For example:</p><pre>Server A → Weight 3<br>Server B → Weight 1</pre><p>means that Server A should receive approximately three times the traffic of Server B over time.</p><p>But the bigger improvement was not just the new algorithm.</p><p>We also changed the architecture of our Load Balancer.</p><p>Instead of tightly coupling server selection with LoadBalancerService, we introduced a common LoadBalancingStrategy interface.</p><pre>LoadBalancingStrategy<br>        |<br>        +---- RoundRobinStrategy<br>        |<br>        +---- WeightedRoundRobinStrategy</pre><p>The routing algorithm can now be selected through configuration:</p><pre>load-balancer.algorithm=ROUND_ROBIN</pre><p>or:</p><pre>load-balancer.algorithm=WEIGHTED_ROUND_ROBIN</pre><p>This allows us to switch algorithms without rewriting the core request-forwarding logic.</p><p>More importantly, our Retry and Circuit Breaker implementations remain independent of the routing algorithm.</p><p>The responsibilities are now clearly separated:</p><blockquote><strong><em>Retry decides whether we should try again.</em></strong></blockquote><blockquote><strong><em>The Load Balancing Strategy decides which server should receive the attempt.</em></strong></blockquote><p>This gives us a much better foundation for adding more advanced load-balancing algorithms in the future.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8af6e7b57a4e" width="1" height="1" alt=""><hr><p><a href="https://towardsdev.com/building-a-configurable-load-balancer-in-java-spring-boot-weighted-round-robin-8af6e7b57a4e">Building a Configurable Load Balancer in Java &amp; Spring Boot — Weighted Round Robin</a> was originally published in <a href="https://towardsdev.com">Towards Dev</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Self-Productivity Tool Series]]></title>
            <link>https://towardsdev.com/self-productivity-tool-series-043ae7200305?source=rss----a648dc4ecb66---4</link>
            <guid isPermaLink="false">https://medium.com/p/043ae7200305</guid>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[practicing-datscy]]></category>
            <dc:creator><![CDATA[Practicing DatScy]]></dc:creator>
            <pubDate>Fri, 09 Oct 2026 10:02:18 GMT</pubDate>
            <atom:updated>2026-10-09T10:02:17.503Z</atom:updated>
            <content:encoded><![CDATA[<h4>Toggle-flick.</h4><p>In this post, a toggle-flick tool is demonstrated. Click-drag the ball to the blue center while trying to counteract the random pattern.</p><h3>Non-compensatory</h3><p>The pointerdown and pointerup user control movements are independent of the randomly applied 3d ball movements.</p><pre>&lt;!DOCTYPE&gt;<br>&lt;html&gt;<br>&lt;head&gt;&lt;/head&gt;<br>&lt;body&gt;<br>&lt;div class=&quot;javascript_css&quot;&gt;&lt;/div&gt;<br>&lt;script&gt;<br>function toggle0(documentElement) {<br>  <br>  if (document.getElementById(&quot;label0&quot;) == null) {<br>  <br>    var labelElement = document.createElement(&quot;label&quot;);<br>    labelElement.setAttribute(&quot;id&quot;, &quot;label0&quot;);<br>    labelElement.setAttribute(&quot;class&quot;, &quot;label_simple&quot;);<br>    labelElement.setAttribute(&quot;onpointerdown&quot;, &quot;processEvent_pointerdown(event)&quot;);<br>    labelElement.setAttribute(&quot;onpointerup&quot;, &quot;processEvent_pointerup(event)&quot;);<br>    labelElement.style.position = &quot;absolute&quot;;<br>    labelElement.style.top = &quot;60px&quot;;<br>    labelElement.style.left = &quot;0px&quot;;<br>    labelElement.style.display = &quot;inline-block&quot;;<br>    labelElement.style.width = &quot;300px&quot;;<br>    labelElement.style.height = &quot;20px&quot;;<br>    labelElement.style.border = &quot;1px solid gray&quot;;<br>    labelElement.style.borderRadius = &quot;10px&quot;;<br>    labelElement.style.backgroundColor = &quot;hsl(0 100% 100%)&quot;;<br>    labelElement.style.background = &quot;linear-gradient(to left, #eeeeee, #eeeeee 25%, #9198e5 50%, #eeeeee 75%, #eeeeee 100%)&quot;;<br>    labelElement.style.zIndex = &quot;5&quot;;<br>    documentElement.appendChild(labelElement);<br>    <br>    var spanElement = document.createElement(&quot;span&quot;);<br>    spanElement.setAttribute(&quot;id&quot;, &quot;span0&quot;);<br>    spanElement.setAttribute(&quot;class&quot;, &quot;span_simple&quot;);<br>    spanElement.style.cursor = &quot;pointer&quot;;<br>    spanElement.style.position = &quot;absolute&quot;;<br>    spanElement.style.top = &quot;0px&quot;;<br>    spanElement.style.left = `${parseInt(document.getElementById(&quot;label0&quot;).style.width.split(&quot;px&quot;)[0])/2}px`;<br>    spanElement.style.width = document.getElementById(&quot;label0&quot;).style.height;<br>    spanElement.style.height = document.getElementById(&quot;label0&quot;).style.height;<br>    spanElement.style.backgroundColor = &quot;hsl(30 100% 90%)&quot;;<br>    spanElement.style.background =&quot;radial-gradient(ellipse 50% 50% at 30% 25%, hsl(30 100% 100%), hsl(30 100% 40%) 100%)&quot;;<br>    spanElement.style.boxShadow = &quot;inset 0px 0px 0px rgba(255,255,255,0.6), inset 0px 0px 0px rgba(0,0,0,0.6)&quot;;<br>    spanElement.style.transition = &quot;0.5s&quot;;<br>    spanElement.style.border = `2px solid ${spanElement.style.backgroundColor}`;<br>    spanElement.style.borderRadius = parseInt(document.getElementById(&quot;label0&quot;).style.height.split(&quot;px&quot;)[0]/2)+&quot;px&quot;;<br>    spanElement.style.fontSize = &quot;12px&quot;;<br>    spanElement.style.touchAction =  &quot;none&quot;;<br>    spanElement.innerText = &quot;&quot;;<br>    document.getElementById(&quot;label0&quot;).appendChild(spanElement);<br>    document.getElementById(&quot;span0&quot;).x0 = 0;<br>    document.getElementById(&quot;span0&quot;).x1 = 0;<br>    <br>    var pElement = document.createElement(&quot;p&quot;);<br>    pElement.setAttribute(&quot;id&quot;, &quot;p0&quot;);<br>    pElement.setAttribute(&quot;class&quot;, &quot;p_simple&quot;);<br>    pElement.setAttribute(&quot;onclick&quot;, &quot;processEvent_click()&quot;);<br>    pElement.style.position = &quot;absolute&quot;;<br>    pElement.style.top = &quot;170px&quot;;<br>    pElement.style.left = &quot;0px&quot;;<br>    pElement.style.width = &quot;300px&quot;;<br>    pElement.style.height = &quot;100px&quot;;<br>    pElement.style.backgroundColor = &quot;hsl(60 100% 90%)&quot;;<br>    pElement.style.overflow = &quot;scroll&quot;;<br>    documentElement.appendChild(pElement);<br>    document.getElementById(&quot;p0&quot;).clicked = false;<br>    document.getElementById(&quot;p0&quot;).count = 0;<br>    document.getElementById(&quot;p0&quot;).innerHTML += &quot;clicked: &quot;+document.getElementById(&quot;p0&quot;).clicked+&quot;&lt;br&gt;&quot;;<br>    <br>  }<br>  <br>}<br><br>function processEvent_pointerdown(event) {<br>  <br>  document.getElementById(&quot;span0&quot;).style.border = &quot;4px solid indianred&quot;;<br>  <br>  event.target.x0 = event.offsetX;<br>  document.getElementById(&quot;p0&quot;).innerHTML += &quot;event.target.x0: &quot;+event.target.x0+&quot;&lt;br&gt;&quot;;<br>    <br>}<br><br>function processEvent_pointerup(event) {<br><br>  document.getElementById(&quot;span0&quot;).style.border = &quot;2px solid hsl(30 100% 90%)&quot;;<br>  <br>  event.target.x1 = event.offsetX;<br>  document.getElementById(&quot;p0&quot;).innerHTML += &quot;event.target.x1: &quot;+event.target.x1+&quot;&lt;br&gt;&quot;;<br>  <br>  var dx = event.target.x1 - event.target.x0;<br>  document.getElementById(&quot;p0&quot;).innerHTML += &quot;dx: &quot;+dx+&quot;&lt;br&gt;&quot;;<br>  <br>  var spanLeft = parseInt(document.getElementById(&quot;span0&quot;).style.left.split(&quot;px&quot;)[0]);<br>  var labelWidth = parseInt(document.getElementById(&quot;label0&quot;).style.width.split(&quot;px&quot;)[0]);<br>  var spanWidth = parseInt(document.getElementById(&quot;span0&quot;).style.width.split(&quot;px&quot;)[0]);<br>  <br>  if (spanLeft+dx &lt; labelWidth+spanWidth) { document.getElementById(&quot;span0&quot;).style.left = `${spanLeft+dx}px`; } else { document.getElementById(&quot;span0&quot;).style.left = `${labelWidth-spanWidth}px`; }<br>  if (spanLeft+dx &gt; 0) { document.getElementById(&quot;span0&quot;).style.left = `${spanLeft+dx}px`; } else { document.getElementById(&quot;span0&quot;).style.left = &quot;0px&quot;; }<br><br>}<br>&lt;/script&gt;<br>&lt;script&gt;<br>async function mainloop() {<br>  document.getElementById(&quot;p0&quot;).innerHTML += &quot;clicked: &quot;+document.getElementById(&quot;p0&quot;).clicked+&quot;&lt;br&gt;&quot;;<br>  <br>  await functionName1();  	<br>  if (document.getElementById(&quot;p0&quot;).clicked == true) {<br>    // keep running for 30 mins:  (30 mins) * (60 secs / 1 min) * (1 count / 2.5 secs) = 720 counts<br>    if (Number(document.getElementById(&quot;p0&quot;).count) &lt; 300) {<br>      await mainloop();<br>    }<br>  } else {<br>    // stop count.<br>    document.getElementById(&quot;p0&quot;).clicked = false; // reset to original state.<br>    document.getElementById(&quot;p0&quot;).count = 0;<br>  } <br>	<br>}<br><br><br>async function functionName1() {<br>  <br>  // task: move 3d ball left and right.<br>  // document.getElementById(&quot;p0&quot;).innerHTML += &quot;task1&lt;br&gt;&quot;; <br>  var len = parseInt(document.getElementById(&quot;label0&quot;).style.width.split(&quot;px&quot;)[0]) - parseInt(document.getElementById(&quot;span0&quot;).style.width.split(&quot;px&quot;)[0]);<br>  var value = [len/4, len/2, (len/4)+(len/2), len];<br>  var maximum = value.length;<br>  var minimum = 0;<br>  var index = Math.floor(Math.random()*(maximum-minimum)+minimum);<br>  document.getElementById(&quot;p0&quot;).innerHTML += &quot;index: &quot;+index+&quot;&lt;br&gt;&quot;;<br>  document.getElementById(&quot;span0&quot;).style.left = value.at(index)+&quot;px&quot;;<br>  <br>  <br>  document.getElementById(&quot;p0&quot;).count = Number(document.getElementById(&quot;p0&quot;).count) + 1;	<br>  <br><br>  document.getElementById(&quot;p0&quot;).innerHTML += &quot;count: &quot;+document.getElementById(&quot;p0&quot;).count+&quot;&lt;br&gt;&quot;;<br>  await new Promise(r =&gt; setTimeout(r, 500));<br>  <br>}<br><br><br>async function processEvent_click() {<br>document.getElementById(&quot;p0&quot;).innerHTML += &quot;clicked: &quot;+document.getElementById(&quot;p0&quot;).clicked+&quot;&lt;br&gt;&quot;;<br><br>  document.getElementById(&quot;p0&quot;).innerHTML += &quot;count: &quot;+document.getElementById(&quot;p0&quot;).count+&quot;&lt;br&gt;&quot;;<br>  <br>  if (document.getElementById(&quot;p0&quot;).count == 0) {<br>    // start count.<br>    document.getElementById(&quot;p0&quot;).clicked = true;<br>    await mainloop();<br>  } else {<br>    // stop count.<br>    document.getElementById(&quot;p0&quot;).clicked = false; // reset to original state.<br>    document.getElementById(&quot;p0&quot;).count = 0;<br>  }<br>    <br>  document.getElementById(&quot;p0&quot;).innerHTML += &quot;clicked: &quot;+document.getElementById(&quot;p0&quot;).clicked+&quot;&lt;br&gt;&quot;;<br>  <br>}<br>&lt;/script&gt;<br>&lt;script&gt;<br>border();<br><br>function border() {<br>  <br>  document.getElementsByClassName(&quot;javascript_css&quot;)[0].style.position = &quot;absolute&quot;;<br>  document.getElementsByClassName(&quot;javascript_css&quot;)[0].style.width = &quot;300px&quot;;<br>  document.getElementsByClassName(&quot;javascript_css&quot;)[0].style.height = &quot;50px&quot;; <br>  document.getElementsByClassName(&quot;javascript_css&quot;)[0].style.border = &quot;1px solid gray&quot;;<br>  document.getElementsByClassName(&quot;javascript_css&quot;)[0].style.borderRadius = &quot;2px&quot;;<br>  document.getElementsByClassName(&quot;javascript_css&quot;)[0].style.fontSize = &quot;12px&quot;;<br>  document.getElementsByClassName(&quot;javascript_css&quot;)[0].innerHTML = &quot;Click-on the yellow box to start or stop the 3d ball moving left or right. Click-drag the 3d ball to the blue center area.&quot;;<br><br>  toggle0(document.getElementsByClassName(&quot;javascript_css&quot;)[0]);<br><br>}<br>&lt;/script&gt;<br>&lt;/body&gt;<br>&lt;/html&gt;<br></pre><h3>Compensatory version</h3><p>The pointerdown and pointerup user control movements are subtracted from the randomly applied 3d ball movements.</p><p>A compensatory task is useful to measure learning of the pattern that the user needs to counteract.</p><pre>&lt;!DOCTYPE&gt;<br>&lt;html&gt;<br>&lt;head&gt;&lt;/head&gt;<br>&lt;body&gt;<br>&lt;div class=&quot;javascript_css&quot;&gt;&lt;/div&gt;<br>&lt;script&gt;<br>function toggle0(documentElement) {<br>  <br>  if (document.getElementById(&quot;label0&quot;) == null) {<br>  <br>    var labelElement = document.createElement(&quot;label&quot;);<br>    labelElement.setAttribute(&quot;id&quot;, &quot;label0&quot;);<br>    labelElement.setAttribute(&quot;class&quot;, &quot;label_simple&quot;);<br>    labelElement.setAttribute(&quot;onpointerdown&quot;, &quot;processEvent_pointerdown(event)&quot;);<br>    labelElement.setAttribute(&quot;onpointerup&quot;, &quot;processEvent_pointerup(event)&quot;);<br>    labelElement.style.position = &quot;absolute&quot;;<br>    labelElement.style.top = &quot;60px&quot;;<br>    labelElement.style.left = &quot;0px&quot;;<br>    labelElement.style.display = &quot;inline-block&quot;;<br>    labelElement.style.width = &quot;300px&quot;;<br>    labelElement.style.height = &quot;20px&quot;;<br>    labelElement.style.border = &quot;1px solid gray&quot;;<br>    labelElement.style.borderRadius = &quot;10px&quot;;<br>    labelElement.style.backgroundColor = &quot;hsl(0 100% 100%)&quot;;<br>    labelElement.style.background = &quot;linear-gradient(to left, #eeeeee, #eeeeee 25%, #9198e5 50%, #eeeeee 75%, #eeeeee 100%)&quot;;<br>    labelElement.style.zIndex = &quot;5&quot;;<br>    documentElement.appendChild(labelElement);<br>    <br>    var spanElement = document.createElement(&quot;span&quot;);<br>    spanElement.setAttribute(&quot;id&quot;, &quot;span0&quot;);<br>    spanElement.setAttribute(&quot;class&quot;, &quot;span_simple&quot;);<br>    spanElement.style.cursor = &quot;pointer&quot;;<br>    spanElement.style.position = &quot;absolute&quot;;<br>    spanElement.style.top = &quot;0px&quot;;<br>    spanElement.style.left = `${parseInt(document.getElementById(&quot;label0&quot;).style.width.split(&quot;px&quot;)[0])/2}px`;<br>    spanElement.style.width = document.getElementById(&quot;label0&quot;).style.height;<br>    spanElement.style.height = document.getElementById(&quot;label0&quot;).style.height;<br>    spanElement.style.backgroundColor = &quot;hsl(30 100% 90%)&quot;;<br>    spanElement.style.background =&quot;radial-gradient(ellipse 50% 50% at 30% 25%, hsl(30 100% 100%), hsl(30 100% 40%) 100%)&quot;;<br>    spanElement.style.boxShadow = &quot;inset 0px 0px 0px rgba(255,255,255,0.6), inset 0px 0px 0px rgba(0,0,0,0.6)&quot;;<br>    spanElement.style.transition = &quot;0.5s&quot;;<br>    spanElement.style.border = `2px solid ${spanElement.style.backgroundColor}`;<br>    spanElement.style.borderRadius = parseInt(document.getElementById(&quot;label0&quot;).style.height.split(&quot;px&quot;)[0]/2)+&quot;px&quot;;<br>    spanElement.style.fontSize = &quot;12px&quot;;<br>    spanElement.style.touchAction =  &quot;none&quot;;<br>    spanElement.innerText = &quot;&quot;;<br>    document.getElementById(&quot;label0&quot;).appendChild(spanElement);<br>    document.getElementById(&quot;span0&quot;).x0 = 0;<br>    document.getElementById(&quot;span0&quot;).x1 = 0;<br>    document.getElementById(&quot;span0&quot;).user = 0;<br>    <br>    var pElement = document.createElement(&quot;p&quot;);<br>    pElement.setAttribute(&quot;id&quot;, &quot;p0&quot;);<br>    pElement.setAttribute(&quot;class&quot;, &quot;p_simple&quot;);<br>    pElement.setAttribute(&quot;onclick&quot;, &quot;processEvent_click()&quot;);<br>    pElement.style.position = &quot;absolute&quot;;<br>    pElement.style.top = &quot;170px&quot;;<br>    pElement.style.left = &quot;0px&quot;;<br>    pElement.style.width = &quot;300px&quot;;<br>    pElement.style.height = &quot;100px&quot;;<br>    pElement.style.backgroundColor = &quot;hsl(60 100% 90%)&quot;;<br>    pElement.style.overflow = &quot;scroll&quot;;<br>    documentElement.appendChild(pElement);<br>    document.getElementById(&quot;p0&quot;).clicked = false;<br>    document.getElementById(&quot;p0&quot;).count = 0;<br>    document.getElementById(&quot;p0&quot;).innerHTML += &quot;clicked: &quot;+document.getElementById(&quot;p0&quot;).clicked+&quot;&lt;br&gt;&quot;;<br>    <br>  }<br>  <br>}<br><br>function processEvent_pointerdown(event) {<br>  <br>  document.getElementById(&quot;span0&quot;).style.border = &quot;4px solid indianred&quot;;<br>  <br>  event.target.x0 = event.offsetX;<br>  document.getElementById(&quot;p0&quot;).innerHTML += &quot;event.target.x0: &quot;+event.target.x0+&quot;&lt;br&gt;&quot;;<br>    <br>}<br><br>function processEvent_pointerup(event) {<br><br>  document.getElementById(&quot;span0&quot;).style.border = &quot;2px solid hsl(30 100% 90%)&quot;;<br>  <br>  event.target.x1 = event.offsetX;<br>  document.getElementById(&quot;p0&quot;).innerHTML += &quot;event.target.x1: &quot;+event.target.x1+&quot;&lt;br&gt;&quot;;<br>  <br>  var dx = event.target.x1 - event.target.x0;<br>  document.getElementById(&quot;p0&quot;).innerHTML += &quot;dx: &quot;+dx+&quot;&lt;br&gt;&quot;;<br>  <br>  var spanLeft = parseInt(document.getElementById(&quot;span0&quot;).style.left.split(&quot;px&quot;)[0]);<br>  var labelWidth = parseInt(document.getElementById(&quot;label0&quot;).style.width.split(&quot;px&quot;)[0]);<br>  var spanWidth = parseInt(document.getElementById(&quot;span0&quot;).style.width.split(&quot;px&quot;)[0]);<br>  <br>  if (spanLeft+dx &lt; labelWidth+spanWidth) { <br>    // document.getElementById(&quot;span0&quot;).style.left = `${spanLeft+dx}px`;<br>    document.getElementById(&quot;span0&quot;).user = dx;<br>  } else {<br>    document.getElementById(&quot;span0&quot;).style.left = `${labelWidth-spanWidth}px`;<br>  }<br>  if (spanLeft+dx &gt; 0) {<br>    // document.getElementById(&quot;span0&quot;).style.left = `${spanLeft+dx}px`;<br>    document.getElementById(&quot;span0&quot;).user = dx;<br>  } else {<br>    document.getElementById(&quot;span0&quot;).style.left = &quot;0px&quot;;<br>  }<br><br>}<br>&lt;/script&gt;<br>&lt;script&gt;<br>async function mainloop() {<br>  document.getElementById(&quot;p0&quot;).innerHTML += &quot;clicked: &quot;+document.getElementById(&quot;p0&quot;).clicked+&quot;&lt;br&gt;&quot;;<br>  <br>  await functionName1();  	<br>  if (document.getElementById(&quot;p0&quot;).clicked == true) {<br>    // keep running for 30 mins:  (30 mins) * (60 secs / 1 min) * (1 count / 2.5 secs) = 720 counts<br>    if (Number(document.getElementById(&quot;p0&quot;).count) &lt; 300) {<br>      await mainloop();<br>    }<br>  } else {<br>    // stop count.<br>    document.getElementById(&quot;p0&quot;).clicked = false; // reset to original state.<br>    document.getElementById(&quot;p0&quot;).count = 0;<br>  } <br>	<br>}<br><br><br>async function functionName1() {<br>  <br>  // task: move 3d ball left and right.<br>  // document.getElementById(&quot;p0&quot;).innerHTML += &quot;task1&lt;br&gt;&quot;; <br>  var len = parseInt(document.getElementById(&quot;label0&quot;).style.width.split(&quot;px&quot;)[0]) - parseInt(document.getElementById(&quot;span0&quot;).style.width.split(&quot;px&quot;)[0]);<br>  var value = [len/4, len/2, (len/4)+(len/2), len].map((v,i) =&gt; { return v/2; });<br>  var maximum = value.length;<br>  var minimum = 0;<br>  var index = Math.floor(Math.random()*(maximum-minimum)+minimum);<br>  document.getElementById(&quot;p0&quot;).innerHTML += &quot;index: &quot;+index+&quot;&lt;br&gt;&quot;; ;<br>  <br>  // Non-compensatory.<br>  // document.getElementById(&quot;span0&quot;).style.left = value.at(index)+&quot;px&quot;;<br>  // OR<br>  // Compensatory.<br>  var left0 = value.at(index) + parseInt(document.getElementById(&quot;span0&quot;).user);<br>  document.getElementById(&quot;span0&quot;).style.left = left0+&quot;px&quot;;<br>  <br>  document.getElementById(&quot;p0&quot;).count = Number(document.getElementById(&quot;p0&quot;).count) + 1;	<br>  <br><br>  document.getElementById(&quot;p0&quot;).innerHTML += &quot;count: &quot;+document.getElementById(&quot;p0&quot;).count+&quot;&lt;br&gt;&quot;;<br>  await new Promise(r =&gt; setTimeout(r, 500));<br>  <br>}<br><br><br>async function processEvent_click() {<br>document.getElementById(&quot;p0&quot;).innerHTML += &quot;clicked: &quot;+document.getElementById(&quot;p0&quot;).clicked+&quot;&lt;br&gt;&quot;;<br><br>  document.getElementById(&quot;p0&quot;).innerHTML += &quot;count: &quot;+document.getElementById(&quot;p0&quot;).count+&quot;&lt;br&gt;&quot;;<br>  <br>  if (document.getElementById(&quot;p0&quot;).count == 0) {<br>    // start count.<br>    document.getElementById(&quot;p0&quot;).clicked = true;<br>    await mainloop();<br>  } else {<br>    // stop count.<br>    document.getElementById(&quot;p0&quot;).clicked = false; // reset to original state.<br>    document.getElementById(&quot;p0&quot;).count = 0;<br>  }<br>    <br>  document.getElementById(&quot;p0&quot;).innerHTML += &quot;clicked: &quot;+document.getElementById(&quot;p0&quot;).clicked+&quot;&lt;br&gt;&quot;;<br>  <br>}<br>&lt;/script&gt;<br>&lt;script&gt;<br>border();<br><br>function border() {<br>  <br>  document.getElementsByClassName(&quot;javascript_css&quot;)[0].style.position = &quot;absolute&quot;;<br>  document.getElementsByClassName(&quot;javascript_css&quot;)[0].style.width = &quot;300px&quot;;<br>  document.getElementsByClassName(&quot;javascript_css&quot;)[0].style.height = &quot;50px&quot;; <br>  document.getElementsByClassName(&quot;javascript_css&quot;)[0].style.border = &quot;1px solid gray&quot;;<br>  document.getElementsByClassName(&quot;javascript_css&quot;)[0].style.borderRadius = &quot;2px&quot;;<br>  document.getElementsByClassName(&quot;javascript_css&quot;)[0].style.fontSize = &quot;12px&quot;;<br>  document.getElementsByClassName(&quot;javascript_css&quot;)[0].innerHTML = &quot;Click-on the yellow box to start or stop the 3d ball moving left or right. Click-drag the 3d ball to the blue center area.&quot;;<br><br>  toggle0(document.getElementsByClassName(&quot;javascript_css&quot;)[0]);<br><br>}<br>&lt;/script&gt;<br>&lt;/body&gt;<br>&lt;/html&gt;</pre><p>Good Luck with your workflows and Happy Practicing! 👋</p><p>🔔 <a href="https://medium.com/@j622amilah">Practicing Datscy @ Medium</a> | 🌷 <a href="http://redirect.medium.systems/r-cJDwrUbazc">Practicing Datscy @ Dev.to</a></p><h3>References</h3><ol><li>Linear-gradient MDN: <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/gradient/linear-gradient">https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Values/gradient/linear-gradient</a></li></ol><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=043ae7200305" width="1" height="1" alt=""><hr><p><a href="https://towardsdev.com/self-productivity-tool-series-043ae7200305">Self-Productivity Tool Series</a> was originally published in <a href="https://towardsdev.com">Towards Dev</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Context Engineering Replaced Prompt Engineering. Here’s the New Job]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://towardsdev.com/context-engineering-replaced-prompt-engineering-heres-the-new-job-c5995ef82068?source=rss----a648dc4ecb66---4"><img src="https://cdn-images-1.medium.com/max/1672/1*WwHNtA0ZiiVZeCcPwdFODQ.png" width="1672"></a></p><p class="medium-feed-snippet">Every token in an agent&#x2019;s window draws on two budgets: attention and money. Here&#x2019;s how to spend both on purpose.</p><p class="medium-feed-link"><a href="https://towardsdev.com/context-engineering-replaced-prompt-engineering-heres-the-new-job-c5995ef82068?source=rss----a648dc4ecb66---4">Continue reading on Towards Dev »</a></p></div>]]></description>
            <link>https://towardsdev.com/context-engineering-replaced-prompt-engineering-heres-the-new-job-c5995ef82068?source=rss----a648dc4ecb66---4</link>
            <guid isPermaLink="false">https://medium.com/p/c5995ef82068</guid>
            <category><![CDATA[ai-agent]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[context-engineering]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <dc:creator><![CDATA[Daniel Valev]]></dc:creator>
            <pubDate>Fri, 09 Oct 2026 10:02:15 GMT</pubDate>
            <atom:updated>2026-10-09T10:02:14.731Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[ Your Spring Boot App Sends Emails Before the Transaction Commits — Here’s the Bug]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://towardsdev.com/your-spring-boot-app-sends-emails-before-the-transaction-commits-heres-the-bug-0bcc6cfb7e31?source=rss----a648dc4ecb66---4"><img src="https://cdn-images-1.medium.com/max/1774/1*_4sDJ1uRvmVLfRODEUKX5Q.png" width="1774"></a></p><p class="medium-feed-snippet">Imagine this Spring Boot code:</p><p class="medium-feed-link"><a href="https://towardsdev.com/your-spring-boot-app-sends-emails-before-the-transaction-commits-heres-the-bug-0bcc6cfb7e31?source=rss----a648dc4ecb66---4">Continue reading on Towards Dev »</a></p></div>]]></description>
            <link>https://towardsdev.com/your-spring-boot-app-sends-emails-before-the-transaction-commits-heres-the-bug-0bcc6cfb7e31?source=rss----a648dc4ecb66---4</link>
            <guid isPermaLink="false">https://medium.com/p/0bcc6cfb7e31</guid>
            <category><![CDATA[spring-boot]]></category>
            <category><![CDATA[transactions]]></category>
            <category><![CDATA[spring]]></category>
            <category><![CDATA[spring-app]]></category>
            <category><![CDATA[softwre-development]]></category>
            <dc:creator><![CDATA[CodeTalks]]></dc:creator>
            <pubDate>Fri, 09 Oct 2026 10:02:11 GMT</pubDate>
            <atom:updated>2026-10-09T10:02:10.128Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[ Your JPA CascadeType.ALL Is More Dangerous Than It Looks]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://towardsdev.com/your-jpa-cascadetype-all-is-more-dangerous-than-it-looks-72ebdc2cdf00?source=rss----a648dc4ecb66---4"><img src="https://cdn-images-1.medium.com/max/1774/1*JyXfdgd8xgQra-_y2v1cAQ.png" width="1774"></a></p><p class="medium-feed-snippet">This annotation looks convenient:</p><p class="medium-feed-link"><a href="https://towardsdev.com/your-jpa-cascadetype-all-is-more-dangerous-than-it-looks-72ebdc2cdf00?source=rss----a648dc4ecb66---4">Continue reading on Towards Dev »</a></p></div>]]></description>
            <link>https://towardsdev.com/your-jpa-cascadetype-all-is-more-dangerous-than-it-looks-72ebdc2cdf00?source=rss----a648dc4ecb66---4</link>
            <guid isPermaLink="false">https://medium.com/p/72ebdc2cdf00</guid>
            <category><![CDATA[spring]]></category>
            <category><![CDATA[jpa]]></category>
            <category><![CDATA[annotations]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[spring-boot]]></category>
            <dc:creator><![CDATA[CodeTalks]]></dc:creator>
            <pubDate>Fri, 09 Oct 2026 10:02:08 GMT</pubDate>
            <atom:updated>2026-10-09T10:02:07.578Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Stop Writing Clamp Helpers: C++26’s Saturation Arithmetic]]></title>
            <link>https://towardsdev.com/c-plus-plus-26-saturation-arithmetic-9f5a3f17d9b9?source=rss----a648dc4ecb66---4</link>
            <guid isPermaLink="false">https://medium.com/p/9f5a3f17d9b9</guid>
            <category><![CDATA[cpp26]]></category>
            <category><![CDATA[technology]]></category>
            <category><![CDATA[coding]]></category>
            <category><![CDATA[c-plus-plus-language]]></category>
            <category><![CDATA[software-development]]></category>
            <dc:creator><![CDATA[Sagar]]></dc:creator>
            <pubDate>Fri, 09 Oct 2026 10:02:06 GMT</pubDate>
            <atom:updated>2026-10-09T10:02:04.882Z</atom:updated>
            <content:encoded><![CDATA[<h4>How C++26 makes integer overflow handling explicit and predictable.</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*MEH55-wek6lj3kqf1w3kAA.png" /></figure><p>Integer overflow is easy to get wrong. Unsigned integers wrap around. <br>Signed integer overflow is undefined behavior.</p><p>If you want a calculation to stop at the minimum or maximum value instead, you need different arithmetic.</p><p>C++26 adds <strong>saturation arithmetic </strong>to <strong>&lt;numeric&gt;</strong>.</p><p>The feature is small, but there is some history behind it. The original proposal used names such as <strong>add_sat</strong> and <strong>sub_sat</strong>. Those names were changed before the feature was finalized.</p><p>This article covers what <a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p0543r3.html">saturation arithmetic</a> does, why the names changed, and how to use the new <strong>std::saturating_*</strong> functions.</p><h4>First, the Problem: Wrapping and Undefined Behavior</h4><p>In C and C++, unsigned integer arithmetic wraps around modulo <strong>2^N</strong>.<br>For an N-bit unsigned integer, the range is: [0, 2^N - 1]<br>For example, an 8-bit unsigned integer can represent values from 0 to 255.</p><p>Add 10 to 255 and the result wraps around: 255 + 10 = 9</p><p>Signed integer overflow is different. Going beyond the range of the signed type results in undefined behavior.</p><p>Sometimes wrapping is exactly what you want. Sometimes it is not.<br>Consider an image with 8-bit pixel values. A white pixel has the value 255.<br>If you add 50 to it using normal unsigned arithmetic: 255 + 50 = 49<br>The value <strong>wraps</strong> around <strong>instead of</strong> staying at white.</p><p>Saturation arithmetic handles this differently. <strong><em>The result is kept inside the representable range. If the result is larger than the maximum, it becomes the maximum. If it is smaller than the minimum, it becomes the minimum.</em></strong></p><p>For an 8-bit unsigned integer: 255 + 50 = 255</p><p>Instead of wrapping, the value stays at the upper limit.</p><p>Here is the difference:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ulBU2YwHLMK0PxdSDh_zrA.png" /></figure><h4>Why This Matters for Systems Programming?</h4><p>Saturation arithmetic is useful anywhere an integer has a fixed range.<br>Some examples:</p><ul><li><strong>Audio processing:</strong> Keep sample values inside their valid range instead of letting them wrap.</li><li><strong>Image processing:</strong> Keep pixel values between their minimum and maximum values.</li><li><strong>Networking:</strong> Avoid unexpected wraparound when calculating lengths, offsets, and sizes.</li><li><strong>Embedded systems:</strong> Keep sensor and control values inside their defined limits.</li><li><strong>Game engines:</strong> Prevent fixed-range values from wrapping when they exceed their limits.</li></ul><p>Before C++26, code that needed this behavior often used custom helper functions or compiler-specific builtins.</p><p>C++26 gives us standard library functions for it.</p><h4>Why the Function Names Changed?</h4><p>The original proposal for saturation arithmetic was <a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p0543r3.html"><strong>P0543R3</strong></a>, written by Jens Maurer. It proposed functions with short names such as:</p><pre>add_sat()<br>sub_sat()<br>mul_sat()<br>div_sat()</pre><p>That naming did not make it into the final C++26 library.<br>Jan Schultke and Corentin Jabot later proposed <a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2026/p4052r0.html"><strong>P4052R0</strong></a>, which looked specifically at the naming. There were a few reasons for changing it.</p><p><strong>1. The naming was inconsistent:<br></strong>The proposal used: add_sat() but also saturate_cast() .<br>One used an abbreviation and the other did not.</p><p><strong>2. </strong><strong>saturating_add was already common:<br></strong>The proposal compared existing code and other programming languages.<br>Names based on <strong>saturating_add</strong> were already common, including in Rust, Java, and C#.</p><p><strong>3. The naming works better for future operations:<br></strong>C++ may eventually add more forms of integer arithmetic.<br>Names such as:</p><pre>saturating_add()<br>saturating_mul()<br>widening_mul()</pre><p>follow a more consistent pattern where the operation describes what it does and the prefix describes the arithmetic variant. The committee adopted the <strong>saturating_</strong> naming style.</p><p>So the final C++26 names are:</p><pre>std::saturating_add<br>std::saturating_sub<br>std::saturating_mul<br>std::saturating_div<br>std::saturating_cast</pre><h4>The C++26 Saturation Toolkit</h4><p>The functions are provided by: #include &lt;numeric&gt; .<br>They are constexpr and available in freestanding implementations.</p><p>The main operations are:</p><ul><li>std::saturating_add</li><li>std::saturating_sub</li><li>std::saturating_mul</li><li>std::saturating_div</li><li>std::saturating_cast</li></ul><p>Let’s look at each one.</p><h4>std::saturating_add</h4><p>std::saturating_add performs addition without allowing the result to go outside the range of the type.</p><p>For example:</p><pre>#include &lt;numeric&gt;<br>#include &lt;iostream&gt;<br>int main() {<br>    unsigned char pixel = 250;<br>    auto bright_pixel =<br>        std::saturating_add&lt;unsigned char&gt;(pixel, 10);<br>    std::cout &lt;&lt; &quot;Bright Pixel: &quot;<br>              &lt;&lt; static_cast&lt;int&gt;(bright_pixel)<br>              &lt;&lt; &#39;\n&#39;;<br>}</pre><p>The mathematical result is <strong>260</strong>. An <strong>unsigned char</strong> cannot represent <strong>260</strong>, so the result is clamped to <strong>255</strong>.</p><pre>Bright Pixel: 255</pre><p><strong>One detail to remember:<br></strong>These functions deliberately do not use the usual integral promotions.<br>The argument types need to match. This does not work:</p><pre>unsigned char pixel = 250;<br>std::saturating_add(pixel, 10);</pre><p><strong>pixel</strong> is an <strong>unsigned char</strong>, while <strong>10</strong> is an <strong>int</strong>.</p><p>You can specify the type:</p><pre>std::saturating_add&lt;unsigned char&gt;(pixel, 10);</pre><p>or convert the second argument:</p><pre>std::saturating_add(<br>    pixel,<br>    static_cast&lt;unsigned char&gt;(10)<br>);</pre><h4>std::saturating_sub</h4><p>Subtraction is another common place where unsigned arithmetic can produce an unexpected result.</p><p>For example:</p><pre>unsigned int health = 0;<br>unsigned int damage = 5;<br>unsigned int new_health =<br>    std::saturating_sub(health, damage);</pre><p>Normal unsigned arithmetic would wrap around:</p><pre>0 - 5 = 4,294,967,291</pre><p>Saturating subtraction gives:</p><pre>0 - 5 = 0</pre><p>The same function also handles signed overflow.</p><pre>#include &lt;limits&gt;<br>int max_int = std::numeric_limits&lt;int&gt;::max();<br>int result =<br>    std::saturating_sub(max_int, -10);</pre><p>Mathematically, this is:</p><pre>INT_MAX - (-10)</pre><p>The result is larger than <strong>INT_MAX</strong>. Normal signed arithmetic would have undefined behavior. Saturating subtraction returns <strong>INT_MAX</strong>.</p><h4>std::saturating_mul</h4><p>Multiplication can exceed the range of an integer very quickly.<br>For example:</p><pre>#include &lt;numeric&gt;<br>#include &lt;iostream&gt;<br>int main() {<br>    int a = 100000;<br>    int b = 100000;<br>    int product =<br>        std::saturating_mul(a, b);<br>    std::cout &lt;&lt; product &lt;&lt; &#39;\n&#39;;<br>}</pre><p>The mathematical result is:</p><pre>100,000 × 100,000 = 10,000,000,000</pre><p>That does not fit in a 32-bit signed int.<br><strong>std::saturating_mul</strong> returns:</p><pre>2,147,483,647</pre><p>which is <strong>INT_MAX</strong>.</p><p>Negative results are handled in the same way:</p><pre>int result =<br>    std::saturating_mul(100000, -100000);</pre><p>The mathematical result is smaller than <strong>INT_MIN</strong>, so the result is clamped to:</p><pre>-2,147,483,648</pre><h4>std::saturating_div</h4><p>Division normally makes numbers smaller, so integer division overflowing looks strange at first.</p><p>There is one signed-integer case that matters:</p><pre>INT_MIN / -1</pre><p>For an 8-bit signed integer, the range is:</p><pre>[-128, 127]</pre><p>So:</p><pre>-128 / -1 = 128</pre><p>But 128 cannot be represented by int8_t. Normal signed integer arithmetic cannot represent that result.</p><p>With C++26:</p><pre>#include &lt;cstdint&gt;<br>#include &lt;numeric&gt;<br>#include &lt;iostream&gt;<br>int main() {<br>    int8_t min_val = -128;<br>    int8_t divisor = -1;<br>    int8_t result =<br>        std::saturating_div(min_val, divisor);<br>    std::cout &lt;&lt; static_cast&lt;int&gt;(result) &lt;&lt; &#39;\n&#39;;<br>}</pre><p>The result is: 127<br>The value is clamped to the maximum representable value.<br>Division by zero is still invalid. Saturating arithmetic does not change that.</p><h4>std::saturating_cast</h4><p>Saturation is not limited to arithmetic operations. C++26 also provides <strong>std::saturating_cast</strong> for converting between integer types.</p><p>Suppose we have:</p><pre>int32_t value = 5000;</pre><p>and want an int8_t.</p><p>An <strong>int8_t</strong> can only represent: -128 ... 127<br>So 5000 does not fit. <br>With:</p><pre>auto result =<br>    std::saturating_cast&lt;int8_t&gt;(value);</pre><p>the result is: 127</p><p>The same idea works when converting negative values to unsigned types:</p><pre>int32_t value = -50;<br>auto result =<br>    std::saturating_cast&lt;uint32_t&gt;(value);</pre><p>Since an unsigned integer cannot represent a negative value, the result is clamped to: 0</p><p>This is useful when a conversion needs to preserve the destination type’s range instead of simply performing a normal integer conversion.</p><h3>Closing Thoughts</h3><p>C++26 adds standard library support for saturation arithmetic.</p><p>The idea itself is simple: keep the result inside the range of the destination type instead of allowing it to wrap or overflow.</p><p>The useful part is that we no longer need a separate custom helper for every operation.</p><p>Instead of writing something like:</p><pre>auto add_clamped(...);<br>auto sub_clamped(...);<br>auto mul_clamped(...);</pre><p>we can use:</p><pre>std::saturating_add(...)<br>std::saturating_sub(...)<br>std::saturating_mul(...)<br>std::saturating_div(...)<br>std::saturating_cast(...)</pre><p>Check the compiler support here: <a href="https://cppreference.com/cpp/compiler_support">https://cppreference.com/cpp/compiler_support</a></p><p>For performance-sensitive code, it is also worth checking the generated assembly for your target architecture. Compiler Explorer is useful for this.</p><p>The main point is simple: <strong>when wrapping or signed overflow is not what you want, C++26 now gives you a standard way to say that directly.</strong></p><p>Found this article helpful? Please <strong>Clap</strong> 👏 and <strong>follow</strong> for more C++ and system programming content.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9f5a3f17d9b9" width="1" height="1" alt=""><hr><p><a href="https://towardsdev.com/c-plus-plus-26-saturation-arithmetic-9f5a3f17d9b9">Stop Writing Clamp Helpers: C++26’s Saturation Arithmetic</a> was originally published in <a href="https://towardsdev.com">Towards Dev</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Android Development in October 2026: 7 Shifts That Actually Change Your Work]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://towardsdev.com/android-development-in-october-2026-7-shifts-that-actually-change-your-work-05046df7aaf6?source=rss----a648dc4ecb66---4"><img src="https://cdn-images-1.medium.com/max/1200/1*xvc53Fqil35L2a2h0VG1Vg.png" width="1200"></a></p><p class="medium-feed-snippet">Introduction</p><p class="medium-feed-link"><a href="https://towardsdev.com/android-development-in-october-2026-7-shifts-that-actually-change-your-work-05046df7aaf6?source=rss----a648dc4ecb66---4">Continue reading on Towards Dev »</a></p></div>]]></description>
            <link>https://towardsdev.com/android-development-in-october-2026-7-shifts-that-actually-change-your-work-05046df7aaf6?source=rss----a648dc4ecb66---4</link>
            <guid isPermaLink="false">https://medium.com/p/05046df7aaf6</guid>
            <category><![CDATA[android-app-development]]></category>
            <category><![CDATA[kotlin]]></category>
            <category><![CDATA[jetpack-compose]]></category>
            <category><![CDATA[android]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <dc:creator><![CDATA[Aslam Hossin]]></dc:creator>
            <pubDate>Fri, 09 Oct 2026 10:02:02 GMT</pubDate>
            <atom:updated>2026-10-09T10:02:01.205Z</atom:updated>
        </item>
    </channel>
</rss>