<?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: Pablo Infante</title>
    <description>The latest articles on DEV Community by Pablo Infante (@pablo_infante).</description>
    <link>https://dev.to/pablo_infante</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%2F4225046%2Fb9868604-4f79-41bd-b344-f46d3c06867f.png</url>
      <title>DEV Community: Pablo Infante</title>
      <link>https://dev.to/pablo_infante</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/pablo_infante"/>
    <language>en</language>
    <item>
      <title>SHA-256 is not encryption — and that's the whole point</title>
      <dc:creator>Pablo Infante</dc:creator>
      <pubDate>Tue, 03 Nov 2026 22:16:54 +0000</pubDate>
      <link>https://dev.to/pablo_infante/sha-256-is-not-encryption-and-thats-the-whole-point-ohe</link>
      <guid>https://dev.to/pablo_infante/sha-256-is-not-encryption-and-thats-the-whole-point-ohe</guid>
      <description>&lt;p&gt;The single most common mix-up I see in code reviews: treating a hash like encryption. They are different tools for different jobs, and the difference is the reason hashing is useful at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  The one distinction that matters
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Encryption&lt;/strong&gt; is &lt;strong&gt;two-way&lt;/strong&gt;: with the key, ciphertext → plaintext. It's for keeping something secret that you later need back.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hashing&lt;/strong&gt; is &lt;strong&gt;one-way&lt;/strong&gt;: input → fixed-size digest, and there is &lt;strong&gt;no "un-hash."&lt;/strong&gt; SHA-256 always returns 256 bits (64 hex chars), whether you feed it one byte or a gigabyte. You cannot recover the input from the digest — not "it's hard," it's &lt;strong&gt;not a reversible operation&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you ever read that a hash "can be decrypted," that text is wrong. What attackers do instead is &lt;em&gt;guess-and-check&lt;/em&gt;: hash candidate inputs and compare digests (that's what a rainbow table or a password cracker is). That's why hashing a password needs salt + a slow KDF (bcrypt/argon2), not raw SHA-256.&lt;/p&gt;

&lt;h2&gt;
  
  
  The properties that make it useful
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Deterministic:&lt;/strong&gt; same input → same digest, every time. That's what lets you verify.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Avalanche effect:&lt;/strong&gt; change one bit of input and ~half the output bits flip. &lt;code&gt;hello&lt;/code&gt; and &lt;code&gt;hellp&lt;/code&gt; produce completely unrelated digests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fixed length:&lt;/strong&gt; 256 bits regardless of input size.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Collision-resistant:&lt;/strong&gt; finding two inputs with the same SHA-256 is computationally infeasible (unlike SHA-1, which is broken for collisions — migrate off it).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where you actually use it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;File integrity:&lt;/strong&gt; publish a download's SHA-256; a user re-hashes the file and compares. Match = not tampered/corrupted.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Change detection / caching:&lt;/strong&gt; hash content to make an ETag or a cache key.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deduplication:&lt;/strong&gt; identical content → identical digest.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Commit/version identity:&lt;/strong&gt; content-addressed storage (Git is the famous example).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not for: storing passwords with raw SHA-256 (too fast — use argon2/bcrypt), and never as a stand-in for encryption.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check a digest quickly
&lt;/h2&gt;

