<?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: Doogal Simpson</title>
    <description>The latest articles on DEV Community by Doogal Simpson (@doogal).</description>
    <link>https://dev.to/doogal</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3657111%2Fac69c96a-33e1-4023-99ef-ee3059b3ccb6.jpeg</url>
      <title>DEV Community: Doogal Simpson</title>
      <link>https://dev.to/doogal</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/doogal"/>
    <language>en</language>
    <item>
      <title>Handling ISO Currency Codes in Software</title>
      <dc:creator>Doogal Simpson</dc:creator>
      <pubDate>Sat, 27 Jun 2026 08:35:36 +0000</pubDate>
      <link>https://dev.to/doogal/handling-iso-currency-codes-in-software-37hi</link>
      <guid>https://dev.to/doogal/handling-iso-currency-codes-in-software-37hi</guid>
      <description>&lt;p&gt;&lt;strong&gt;Quick Answer:&lt;/strong&gt; When processing financial transactions over the internet, systems cannot rely on ambiguous symbols like the dollar sign ($) because dozens of countries share it. Instead, banking APIs use ISO 4217, a global standard that assigns a unique three-letter code (like USD, CAD, or AUD) to unambiguously identify every currency.&lt;/p&gt;

&lt;p&gt;If you were to write "$129" in a text message, your friend probably knows exactly what you mean based on where you live. But if you send "$129" to a banking API, you have a problem.&lt;/p&gt;

&lt;p&gt;Think of currency symbols like local time zones. If I tell a globally distributed engineering team, "Let's deploy at 9:00 AM," chaos ensues. Which 9:00 AM? The exact same thing happens with money. The internet doesn't intuitively know which type of dollar you are talking about. If a user expects to be paid in U.S. dollars but your system mistakenly pays them in Canadian dollars, you are going to have some very annoyed users on your hands.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is the dollar sign ambiguous in software systems?
&lt;/h2&gt;

&lt;p&gt;The dollar sign ($) is inherently ambiguous because it is shared by over 20 different global currencies, each with wildly different exchange rates. If a banking system only receives the symbol and a number, it lacks the context needed to accurately process the transaction.&lt;/p&gt;

&lt;p&gt;Let's say you have an e-commerce microservice handling checkouts. A user in Australia buys an item, and the payload simply says &lt;code&gt;$129&lt;/code&gt;. Is that the United States dollar? The Canadian dollar? The Australian dollar? They all use the exact same visual symbol. If your backend defaults to USD, but the customer intended to use AUD, you've just overcharged them significantly due to the exchange rate gap. Relying on symbols to dictate business logic is a recipe for catastrophic accounting bugs.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the ISO 4217 standard for currency codes?
&lt;/h2&gt;

&lt;p&gt;ISO 4217 is a standard published by the International Organization for Standardization that dictates how we represent currencies in software and banking. It replaces ambiguous symbols with unique, three-letter alphabetic codes for every single currency in the world.&lt;/p&gt;

&lt;p&gt;To solve the "$129" problem, a global committee got together to map out every active currency. They created a definitive list of three-letter strings that unambiguously tell systems apart. The first two letters usually refer to the country code, and the third letter refers to the currency name. So, the United States dollar becomes USD. The Canadian dollar becomes CAD. The Australian dollar becomes AUD.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do you format currency data in an API payload?
&lt;/h3&gt;

&lt;p&gt;To guarantee accuracy, API payloads represent money using a combination of the integer amount alongside the explicit three-letter ISO currency code. The actual visual symbol is strictly reserved for the frontend UI, keeping the backend data unambiguous.&lt;/p&gt;

&lt;p&gt;If your team is building a fintech app or integrating a payment gateway like Stripe, you will notice that the API never asks for a symbol. It asks for the unambiguous code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"amount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12900&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USD"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This explicit data structure leaves zero room for misinterpretation. The backend knows exactly what currency to process.&lt;/p&gt;

&lt;p&gt;Here is a breakdown of how the highly ambiguous &lt;code&gt;$&lt;/code&gt; symbol maps to strict ISO codes depending on the region:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Country&lt;/th&gt;
&lt;th&gt;Ambiguous Symbol&lt;/th&gt;
&lt;th&gt;Unambiguous ISO Code&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;United States&lt;/td&gt;
&lt;td&gt;$&lt;/td&gt;
&lt;td&gt;USD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Canada&lt;/td&gt;
&lt;td&gt;$&lt;/td&gt;
&lt;td&gt;CAD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Australia&lt;/td&gt;
&lt;td&gt;$&lt;/td&gt;
&lt;td&gt;AUD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;New Zealand&lt;/td&gt;
&lt;td&gt;$&lt;/td&gt;
&lt;td&gt;NZD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Singapore&lt;/td&gt;
&lt;td&gt;$&lt;/td&gt;
&lt;td&gt;SGD&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  How does the frontend know which currency symbol to display?
&lt;/h2&gt;

&lt;p&gt;Modern frontend applications use native internationalization libraries to map the backend's ISO code back to the correct local symbol. This keeps the backend data pure while ensuring the user sees the formatting they expect in the browser.&lt;/p&gt;

&lt;p&gt;Once your unambiguous &lt;code&gt;USD&lt;/code&gt; or &lt;code&gt;CAD&lt;/code&gt; data arrives at the client, the UI layer takes over. Languages like JavaScript have built-in APIs that take the ISO code and the user's locale, and automatically figure out whether to render &lt;code&gt;$129.00&lt;/code&gt;, &lt;code&gt;US$129.00&lt;/code&gt;, or &lt;code&gt;129,00 $&lt;/code&gt;. The internet knows what dollar you mean because we stripped the symbol out at the source, relied on the ISO code for the heavy lifting across the wire, and only put the symbol back at the very end.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What happens if I store currency symbols in my database?
&lt;/h3&gt;

&lt;p&gt;Storing currency symbols in a database breaks data normalization and makes mathematical operations extremely difficult. You should always store the numeric amount and the ISO currency code in separate columns, leaving the visual symbol entirely out of your database schema.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why do payment APIs use integers instead of decimals?
&lt;/h3&gt;

&lt;p&gt;While the ISO code handles currency identification, using integers (like 12900 instead of 129.00) prevents floating-point math errors. When processing money, robust systems use the lowest denomination (like cents) combined with the ISO code to maintain absolute precision.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where can I find the full list of ISO 4217 currency codes?
&lt;/h3&gt;

&lt;p&gt;The complete, actively maintained list of three-letter currency codes is published by the International Organization for Standardization. You can easily find the up-to-date registry on Wikipedia or directly through the ISO website when building out your application's supported currencies.&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>fintech</category>
      <category>programming</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How Git Uses Merkle Trees for Fast Verification</title>
      <dc:creator>Doogal Simpson</dc:creator>
      <pubDate>Sat, 27 Jun 2026 08:35:20 +0000</pubDate>
      <link>https://dev.to/doogal/how-git-uses-merkle-trees-for-fast-verification-5179</link>
      <guid>https://dev.to/doogal/how-git-uses-merkle-trees-for-fast-verification-5179</guid>
      <description>&lt;p&gt;&lt;strong&gt;Quick Answer: Git doesn't compare files line-by-line. Instead, it uses a Merkle tree data structure. Every file's content is hashed into a "blob," which is then hashed into a "tree," culminating in a top-level "commit" hash. By simply comparing two commit hashes, Git instantly verifies if entire directories are perfectly identical in milliseconds.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Ever wonder how Git can look at two directories containing hundreds, or even thousands, of files and instantly know if they match? If you or I were to write a script to do this naively, we'd probably loop through every single file, comparing the contents bit by bit. That is an O(N) operation. It works, but as the repository grows, it gets painfully slow. &lt;/p&gt;

&lt;p&gt;Yet, Git does this in milliseconds. Let's break down exactly how it pulls this off without reading every file every time you type a command.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does Git verify directory contents so fast?
&lt;/h2&gt;

&lt;p&gt;Git relies on a cryptographic data structure called a Merkle tree. Instead of scanning raw file contents, Git assigns a unique hash to every file and directory, rolling them up into a single master hash.&lt;/p&gt;

&lt;p&gt;Think of it like managing a massive library of books. If you needed to verify that two identical library buildings contained the exact same books, reading every page would take lifetimes. Instead, imagine you generate a unique barcode for every book based on its text. Then, you generate a barcode for the shelf based on the books it holds, and finally, a master barcode for the whole building based on its shelves. &lt;/p&gt;

&lt;p&gt;If the master barcodes match, the libraries are perfectly identical. Git does exactly this, but with your source code.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do Git blobs, trees, and commits work together?
&lt;/h2&gt;

&lt;p&gt;When you create a commit, Git stores your data in a hierarchy of objects: blobs for file content, trees for directory structures, and commits for the snapshot metadata. Each level generates a hash that depends entirely on the hashes below it.&lt;/p&gt;

