<?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: Sarfaraz</title>
    <description>The latest articles on DEV Community by Sarfaraz (@sarfaraznaushad).</description>
    <link>https://dev.to/sarfaraznaushad</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%2F4073896%2Fb75e581e-8f79-4b3e-900e-746a3c7c0ce4.png</url>
      <title>DEV Community: Sarfaraz</title>
      <link>https://dev.to/sarfaraznaushad</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/sarfaraznaushad"/>
    <language>en</language>
    <item>
      <title>Your SSL Certificate Is Expiring and Nobody Told You (Until Now)</title>
      <dc:creator>Sarfaraz</dc:creator>
      <pubDate>Tue, 01 Sep 2026 02:38:46 +0000</pubDate>
      <link>https://dev.to/sarfaraznaushad/your-ssl-certificate-is-expiring-and-nobody-told-you-until-now-47ha</link>
      <guid>https://dev.to/sarfaraznaushad/your-ssl-certificate-is-expiring-and-nobody-told-you-until-now-47ha</guid>
      <description>&lt;p&gt;There's a special kind of production incident where nothing changed in your code, no deploy went out, and yet everything is on fire. Nine times out of ten, that's an SSL certificate that quietly expired at 3 AM on a Sunday.&lt;/p&gt;

&lt;p&gt;It's one of the most avoidable outages in web development, and also one of the most common — because certificates don't fail loudly in advance. They just work, right up until the exact second they don't.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this keeps happening
&lt;/h2&gt;

&lt;p&gt;Certificates aren't a "set once" thing. Most are valid for 90 days (shorter than they used to be, thanks to Let's Encrypt and browser vendors pushing shorter lifespans) or up to a year for some paid CAs. That means renewal is a recurring chore, not a one-time setup step — and recurring chores are exactly the kind of thing that fall through the cracks when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The person who set up the cert three renewal cycles ago has left the team&lt;/li&gt;
&lt;li&gt;Auto-renewal (certbot, ACME, load balancer integration) is assumed to be working but silently isn't&lt;/li&gt;
&lt;li&gt;A subdomain or staging environment was added later and never got its own monitoring&lt;/li&gt;
&lt;li&gt;The cert is fine, but the &lt;em&gt;chain&lt;/em&gt; is incomplete — valid on your server, broken on some clients&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last one is sneaky. A certificate can look perfectly fine in your own browser (which caches intermediate certs) while failing for a fraction of your users whose devices don't have the intermediate cert cached — usually because the server isn't sending the full chain.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "checking your SSL" actually means
&lt;/h2&gt;

&lt;p&gt;It's not just "is it expired." A proper check covers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Expiry date&lt;/strong&gt; — obviously, but also &lt;em&gt;how soon&lt;/em&gt;. 30 days out is a warning; 3 days out is an incident.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chain completeness&lt;/strong&gt; — is the full chain (leaf → intermediate → root) being served, or just the leaf cert?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hostname match&lt;/strong&gt; — does the certificate actually cover the domain (and subdomains, if it's a wildcard) being served?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Protocol and cipher support&lt;/strong&gt; — is the server still allowing deprecated protocols like TLS 1.0/1.1 that some compliance frameworks and browsers now flag or block?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Issuer trust&lt;/strong&gt; — is it signed by a CA that's actually in modern trust stores, and not self-signed in a production context?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Missing any one of these can mean "works for me" while a meaningful slice of real users get a scary browser warning or an outright connection failure.&lt;/p&gt;

&lt;h2&gt;
  
  
  The cost of getting this wrong
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Browsers don't politely degrade on an expired or mismatched cert — they show a full-page warning that most non-technical users won't click past.&lt;/li&gt;
&lt;li&gt;API clients and mobile apps often fail &lt;em&gt;harder&lt;/em&gt; than browsers — no warning screen, just a TLS handshake error and a support ticket.&lt;/li&gt;
&lt;li&gt;SEO and trust signals take a hit; search engines and security scanners both penalize broken HTTPS.&lt;/li&gt;
&lt;li&gt;Compliance audits (PCI-DSS, SOC 2, etc.) will flag weak protocols or incomplete chains even if the site "works."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The frustrating part is that all of this is checkable in advance — it just requires actually looking, on a schedule, instead of finding out when a customer emails you.&lt;/p&gt;

&lt;h2&gt;
  
  
  A faster way to look
&lt;/h2&gt;

&lt;p&gt;Rather than pulling out &lt;code&gt;openssl s_client&lt;/code&gt; every time (which, fine, works, but nobody wants to remember the flags), I built a quick certificate checker that surfaces expiry date, days remaining, chain status, and issuer info for any domain in one pass:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://samtoolkit.com/tools/ssl-checker" rel="noopener noreferrer"&gt;SSL Certificate Checker&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's part of &lt;a href="https://samtoolkit.com" rel="noopener noreferrer"&gt;samtoolkit.com&lt;/a&gt; — small, client-side developer tools (JWT decoder, HTTP status/header reference, webhook bin, env validator, and others). Nothing you check gets logged or stored server-side.&lt;/p&gt;

&lt;p&gt;If you've had a cert-expiry outage story, I'd genuinely like to hear it in the comments — everyone has one, and they're always more avoidable in hindsight than they felt at 3 AM.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>security</category>
      <category>ssl</category>
      <category>devops</category>
    </item>
    <item>
      <title>The HTTP Status Code You're Returning Is Probably Wrong (And Nobody Notices)</title>
      <dc:creator>Sarfaraz</dc:creator>
      <pubDate>Tue, 01 Sep 2026 02:33:03 +0000</pubDate>
      <link>https://dev.to/sarfaraznaushad/the-http-status-code-youre-returning-is-probably-wrong-and-nobody-notices-a4o</link>
      <guid>https://dev.to/sarfaraznaushad/the-http-status-code-youre-returning-is-probably-wrong-and-nobody-notices-a4o</guid>
      <description>&lt;p&gt;Quick test: what's the difference between &lt;code&gt;401&lt;/code&gt; and &lt;code&gt;403&lt;/code&gt;? What about &lt;code&gt;502&lt;/code&gt; vs &lt;code&gt;503&lt;/code&gt;? If you had to pause even for a second, you're not alone — and you're also not off the hook, because the client code (or the poor human) on the other end of your API is making decisions based on that number.&lt;/p&gt;

&lt;p&gt;Status codes and headers are one of those "I'll just wing it" corners of web development. Everyone's shipped a &lt;code&gt;500&lt;/code&gt; for a validation error at least once. This post is a quick, practical pass through the ones that actually matter, the ones people mix up, and the headers most APIs quietly get wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this is worth five minutes
&lt;/h2&gt;

&lt;p&gt;A status code is a contract. It tells the caller &lt;em&gt;what kind of thing just happened&lt;/em&gt; without them reading your response body:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Should I retry this request?&lt;/li&gt;
&lt;li&gt;Is this my fault or the server's?&lt;/li&gt;
&lt;li&gt;Should I refresh my auth token?&lt;/li&gt;
&lt;li&gt;Is this safe to cache?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Get the code wrong, and you break that contract. Clients retry things they shouldn't, cache things they can't, or silently swallow errors because your &lt;code&gt;200&lt;/code&gt; said everything was fine.&lt;/p&gt;