&lt;p&gt;When I just need to verify a string's hash or compare against a published checksum, &lt;a href="https://toolonium.com/en/tools/hash-generator/" rel="noopener noreferrer"&gt;Toolonium's hash generator&lt;/a&gt; does it in the browser — type or paste, get the SHA-256 (and other algorithms), nothing uploaded. Good for the "does this checksum match?" moment without dropping into a shell.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;Reach for a hash when you need to &lt;strong&gt;verify or fingerprint&lt;/strong&gt;, for encryption when you need to &lt;strong&gt;get the data back&lt;/strong&gt;. They're not interchangeable, and "decrypt a hash" isn't a thing. Need a quick digest? &lt;a href="https://toolonium.com/en/tools/hash-generator/" rel="noopener noreferrer"&gt;Generate one here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Still seeing raw-SHA-256 password storage in the wild? Share the worst one you've had to fix.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>webdev</category>
      <category>crypography</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Password entropy, explained with the one number that matters</title>
      <dc:creator>Pablo Infante</dc:creator>
      <pubDate>Tue, 03 Nov 2026 22:11:13 +0000</pubDate>
      <link>https://dev.to/pablo_infante/password-entropy-explained-with-the-one-number-that-matters-i60</link>
      <guid>https://dev.to/pablo_infante/password-entropy-explained-with-the-one-number-that-matters-i60</guid>
      <description>&lt;p&gt;"Use a strong password" is useless advice without a definition of strong. The definition that actually holds up is &lt;strong&gt;entropy&lt;/strong&gt; — how many bits of randomness the password has — and once you see the math, most "complex-looking" passwords turn out weak.&lt;/p&gt;

&lt;h2&gt;
  
  
  The formula
&lt;/h2&gt;

&lt;p&gt;Entropy in bits is: &lt;code&gt;bits = length × log2(poolSize)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;poolSize&lt;/code&gt; is how many characters each position could have been:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;lowercase only → 26 → ~4.7 bits/char&lt;/li&gt;
&lt;li&gt;lower + upper → 52 → ~5.7 bits/char&lt;/li&gt;
&lt;li&gt;lower + upper + digits → 62 → ~5.95 bits/char&lt;/li&gt;
&lt;li&gt;+ symbols → ~95 → ~6.55 bits/char&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So a &lt;strong&gt;16-character&lt;/strong&gt; password using all four classes is about &lt;code&gt;16 × 6.55 ≈ 105&lt;/code&gt; bits in theory. A rough rule of thumb: &lt;strong&gt;&amp;lt; 50 bits is weak, 70–100 is solid, 100+ is strong&lt;/strong&gt; for anything that could be attacked offline.&lt;/p&gt;

&lt;p&gt;The punchline developers underestimate: &lt;strong&gt;length beats symbol-soup&lt;/strong&gt;. &lt;code&gt;Tr0ub4dour&amp;amp;3&lt;/code&gt; (clever substitutions, 12 chars) has less entropy than a longer passphrase, because entropy scales linearly with length but only logarithmically with pool size. Add characters, not cleverness.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why "human-chosen" ruins the math
&lt;/h2&gt;

&lt;p&gt;The formula assumes each character is &lt;em&gt;independently random&lt;/em&gt;. A human picking &lt;code&gt;P@ssw0rd2024!&lt;/code&gt; doesn't get 13×6.55 bits — attackers' wordlists and mangling rules know that pattern, so the effective entropy collapses. The entropy number is only honest if the generator is actually random.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generate one and see the number
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://toolonium.com/en/tools/password-generator/" rel="noopener noreferrer"&gt;Toolonium's password generator&lt;/a&gt; does exactly this and shows you the entropy as you tune it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Length 8–64&lt;/strong&gt; (slider), default 16.&lt;/li&gt;
&lt;li&gt;Toggle uppercase, lowercase, digits, symbols.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"No ambiguous characters"&lt;/strong&gt; option to drop &lt;code&gt;l/1/I/O/0&lt;/code&gt; — useful when the password will be read aloud or typed from a screen.&lt;/li&gt;
&lt;li&gt;It reports &lt;strong&gt;estimated entropy in bits&lt;/strong&gt; and a strength label, so you're choosing with a number, not a vibe.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It runs &lt;strong&gt;entirely in the browser&lt;/strong&gt; — the generated password is never sent anywhere, which is the only acceptable design for a tool whose whole job is producing a secret. (As always: for app-level generation use your platform's CSPRNG, e.g. &lt;code&gt;crypto.getRandomValues&lt;/code&gt;; this is for the quick human-facing case — a new DB user, a seed, a one-off account.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;Stop eyeballing passwords. Pick a target of ~100 bits, get there with &lt;strong&gt;length&lt;/strong&gt; first, and use a generator that's random and local so the entropy estimate is real. &lt;a href="https://toolonium.com/en/tools/password-generator/" rel="noopener noreferrer"&gt;Try it here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What entropy floor does your team enforce for service accounts? Curious where people draw the line.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>webdev</category>
      <category>passwords</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Why I reach for a UUID v4 instead of an auto-increment ID</title>
      <dc:creator>Pablo Infante</dc:creator>
      <pubDate>Tue, 03 Nov 2026 22:10:06 +0000</pubDate>
      <link>https://dev.to/pablo_infante/why-i-reach-for-a-uuid-v4-instead-of-an-auto-increment-id-3b89</link>
      <guid>https://dev.to/pablo_infante/why-i-reach-for-a-uuid-v4-instead-of-an-auto-increment-id-3b89</guid>
      <description>&lt;p&gt;Auto-increment primary keys are fine until they aren't. The moment you have two services writing to the same logical dataset, a sharded table, an offline client that creates rows before it syncs, or a public URL you'd rather not have &lt;code&gt;/orders/1&lt;/code&gt;, &lt;code&gt;/orders/2&lt;/code&gt; leaking your volume — the sequential integer starts to hurt.&lt;/p&gt;