&lt;p&gt;Let's say your team is building a standard web app. When you run a commit, Git structures your data in three distinct layers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Blobs:&lt;/strong&gt; Git takes the raw content of your files and hashes them. This creates a "blob" (binary large object). The blob only cares about the raw content, completely ignoring the filename.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Trees:&lt;/strong&gt; Next, Git creates a tree object to represent your directory structure. This tree contains the filenames and points to the generated blob hashes. Git then hashes this entire tree object.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Commits:&lt;/strong&gt; Finally, the commit object wraps around the root tree. It pairs the root tree hash with metadata like the author name, timestamp, and parent commit. Then, the commit itself gets hashed.&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Git Object&lt;/th&gt;
&lt;th&gt;What It Represents&lt;/th&gt;
&lt;th&gt;What It Hashes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Blob&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Raw file content&lt;/td&gt;
&lt;td&gt;Only the file's text/data (ignoring filename)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tree&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Directory structure&lt;/td&gt;
&lt;td&gt;Filenames, permissions, and hashes of underlying blobs/trees&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Commit&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Repository snapshot&lt;/td&gt;
&lt;td&gt;Author, timestamp, parent commit hash, and root tree hash&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Why does changing one file change the commit hash?
&lt;/h2&gt;

&lt;p&gt;Because the Merkle tree structure creates a strict chain of cryptographic dependencies. A modified file produces a new blob hash, which forces the parent tree to generate a new hash, causing a ripple effect straight to the commit object.&lt;/p&gt;

&lt;p&gt;This is the real power of the Merkle tree design. If you fix a single typo in a CSS file deep within your project, Git doesn't rescan the rest of your codebase. That modified CSS file gets a brand new blob hash. Because the tree (directory) holding that file now points to a new blob hash, the tree's hash changes. This bubbles all the way up to the top-level commit hash.&lt;/p&gt;

&lt;p&gt;Therefore, if two commit hashes are identical, you have a mathematical guarantee that every single file and folder underneath them is the exact same content. If the commit hashes are different, you instantly know something shifted in the repository, and Git simply follows the changed tree hashes down the branches to find exactly which file was modified.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What hashing algorithm does Git use?
&lt;/h3&gt;

&lt;p&gt;Historically, Git uses the SHA-1 algorithm to generate object hashes. While SHA-1 has known theoretical vulnerabilities in a strict security context, Git uses it primarily for data integrity and consistency. Newer versions of Git are actively transitioning to the more secure SHA-256 algorithm.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does Git hash the filename or just the content?
&lt;/h3&gt;

&lt;p&gt;Git blobs only hash the raw file content. The actual filename and file permissions are stored and hashed inside the tree object that points to the blob. This is why if you rename a file without changing its contents, Git recognizes it as a rename—the underlying blob hash remains exactly the same.&lt;/p&gt;

&lt;h3&gt;
  
  
  What happens to old blobs when a file is changed?
&lt;/h3&gt;

&lt;p&gt;Git never overwrites your old blobs. It creates an entirely new blob object for the changed content. The old blob safely remains in your local database, allowing you to easily checkout older commits. If a blob becomes completely unreachable, it is eventually cleaned up by Git's background garbage collection.&lt;/p&gt;

</description>
      <category>git</category>
      <category>softwareengineering</category>
      <category>datastructures</category>
      <category>versioncontrol</category>
    </item>
    <item>
      <title>How Merkle Trees Verify Large Files</title>
      <dc:creator>Doogal Simpson</dc:creator>
      <pubDate>Fri, 26 Jun 2026 17:02:16 +0000</pubDate>
      <link>https://dev.to/doogal/how-merkle-trees-verify-large-files-242p</link>
      <guid>https://dev.to/doogal/how-merkle-trees-verify-large-files-242p</guid>
      <description>&lt;p&gt;&lt;strong&gt;Quick Answer:&lt;/strong&gt; To verify massive files from untrusted sources, I use a Merkle tree. This cryptographic data structure chops the file into manageable chunks, hashes each one, and pairs them recursively into a pyramid. The final output is a single root hash that instantly alerts you if even one bit was altered.&lt;/p&gt;

&lt;p&gt;Let's say you are torrenting. You are pulling down a massive 50-gigabyte file from hundreds of strangers across the internet. People you don't know are firing raw bits of data at your machine.&lt;/p&gt;

&lt;p&gt;How do you know someone hasn't messed with it? How do you guarantee that a random peer didn't inject malicious code into one of those payloads?&lt;/p&gt;

&lt;p&gt;You don't want to blindly trust the network, and you certainly don't want to wait until the entire 50GB is assembled on your hard drive to find out it is compromised. When I explain this concept to other engineers, I point them straight to Merkle trees.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a Merkle tree and how does it verify data integrity?
&lt;/h2&gt;

&lt;p&gt;A Merkle tree is a tree-like data structure that uses cryptography to verify the contents of large datasets efficiently. It works by breaking a payload down into smaller chunks, hashing them, and combining those hashes recursively until a single root hash remains.&lt;/p&gt;

&lt;p&gt;When you initiate the download for a massive file, the provider also hands you a 64-character hash. Hash functions are deterministic. They will always return the exact same output for the exact same input. More importantly, they are extremely sensitive to change. If you alter a single letter, or even flip a single bit in a 50GB file, the resulting hash completely changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you build a Merkle tree step-by-step?
&lt;/h2&gt;

&lt;p&gt;You build a Merkle tree by dividing your large file into uniform blocks and running each one through a hash function. You then pair the resulting hashes, hash the pairs, and repeat this process layer by layer until you reach the top of the pyramid.&lt;/p&gt;

&lt;p&gt;I find it easiest to visualize this as a literal pyramid built from the ground up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Step 1: Chunk the file.&lt;/strong&gt; Take your 50GB file and slice it into smaller pieces. Let's say we chop it into 1,000 distinct chunks.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step 2: Hash the base layer.&lt;/strong&gt; Run each of those 1,000 chunks through a cryptographic hash function. We now have 1,000 unique 64-character strings.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step 3: Pair and hash.&lt;/strong&gt; Take those 1,000 hashes, group them into pairs of two, and hash the combined pairs. The pile is reduced from 1,000 hashes down to 500.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step 4: Repeat up the pyramid.&lt;/strong&gt; Take those 500 hashes, pair them up, and hash the results to get 250.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step 5: Extract the root hash.&lt;/strong&gt; Continue this pairing-and-hashing loop until exactly one 64-character string remains at the very top.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What happens if someone tampers with the file during download?
&lt;/h2&gt;

&lt;p&gt;If a malicious peer alters any part of the file, the hash of that specific chunk changes completely, which triggers a cascading mismatch all the way up the pyramid. The final root hash you calculate will not match the expected root hash you were originally given.&lt;/p&gt;

&lt;p&gt;Imagine a scenario where someone tampers with the third chunk of your file. Because the input changed, the hash for chunk three changes. When you pair chunk three's new hash with chunk four's hash, their combined output changes. This chain reaction travels all the way up the tree. &lt;/p&gt;

&lt;p&gt;When I finish running a Merkle tree over an assembled file, the top-level hash has to match the source of truth. If it looks entirely different from the one I was supposed to get, that mismatch is absolute proof that someone messed with the file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why use a Merkle tree instead of hashing the whole file at once?
&lt;/h2&gt;

&lt;p&gt;Hashing a massive file all at once is inefficient because if the final hash fails, you have no way of knowing which specific part of the file is corrupted, forcing you to redownload the entire thing. Merkle trees isolate the exact location of the tampering, allowing you to reject and redownload only the corrupted chunks.&lt;/p&gt;

&lt;p&gt;By using a tree structure, you can verify massive files at scale without having to process every single bit of the file simultaneously. You verify the integrity piece by piece as the chunks arrive.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How do you fix a file if the Merkle tree root hash does not match?
&lt;/h3&gt;

&lt;p&gt;When the root hash fails, your software doesn't throw away the whole file. It asks peers for the hashes of the branches directly below the root, traversing down the tree to isolate exactly which chunks caused the mismatch. You then delete only those specific bad chunks and redownload them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Are Merkle trees only used for torrenting?
&lt;/h3&gt;

&lt;p&gt;No. While they are heavily used in peer-to-peer file sharing, they are highly common across distributed systems. Git uses them to track file changes efficiently, and blockchain networks use them to verify blocks of transactions without needing to download the entire ledger.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does a Merkle tree encrypt the file data?
&lt;/h3&gt;

&lt;p&gt;No, a Merkle tree provides verification, not encryption. Hash functions are one-way cryptographic tools used to generate a unique fingerprint for the data. The actual contents of the 50GB file remain entirely unencrypted unless you separately apply an encryption algorithm to the payload.&lt;/p&gt;