&lt;h2&gt;
  
  
  The mix-ups that happen constantly
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;401 vs 403&lt;/strong&gt; — &lt;code&gt;401 Unauthorized&lt;/code&gt; actually means "I don't know who you are" (no or invalid credentials). &lt;code&gt;403 Forbidden&lt;/code&gt; means "I know exactly who you are, and the answer is no." Mixing these up gives users the wrong recovery path — one says "log in again," the other says "stop asking."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;404 vs 410&lt;/strong&gt; — &lt;code&gt;404&lt;/code&gt; says "not found," which invites retries and crawlers to keep checking. &lt;code&gt;410 Gone&lt;/code&gt; says "this existed and is never coming back." If you deleted a resource on purpose, &lt;code&gt;410&lt;/code&gt; is the more honest answer and it tells search engines to stop indexing it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;500 vs 502 vs 503&lt;/strong&gt; — &lt;code&gt;500&lt;/code&gt; is your app's own bug. &lt;code&gt;502 Bad Gateway&lt;/code&gt; means you're a proxy/gateway and the upstream service sent garbage. &lt;code&gt;503 Service Unavailable&lt;/code&gt; means you're intentionally down (maintenance, overload) and should usually come with a &lt;code&gt;Retry-After&lt;/code&gt; header. These three get used interchangeably in a lot of codebases, and it makes on-call debugging harder than it needs to be.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;PUT vs PATCH response codes&lt;/strong&gt; — a successful &lt;code&gt;PUT&lt;/code&gt; that creates a new resource should return &lt;code&gt;201 Created&lt;/code&gt;; one that updates an existing one should return &lt;code&gt;200 OK&lt;/code&gt; (or &lt;code&gt;204 No Content&lt;/code&gt; if you're not returning a body). A lot of APIs just return &lt;code&gt;200&lt;/code&gt; for everything, which quietly discards useful information.&lt;/p&gt;

&lt;h2&gt;
  
  
  The headers people forget exist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Cache-Control&lt;/code&gt;&lt;/strong&gt; — the single header most responsible for either a snappy site or a stale one. &lt;code&gt;no-store&lt;/code&gt;, &lt;code&gt;no-cache&lt;/code&gt;, and &lt;code&gt;max-age=0&lt;/code&gt; all mean subtly different things.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ETag&lt;/code&gt;&lt;/strong&gt; combined with &lt;code&gt;If-None-Match&lt;/code&gt; — lets clients skip re-downloading unchanged resources. Massively underused outside of CDNs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Retry-After&lt;/code&gt;&lt;/strong&gt; — pairs with &lt;code&gt;429&lt;/code&gt; and &lt;code&gt;503&lt;/code&gt; to tell clients exactly when to come back, instead of making them guess with exponential backoff.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Content-Security-Policy&lt;/code&gt;&lt;/strong&gt; — not just a security checkbox; misconfiguring it is one of the more common reasons a perfectly good frontend "randomly" stops loading fonts or scripts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Vary&lt;/code&gt;&lt;/strong&gt; — if your response changes based on a header like &lt;code&gt;Accept-Encoding&lt;/code&gt; or &lt;code&gt;Authorization&lt;/code&gt;, and you're not setting &lt;code&gt;Vary&lt;/code&gt;, your CDN or browser cache can serve the wrong version to the wrong user.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Where this actually bites you
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A retry storm because your API returned &lt;code&gt;500&lt;/code&gt; instead of &lt;code&gt;429&lt;/code&gt; for rate limiting, so clients treated a "slow down" as a random failure and hammered you harder.&lt;/li&gt;
&lt;li&gt;A broken cache because a response with private data went out with &lt;code&gt;Cache-Control: public&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A frontend team that built retry logic around &lt;code&gt;503&lt;/code&gt; because your team returned it for something that was actually a permanent &lt;code&gt;410&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these are exotic edge cases — they're the ordinary Tuesday bugs that come from treating status codes and headers as decoration instead of protocol.&lt;/p&gt;

&lt;h2&gt;
  
  
  A reference beats a memory test
&lt;/h2&gt;

&lt;p&gt;Honestly, memorizing all ~60 status codes and every header's exact semantics isn't a good use of anyone's time. I put together a quick lookup tool that covers status codes (grouped by category, with the "what it actually means" and "when to use it" spelled out) and common request/response headers, so you can check instead of guess mid-code-review:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://samtoolkit.com/tools/http-reference" rel="noopener noreferrer"&gt;HTTP Status Code &amp;amp; Header Reference&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's part of &lt;a href="https://samtoolkit.com" rel="noopener noreferrer"&gt;samtoolkit.com&lt;/a&gt; — a set of small, client-side developer tools (JWT decoder, .gitignore generator, env validator, webhook bin, and a few others). Nothing gets uploaded anywhere; everything runs in your browser.&lt;/p&gt;

&lt;p&gt;If you've got a favorite status-code horror story — a &lt;code&gt;200&lt;/code&gt; that lied to you, a &lt;code&gt;418&lt;/code&gt; used unironically in production, whatever — drop it in the comments. I collect these.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>api</category>
      <category>http</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Stop Hand-Editing Text Case — You're Wasting Time You Don't Need To</title>
      <dc:creator>Sarfaraz</dc:creator>
      <pubDate>Sat, 29 Aug 2026 13:06:04 +0000</pubDate>
      <link>https://dev.to/sarfaraznaushad/stop-hand-editing-text-case-youre-wasting-time-you-dont-need-to-499p</link>
      <guid>https://dev.to/sarfaraznaushad/stop-hand-editing-text-case-youre-wasting-time-you-dont-need-to-499p</guid>
      <description>&lt;h2&gt;
  
  
  The Ten Seconds You Pay Over and Over
&lt;/h2&gt;

&lt;p&gt;You've done this: you paste a column of &lt;code&gt;Product Name&lt;/code&gt; headers into your code and need &lt;code&gt;product_name&lt;/code&gt; for a database schema. Or a designer hands you &lt;code&gt;Hero Title&lt;/code&gt; copy and your CSS needs &lt;code&gt;hero-title&lt;/code&gt; classes. Or you're renaming a variable from &lt;code&gt;userEmail&lt;/code&gt; to match a &lt;code&gt;user_email&lt;/code&gt; API response.&lt;/p&gt;

&lt;p&gt;None of these take long individually — select the word, retype it, fix the capitalization, move to the next one. But you do this constantly, across renaming variables, cleaning up spreadsheet headers, converting API field names, prepping CSS classes, and reformatting copy. Ten seconds here, twenty there. It adds up to real, avoidable time spent on something with zero decision-making involved.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why It's Always Manual
&lt;/h2&gt;

&lt;p&gt;Casing conversion is one of those tasks that's too small to justify writing a script for in the moment, but tedious enough to be annoying every single time you hit it by hand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It's rarely just one word.&lt;/strong&gt; A list of 40 column headers going from Title Case to snake_case is 40 manual edits, or a regex you have to half-remember how to write.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The rules aren't always obvious.&lt;/strong&gt; Converting &lt;code&gt;getUserID&lt;/code&gt; to snake_case — is it &lt;code&gt;get_user_id&lt;/code&gt; or &lt;code&gt;get_userid&lt;/code&gt;? Acronyms and abbreviations make "just retype it" trickier than it sounds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Every language has a different convention.&lt;/strong&gt; JavaScript wants camelCase, Python and SQL want snake_case, CSS and URLs want kebab-case, constants want SCREAMING_SNAKE_CASE. Moving data between a frontend, a backend, and a database often means converting the same field name three different ways in one sitting.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this is hard. It's just friction — the kind that's easy to tolerate because each instance is small, right up until you count how often it happens in a week.&lt;/p&gt;

&lt;h2&gt;
  
  
  Just Paste It and Convert
&lt;/h2&gt;

&lt;p&gt;The fix is exactly as unglamorous as the problem: a tool that takes text and spits out every casing format at once, so you copy whichever one you need instead of retyping anything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://samtoolkit.com/tools/case-converter" rel="noopener noreferrer"&gt;Case Converter&lt;/a&gt;&lt;/strong&gt; on SamToolkit does this — paste in text (a single term or a whole list) and get camelCase, PascalCase, snake_case, kebab-case, CONSTANT_CASE, Title Case, and more, instantly, entirely in your browser. No upload, no account, no waiting on a page to load a heavier app just to flip some text into a different format.&lt;/p&gt;