&lt;p&gt;That's the case for &lt;strong&gt;UUID v4&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a v4 actually is
&lt;/h2&gt;

&lt;p&gt;A UUID v4 is 128 bits, of which &lt;strong&gt;122 are random&lt;/strong&gt; (the rest are fixed version/variant bits). You get a value like:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;f47ac10b-58cc-4372-a567-0e02b2c3d479&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The useful property: you can generate one &lt;strong&gt;anywhere&lt;/strong&gt; — on the client, in a worker, in three services at once — without coordinating with a central sequence, and the chance of a collision is negligible for any realistic volume. No round-trip to the database to ask "what's the next number?"&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it earns its keep
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Client-generated IDs:&lt;/strong&gt; a mobile app can create a record offline, assign the UUID itself, and sync later with no renumbering.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Distributed writes:&lt;/strong&gt; merge data from several sources without PK clashes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Opaque public identifiers:&lt;/strong&gt; &lt;code&gt;/invoice/f47ac10b-…&lt;/code&gt; doesn't reveal how many invoices exist, unlike &lt;code&gt;/invoice/4471&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Idempotency keys:&lt;/strong&gt; send one with a request so a retry doesn't double-charge.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The honest trade-offs
&lt;/h2&gt;

&lt;p&gt;I'm not going to pretend UUIDs are free:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;They're &lt;strong&gt;bigger&lt;/strong&gt; (16 bytes vs 4–8 for an int), which matters at index scale.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Random&lt;/strong&gt; v4s scatter across a B-tree index, hurting insert locality — if that's your bottleneck, look at UUID v7 (time-ordered) or keep ints internally and expose UUIDs externally.&lt;/li&gt;
&lt;li&gt;They're &lt;strong&gt;not secrets&lt;/strong&gt; — unguessable isn't the same as authenticated. Don't use a UUID as a bearer token.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Grabbing one quickly
&lt;/h2&gt;

&lt;p&gt;When I just need a handful for seed data, a config, or a test fixture, I use &lt;a href="https://toolonium.com/en/tools/uuid-generator/" rel="noopener noreferrer"&gt;Toolonium's UUID generator&lt;/a&gt; — it produces v4 UUIDs in the browser, nothing sent to a server, no install. For bulk generation at runtime you'd obviously use your language's native library (&lt;code&gt;crypto.randomUUID()&lt;/code&gt; in modern JS, &lt;code&gt;uuid&lt;/code&gt; in Python, etc.), but for a quick one-off it saves opening a REPL.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;Reach for UUID v4 when IDs must be created without central coordination or must not leak counts. Know the index cost, and consider v7 if insert ordering matters. Need one right now? &lt;a href="https://toolonium.com/en/tools/uuid-generator/" rel="noopener noreferrer"&gt;Generate it here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Teams that went UUID-only on primary keys: did the index trade-off ever bite you in production?&lt;/em&gt;&lt;/p&gt;