</description>
      <category>cryptography</category>
      <category>datastructures</category>
      <category>computerscience</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Storing Exchange Rates for Multi-Currency Systems</title>
      <dc:creator>Doogal Simpson</dc:creator>
      <pubDate>Fri, 26 Jun 2026 17:01:57 +0000</pubDate>
      <link>https://dev.to/doogal/storing-exchange-rates-for-multi-currency-systems-50m2</link>
      <guid>https://dev.to/doogal/storing-exchange-rates-for-multi-currency-systems-50m2</guid>
      <description>&lt;p&gt;&lt;strong&gt;Quick Answer: When building an e-commerce system, never use today's exchange rate to calculate the value of past transactions. Currency values fluctuate daily. To keep financial analytics accurate, you must store the exact exchange rate on the transaction record at checkout, or fetch the precise historical rate at runtime.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Building a system to sell things online seems straightforward until international money enters the picture. Imagine your team is building a storefront that sells t-shirts. Your main operations are in the UK, so your base currency is GBP. Everything works perfectly until an overseas customer buys a shirt using USD. You convert the dollars to pounds, ship the shirt, and everyone goes home happy.&lt;/p&gt;

&lt;p&gt;Fast forward a year. Your team runs analytics to calculate the total annual revenue. This is exactly where I see developers get tripped up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why shouldn't I use current exchange rates for past transactions?
&lt;/h2&gt;

&lt;p&gt;Using current exchange rates for past transactions skews your reporting. Currencies fluctuate from day to day, meaning calculating a purchase from a year ago with today's rate will give you inaccurate historical revenue numbers.&lt;/p&gt;

&lt;p&gt;When that customer bought the t-shirt last year, the GBP to USD rate was specific to that exact day. If you didn't record that rate, and you try to calculate the transaction value using today's rate, your analytics will simply be wrong. I have seen developers hardcode an exchange rate into a configuration file and use it forever because it feels "roughly right." But roughly right usually means being quite off—sometimes by 10% or 20% in some instances. That adds up quickly when you are trying to total up all your transactions over the course of a year.&lt;/p&gt;

&lt;h2&gt;
  
  
  How should I store multi-currency transactions in a database?
&lt;/h2&gt;

&lt;p&gt;You should save the exchange rate directly on the transaction row at the exact moment the checkout completes. This saves the exact state of the conversion alongside the purchase data, giving you an accurate record for future analytics.&lt;/p&gt;

&lt;p&gt;Instead of relying on math later, make the math a permanent part of the database record. When the checkout succeeds, your backend needs to capture the original currency amount, the exchange rate applied, and the final converted amount in your base currency. &lt;/p&gt;

&lt;p&gt;Here is exactly what you need to capture to avoid historical inaccuracies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Original Amount:&lt;/strong&gt; The exact numeric value the user paid (e.g., 20.00).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Original Currency:&lt;/strong&gt; The ISO currency code used by the buyer (e.g., USD).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exchange Rate:&lt;/strong&gt; The exact multiplier used at the time of purchase (e.g., 0.78).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Base Amount:&lt;/strong&gt; The final calculated value in your system's default currency (e.g., 15.60).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Base Currency:&lt;/strong&gt; Your system's default currency (e.g., GBP).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timestamp:&lt;/strong&gt; The exact time the transaction completed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's look at a brief SQL schema to visualize 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;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;transactions&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="n"&gt;UUID&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;original_amount&lt;/span&gt; &lt;span class="nb"&gt;DECIMAL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;original_currency&lt;/span&gt; &lt;span class="nb"&gt;VARCHAR&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;exchange_rate&lt;/span&gt; &lt;span class="nb"&gt;DECIMAL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;base_amount&lt;/span&gt; &lt;span class="nb"&gt;DECIMAL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Should I store exchange rates or fetch them at runtime?
&lt;/h2&gt;

&lt;p&gt;Storing the exchange rate directly in your database is highly recommended because it simplifies data retrieval and speeds up reporting. Fetching historical rates dynamically at runtime from an external API is possible, but it introduces heavy network latency to your analytics pipeline.&lt;/p&gt;

&lt;p&gt;If you really want to calculate things after the fact, you can hit a financial API at runtime to ask for the historical GBP to USD exchange rate for a specific past date. But doing this for thousands of rows during an analytics run is a significant performance bottleneck. &lt;/p&gt;

&lt;p&gt;Storing the rate directly on the transaction acts as a cache and a reliable historical source of truth. It guarantees that if the external API goes down, or you migrate to a new payment provider, your internal revenue calculations remain perfectly stable.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How do you handle refunds in a different currency?
&lt;/h3&gt;

&lt;p&gt;Refunds should generally use the exact exchange rate of the original transaction to avoid losing money on currency fluctuations. Alternatively, some payment gateways refund the exact original currency amount and force the merchant to absorb the difference. Either way, you must record the refund rate just like you record the purchase rate.&lt;/p&gt;

&lt;h3&gt;
  
  
  What data type should I use for currency exchange rates?
&lt;/h3&gt;

&lt;p&gt;Always use a decimal or numeric data type (like DECIMAL or NUMERIC in SQL) with high precision, such as 4 to 6 decimal places. Never use floating-point types for financial data because they introduce fractional rounding errors.&lt;/p&gt;

&lt;h3&gt;
  
  
  How often should e-commerce platforms update live exchange rates?
&lt;/h3&gt;

&lt;p&gt;For live checkouts, most platforms update their exchange rates at least daily, though high-volume trading platforms might update them every few minutes. You should use a reliable third-party API and cache the rate locally for your specified time window to keep checkout fast.&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>architecture</category>
      <category>databasedesign</category>
      <category>ecommerce</category>
    </item>
    <item>
      <title>Merkle Trees Explained: How We Verify Large Files</title>
      <dc:creator>Doogal Simpson</dc:creator>
      <pubDate>Tue, 23 Jun 2026 07:52:32 +0000</pubDate>
      <link>https://dev.to/doogal/merkle-trees-explained-how-we-verify-large-files-3e2o</link>
      <guid>https://dev.to/doogal/merkle-trees-explained-how-we-verify-large-files-3e2o</guid>
      <description>&lt;p&gt;&lt;strong&gt;Quick Answer: A Merkle tree is a data structure used to quickly verify the integrity of massive files downloaded from untrusted sources. By chopping a file into smaller chunks, hashing them in pairs, and combining them into a single root hash, you can instantly detect tampering without processing the entire dataset at once.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you’ve ever downloaded a massive file using a torrent client, you were likely pulling chunks of data from hundreds of strangers' computers simultaneously. But pulling random bytes from anonymous sources introduces a massive security risk. How do you know none of those peers slipped a malicious payload into the middle of your download instead of the software you requested? The mechanism that keeps peer-to-peer downloads safe and verifies massive datasets really, really quickly is called a Merkle tree.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a Merkle tree and how does it verify data?
&lt;/h2&gt;

&lt;p&gt;A Merkle tree is a hierarchical data structure that breaks large files down into smaller, manageable blocks and assigns each a cryptographic hash. These hashes are paired and hashed together repeatedly until they form a single, overarching root hash that acts as a unique fingerprint for the entire file.&lt;/p&gt;

&lt;p&gt;Let's say you have a peer-to-peer network routing a 50-gigabyte file. Verifying 50 gigabytes all at once is incredibly inefficient. Instead, we chop that data up into small chunks. To visualize how a Merkle tree processes these chunks without getting bogged down in cryptographic strings, I prefer to use a color mixing analogy. We can pretend each chunk of our 50-gigabyte file maps to a specific baseline color.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does the color mixing analogy explain cryptographic hashing?
&lt;/h2&gt;

&lt;p&gt;In a Merkle tree, hashing two chunks of data together produces a completely new, predictable hash, just like mixing two primary colors produces a predictable secondary color. By repeatedly combining these pairs up a pyramid-like structure, you eventually reach a single final color at the very top.&lt;/p&gt;

&lt;p&gt;Let’s keep the math simple and say we chop our massive file into exactly four chunks.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Chunk one is blue.&lt;/li&gt;
&lt;li&gt;Chunk two is yellow.&lt;/li&gt;
&lt;li&gt;Chunk three is red.&lt;/li&gt;
&lt;li&gt;Chunk four is white.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, we start mixing these baseline colors together to build the next level of our tree. We mix chunk one (blue) and chunk two (yellow) together, giving us a green hash. We mix chunk three (red) and chunk four (white) together, giving us a pink hash.&lt;/p&gt;

&lt;p&gt;Finally, we mix those new secondary colors together. Green and pink combine to give us a brown hash at the very top of our pyramid. That brown color is our root hash.&lt;/p&gt;

&lt;h3&gt;
  
  
  What happens when a file chunk is tampered with?