&lt;p&gt;It's a genuinely small tool. That's the point — it's meant to be faster to open, paste, and copy than it is to do the conversion by hand, even for a single word.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where This Actually Saves Time
&lt;/h2&gt;

&lt;p&gt;A few places this comes up more than people expect:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Renaming variables to match a style guide&lt;/strong&gt; after a linter flags a batch of inconsistent names.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Converting spreadsheet or CSV headers&lt;/strong&gt; into valid database column names or API field names in one pass.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prepping CSS class names or IDs&lt;/strong&gt; from design copy or content docs that come in Title Case or Sentence case.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Matching naming conventions across a stack&lt;/strong&gt; — same concept, different casing, in your frontend, backend, and database schema.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these are hard problems. They're just the kind of small, repeated friction that's worth removing once and not thinking about again.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;What's your most-used text conversion — camelCase, snake_case, something else? Curious what comes up most in other people's workflows.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>webdev</category>
      <category>tools</category>
      <category>javascript</category>
    </item>
    <item>
      <title>5 JWT Mistakes That Quietly Break Your Auth Security</title>
      <dc:creator>Sarfaraz</dc:creator>
      <pubDate>Sat, 29 Aug 2026 12:58:28 +0000</pubDate>
      <link>https://dev.to/sarfaraznaushad/5-jwt-mistakes-that-quietly-break-your-auth-security-3i7o</link>
      <guid>https://dev.to/sarfaraznaushad/5-jwt-mistakes-that-quietly-break-your-auth-security-3i7o</guid>
      <description>&lt;h2&gt;
  
  
  A Token That "Just Works" Isn't the Same as a Secure One
&lt;/h2&gt;

&lt;p&gt;JWTs are everywhere — API auth, SSO, session management — because they're simple to generate and easy to pass around. That simplicity is also exactly why they get misused. A JWT can be perfectly well-formed, verify correctly, and &lt;em&gt;still&lt;/em&gt; be a security hole, because the format doesn't protect you from bad decisions about what goes inside it or how it's checked.&lt;/p&gt;

&lt;p&gt;Here are the mistakes that show up most often — and how to catch them by actually looking at what's inside your tokens.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Trusting the &lt;code&gt;alg&lt;/code&gt; Header Too Much
&lt;/h2&gt;

&lt;p&gt;A JWT's header names the signing algorithm — &lt;code&gt;HS256&lt;/code&gt;, &lt;code&gt;RS256&lt;/code&gt;, &lt;code&gt;none&lt;/code&gt;. The catch: that field is set by whoever created the token, and some JWT libraries will happily verify a token using whatever algorithm the header claims, including &lt;code&gt;none&lt;/code&gt; (no signature at all) if the server-side code doesn't explicitly reject it.&lt;/p&gt;

&lt;p&gt;This is the classic "alg confusion" attack: an attacker takes a token meant to be verified with an RSA public key, changes the header to &lt;code&gt;HS256&lt;/code&gt;, and signs it using that same public key as an HMAC secret — because public keys are, by definition, public. If your verification code doesn't pin down the expected algorithm, it can be tricked into accepting a forged token.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; always specify and enforce the exact algorithm you expect server-side; never let the token's own header decide how it gets verified.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Sensitive Data Sitting in Plaintext
&lt;/h2&gt;

&lt;p&gt;The "signed" part of JSON Web Token only guarantees the payload hasn't been &lt;em&gt;tampered with&lt;/em&gt; — it says nothing about who can &lt;em&gt;read&lt;/em&gt; it. The header and payload are just Base64URL-encoded, not encrypted. Anyone with the token — a browser extension, a proxy log, a teammate glancing at devtools — can decode it in seconds and read every claim inside.&lt;/p&gt;

&lt;p&gt;Tokens with emails, internal role names, permission lists, or anything resembling PII in the payload are handing that data to anyone who intercepts the token, signature or not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; keep the payload to what's needed for authorization (subject ID, expiry, minimal scopes). If you need to transmit sensitive data, that's what encrypted JWTs (JWE) or a server-side session lookup are for.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. No Expiry, or an Expiry Nobody Checks
&lt;/h2&gt;

&lt;p&gt;A JWT without an &lt;code&gt;exp&lt;/code&gt; claim is valid forever, which means a leaked token — from a log file, a compromised device, an old bookmarked URL — stays usable indefinitely. And even when &lt;code&gt;exp&lt;/code&gt; is present, it's only useful if the verification code actually checks it; a surprising number of custom JWT-handling implementations decode the payload and manually pull out claims without running full verification, silently skipping the expiry check entirely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; always set a short, sensible &lt;code&gt;exp&lt;/code&gt;, and confirm your verification path is actually validating it — not just parsing the payload and trusting it.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Reusing the Same Secret Everywhere
&lt;/h2&gt;

&lt;p&gt;It's common for a codebase to have one &lt;code&gt;JWT_SECRET&lt;/code&gt; env var used for every kind of token — access tokens, refresh tokens, password reset links, email verification tokens — all signed with the same key. If any one of those flows has a vulnerability (say, a reset-token endpoint that leaks a valid signature), an attacker can potentially forge tokens for a completely different flow, since they all trust the same secret.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; use distinct signing secrets (or key pairs) per token purpose, so a compromise in one flow doesn't cascade into every other one.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Not Actually Looking at What's Inside Your Tokens
&lt;/h2&gt;

&lt;p&gt;This sounds almost too simple, but it's the root cause behind most of the above: teams generate JWTs, ship them, and never actually inspect what ends up in the payload in production. Claims drift over time as features get added — someone adds a &lt;code&gt;isAdmin&lt;/code&gt; boolean "temporarily" for testing and it's still there eight months later, sitting in every token, readable by anyone who decodes one.&lt;/p&gt;

&lt;p&gt;The fastest way to catch this is to just decode a real token and read it, the same way you'd review an API response. &lt;strong&gt;&lt;a href="https://samtoolkit.com/tools/jwt-decoder" rel="noopener noreferrer"&gt;JWT Decoder&lt;/a&gt;&lt;/strong&gt; on SamToolkit does exactly that — paste a token in and instantly see the decoded header and payload, with nothing sent to a server (it runs entirely in your browser, so you're not pasting production tokens into someone else's backend). It's a fast way to sanity-check exactly what a token exposes, verify the algorithm and expiry are what you expect, and catch stray claims before they become a habit.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Underlying Principle
&lt;/h2&gt;

&lt;p&gt;JWTs give you integrity (tamper detection) for free. They give you confidentiality and correct verification only if you set them up carefully. Treat every claim you add to a payload as something a stranger might read, treat the &lt;code&gt;alg&lt;/code&gt; header as attacker-controlled input, and periodically decode your own tokens to check what's actually shipping — before someone else does it for you.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;What's the worst JWT misconfiguration you've caught in a code review? Share it below.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>authentication</category>
    </item>
    <item>
      <title>The .env Mistake That Takes Down Production (And How to Catch It Before You Deploy)</title>
      <dc:creator>Sarfaraz</dc:creator>
      <pubDate>Sat, 29 Aug 2026 12:47:03 +0000</pubDate>
      <link>https://dev.to/sarfaraznaushad/the-env-mistake-that-takes-down-production-and-how-to-catch-it-before-you-deploy-gjg</link>
      <guid>https://dev.to/sarfaraznaushad/the-env-mistake-that-takes-down-production-and-how-to-catch-it-before-you-deploy-gjg</guid>
      <description>&lt;h2&gt;
  
  
  The 2 AM Deploy That Wasn't Supposed to Break Anything
&lt;/h2&gt;