</description>
      <category>database</category>
      <category>webdev</category>
      <category>programming</category>
      <category>tooling</category>
    </item>
    <item>
      <title>A no-upload JSON formatter that runs entirely in your browser</title>
      <dc:creator>Pablo Infante</dc:creator>
      <pubDate>Tue, 03 Nov 2026 22:04:20 +0000</pubDate>
      <link>https://dev.to/pablo_infante/a-no-upload-json-formatter-that-runs-entirely-in-your-browser-34m5</link>
      <guid>https://dev.to/pablo_infante/a-no-upload-json-formatter-that-runs-entirely-in-your-browser-34m5</guid>
      <description>&lt;p&gt;Every developer hits the same 30-second problem a dozen times a day: an API returns a wall of minified JSON, and you need to &lt;em&gt;read&lt;/em&gt; it. You paste it somewhere, prettify it, maybe sort the keys, and move on.&lt;/p&gt;

&lt;p&gt;The catch with "somewhere": a lot of online JSON formatters POST your payload to a server. When that payload is a production response with tokens, PII, or internal IDs, that's a quiet data-handling problem nobody signed off on.&lt;/p&gt;

&lt;h2&gt;
  
  
  The small rule I follow now
&lt;/h2&gt;

&lt;p&gt;If a tool only transforms text, it has no business sending that text anywhere. Formatting JSON is pure string work — indent, parse, re-serialize. It can all happen on the client.&lt;/p&gt;

&lt;p&gt;So when I need a quick format I use a client-side one: &lt;a href="https://toolonium.com/en/tools/json-formatter/" rel="noopener noreferrer"&gt;Toolonium's JSON formatter&lt;/a&gt;. The parsing and pretty-printing run in your browser; nothing is uploaded. No account, no paste-into-a-logged-server.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it actually does
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Indent control:&lt;/strong&gt; 2 spaces, 4 spaces, tab, or minify (&lt;code&gt;min&lt;/code&gt;) to go the other way and compact a blob.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sort keys:&lt;/strong&gt; alphabetize object keys — handy when you're diffing two responses that return fields in different orders.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Quick stats:&lt;/strong&gt; it reports the &lt;strong&gt;size in bytes&lt;/strong&gt;, the &lt;strong&gt;key count&lt;/strong&gt;, and the &lt;strong&gt;nesting depth&lt;/strong&gt;. Depth is the sleeper feature: a surprise depth of 7 usually means your "flat config" grew a structure you didn't intend.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A tiny example of the kind of input it's built for:&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="nl"&gt;"project"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"Toolonium"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"tools"&lt;/span&gt;&lt;span class="p"&gt;:[&lt;/span&gt;&lt;span class="s2"&gt;"calculate"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"convert"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"create"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="nl"&gt;"free"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;true&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;Formatted at 2-space indent that becomes readable in one click, and it'll tell you it's ~92 bytes with 3 top-level keys.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to reach for it vs your editor
&lt;/h2&gt;

&lt;p&gt;Your editor's formatter is great &lt;em&gt;inside a file&lt;/em&gt;. A browser tool wins for the throwaway case: a response pasted from &lt;code&gt;curl&lt;/code&gt;, a log line, a webhook body you're debugging in a meeting. No file, no project, no extension — just read it and close the tab.&lt;/p&gt;

&lt;p&gt;And because it's client-side, it's safe to paste the ugly real-world payload, not a sanitized toy one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;Prefer formatters that transform locally. For JSON specifically, there's no reason the text should leave your machine. If you want a zero-upload one, &lt;a href="https://toolonium.com/en/tools/json-formatter/" rel="noopener noreferrer"&gt;try it here&lt;/a&gt; — it's free and there's nothing to install.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What's your rule for pasting production JSON into online tools? Curious how others handle the "is this thing uploading my payload" question.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>json</category>
      <category>webdev</category>
      <category>productivity</category>
      <category>tooling</category>
    </item>
  </channel>
</rss>