&lt;/h3&gt;

&lt;p&gt;If a malicious actor alters even a single bit of a file chunk, its foundational color changes, which inevitably alters every combination above it. The resulting mismatch at the very top of the tree immediately alerts your system that the file has been tampered with.&lt;/p&gt;

&lt;p&gt;Let’s say a peer on the network decides to mess with the first chunk. Instead of serving you the correct data (blue), they slip in a virus, which changes that chunk's color to red. Now your base chunks are red, yellow, red, and white.&lt;/p&gt;

&lt;p&gt;When your torrent client does the mixing, red and yellow combine to make orange. The second half of the tree remains the same: red and white still make pink. But when you mix your new orange hash with the pink hash, you don't get brown anymore. You get a shade of coral.&lt;/p&gt;

&lt;p&gt;When the original sender distributed this massive file, they included the expected root color: brown. Your client computed coral. Because the final color doesn't match the expected output of your color mixing strategy, you know instantly that someone tampered with the file at the bottom of the tree.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why use a Merkle tree instead of hashing the entire file?
&lt;/h2&gt;

&lt;p&gt;Hashing an entire file requires downloading the complete dataset before you can run a single integrity check. Merkle trees allow you to verify isolated chunks as they arrive, meaning you only need to discard and re-download the specific piece of corrupted data rather than the entire file.&lt;/p&gt;

&lt;p&gt;If you are pulling a 50-gigabyte file, a single corrupted byte shouldn't force you to start the download over from zero. Because the tree is structured hierarchically, your system can walk down the branches to locate the exact chunk that caused the final mismatch.&lt;/p&gt;

&lt;p&gt;Here is a breakdown of why peer-to-peer systems rely on tree structures rather than flat file hashing:&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;Flat File Hashing&lt;/th&gt;
&lt;th&gt;Merkle Tree Hashing&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Verification Timing&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Must download 100% of the file first.&lt;/td&gt;
&lt;td&gt;Can verify individual branches as data arrives.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Corruption Recovery&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Must discard and re-download the entire file.&lt;/td&gt;
&lt;td&gt;Only discard and re-download the single broken chunk.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Network Efficiency&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;High bandwidth waste on corrupted transfers.&lt;/td&gt;
&lt;td&gt;Minimal waste due to localized error detection.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What else do developers ask about Merkle trees?
&lt;/h2&gt;

&lt;p&gt;If you're digging into peer-to-peer networking for the first time, you probably have a few more questions about how these data structures actually get used in the wild. I usually see engineers asking about blockchain implementations, tree types, and hashing algorithms, so let's tackle those real quick.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do blockchains use Merkle trees?
&lt;/h3&gt;

&lt;p&gt;Yes, blockchain networks like Bitcoin use Merkle trees to efficiently summarize all the transactions in a single block. This allows nodes to verify whether a specific transaction is included in a block without having to download the entire ledger.&lt;/p&gt;

&lt;p&gt;By storing just the Merkle root in the block header, lightweight clients can request a cryptographic proof from full nodes. This proves mathematically that a transaction exists in the tree without exposing the client to massive bandwidth requirements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Are Merkle trees and binary trees the same thing?
&lt;/h3&gt;

&lt;p&gt;While a Merkle tree is usually implemented as a binary tree, they are not strictly the same thing. A binary tree is a general data structure where each node has at most two children, whereas a Merkle tree specifically requires nodes to contain cryptographic hashes of their children.&lt;/p&gt;

&lt;p&gt;You can theoretically build a Merkle tree with more than two children per node, but pairing hashes into a binary structure is the standard approach because it simplifies the hashing logic.&lt;/p&gt;

&lt;h3&gt;
  
  
  What hashing algorithms are used in Merkle trees?
&lt;/h3&gt;

&lt;p&gt;Merkle trees can use any cryptographic hash function, but SHA-256 is the most common industry standard. The specific algorithm chosen usually depends on the security requirements and performance constraints of the network.&lt;/p&gt;

&lt;p&gt;For peer-to-peer file sharing protocols or decentralized storage systems, speed is prioritized alongside collision resistance, making algorithms like SHA-256 highly effective for generating the node colors we discussed earlier.&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>computerscience</category>
      <category>datastructures</category>
      <category>cryptography</category>
    </item>
    <item>
      <title>Correct by Design vs Correct by Coincidence</title>
      <dc:creator>Doogal Simpson</dc:creator>
      <pubDate>Tue, 23 Jun 2026 07:52:11 +0000</pubDate>
      <link>https://dev.to/doogal/correct-by-design-vs-correct-by-coincidence-36mp</link>
      <guid>https://dev.to/doogal/correct-by-design-vs-correct-by-coincidence-36mp</guid>
      <description>&lt;p&gt;&lt;strong&gt;Quick Answer: Code is "correct by coincidence" when it relies on duplicated, fragile details—like matching magic strings—that just happen to align perfectly to make the application work. Code is "correct by design" when you structurally enforce stability, such as centralizing shared values, ensuring the system cannot easily break from simple typos.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can build an application a hundred different ways. From the outside, the users are happy, the behaviors are exactly what the product manager asked for, and your entire test suite is glowing green. But on the inside, the reality can be vastly different. The codebase might be a solid domain model accurately reflecting the real world—or it could be held together with tape, string, and pure luck.&lt;/p&gt;

&lt;p&gt;This brings up a question I ask myself a lot: Is this codebase correct by coincidence, or is it correct by design?&lt;/p&gt;

&lt;h2&gt;
  
  
  What does "correct by coincidence" mean in programming?
&lt;/h2&gt;

&lt;p&gt;Correct by coincidence means your software functions properly only because loosely connected, duplicated details happen to match up perfectly at runtime. If any single unlinked detail changes or a developer makes a tiny typo, the entire system breaks.&lt;/p&gt;

&lt;p&gt;Imagine a city planner who builds a bridge where the support beams aren't bolted together, but they stay standing simply because the wind happens to be blowing with equal force from both sides. It works today, but it is incredibly fragile.&lt;/p&gt;

&lt;p&gt;Let's look at a concrete software scenario. Imagine your team is building a microservice that needs to read a configuration file—let's call it &lt;code&gt;config_init.json&lt;/code&gt;. This file is absolutely critical and needs to be read in ten different places across your application.&lt;/p&gt;

&lt;p&gt;If you write the application so that in each of those ten places you manually type out the magic string &lt;code&gt;"config_init.json"&lt;/code&gt;, your code is correct by coincidence. It is purely a coincidence that each of those ten scattered locations happens to contain the exact same magic string required to successfully read the file. The moment someone renames the file but only updates nine of those ten strings, the application blows up.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you write code that is "correct by design"?
&lt;/h2&gt;

&lt;p&gt;Writing code that is correct by design involves structuring your system so that fragile duplications are eliminated and invalid states are hard to represent. You establish a single source of truth—like a centralized constant—so correctness is enforced structurally rather than relying on human memory.&lt;/p&gt;

&lt;p&gt;To fix the coincidental correctness in our configuration file example, the solution is remarkably simple. You extract that magic string, pull it into a single shared variable, and reference that variable in all ten locations.&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="c1"&gt;// Correct by coincidence&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;config_init.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Correct by design&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CONFIG_FILE_PATH&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;config_init.json&lt;/span&gt;&lt;span class="dl"&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;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CONFIG_FILE_PATH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By doing this, you've moved the responsibility of correctness away from human memory and handed it to the structure of the code itself. If the filename needs to change, you update it in exactly one place. If you typo the variable name, the compiler or linter yells at you immediately.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are the main risks of coincidental correctness?
&lt;/h2&gt;

&lt;p&gt;The primary risk is creating a brittle codebase where minor updates trigger unpredictable, cascading failures. This ultimately destroys developer confidence, slows down feature delivery, and turns routine refactoring into a high-risk operation.&lt;/p&gt;

&lt;p&gt;When a codebase relies on coincidence, you will typically notice a few recurring symptoms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hidden Coupling:&lt;/strong&gt; Unrelated modules become secretly dependent on each other because they share the same hardcoded values.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Refactoring Fear:&lt;/strong&gt; Developers become terrified to change file names, database keys, or route paths because they don't know where else those strings are secretly hiding.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Silent Failures:&lt;/strong&gt; Because coincidences aren't structurally enforced, regressions often are not caught until runtime, sometimes making it all the way to production.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Onboarding Friction:&lt;/strong&gt; New engineers have no way of knowing the unspoken rules of the codebase, making it easy for them to accidentally break things when adding new features.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How do I spot code that is correct by coincidence?
&lt;/h3&gt;

&lt;p&gt;Look for magic strings, magic numbers, or duplicated logic that appears in multiple files without a shared reference. If you find yourself using global search-and-replace to safely rename a concept in your application, you are likely looking at coincidental correctness.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is "correct by coincidence" the same as technical debt?
&lt;/h3&gt;