&lt;p&gt;You merge the PR. CI is green. You deploy. Five minutes later, your error tracker lights up like a Christmas tree.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;TypeError: Cannot read properties of undefined (reading 'apiKey')&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;You SSH in, check the logs, and there it is — &lt;code&gt;STRIPE_API_KEY&lt;/code&gt; was never set in the production environment. It was in &lt;code&gt;.env.example&lt;/code&gt;. It was in your local &lt;code&gt;.env&lt;/code&gt;. Someone just forgot to add it to the deploy target.&lt;/p&gt;

&lt;p&gt;Nothing in your build pipeline caught it, because there was nothing to catch. The app didn't know that variable was required until it tried to use it — in production, in front of real users.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Keeps Happening
&lt;/h2&gt;

&lt;p&gt;Environment variables are one of the least-tested parts of most codebases, mostly because they don't feel like "real" code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;They're invisible until runtime.&lt;/strong&gt; A missing or malformed env var doesn't show up in a type checker or a linter. It shows up when that specific code path executes — which might be five minutes after deploy, or five weeks, if it's a rarely-hit feature flag or a seasonal job.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;.env.example&lt;/code&gt; drifts.&lt;/strong&gt; Someone adds a new required variable, updates their own &lt;code&gt;.env&lt;/code&gt;, and forgets to update the example file or tell the team. Weeks later, a new hire or a fresh CI runner fails in a confusing way.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;"String" isn't a type check.&lt;/strong&gt; Even when a variable is present, nothing verifies it's actually a valid URL, a number in range, or one of an expected set of values. &lt;code&gt;PORT=abc&lt;/code&gt; will pass any "is it set" check and then fail somewhere deep in your HTTP server setup.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Different environments, different expectations.&lt;/strong&gt; A variable might be optional in development but required in production, or vice versa (test API keys vs. live ones). Most setups don't encode that distinction anywhere.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Individually, each of these is a minor oversight. Together, across a team and a growing list of environment variables, they turn into a steady trickle of preventable outages and "works on my machine" debugging sessions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Validate the Shape, Not Just the Presence
&lt;/h2&gt;

&lt;p&gt;The fix isn't "be more careful" — it's making the shape of your environment explicit and checking it automatically, the same way you'd validate an API request body.&lt;/p&gt;