&lt;p&gt;It is a specific type of technical debt. While technical debt can encompass anything from poor overall architecture to missing documentation, coincidental correctness specifically refers to fragile implementation details that rely on unlinked elements perfectly aligning to function.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can unit tests prevent coincidental correctness?
&lt;/h3&gt;

&lt;p&gt;Not always. If you write your tests using the same scattered magic strings as your implementation code, your tests will pass, but the underlying design flaw remains. Tests prove the behavior works, but structural design proves it will &lt;em&gt;keep&lt;/em&gt; working when the system changes.&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>cleancode</category>
      <category>refactoring</category>
      <category>architecture</category>
    </item>
    <item>
      <title>How to Represent Money in Software</title>
      <dc:creator>Doogal Simpson</dc:creator>
      <pubDate>Tue, 23 Jun 2026 07:51:54 +0000</pubDate>
      <link>https://dev.to/doogal/how-to-represent-money-in-software-2lfl</link>
      <guid>https://dev.to/doogal/how-to-represent-money-in-software-2lfl</guid>
      <description>&lt;p&gt;&lt;strong&gt;Quick Answer: Never use floating-point numbers to represent money in software. Because floating-point math cannot perfectly represent base-10 decimals, it introduces microscopic inaccuracies that compound over time. I recommend using either a dedicated arbitrary-precision decimal library or storing currency as integers representing micro-units to guarantee exact calculations.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If I ask you how to represent £1.29 in code, your naive first instinct might be to just drop it into a floating-point variable. I see this often, but if you start representing money as floating points, you are going to introduce systemic inaccuracies into your application. If there is one thing I know for sure, it's that people really don't like having inaccuracies anywhere close to their money. Let's look at why this happens and what you should do instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why do floating-point numbers fail for currency calculations?
&lt;/h2&gt;

&lt;p&gt;Floating-point numbers fail because they use binary fractions to approximate base-10 decimals, meaning simple arithmetic often yields imprecise results. When you execute a long sequence of these operations, those tiny inaccuracies compound into real financial errors.&lt;/p&gt;

&lt;p&gt;I have a whole other video on why &lt;code&gt;0.1 + 0.2&lt;/code&gt; doesn't equal &lt;code&gt;0.3&lt;/code&gt; in most programming languages, but it's the classic example of this problem. If you run that calculation using standard floating-point operations, you'll get something like &lt;code&gt;0.30000000000000004&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="c1"&gt;// This is why floats and money don't mix&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;itemOne&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.1&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;itemTwo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.2&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;itemOne&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;itemTwo&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Evaluates to false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your business logic relies on checking if those two items total exactly 0.3, the code evaluates to false. Do this thousands of times across a financial system, and those compounding rounding errors eventually change the amount of money a user actually has.&lt;/p&gt;

&lt;h2&gt;
  
  
  What are the best methods to handle money in programming?
&lt;/h2&gt;

&lt;p&gt;The two reliable methods for handling money are using an arbitrary-precision decimal library or representing the currency as an integer. Decimal libraries give you exact math natively, while the integer method relies on fast, reliable whole-number operations.&lt;/p&gt;

&lt;p&gt;Most modern languages include built-in libraries capable of doing accurate decimal operations. These are great because they solve the problem entirely at the language level. However, they can be somewhat heavyweight and a bit slower to execute. If you want an alternative that keeps things lightweight and fast, you can use integers instead.&lt;/p&gt;

&lt;p&gt;Here is how the two viable strategies compare:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Built-in Decimal Libraries:&lt;/strong&gt; Provide perfect mathematical accuracy and high readability, but are heavier in memory and slower for the CPU to compute.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Integer Representation:&lt;/strong&gt; Extremely fast for the CPU to process and highly accurate, but requires manual mathematical scaling logic before rendering values to the UI.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  How do you implement the integer micro-unit pattern?
&lt;/h3&gt;

&lt;p&gt;To use the integer micro-unit pattern, you multiply the currency value by a large factor—like 100,000—so you only perform arithmetic on whole numbers. This pushes any unavoidable rounding errors from division operations so far down the decimal chain that they become completely irrelevant.&lt;/p&gt;

&lt;p&gt;Your first thought might be to just store pence or cents. So, instead of &lt;code&gt;1.29&lt;/code&gt;, you store &lt;code&gt;129&lt;/code&gt;. That works perfectly for basic addition and subtraction. However, as soon as you need to divide a bill or calculate a percentage, you get rounding errors on the penny.&lt;/p&gt;

&lt;p&gt;Instead, I recommend scaling the value up further into micro-units. For example, you represent £1.29 as &lt;code&gt;129,000&lt;/code&gt;. Adding and subtracting remains pure, fast integer math. If you eventually hit a rounding error during a complex division step, that error happens at such a microscopic decimal place that you simply don't care. Everything works, and your users' balances remain accurate.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Should I use a standard Decimal library or integers for my project?
&lt;/h3&gt;

&lt;p&gt;If your application doesn't have extreme performance constraints, I recommend sticking to your language's built-in Decimal library for safety and readability. Use integer micro-units if you are optimizing for processing speed or working in a highly distributed system where payload size matters.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do databases typically store money values?
&lt;/h3&gt;

&lt;p&gt;Most relational databases offer a &lt;code&gt;DECIMAL&lt;/code&gt; or &lt;code&gt;NUMERIC&lt;/code&gt; column type designed specifically for exact precision. If you use the integer micro-unit pattern in your application, you can simply store those scaled values in a standard &lt;code&gt;BIGINT&lt;/code&gt; database column.&lt;/p&gt;

&lt;h3&gt;
  
  
  What happens to fractional cents when dividing payments?
&lt;/h3&gt;

&lt;p&gt;When splitting a value (like dividing 10 cents three ways), standard accounting practice dictates allocating the base divided amount to all parties, then distributing the remainder penny by penny until the remainder is zero. This ensures no money is ever created or destroyed by rounding.&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>architecture</category>
      <category>programmingtips</category>
      <category>computerscience</category>
    </item>
    <item>
      <title>How DRAM Works: The 64ms Memory Refresh Cycle</title>
      <dc:creator>Doogal Simpson</dc:creator>
      <pubDate>Sun, 21 Jun 2026 10:34:16 +0000</pubDate>
      <link>https://dev.to/doogal/how-dram-works-the-64ms-memory-refresh-cycle-1kkn</link>
      <guid>https://dev.to/doogal/how-dram-works-the-64ms-memory-refresh-cycle-1kkn</guid>
      <description>&lt;p&gt;&lt;strong&gt;Quick Answer: Dynamic Random Access Memory (DRAM) stores data as 1s and 0s using microscopic capacitors. Because these capacitors constantly leak electrons, they lose their charge almost instantly. To prevent total data corruption, your device's hardware must scan, evaluate, and refresh every single memory cell roughly every 64 milliseconds.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Right now, the device you're reading this on is constantly fighting off amnesia. &lt;/p&gt;

&lt;p&gt;Whether it's your smartphone or your laptop, the data sitting in its active memory is tens of milliseconds away from vanishing. I often take for granted that when I write a bit to memory, it just stays there. But at the hardware level, that isn't true at all. Let's look at the technical trade-off that keeps our volatile memory alive and why your RAM has to work incredibly hard just to remember a simple &lt;code&gt;1&lt;/code&gt; or &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does DRAM need to be constantly refreshed?
&lt;/h2&gt;

&lt;p&gt;DRAM must be refreshed because it stores data in tiny capacitors that constantly leak electrical charge. Without a refresh cycle scanning and recharging these cells, the stored electrons would escape, and the data would disappear completely.&lt;/p&gt;

&lt;p&gt;To understand why this happens, look at how simple a DRAM cell actually is. Every bit of data lives inside a cell made up of just two components: a single transistor and a single capacitor. &lt;/p&gt;

&lt;p&gt;Think of the capacitor as a microscopic bucket holding a charge. When the bucket is full of electrons, it represents a binary &lt;code&gt;1&lt;/code&gt;. When it is empty, it represents a binary &lt;code&gt;0&lt;/code&gt;. The problem is that this bucket has a leak. A fully charged capacitor holds about 30,000 electrons. While 30,000 sounds like a massive number in the macro world, down at the quantum scale, it's practically nothing. These electrons are incredibly small, sneaky, and constantly trying to escape. If the system just writes a &lt;code&gt;1&lt;/code&gt; and walks away, that bucket will quickly drain, flipping the &lt;code&gt;1&lt;/code&gt; into a &lt;code&gt;0&lt;/code&gt; and destroying your data.&lt;/p&gt;

&lt;h2&gt;
  
  
  How exactly does the 64-millisecond memory refresh work?
&lt;/h2&gt;

&lt;p&gt;Roughly every 64 milliseconds, the memory controller scans every single cell in your RAM to check its current charge level. It compares the remaining electrons against a strict midpoint threshold to determine what the data was supposed to be, and then restores that cell to its correct state.&lt;/p&gt;

&lt;p&gt;Because the hardware knows the capacitors are leaking, it enforces a never-ending patrol. Let's walk through a hypothetical scenario of how this actually plays out at the hardware level.&lt;/p&gt;

&lt;p&gt;Imagine a single memory cell was written as a &lt;code&gt;1&lt;/code&gt; (fully charged with 30,000 electrons). A few milliseconds pass, and those sneaky electrons start leaking out. The memory controller swings by for its 64-millisecond checkup. It has a programmed midpoint—let's say 15,000 electrons.&lt;/p&gt;

&lt;p&gt;When it reads the cell, it finds 16,000 electrons left. Because 16,000 is above the midpoint threshold, the controller assumes, "You must be a 1," and immediately pumps the cell back up to the full 30,000 electrons. But if the controller had been delayed and found only 14,000 electrons, it would fall below the midpoint. The controller would wrongly assume the cell was intended to be a &lt;code&gt;0&lt;/code&gt; and discharge it, resulting in a flipped bit.&lt;/p&gt;

&lt;p&gt;Here is the exact decision tree the memory controller uses during a refresh sweep:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Cell charge &amp;gt; Midpoint threshold:&lt;/strong&gt; The controller assumes the bit is a &lt;code&gt;1&lt;/code&gt; and tops the capacitor back up to full charge (e.g., 30,000 electrons).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Cell charge &amp;lt; Midpoint threshold:&lt;/strong&gt; The controller assumes the bit is a &lt;code&gt;0&lt;/code&gt; and leaves the cell completely discharged.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How does the system handle refreshing billions of cells?
&lt;/h2&gt;

&lt;p&gt;The hardware executes this evaluate-and-recharge cycle across the entire memory bank approximately 15 to 16 times every second. It relies on a dedicated memory controller to scan every single cell it is responsible for without completely stalling the main processor.&lt;/p&gt;

&lt;p&gt;When you scale this operation up to modern hardware, the numbers are staggering. If you have a laptop with 16 gigabytes of RAM, we are talking about hundreds of billions of individual transistor-capacitor pairs. The system has to individually check the electron count of every single one of those cells, compare it to the midpoint threshold, and rewrite the charge.&lt;/p&gt;

&lt;p&gt;It does this entire sweep roughly every 64 milliseconds. That is an immense amount of background work happening purely to maintain state. If this cycle pauses for even a moment—if the controller misses a beat and the electrons leak past that halfway mark—the hardware simply forgets everything it was holding.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What happens to DRAM data when the power is turned off?
&lt;/h3&gt;

&lt;p&gt;Because DRAM relies on an active power source to run its 64-millisecond refresh cycles, turning off the power stops the refresh immediately. The capacitors leak their remaining charge in a fraction of a second, and all data is permanently lost.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does the DRAM refresh cycle impact CPU performance?
&lt;/h3&gt;

&lt;p&gt;Yes, while a memory bank is actively being refreshed, it cannot be read from or written to by the CPU. Modern controllers handle this by staggering refreshes across different banks to keep the overall processor stall times as minimal as possible.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can the 64ms DRAM refresh rate change based on temperature?
&lt;/h3&gt;

&lt;p&gt;Yes. Capacitors leak charge much faster when they get hot. In many modern systems, if the RAM operating temperature rises significantly under a heavy workload, the memory controller will dynamically double the refresh rate to 32 milliseconds to prevent the electrons from draining below the midpoint threshold too quickly.&lt;/p&gt;

</description>
      <category>computerscience</category>
      <category>hardware</category>
      <category>computerarchitecture</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Engineering Intuition: Knowing When to Refactor</title>
      <dc:creator>Doogal Simpson</dc:creator>
      <pubDate>Sun, 21 Jun 2026 10:33:38 +0000</pubDate>
      <link>https://dev.to/doogal/engineering-intuition-knowing-when-to-refactor-1fp8</link>
      <guid>https://dev.to/doogal/engineering-intuition-knowing-when-to-refactor-1fp8</guid>
      <description>&lt;p&gt;&lt;strong&gt;Quick Answer:&lt;/strong&gt; That sudden feeling of dread when opening a code file is a highly valuable engineering signal from your subconscious "lizard brain." Before your logical mind processes the mess, your gut recognizes technical debt. Use this instinct to identify areas needing refactoring, but balance it with pragmatic "good enough engineering" to avoid endless rewrites.&lt;/p&gt;

&lt;p&gt;We have all been there. You click on a file in your editor, the syntax highlighting kicks in, the scrollbar shrinks to a microscopic sliver, and an immediate pit forms in your stomach.&lt;/p&gt;

&lt;p&gt;You haven't even read a single function yet, but you already know you don't want to be here.&lt;/p&gt;

&lt;p&gt;Sometimes, I think you need to follow that lizard brain when you are reading code. It is a primitive, instinctual reaction, and in software engineering, it is one of the most reliable signals you have. Before your higher brain can logically parse the variables, dependencies, and control flows, your subconscious has already pattern-matched the file as a danger zone. Instead of ignoring that dread, you can use it to guide your technical decisions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why do developers get a feeling of dread when reading code?
&lt;/h2&gt;

&lt;p&gt;Developers feel dread because their subconscious pattern-matching engine recognizes high-risk, overly complex code before their conscious mind can process it. It is a rapid, biological warning system alerting you to massive technical debt or fragile architecture.&lt;/p&gt;

&lt;p&gt;Imagine your team is taking over an older e-commerce platform. You open the main checkout module and instantly feel a wave of anxiety. This happens because your brain is reacting to visual cues—deep nesting, endless blocks of text without whitespace, or a massive import list—that historically correlate with painful debugging sessions.&lt;/p&gt;

&lt;p&gt;When you get this feeling, pause. Instead of forcing yourself to immediately build your new feature on top of a terrible foundation, sit there and let your higher brain catch up. Analyze the file. Usually, you will quickly spot exactly what triggered your lizard brain: a god object, tangled state management, or tightly coupled logic. Recognizing this early gives you a chance to clean up the workspace before you accidentally break it.&lt;/p&gt;

&lt;h2&gt;
  
  
  When should I refactor bad code versus leaving it alone?
&lt;/h2&gt;

&lt;p&gt;You should refactor when the bad code actively blocks your current task, makes testing impossible, or introduces high risk to an upcoming change. If the code is a mess but is rarely touched and functions perfectly in production, it is usually better to leave it alone to prioritize delivering actual value.&lt;/p&gt;

&lt;p&gt;Constantly refactoring everything just because a file gives you a bad vibe is a trap. If you rewrite every ugly file you encounter, you will never ship anything. This is especially true if you are a newer engineer. When you are just starting out, almost &lt;em&gt;every&lt;/em&gt; complex file looks intimidating and induces dread. You have to learn the difference between code that is genuinely broken and code that is just unfamiliar.&lt;/p&gt;

&lt;p&gt;To help decide when to act on that feeling of dread, use this decision framework:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Active Modification:&lt;/strong&gt; Are you required to add new logic to this specific file today? (If yes, consider refactoring).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Blast Radius:&lt;/strong&gt; Will touching this code likely break unrelated features? (If yes, add test coverage and refactor first).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Frequency of Change:&lt;/strong&gt; Is this a file the team touches weekly, or hasn't it been modified in three years? (If rarely touched, leave it alone).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Deliverable Timeline:&lt;/strong&gt; Do you have the buffer to clean this up without missing a hard product deadline? (If no, log it as technical debt and move on).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How do I balance code quality with fast delivery?
&lt;/h2&gt;

&lt;p&gt;Balancing quality and delivery requires treating refactoring as a targeted, incremental cost rather than a separate, massive project. You achieve this through "good enough engineering," where you intentionally balance the strict pursuit of clean code against the immediate need to ship deliverables.&lt;/p&gt;

&lt;p&gt;Refactoring takes time, and time is a tangible cost to your business. You cannot fix everything. Good enough engineering means accepting that some files will remain ugly.&lt;/p&gt;

&lt;p&gt;Let's say you have a legacy analytics service that processes background events. It is messy, but it works flawlessly and no one needs to update its core logic. Spending three days making it beautiful provides zero value to the end user. However, if you are actively working inside a tangled billing module to add a new payment gateway, spending a few hours untangling the specific functions you need to interact with is a highly pragmatic investment.&lt;/p&gt;

&lt;p&gt;Listen to your lizard brain when it warns you of danger, but let your logical brain decide if fighting that danger is actually worth the time.&lt;/p&gt;

&lt;h2&gt;
  
  
  FAQ