&lt;p&gt;A solid env validation setup answers three questions before your app is allowed to boot:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Is everything required actually present?&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Does each value match the type and format it should?&lt;/strong&gt; (number, boolean, URL, enum, non-empty string, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Are the rules different per environment&lt;/strong&gt;, and are those differences intentional?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you're using something like &lt;code&gt;zod&lt;/code&gt;, &lt;code&gt;envalid&lt;/code&gt;, or &lt;code&gt;@t3-oss/env-nextjs&lt;/code&gt; in a Node/TypeScript project, you can express this as a schema and fail fast at startup with a clear error message instead of a cryptic runtime crash three layers deep. The principle holds regardless of stack — Python's &lt;code&gt;pydantic-settings&lt;/code&gt;, Go's &lt;code&gt;envconfig&lt;/code&gt;, and similar libraries all do the same job: define the contract once, validate it every time the app starts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Checking It Before It Even Gets to CI
&lt;/h2&gt;

&lt;p&gt;Schema validation at startup is the safety net for your running app, but a lot of these mistakes happen &lt;em&gt;before&lt;/em&gt; code is even deployed — while writing a &lt;code&gt;.env&lt;/code&gt; file by hand, or copying one from a teammate and missing a line.&lt;/p&gt;

&lt;p&gt;That's the gap a quick pre-flight check fills: paste in your &lt;code&gt;.env&lt;/code&gt; content and instantly see what's missing, misformatted, or inconsistent with what the app expects — before you've built, deployed, or asked a teammate to debug it with you.&lt;/p&gt;

&lt;p&gt;That's exactly what &lt;strong&gt;&lt;a href="https://samtoolkit.com/tools/env-validator" rel="noopener noreferrer"&gt;Env Validator&lt;/a&gt;&lt;/strong&gt; on SamToolkit does. It's a free, browser-based tool — nothing you paste is uploaded or logged, it all runs client-side — that checks your environment variables for the common failure modes above: missing required keys, empty values, malformed URLs and numbers, and inconsistencies between what you have and what your app expects.&lt;/p&gt;

&lt;p&gt;It's the kind of five-second check that's easy to skip under deadline pressure and expensive to skip when it causes an incident.&lt;/p&gt;

&lt;h2&gt;
  
  
  Make It a Habit, Not a Hero Moment
&lt;/h2&gt;

&lt;p&gt;The teams that stop having "it worked locally" env incidents usually do two small things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add schema validation at app startup, so a bad environment fails loudly at boot instead of quietly at runtime.&lt;/li&gt;
&lt;li&gt;Get in the habit of double-checking &lt;code&gt;.env&lt;/code&gt; files — especially before a new environment setup or a deploy — with a quick tool pass rather than a manual read-through.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Neither takes long. Both are far cheaper than a 2 AM page.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Have an env-related horror story of your own? Drop it in the comments — misery loves company.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>devops</category>
      <category>productivity</category>
    </item>
    <item>
      <title>I Found an API Key in My Git History. Here's How I Stopped Doing That.</title>
      <dc:creator>Sarfaraz</dc:creator>
      <pubDate>Fri, 28 Aug 2026 16:27:27 +0000</pubDate>
      <link>https://dev.to/sarfaraznaushad/i-found-an-api-key-in-my-git-history-heres-how-i-stopped-doing-that-540n</link>
      <guid>https://dev.to/sarfaraznaushad/i-found-an-api-key-in-my-git-history-heres-how-i-stopped-doing-that-540n</guid>
      <description>&lt;p&gt;A while back I ran &lt;code&gt;git log -p&lt;/code&gt; on an old side project and found a &lt;code&gt;.env&lt;/code&gt; file sitting in the history, plaintext API key and all. The file wasn't even in the current commit — I'd deleted it two commits later. Didn't matter. It was in the history forever, in a public repo, for anyone to &lt;code&gt;git clone&lt;/code&gt; and scroll back through.&lt;/p&gt;

&lt;p&gt;That's the moment &lt;code&gt;.gitignore&lt;/code&gt; stopped being "some file GitHub adds when you create a repo" and started being something I actually think about on day one of every project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this keeps happening
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;.gitignore&lt;/code&gt; is one of those files everyone knows they should set up properly, and almost nobody does, for a pretty simple reason: nobody wants to sit down and type out ignore patterns for &lt;code&gt;node_modules&lt;/code&gt;, &lt;code&gt;.env&lt;/code&gt;, &lt;code&gt;__pycache__&lt;/code&gt;, &lt;code&gt;.vscode&lt;/code&gt;, &lt;code&gt;bin/&lt;/code&gt;, &lt;code&gt;obj/&lt;/code&gt;, &lt;code&gt;.DS_Store&lt;/code&gt;, and whatever else your specific stack throws off. So the usual paths are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Skip it, &lt;code&gt;git add .&lt;/code&gt;, and hope for the best&lt;/li&gt;
&lt;li&gt;Copy a &lt;code&gt;.gitignore&lt;/code&gt; from a random old project that only half-matches your current stack&lt;/li&gt;
&lt;li&gt;Add patterns reactively, &lt;em&gt;after&lt;/em&gt; &lt;code&gt;git status&lt;/code&gt; shows you something ugly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All three have the same failure mode: &lt;strong&gt;anything ignored late was already trackable before you added the rule&lt;/strong&gt;, which means it can already be committed. Once a secret is in your Git history, adding it to &lt;code&gt;.gitignore&lt;/code&gt; afterward does nothing — the file is still there in every clone, every fork, every previous commit. You have to rewrite history to actually remove it, which is its own headache.&lt;/p&gt;

&lt;p&gt;The other failure mode is just noise. Without a proper &lt;code&gt;.gitignore&lt;/code&gt;, your diffs fill up with &lt;code&gt;bin/Debug/&lt;/code&gt;, &lt;code&gt;node_modules/&lt;/code&gt;, build artifacts, and IDE config — real changes get buried, and every PR review takes longer because reviewers are scrolling past junk.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix: get it right before your first commit
&lt;/h2&gt;

&lt;p&gt;I built a &lt;strong&gt;&lt;a href="https://samtoolkit.com/tools/gitignore-generator" rel="noopener noreferrer"&gt;.gitignore Generator&lt;/a&gt;&lt;/strong&gt; into SamToolkit specifically so "set up &lt;code&gt;.gitignore&lt;/code&gt; properly" stops being a step people skip.&lt;/p&gt;

&lt;p&gt;You search and multi-select whatever's in your stack — frameworks (Docker, React, Next.js), editors (VS Code, JetBrains), languages (Node, Python, Java, Rust, Go, Blazor/.NET), and OS (macOS, Windows) — and it merges everything into one file. A few details that matter more than they sound:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It de-dupes across stacks.&lt;/strong&gt; Pick Node and Python together and you won't get the same log/cache patterns listed twice — the tool merges the selected templates into one clean file instead of concatenating them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Each stack gets its own labeled section&lt;/strong&gt;, so when you add a language six months from now you can find and extend the right block instead of guessing where a pattern lives.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It's additive.&lt;/strong&gt; Starting a repo as pure Node and adding Python later? Re-run it, select both, and get the combined file — no manually diffing two template files to merge them by hand.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Live preview before you commit to anything&lt;/strong&gt; (pun intended) — you see the generated file, then copy or download it.&lt;/p&gt;

&lt;p&gt;Like the rest of SamToolkit, it runs entirely client-side — nothing about your stack selections gets sent anywhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  The actual habit to build
&lt;/h2&gt;

&lt;p&gt;The tool solves the "typing it out is annoying" problem, but the underlying habit matters more than the tool: &lt;strong&gt;&lt;code&gt;.gitignore&lt;/code&gt; is a day-one file, not a day-30 file.&lt;/strong&gt; Generate it before your first &lt;code&gt;git add&lt;/code&gt;, not after &lt;code&gt;git status&lt;/code&gt; embarrasses you.&lt;/p&gt;

&lt;p&gt;And if you've already committed something sensitive — a &lt;code&gt;.gitignore&lt;/code&gt; entry won't save you retroactively. You'll need &lt;code&gt;git rm --cached&lt;/code&gt; at minimum, and a history rewrite (&lt;code&gt;git filter-repo&lt;/code&gt; or BFG) if it's already pushed somewhere public.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://samtoolkit.com/tools/gitignore-generator" rel="noopener noreferrer"&gt;samtoolkit.com/tools/gitignore-generator&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Curious — has anyone else had a "found a secret in old Git history" moment? What was it?&lt;/p&gt;

</description>
      <category>git</category>
      <category>webdev</category>
      <category>security</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Why Your README Still Sucks (And How I Fixed Mine in 90 Seconds)</title>
      <dc:creator>Sarfaraz</dc:creator>
      <pubDate>Fri, 28 Aug 2026 16:20:54 +0000</pubDate>
      <link>https://dev.to/sarfaraznaushad/why-your-readme-still-sucks-and-how-i-fixed-mine-in-90-seconds-2125</link>
      <guid>https://dev.to/sarfaraznaushad/why-your-readme-still-sucks-and-how-i-fixed-mine-in-90-seconds-2125</guid>
      <description>&lt;p&gt;You just finished a project. The code works, the tests pass, and you're proud of it. Then you get to the README, and your energy dies on the spot.&lt;/p&gt;

&lt;p&gt;So you type &lt;code&gt;# Project Name&lt;/code&gt;, maybe a one-line description, and call it done. Six months later someone opens your repo, sees nothing but a title and a wall of source files, and closes the tab.&lt;/p&gt;

&lt;p&gt;I've done this more times than I'd like to admit. And I don't think it's laziness — I think READMEs are just a genuinely awkward writing task.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why READMEs are harder than they look
&lt;/h2&gt;

&lt;p&gt;A good README has to do several unrelated jobs at once:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sell the project&lt;/strong&gt; in the first three lines, before anyone scrolls&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prove it's alive&lt;/strong&gt; with badges (build status, version, license, coverage)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Get someone from zero to running code&lt;/strong&gt; with install and usage steps&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Signal what stack it's built on&lt;/strong&gt;, so people self-select in or out&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set expectations for contributors&lt;/strong&gt;, if you're open to them&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's marketing copy, technical writing, and project management, all crammed into one Markdown file — usually written at 11pm right before you push. No wonder most READMEs are either a single sentence or a copy-pasted template with half the placeholders still in it.&lt;/p&gt;

&lt;p&gt;The annoying part is that the &lt;em&gt;shape&lt;/em&gt; of a good README barely changes between projects. Title, badges, description, tech stack, install, usage, license — it's the same skeleton every time. Yet we free-hand it from scratch on every single repo.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix: stop writing the skeleton by hand
&lt;/h2&gt;

&lt;p&gt;That repetition is exactly what pushed me to build a &lt;strong&gt;&lt;a href="https://samtoolkit.com/tools/readme-generator" rel="noopener noreferrer"&gt;README Generator&lt;/a&gt;&lt;/strong&gt; into SamToolkit — a free, client-side tool that turns a short form into a polished &lt;code&gt;README.md&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The idea isn't to write your README &lt;em&gt;for&lt;/em&gt; you — it's to stop you from re-typing the same boilerplate every time so you can spend your energy on the two paragraphs that actually matter: what the project does and why someone should care.&lt;/p&gt;

&lt;p&gt;A few things about how it's built:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Three templates, not one.&lt;/strong&gt; You pick between Minimal, Detailed/Enterprise, and Open Source + Contributing, depending on whether you're shipping a weekend script or a project that expects pull requests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Badges without hunting for shields.io syntax.&lt;/strong&gt; Build status, version, coverage, license, and a "PRs welcome" badge are all toggles — no remembering markdown-badge syntax.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A real tech stack picker.&lt;/strong&gt; Instead of typing out language and framework names, you select from a proper list — TypeScript, Rust, Go, ASP.NET Core, Docker, Kubernetes, Postgres, and dozens more — and it renders cleanly into the README.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The sections people forget.&lt;/strong&gt; Installation steps, a usage example block, an API reference stub, contributing guidelines, and a screenshot/GIF placeholder are all one-click additions instead of things you remember only after someone opens an issue asking "how do I even run this?"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;License text without copy-pasting from GitHub's license picker.&lt;/strong&gt; MIT, Apache 2.0, GPLv3, BSD 3-Clause, Unlicense, or Proprietary — pick one and the correct boilerplate is in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Live preview, then copy or download.&lt;/strong&gt; You see the rendered Markdown as you build it, and you either copy it straight into your repo or download the file.&lt;/p&gt;

&lt;p&gt;And since it's built the same way as the rest of SamToolkit, the whole thing runs client-side in your browser — nothing you type gets sent to a server.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;If your last three repos all have a README that's one line and a "TODO," this will take less time than writing this sentence took me:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://samtoolkit.com/tools/readme-generator" rel="noopener noreferrer"&gt;samtoolkit.com/tools/readme-generator&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It's free, no signup, and it's one of ~30 browser-based dev tools on the site — worth a look if you're the kind of developer who'd rather generate boilerplate than write it by hand.&lt;/p&gt;

&lt;p&gt;What do you always forget to put in your README? Curious what people consider the "must-have" section I might be missing.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>opensource</category>
      <category>productivity</category>
      <category>github</category>
    </item>
    <item>
      <title>I Built a Free Timestamp Converter That Never Sends Your Data Anywhere</title>
      <dc:creator>Sarfaraz</dc:creator>
      <pubDate>Thu, 27 Aug 2026 08:36:13 +0000</pubDate>
      <link>https://dev.to/sarfaraznaushad/i-built-a-free-timestamp-converter-that-never-sends-your-data-anywhere-2li7</link>
      <guid>https://dev.to/sarfaraznaushad/i-built-a-free-timestamp-converter-that-never-sends-your-data-anywhere-2li7</guid>
      <description>&lt;p&gt;If you've ever worked with logs, APIs, or databases, you know the drill: you're staring at a number like &lt;code&gt;1735689600&lt;/code&gt; and you need to know what date that actually is — right now, not after signing up for something.&lt;/p&gt;

&lt;p&gt;I got tired of the usual options: sketchy ad-heavy sites, tools that quietly send your data to a server, or switching to a Python shell just to run &lt;code&gt;datetime.fromtimestamp()&lt;/code&gt;. So I added a &lt;strong&gt;Timestamp Converter&lt;/strong&gt; to &lt;a href="https://samtoolkit.com" rel="noopener noreferrer"&gt;samtoolkit.com&lt;/a&gt; — one of ~30 browser-based developer utilities on the site, all running entirely client-side.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Converts Unix timestamps (seconds or milliseconds) to human-readable dates and back&lt;/li&gt;
&lt;li&gt;Supports UTC and local timezone views side by side&lt;/li&gt;
&lt;li&gt;Handles common formats developers actually deal with — ISO 8601, RFC 2822, epoch&lt;/li&gt;
&lt;li&gt;Live "current timestamp" ticker so you can grab "now" instantly&lt;/li&gt;
&lt;li&gt;No sign-up, no API calls, no data leaving your browser&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why client-side matters here
&lt;/h2&gt;

&lt;p&gt;Timestamps often show up next to sensitive context — user IDs, order numbers, log lines you shouldn't be pasting into a random third-party server. Since samtoolkit.com tools are built with Blazor WebAssembly, the conversion logic runs entirely in your browser. Nothing you type is ever sent anywhere. You could unplug your ethernet cable mid-conversion and it would keep working.&lt;/p&gt;

&lt;p&gt;This is the same philosophy behind the rest of the toolkit — JSON formatters, base64 encoders, regex testers, and now this — all "nothing leaves your browser" by design.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;👉 &lt;a href="https://samtoolkit.com/tools/timestamp-converter" rel="noopener noreferrer"&gt;Timestamp Converter on samtoolkit.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's free, has no ads blocking the actual tool, and works offline once loaded. If you run into an edge case it doesn't handle well, I'd genuinely like to hear about it in the comments — most tools on the site started as exactly that kind of feedback.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;samtoolkit.com is a growing collection of privacy-first developer tools — no tracking, no accounts, everything runs in your browser.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>tools</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Cron Syntax Is a Trap — Here's a Builder That Explains What You're Actually Scheduling</title>
      <dc:creator>Sarfaraz</dc:creator>
      <pubDate>Wed, 26 Aug 2026 03:36:58 +0000</pubDate>
      <link>https://dev.to/sarfaraznaushad/cron-syntax-is-a-trap-heres-a-builder-that-explains-what-youre-actually-scheduling-2hc9</link>
      <guid>https://dev.to/sarfaraznaushad/cron-syntax-is-a-trap-heres-a-builder-that-explains-what-youre-actually-scheduling-2hc9</guid>
      <description>&lt;p&gt;Nobody writes cron expressions from memory. Not really. You either copy one from Stack Overflow and hope, or you open crontab.guru, squint at it for a minute, and copy-paste the result into a deploy config you'll never look at again — until it fires at 3 AM on a Saturday instead of 3 PM on a weekday, and now you're debugging a scheduler instead of sleeping.&lt;/p&gt;

&lt;p&gt;The problem isn't that cron syntax is hard, exactly. It's that it's &lt;em&gt;terse&lt;/em&gt; — five fields, no labels, and a handful of special characters (&lt;code&gt;*&lt;/code&gt;, &lt;code&gt;/&lt;/code&gt;, &lt;code&gt;-&lt;/code&gt;, &lt;code&gt;,&lt;/code&gt;) that all mean something different depending on which field they're in. &lt;code&gt;0 3 * * 1-5&lt;/code&gt; is either "every weekday at 3 AM" or a typo away from something else entirely, and there's nothing in the string itself that tells you which one you actually wrote.&lt;/p&gt;

&lt;p&gt;That's the gap the &lt;a href="https://samtoolkit.com/tools/cron-builder" rel="noopener noreferrer"&gt;Cron Expression Builder&lt;/a&gt; on SamToolkit is built to close.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build it visually, read it in plain English
&lt;/h2&gt;

&lt;p&gt;Instead of starting from five blank fields and hoping you remember the order (minute, hour, day-of-month, month, day-of-week — in that order, always forget it), the builder gives you a form: pick a frequency, pick the times, pick the days. It generates the expression for you.&lt;/p&gt;

&lt;p&gt;But the more useful part runs the other direction. Paste in an existing cron expression from a codebase you've inherited, and it explains it back to you in plain English — "runs at 3:00 AM, Monday through Friday" — so you're not reverse-engineering someone else's scheduling logic field by field.&lt;/p&gt;

&lt;p&gt;A few things that come up constantly and are easy to get wrong by hand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Day-of-month vs. day-of-week interactions&lt;/strong&gt; — cron treats these as OR'd together when both are restricted, which trips up almost everyone the first time&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step values&lt;/strong&gt; (&lt;code&gt;*/15&lt;/code&gt; for every 15 minutes) vs. &lt;strong&gt;ranges&lt;/strong&gt; (&lt;code&gt;9-17&lt;/code&gt;) vs. &lt;strong&gt;lists&lt;/strong&gt; (&lt;code&gt;1,15&lt;/code&gt; for the 1st and 15th)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Common schedule shortcuts&lt;/strong&gt; like &lt;code&gt;@daily&lt;/code&gt;, &lt;code&gt;@hourly&lt;/code&gt;, and &lt;code&gt;@reboot&lt;/code&gt;, and what they expand to under the hood&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timezone ambiguity&lt;/strong&gt; — cron expressions don't carry timezone info themselves, and the builder flags that explicitly so you don't assume UTC when your server is running IST&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A quick example
&lt;/h2&gt;

&lt;p&gt;Say you want a job that runs every 15 minutes, but only during business hours, Monday to Friday. The instinct is often something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;*/15 9-17 * * 1-5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That looks right — and mostly is — but it's worth checking what "9-17" actually means here: it runs the job at :00, :15, :30, :45 past &lt;em&gt;every hour from 9 through 17&lt;/em&gt;, which includes the 17:45 run. If you meant "stop by 5 PM sharp," you actually want a narrower end hour, or an accepted trailing run trimmed depending on your intent. The builder's plain-English readout — "at every 15th minute past every hour from 9 through 17, Monday through Friday" — makes that boundary explicit instead of leaving you to work it out from the raw digits.&lt;/p&gt;

&lt;p&gt;Here's what scheduling that in Node might look like with &lt;code&gt;node-cron&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cron&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node-cron&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Every 15 minutes, 9 AM–5:45 PM, Mon–Fri&lt;/span&gt;
&lt;span class="nx"&gt;cron&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;schedule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*/15 9-17 * * 1-5&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Running business-hours job:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Asia/Kolkata&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note the explicit &lt;code&gt;timezone&lt;/code&gt; option — this is exactly the kind of thing that's invisible in the cron string itself and easy to forget until a deploy to a server in a different region quietly shifts every job by 5.5 hours.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it's worth using over guessing
&lt;/h2&gt;

&lt;p&gt;Cron bugs are uniquely annoying because they don't fail loudly — a misconfigured schedule doesn't throw an error, it just runs at the wrong time, or not at all, and you usually find out from a missed report or a stale cache rather than a stack trace. Verifying the expression against a plain-English explanation &lt;em&gt;before&lt;/em&gt; it goes into a crontab or a CI config catches that class of bug before it becomes a production mystery.&lt;/p&gt;

&lt;p&gt;Like the rest of the toolkit, it runs entirely client-side — no data leaves your browser, and nothing about your infrastructure or schedule gets logged anywhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;If you've got a cron expression sitting in a deploy script that you're not 100% sure you understand anymore, paste it in and see if the explanation matches what you expect.&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://samtoolkit.com/tools/cron-builder" rel="noopener noreferrer"&gt;Cron Expression Builder on SamToolkit&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's one of 30 free, browser-only developer tools on &lt;a href="https://samtoolkit.com" rel="noopener noreferrer"&gt;SamToolkit&lt;/a&gt; — no sign-up, nothing tracked, nothing uploaded.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;What's the worst cron scheduling mistake you've made? Drop it in the comments.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>cron</category>
      <category>devops</category>
      <category>webdev</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Stop Guessing: A Free Regex Tester That Actually Explains Your Pattern</title>
      <dc:creator>Sarfaraz</dc:creator>
      <pubDate>Wed, 26 Aug 2026 03:33:14 +0000</pubDate>
      <link>https://dev.to/sarfaraznaushad/stop-guessing-a-free-regex-tester-that-actually-explains-your-pattern-453f</link>
      <guid>https://dev.to/sarfaraznaushad/stop-guessing-a-free-regex-tester-that-actually-explains-your-pattern-453f</guid>
      <description>&lt;p&gt;Every developer has a regex horror story. Mine involves a "simple" email validator that quietly rejected half our signups for three days before anyone noticed. The pattern &lt;em&gt;looked&lt;/em&gt; right. It just wasn't.&lt;/p&gt;

&lt;p&gt;Regex is one of those skills where reading a pattern back is way harder than writing it. &lt;code&gt;^(?=.*[A-Z])(?=.*\d).{8,}$&lt;/code&gt; makes sense while you're typing it out character by character — and then completely unreadable the moment you close the tab and come back a week later.&lt;/p&gt;

&lt;p&gt;That's the exact gap I wanted to close with the &lt;a href="https://samtoolkit.com/tools/regex-tester" rel="noopener noreferrer"&gt;Regex Tester &amp;amp; Explainer&lt;/a&gt; on SamToolkit.&lt;/p&gt;

&lt;h2&gt;
  
  
  What makes it different from "just another regex tester"
&lt;/h2&gt;

&lt;p&gt;There's no shortage of regex testers online. Most do one thing well: highlight matches in real time as you type. That's useful, but it only answers "does this match?" — not "why does this match, and what happens with the next weird edge case someone throws at it?"&lt;/p&gt;

&lt;p&gt;The tool adds a plain-English breakdown alongside the match highlighting. Instead of staring at &lt;code&gt;(?:\d{3}[-.\s]?){2}\d{4}&lt;/code&gt; and mentally parsing it token by token, you get each piece explained in order — group boundaries, quantifiers, character classes, lookaheads — so you can actually verify your pattern does what you &lt;em&gt;meant&lt;/em&gt;, not just what it happens to match on your one test string.&lt;/p&gt;

&lt;p&gt;A few things it handles that come up constantly in real work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Named and unnamed capture groups&lt;/strong&gt;, broken out individually so you can see exactly what each one grabs&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flags&lt;/strong&gt; (&lt;code&gt;g&lt;/code&gt;, &lt;code&gt;i&lt;/code&gt;, &lt;code&gt;m&lt;/code&gt;, &lt;code&gt;s&lt;/code&gt;, &lt;code&gt;u&lt;/code&gt;) with a note on how each one changes the matching behavior&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live match highlighting&lt;/strong&gt; against your own sample text, updated as you type — no submit button, no page reload&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Common gotchas flagged inline&lt;/strong&gt; — unescaped special characters, greedy quantifiers that will overmatch, missing anchors&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A quick example
&lt;/h2&gt;

&lt;p&gt;Say you're validating Indian mobile numbers and land on something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;^[6-9]\d{9}$
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks fine. Ten digits, starting with 6 through 9. But paste it in and test against &lt;code&gt;+91 98765 43210&lt;/code&gt; and it fails immediately — because the anchors don't account for a country code or formatting characters. The explainer walks through &lt;em&gt;why&lt;/em&gt;: the &lt;code&gt;^&lt;/code&gt; and &lt;code&gt;$&lt;/code&gt; anchors require the entire string to be exactly ten digits with nothing else, so any prefix or spacing breaks the match. That's the kind of thing that's obvious once someone points it out and completely invisible when you're staring at your own pattern for the fifth time.&lt;/p&gt;

&lt;p&gt;Once you've confirmed the pattern in the tester, dropping it into actual JS is the easy part:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mobileRegex&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;(?:\+&lt;/span&gt;&lt;span class="sr"&gt;91&lt;/span&gt;&lt;span class="se"&gt;[\s&lt;/span&gt;&lt;span class="sr"&gt;-&lt;/span&gt;&lt;span class="se"&gt;]?)?[&lt;/span&gt;&lt;span class="sr"&gt;6-9&lt;/span&gt;&lt;span class="se"&gt;]\d{9}&lt;/span&gt;&lt;span class="sr"&gt;$/&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;isValidIndianMobile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&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;cleaned&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\s&lt;/span&gt;&lt;span class="sr"&gt;+/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;mobileRegex&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cleaned&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;isValidIndianMobile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;9876543210&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;       &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;span class="nf"&gt;isValidIndianMobile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;+91 98765 43210&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// true&lt;/span&gt;
&lt;span class="nf"&gt;isValidIndianMobile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;123456789&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;        &lt;span class="c1"&gt;// false — wrong starting digit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the fixed version wraps the optional country code in a non-capturing group &lt;code&gt;(?:\+91[\s-]?)?&lt;/code&gt; — exactly the kind of change that's obvious in the tester's breakdown but easy to miss when you're just eyeballing the raw pattern.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it runs the way it does
&lt;/h2&gt;

&lt;p&gt;Like the rest of the toolkit, this one is 100% client-side. Your pattern and your test string never leave the browser — nothing is sent to a server, nothing is logged. That matters more than it sounds: regex testing often happens &lt;em&gt;on real data&lt;/em&gt; — actual customer emails, actual log lines, actual PII you're trying to write a validator around. A tool that ships that data off to a backend for "processing" is a tool you shouldn't be pasting real data into. This one, you can.&lt;/p&gt;

&lt;p&gt;It also means it's fast. No network round-trip between keystrokes, no debounce lag waiting for a server response — the match highlighting and explanation update instantly because the regex engine is running right there in your tab.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;If you've ever shipped a regex that worked in your one test case and broke in production, this is built for exactly that failure mode. Give it a real pattern — something gnarly from your own codebase — and see if the explanation matches what you &lt;em&gt;thought&lt;/em&gt; it did.&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://samtoolkit.com/tools/regex-tester" rel="noopener noreferrer"&gt;Regex Tester &amp;amp; Explainer on SamToolkit&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's one of 30 free, browser-only developer tools on &lt;a href="https://samtoolkit.com" rel="noopener noreferrer"&gt;SamToolkit&lt;/a&gt; — no sign-up, nothing tracked, nothing uploaded.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;What's the worst regex bug you've shipped? Drop it in the comments — I'll try to break the tester with it.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>regex</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>productivity</category>
    </item>
    <item>
      <title>I Built a Free Color Palette Extractor That Never Uploads Your Images</title>
      <dc:creator>Sarfaraz</dc:creator>
      <pubDate>Tue, 25 Aug 2026 08:33:38 +0000</pubDate>
      <link>https://dev.to/sarfaraznaushad/i-built-a-free-color-palette-extractor-that-never-uploads-your-images-1e21</link>
      <guid>https://dev.to/sarfaraznaushad/i-built-a-free-color-palette-extractor-that-never-uploads-your-images-1e21</guid>
      <description>&lt;p&gt;Ever needed to pull a color scheme out of a photo, a screenshot, or a client's logo — and hesitated for a second because you didn't want to upload it to some random website?&lt;/p&gt;

&lt;p&gt;That hesitation is exactly why I built the &lt;strong&gt;Color Palette Extractor&lt;/strong&gt; on &lt;a href="https://samtoolkit.com/tools/color-palette-extractor" rel="noopener noreferrer"&gt;SamToolkit&lt;/a&gt;, a free, privacy-first developer tools site. Every tool on SamToolkit runs entirely in your browser using Blazor WebAssembly — nothing you upload ever touches a server.&lt;/p&gt;

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

&lt;p&gt;Drop in any image — a photo, a mockup, a brand logo — and the tool analyzes the pixel data locally and returns a ready-to-use palette:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dominant colors&lt;/strong&gt;, ranked by prevalence&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hex, RGB, and HSL&lt;/strong&gt; values for every extracted color, one click to copy&lt;/li&gt;
&lt;li&gt;A quick &lt;strong&gt;preview swatch strip&lt;/strong&gt; so you can eyeball the palette before using it&lt;/li&gt;
&lt;li&gt;Works with PNG, JPG, and WebP&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's built for the moment you're staring at a reference image thinking "I just need these five colors as hex codes" — no account, no upload limit, no image leaving your machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why client-side matters here
&lt;/h2&gt;

&lt;p&gt;Color extraction is a genuinely good fit for running entirely in-browser:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Privacy&lt;/strong&gt; — brand assets, unreleased designs, or client photos never leave your device.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Speed&lt;/strong&gt; — no round trip to a server means the palette appears almost instantly after you drop the file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No limits&lt;/strong&gt; — since there's no backend processing cost, there's no arbitrary cap on how many images you can run through it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Under the hood, the tool decodes the image into a pixel buffer, buckets similar colors together (so you get a clean palette instead of 40 near-identical shades of blue), and sorts by frequency — all inside WebAssembly, at native-ish speed.&lt;/p&gt;

&lt;h2&gt;
  
  
  A quick example
&lt;/h2&gt;

&lt;p&gt;Say you're building a landing page and the client sent over a product photo with a color scheme you want to match. Instead of eyeballing hex codes in a screenshot tool:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the &lt;a href="https://samtoolkit.com/tools/color-palette-extractor" rel="noopener noreferrer"&gt;Color Palette Extractor&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Drag the image in&lt;/li&gt;
&lt;li&gt;Copy the hex codes straight into your CSS:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nd"&gt;:root&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--color-primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#2f6f4f&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--color-secondary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#e8b04b&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--color-accent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#c1443c&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="py"&gt;--color-neutral&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#f4f1ea&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Done — no design software required.&lt;/p&gt;

&lt;h2&gt;
  
  
  Part of a bigger toolkit
&lt;/h2&gt;

&lt;p&gt;The Color Palette Extractor is one of about 30 browser-based utilities on SamToolkit — JSON formatters, image converters, text diff tools, and more, all following the same rule: &lt;strong&gt;nothing leaves your browser.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If you try it out, I'd love to hear what you think — and if there's a color or design tool you wish existed, drop it in the comments.&lt;/p&gt;

&lt;p&gt;🔗 &lt;a href="https://samtoolkit.com/tools/color-palette-extractor" rel="noopener noreferrer"&gt;Try the Color Palette Extractor&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>css</category>
      <category>design</category>
      <category>opensource</category>
    </item>
    <item>
      <title>3 Free CSS Generators That'll Save You From Fighting box-shadow, gradient, and border-radius Syntax</title>
      <dc:creator>Sarfaraz</dc:creator>
      <pubDate>Tue, 25 Aug 2026 02:30:39 +0000</pubDate>
      <link>https://dev.to/sarfaraznaushad/3-free-css-generators-thatll-save-you-from-fighting-box-shadow-gradient-and-border-radius-syntax-3fa6</link>
      <guid>https://dev.to/sarfaraznaushad/3-free-css-generators-thatll-save-you-from-fighting-box-shadow-gradient-and-border-radius-syntax-3fa6</guid>
      <description>&lt;p&gt;If you've ever spent 20 minutes nudging &lt;code&gt;box-shadow&lt;/code&gt; offsets, or hand-writing a &lt;code&gt;linear-gradient()&lt;/code&gt; only to get the angle wrong three times in a row, you already know: CSS values that &lt;em&gt;look&lt;/em&gt; simple are often the most fiddly to write from memory.&lt;/p&gt;

&lt;p&gt;That's exactly the itch these three tools scratch. They're all free, run entirely in your browser (nothing gets uploaded anywhere), and give you a live preview so you can just drag sliders until it looks right — then copy the generated CSS straight into your stylesheet.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Box Shadow Generator
&lt;/h2&gt;

&lt;p&gt;Getting a shadow to look "just right" — not too harsh, not invisible — usually means tweaking four or five values at once: horizontal offset, vertical offset, blur radius, spread radius, and color/opacity. Doing that by editing raw CSS and refreshing the browser every time is slow.&lt;/p&gt;

&lt;p&gt;The Box Shadow Generator gives you sliders for each of these with an instant live preview, plus support for multiple stacked shadows (handy for that popular "layered depth" look). When it looks right, hit copy and you've got production-ready CSS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Good for:&lt;/strong&gt; card hover effects, modal elevation, neumorphic UI, button depth.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Gradient Generator
&lt;/h2&gt;

&lt;p&gt;Gradients are deceptively easy to get wrong — the wrong angle or color stop placement can make a background look muddy instead of smooth. This tool lets you pick your colors, drag stop positions, and switch between linear and radial gradients while watching the preview update live.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Good for:&lt;/strong&gt; hero backgrounds, button fills, subtle section dividers, dark-mode-friendly overlays.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Border Radius Generator
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;border-radius&lt;/code&gt; looks like a one-value property until you need asymmetric corners (top-left different from bottom-right) for that "organic blob" or "squircle" look that's become common in modern UI design. Writing that shorthand by hand — &lt;code&gt;border-radius: 30% 70% 70% 30% / 30% 30% 70% 70%&lt;/code&gt; — from memory is basically impossible.&lt;/p&gt;

&lt;p&gt;This generator lets you drag each corner independently and see the shape update in real time, so you can land on something that actually looks intentional instead of guessing at percentages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Good for:&lt;/strong&gt; blob shapes, avatar frames, illustrative UI accents, card corner treatments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why browser-based tools like these are worth bookmarking
&lt;/h2&gt;

&lt;p&gt;None of these three require an account, a download, or sending your design data anywhere — everything runs client-side. That matters if you care about privacy, but it also just means they're fast: no loading spinners, no server round-trips, just instant feedback as you adjust values.&lt;/p&gt;

&lt;p&gt;You can find all three (and more browser-based dev utilities) at &lt;a href="https://samtoolkit.com" rel="noopener noreferrer"&gt;samtoolkit.com&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Which of these do you reach for most — shadows, gradients, or border-radius? Let me know in the comments.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>css</category>
      <category>webdev</category>
      <category>frontend</category>
      <category>tools</category>
    </item>
  </channel>
</rss>