&lt;/h2&gt;

&lt;h3&gt;
  
  
  How do you know if code is actually bad or if you are just inexperienced?
&lt;/h3&gt;

&lt;p&gt;Inexperience often makes standard, complex patterns feel overwhelming. The best way to check is to consult a senior engineer or run a static analysis tool. If the complexity is necessary for the domain, it is just a learning curve; if the logic is genuinely tangled and untested, it is bad code.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is "good enough engineering"?
&lt;/h3&gt;

&lt;p&gt;Good enough engineering is a pragmatic approach to software development where you write code that is secure, functional, and maintainable without obsessing over perfect architecture. It is the active balancing of technical quality against business deliverables and deadlines.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I justify refactoring time to product managers?
&lt;/h3&gt;

&lt;p&gt;Frame refactoring in terms of business value and risk mitigation, not code aesthetics. Explain that cleaning up a specific module will reduce bugs in the upcoming release and significantly speed up the time it takes to add the next feature.&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>refactoring</category>
      <category>cleancode</category>
      <category>codequality</category>
    </item>
    <item>
      <title>Why Code Documentation Is a First-Class Citizen</title>
      <dc:creator>Doogal Simpson</dc:creator>
      <pubDate>Sat, 20 Jun 2026 08:24:23 +0000</pubDate>
      <link>https://dev.to/doogal/why-code-documentation-is-a-first-class-citizen-1a34</link>
      <guid>https://dev.to/doogal/why-code-documentation-is-a-first-class-citizen-1a34</guid>
      <description>&lt;p&gt;&lt;strong&gt;Quick Answer: Elite software teams treat documentation as a first-class citizen. API specs and inline comments are just as vital as the code itself. Without explaining the "why" and "how," I've seen teams frustrate human users, confuse AI agents, and rely on fragile oral histories that break the moment a colleague leaves.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I look at a lot of codebases, and the defining trait of a truly elite software team isn't just the code they write—it's how they treat their documentation. The best teams treat documentation as a first-class citizen. The API specs you write and the comments scattered throughout your files are fundamentally as important as the logic itself. If you only write code, your software is only half-finished.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why should developers treat API documentation as code?
&lt;/h2&gt;

&lt;p&gt;Treating API documentation as code ensures your software is actually usable. If you only document paths and schemas without the surrounding context, I can almost guarantee developers will either abandon your API entirely or use it incorrectly and flood your queue with support tickets.&lt;/p&gt;

&lt;p&gt;It is not enough to just list the endpoints, inputs, and outputs. A good API spec needs extra flavor. I always look for specs that answer: &lt;em&gt;What is happening here? Why is it happening? How should I actually use this in a workflow?&lt;/em&gt; Imagine your team is building a fintech application. If you hand over a raw schema without explaining the specific sequence of calls required to clear a transaction, the integrating developer is going to be completely lost.&lt;/p&gt;

&lt;p&gt;Generating the baseline boilerplate is usually pretty easy. There are plenty of deterministic tools that sit within your codebase and spit out the foundation. But your job is to add the flavor and context on top of that baseline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is the "oral history" of a codebase dangerous?
&lt;/h2&gt;

&lt;p&gt;Relying on oral history means the reasons behind weird technical decisions only exist in the minds of your teammates. If those colleagues get sick, go on vacation, or leave the company, that context disappears instantly.&lt;/p&gt;

&lt;p&gt;I constantly see teams operating on this concept of an oral history to pass down knowledge. You open up a piece of code, spot something bizarre, and ask the person next to you why it was built that way. They'll usually say something like, "Oh yeah, we have to do that because the payment processor has a really awkward way of dealing with input," or "That engineer really wanted to try a wacky framework and we haven't had a chance to refactor it yet."&lt;/p&gt;

&lt;p&gt;If none of that context is in the code, you are flying blind the second that colleague is unavailable. This is exactly why I advocate that inline code comments must describe the &lt;em&gt;why&lt;/em&gt;, not the &lt;em&gt;how&lt;/em&gt;. The code already explains the execution steps. Your comments need to capture that oral history before it walks out the door.&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="c1"&gt;// BAD: Explaining the "how"&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;activeUsers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;u&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mi"&gt;9999&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// GOOD: Explaining the "why"&lt;/span&gt;
&lt;span class="c1"&gt;// ID 9999 is a hardcoded test account used by the legacy payment&lt;/span&gt;
&lt;span class="c1"&gt;// processor. It must be excluded from daily billing batches.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;activeUsers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;users&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;u&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mi"&gt;9999&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How do AI agents change the way we write documentation?
&lt;/h2&gt;

&lt;p&gt;Humans are no longer the only consumers of your APIs and codebases. AI agents are increasingly interacting with your software, and they rely entirely on rich context and descriptive comments to figure out how to execute tasks accurately.&lt;/p&gt;

&lt;p&gt;Giving an agent a naked API schema is like giving someone a steering wheel without the car. If a human struggles to infer the purpose of an undocumented endpoint, an agent will completely fail. They need all of this context explicitly written out to be able to actually use your software.&lt;/p&gt;

&lt;p&gt;To make sure your software is ready for both humans and agents, I recommend structuring your documentation with a mix of structural and contextual data:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Base Schemas:&lt;/strong&gt; Deterministic definitions of paths, inputs, and outputs (easily generated by tools).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Workflow Context:&lt;/strong&gt; Explanations of how endpoints chain together to achieve a real business goal.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;The "Why" Behind the "How":&lt;/strong&gt; Inline comments explaining bizarre constraints, technical debt, or external system quirks.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Semantic Instructions:&lt;/strong&gt; Plain-text descriptions specifically designed to help LLMs understand the exact purpose of a function.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What tools can I use to auto-generate API documentation?
&lt;/h3&gt;

&lt;p&gt;Tools like Swagger, Redoc, and tsoa can automatically generate baseline deterministic documentation directly from your code annotations. These handle the schemas, but you still need to manually add the contextual explanations. Without your added flavor, the auto-generated docs are rarely enough for complex integrations.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do I stop writing redundant code comments?
&lt;/h3&gt;

&lt;p&gt;Stop describing what the syntax is doing and start focusing on the business logic constraints. Instead of writing a comment that says you are looping through an array, explain that you are batching the payload because a downstream service drops requests larger than 5MB. If the comment just translates the code into English, delete it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do AI agents actually read API endpoint descriptions?
&lt;/h3&gt;

&lt;p&gt;Yes, agents rely heavily on the textual descriptions attached to your endpoints and functions. When using LLMs for tool calling or orchestration, the descriptions you provide directly dictate whether the agent chooses the right tool for the job. Detailed, context-heavy descriptions dramatically reduce agent errors.&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>documentation</category>
      <category>cleancode</category>
      <category>aiagents</category>
    </item>
    <item>
      <title>Agile is a Mindset, Not Endless Sprint Meetings</title>
      <dc:creator>Doogal Simpson</dc:creator>
      <pubDate>Fri, 19 Jun 2026 13:21:19 +0000</pubDate>
      <link>https://dev.to/doogal/agile-is-a-mindset-not-endless-sprint-meetings-180d</link>
      <guid>https://dev.to/doogal/agile-is-a-mindset-not-endless-sprint-meetings-180d</guid>
      <description>&lt;p&gt;&lt;strong&gt;Quick Answer: I firmly believe true Agile is a mindset focused on shipping small, incremental changes based on direct user feedback to navigate uncertainty. If I see a team spending more time sitting in sprint planning and retrospective meetings than actually writing working code, I know they are executing a rigid administrative process, not practicing Agile.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I often talk to engineers who stare at ticketing boards for so long they forget what their IDE looks like. If I find myself spending all of my time doing stand-ups, sprint planning sessions, and retrospectives—and significantly less time actually writing code—my immediate thought is always that my team isn't doing Agile.&lt;/p&gt;

&lt;p&gt;It is incredibly common to confuse the ceremonies of Agile for the actual methodology. Let's break down what Agile actually means, why I see software engineers getting buried in meetings, and why the business side of a company probably despises the true version of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the actual definition of Agile in software development?
&lt;/h2&gt;

&lt;p&gt;Agile is a development mindset focused on delivering small, fully functional, incremental changes in direct response to user feedback. It is a strategy designed specifically for handling uncertainty, allowing teams to pivot quickly rather than following a rigid, predefined master plan.&lt;/p&gt;

&lt;p&gt;I like to think of software development like driving a car. If I am driving on a perfectly straight, empty highway with total visibility, I can lock in my cruise control and just go. That is fixed-scope development. True Agile, however, is like driving down a winding mountain road in heavy fog. I cannot lock in a master plan. I have to make constant, tiny adjustments to the steering wheel based on exactly what I see directly in front of my bumper. &lt;/p&gt;

&lt;p&gt;To drive in that fog, I need a close, almost hugging relationship with the users. I have to figure out what they are feeling, understand their workflow, and deliver a small piece of working code to see if it immediately solves their problem. &lt;/p&gt;

&lt;h2&gt;
  
  
  Why do software engineers spend so much time in Agile meetings?
&lt;/h2&gt;

&lt;p&gt;Engineers get trapped in endless meetings because management often mistakes the administrative ceremonies of Agile—like planning and retrospectives—for the actual methodology. Implementing the meetings without the fast, incremental shipping mindset simply creates bloated process overhead rather than true agility.&lt;/p&gt;

&lt;p&gt;I always remind developers that Agile is not a process. I cannot just set up a bunch of calendar invites and declare an engineering department Agile. When the focus shifts to maintaining the process rather than delivering the code, everything grinds to a halt. &lt;/p&gt;

&lt;p&gt;Here is a breakdown of how I distinguish a real Agile mindset from fake Agile process overhead:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Code over ceremonies:&lt;/strong&gt; A true Agile setup prioritizes writing code and delivering working software, whereas a fake process focuses heavily on managing tickets and attending meetings.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Direct user interaction:&lt;/strong&gt; Agile requires a tight, constant feedback loop with actual users, rather than relying on feedback filtered through layers of product management.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Continuous releases:&lt;/strong&gt; Success means delivering frequent, small, and functional incremental changes, not massive feature drops at the end of a long, rigid sprint.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Adaptability:&lt;/strong&gt; Real Agile means pivoting immediately when circumstances change, rather than sticking strictly to a quarterly roadmap no matter what.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When should a development team use the Agile methodology?
&lt;/h2&gt;

&lt;p&gt;Agile should be used strictly when you do not know exactly what the final product should look like and need to figure it out through continuous user testing. It thrives in highly competitive environments where requirements change rapidly and pivoting is essential.&lt;/p&gt;

&lt;p&gt;Let's say your team is building a fintech app, and suddenly a rival firm drops a disruptive new feature. Agile is the perfect vehicle for this scenario. I can instantly shift my engineering priorities, ship a small counter-feature, and see how the user base reacts. The methodology is built entirely for these rapid pivots.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why do business stakeholders and companies hate real Agile?
&lt;/h2&gt;

&lt;p&gt;Companies generally dislike real Agile because it fundamentally lacks rigid delivery dates and guaranteed, upfront feature lists. Traditional business models rely on exchanging a fixed financial investment for a specific, guaranteed result by an exact deadline, which directly contradicts the core purpose of Agile.&lt;/p&gt;

&lt;p&gt;The corporate world naturally hates the phrase, "We don't know when it will be done." Executives want to say, "I am putting an investment of this much money into this product, and I demand to have this exact result at the end of this particular time period."&lt;/p&gt;

&lt;p&gt;But if I am doing Agile, the lack of a fixed deadline is the entire point. If I knew exactly what I needed to build upfront, I wouldn't be doing Agile—I would just build the predetermined application. Agile exists explicitly for the times when I don't know what I am building and I need the freedom to figure it out step-by-step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions About Agile Development
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Can Agile work with fixed budgets and strict deadlines?&lt;/strong&gt;&lt;br&gt;
It struggles heavily under these constraints. True Agile requires flexibility in either scope or timeline. If the budget, deadline, and feature requirements are all strictly locked upfront, I consider that a Waterfall environment, even if the work is organized into two-week sprints.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Do developers still need sprint planning in true Agile?&lt;/strong&gt;&lt;br&gt;
Yes, but it should be incredibly brief. Planning should only cover the small, incremental changes the team is about to ship. If planning meetings take hours and look months into the future, that is roadmap planning, not an Agile sprint.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How do you measure success in Agile software engineering?&lt;/strong&gt;&lt;br&gt;
I measure success by delivering working software that actively solves user problems. It is not measured by story points burned down, velocity charts, or how efficiently a team navigates a ticketing board.&lt;/p&gt;

</description>
      <category>agile</category>
      <category>softwareengineering</category>
      <category>scrum</category>
      <category>agilemethodology</category>
    </item>
    <item>
      <title>Good Enough Software: Balancing Quality and Time to Market</title>
      <dc:creator>Doogal Simpson</dc:creator>
      <pubDate>Fri, 19 Jun 2026 13:20:13 +0000</pubDate>
      <link>https://dev.to/doogal/good-enough-software-balancing-quality-and-time-to-market-1moj</link>
      <guid>https://dev.to/doogal/good-enough-software-balancing-quality-and-time-to-market-1moj</guid>
      <description>&lt;p&gt;&lt;strong&gt;Quick Answer: The pursuit of perfect software often leads to delayed launches, burned budgets, and building the wrong product entirely. I always say that by adopting a "good enough" engineering philosophy, you trade absolute perfection for faster time-to-market and real-world user feedback. However, this rule goes out the window for life-or-death systems.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Engineers naturally want to write pristine, bulletproof code. I get it. I always want to account for every edge case, optimize every database query, and ensure my architecture can handle millions of users on day one. But here's the harsh reality I've come to accept: waiting to write absolutely perfect software might actually be the thing that bankrupts the company you work for.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is "good enough" software development?
&lt;/h2&gt;

&lt;p&gt;"Good enough" software development is the strategic trade-off between code quality, budget, and time-to-market. It means shipping a functional, safe product that solves the core problem, rather than delaying the release to polish non-essential features.&lt;/p&gt;

&lt;p&gt;Let's say your team is building a new B2B SaaS analytics dashboard. If you spend five months trying to perfectly abstract the database layer and build a custom caching engine, your competitors will beat you to market. If you spend three weeks shipping a slightly slower, direct-query version that actually shows customers their data, you're in the game. You're accepting a manageable level of technical debt to validate that the software actually needs to exist.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does shipping fast help find product-market fit?
&lt;/h2&gt;

&lt;p&gt;Releasing software quickly lets you test your assumptions against real user behavior before burning through your runway. Real users act as the ultimate filter; if you build the wrong thing, they'll tell you immediately by simply abandoning the product.&lt;/p&gt;

&lt;p&gt;Especially in the early days of a product lifecycle, you don't actually know what you're building yet. You just have a hypothesis. I firmly believe that if you spend eighteen months in a vacuum building the "perfect" solution to a problem nobody has, you've just wasted a massive amount of time and money. Users are brutally honest. They usually won't write you a detailed GitHub issue about why your UX is confusing or your feature set is lacking. They just bounce. Getting software out faster tightens that feedback loop, so you find out incredibly fast whether you're on the right track or if you need to pivot entirely.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Trade-Off Matrix
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;The "Good Enough" Approach&lt;/th&gt;
&lt;th&gt;The "Perfect" Approach&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Time to Market&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Days or weeks (rapid validation)&lt;/td&gt;
&lt;td&gt;Months or years (delayed entry)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Feedback Loop&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Driven by real-world user data&lt;/td&gt;
&lt;td&gt;Driven by internal assumptions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Financial Risk&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Low initial budget burn&lt;/td&gt;
&lt;td&gt;High burn rate before launch&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Technical Debt&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Expected and managed iteratively&lt;/td&gt;
&lt;td&gt;Minimized, but at a massive time cost&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  When is "good enough" software a terrible idea?
&lt;/h2&gt;

&lt;p&gt;Shipping "good enough" software is a catastrophic mistake when working in high-stakes industries where failures result in physical harm, massive financial collapse, or loss of life. In these domains, the baseline requirement for "good enough" is zero bugs.&lt;/p&gt;

&lt;p&gt;Context is everything. If you're building a consumer to-do list app, a server crash is an inconvenience. But imagine you're writing software for autonomous vehicles, medical pacemakers, or navigation systems for a space station—a crash is fatal. In those environments, I always remind developers that you don't get to rely on users hitting the refresh button when things freeze. The philosophy completely flips. You can't A/B test a braking system on a highway to see if users engage with it. If the stakes are life and death, absolute perfection is the only standard.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What is the difference between an MVP and good enough software?
&lt;/h3&gt;

&lt;p&gt;An MVP (Minimum Viable Product) defines the absolute smallest feature set required to launch and test an idea. "Good enough" refers to the engineering standard and code quality I apply to those specific features.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do developers manage technical debt during rapid development?
&lt;/h3&gt;

&lt;p&gt;I recommend managing rapid development by treating technical debt as a temporary loan to buy time-to-market. The key is tracking those architectural shortcuts and scheduling dedicated refactoring time once the product's value is validated by users.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can you refactor good enough code later?
&lt;/h3&gt;

&lt;p&gt;Yes, and that's the entire point of the strategy. Once a product gains traction and proves its market fit, you can confidently allocate engineering resources to stabilize the architecture and pay down the accumulated technical debt.&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>bestpractices</category>
      <category>startup</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
