<?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: Akbar Ali</title>
    <description>The latest articles on DEV Community by Akbar Ali (@akbrews).</description>
    <link>https://dev.to/akbrews</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%2F794441%2Ff731ca65-4e67-41c2-859c-1fc457169f43.jpeg</url>
      <title>DEV Community: Akbar Ali</title>
      <link>https://dev.to/akbrews</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/akbrews"/>
    <language>en</language>
    <item>
      <title>Sign it without leaving the page</title>
      <dc:creator>Akbar Ali</dc:creator>
      <pubDate>Sun, 13 Sep 2026 17:06:31 +0000</pubDate>
      <link>https://dev.to/akbrews/sign-it-without-leaving-the-page-4mjb</link>
      <guid>https://dev.to/akbrews/sign-it-without-leaving-the-page-4mjb</guid>
      <description>&lt;p&gt;A guy sent me a screen recording last month. No message with it. Just the video.&lt;/p&gt;

&lt;p&gt;It's his onboarding. Five steps, looks great, he clearly spent time on it. Then step six yanks the user out to our app. Our colours, our domain, our everything.&lt;/p&gt;

&lt;p&gt;And you can see the poor user pause. Like, hold on, who am I signing with now?&lt;/p&gt;

&lt;p&gt;He never said a word about it. Just the video. Somehow that's worse than a complaint.&lt;/p&gt;

&lt;p&gt;Fine. We built an API. And an embed. Here's the whole thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The API just calls the same stuff the UI calls
&lt;/h2&gt;

&lt;p&gt;This is the only decision I actually thought about.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;requireApiCaller&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;request&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ApiCaller&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...check the bearer key, hand back a `SessionUser`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See the return type? &lt;code&gt;SessionUser&lt;/code&gt;. That's it, that's the whole idea.&lt;/p&gt;

&lt;p&gt;Because &lt;code&gt;createDocumentFromTemplate&lt;/code&gt; takes one. &lt;code&gt;getTemplate&lt;/code&gt; takes one. The quota check takes one. They all already take one. So the API doesn't get to invent its own rules about who can touch what. It borrows the rules that were already sitting there.&lt;/p&gt;

&lt;p&gt;Two copies of "is this person allowed" is two chances to screw it up. And they always drift. You look away for a week and they're different.&lt;/p&gt;

&lt;p&gt;Endpoints, nothing exciting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET  /v1/templates
POST /v1/templates/:id/send
GET  /v1/documents
GET  /v1/documents/:id
GET  /v1/documents/:id/file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The list uses a cursor instead of &lt;code&gt;?page=2&lt;/code&gt;. Had to. It's sorted by &lt;code&gt;updated_at&lt;/code&gt; and that thing moves constantly, every time somebody opens a document or signs or comments. So rows keep jumping around. Page through that with offsets and you'll skip stuff, silently, and never know. Grabbing one extra row tells you if there's more without a count query.&lt;/p&gt;

&lt;p&gt;Errors look the same everywhere:&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;"error"&lt;/span&gt;&lt;span class="p"&gt;:&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;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"quota_exceeded"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"You have sent 10 of 10 documents this month."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"quota"&lt;/span&gt;&lt;span class="p"&gt;:&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;span class="nl"&gt;"used"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"limit"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"resets_at"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-10-01T00:00:00.000Z"&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;span class="p"&gt;}&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;Check &lt;code&gt;code&lt;/code&gt;, not the message. The message is for you at 2am with your face in the logs (or me, let's be honest).&lt;/p&gt;

&lt;p&gt;You know what actually ate the most time? Making sure you never get HTML back. React Router throws &lt;code&gt;Response&lt;/code&gt;s all over. A 404 here, a 429 from the rate limiter there, and a &lt;code&gt;redirect&lt;/code&gt; that shoves the browser at the marketing page. All perfectly fine when a human is looking at it. All completely useless to your &lt;code&gt;res.json()&lt;/code&gt;. So everything goes through one wrapper that cleans it up, and that redirect becomes a &lt;code&gt;401&lt;/code&gt;, because your script does not have a session to go fix, does it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The embed, and the thing that nearly bit me
&lt;/h2&gt;

&lt;p&gt;Send with &lt;code&gt;embed: true&lt;/code&gt;, we skip the emails entirely, you get a URL per person:&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;"embed"&lt;/span&gt;&lt;span class="p"&gt;:&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;span class="nl"&gt;"expires_in"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1800&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"sessions"&lt;/span&gt;&lt;span class="p"&gt;:&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;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"recipient_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rcp_b4c1..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://putmysign.com/embed/sign/GfT9…"&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;span class="p"&gt;]&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;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;Shove it in an iframe yourself if you want. Or use the script, which is honestly just an iframe and a &lt;code&gt;message&lt;/code&gt; listener, that's it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"sign"&lt;/span&gt; &lt;span class="na"&gt;style=&lt;/span&gt;&lt;span class="s"&gt;"height: 800px"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/div&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://putmysign.com/embed.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;Putmysign&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#sign&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="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;onSigned&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;finish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentId&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;onDeclined&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;bail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentId&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;mount&lt;/code&gt; hands you back a &lt;code&gt;destroy()&lt;/code&gt;. Please call it. Swap that container out in a SPA without it and the old listener is still hanging around, so next time every single handler fires twice and you sit there going "why did it save twice". I found this out the way everybody finds this out.&lt;/p&gt;

&lt;p&gt;Ok now the good part.&lt;/p&gt;

&lt;p&gt;Our normal signing page, the one from the email, cannot be put in a frame by anyone. &lt;code&gt;frame-ancestors 'none'&lt;/code&gt;. Nobody. The only reason the embed one can be framed is that a customer told us, with a real authenticated key, which of their own domains to trust.&lt;/p&gt;

&lt;p&gt;So the URL you get isn't the actual signing token. It's a little sealed box around it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;EmbedPayload&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;t&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;    &lt;span class="c1"&gt;// the real signing token&lt;/span&gt;
  &lt;span class="nl"&gt;k&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;    &lt;span class="c1"&gt;// which API key made this&lt;/span&gt;
  &lt;span class="nl"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// epoch ms&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;k&lt;/code&gt; is doing all the work. Opening the box is how the route finds out which origins are allowed to frame the page. So the CSP gets built fresh on every request off the key, not parked in some static header.&lt;/p&gt;

&lt;p&gt;And if your key has no origins set? You get &lt;code&gt;'none'&lt;/code&gt;. It renders nowhere. Blank box.&lt;/p&gt;

&lt;p&gt;Felt kind of mean when I wrote it. Some dev wires it all up, gets nothing, curses my name.&lt;/p&gt;

&lt;p&gt;But the other way is defaulting to open, and defaulting to open means somebody's half finished Friday afternoon setup is now a signing page that literally any website can wrap in a frame and aim at their own users. Nah. Blank box it is.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If a default is going to fail, make it fail loud. The quiet ones become bugs nobody ever reports.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The 30 minute expiry is the same paranoia from the other direction. An emailed link has to survive a week because people open those things next Tuesday. But that's the worst possible property for something you hand to somebody else's webpage, where it just sits in their DOM and their logs and their analytics the entire time it's valid. So the expiry lives inside the sealed box. Can't stretch it by messing with something next to it. No table, no cleanup job, nothing to forget about.&lt;/p&gt;

&lt;h2&gt;
  
  
  Webhooks, because the iframe only sees its own little world
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;onSigned&lt;/code&gt; means that one person signed. Cool. Three other people might still owe you a signature.&lt;/p&gt;

&lt;p&gt;For "ok it's actually done now", you want these:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;document.sent · recipient.viewed · recipient.signed
recipient.declined · recipient.commented · document.completed
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Signed &lt;code&gt;t=&amp;lt;timestamp&amp;gt;,v1=&amp;lt;hmac&amp;gt;&lt;/code&gt;, five tries over about a day, at least once delivery. At least once, meaning yes, the same event will show up twice sometimes. Check the &lt;code&gt;id&lt;/code&gt;, throw away the repeat.&lt;/p&gt;

&lt;p&gt;There's a signature checker right on the docs page that runs the actual function our dispatcher signs with. Paste in a real delivery and you'll know in ten seconds if it's your code or mine.&lt;/p&gt;

&lt;p&gt;It's raw body vs parsed body. I'm telling you now. It's always raw body vs parsed body.&lt;/p&gt;




&lt;p&gt;That's it really. Docs at &lt;a href="https://putmysign.com/docs/api" rel="noopener noreferrer"&gt;putmysign.com/docs/api&lt;/a&gt;, keys and webhooks live under Developers, and test keys do the full run without emailing anybody or charging you.&lt;/p&gt;

&lt;p&gt;The bit I'm still chewing on is that origins default. Have you shipped an embed thing like this? Did you make people set up the allowlist before anything works, or let them start wide open and nag them later? And did anyone actually ever go back and fix it?&lt;/p&gt;

</description>
      <category>api</category>
      <category>webhooks</category>
      <category>security</category>
      <category>showdev</category>
    </item>
    <item>
      <title>True or false?</title>
      <dc:creator>Akbar Ali</dc:creator>
      <pubDate>Fri, 04 Sep 2026 15:54:06 +0000</pubDate>
      <link>https://dev.to/akbrews/true-or-false-4c1a</link>
      <guid>https://dev.to/akbrews/true-or-false-4c1a</guid>
      <description>&lt;p&gt;Someone emailed me a signed contract, and I forwarded it to myself to test something.&lt;/p&gt;

&lt;p&gt;Outlook did what Outlook does. Re-saved the attachment on the way through. Same contract, same signatures, same everything a human would care about.&lt;/p&gt;

&lt;p&gt;Different bytes.&lt;/p&gt;

&lt;p&gt;My verifier, which at that point returned a boolean, looked at it and said &lt;strong&gt;invalid&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That's the moment I realised I had built a liar (a very confident one, which is worse).&lt;/p&gt;




&lt;h2&gt;
  
  
  The thing I got wrong
&lt;/h2&gt;

&lt;p&gt;I'm building &lt;a href="https://putmysign.com" rel="noopener noreferrer"&gt;Putmysign&lt;/a&gt;, a small app for sending PDFs out and collecting signatures. At the end of it there's a &lt;code&gt;/verify&lt;/code&gt; page: you drop a signed PDF on it, and it tells you whether the thing in your hands is real.&lt;/p&gt;

&lt;p&gt;The obvious design is a checkbox. Valid or not valid. Green or red.&lt;/p&gt;

&lt;p&gt;The obvious design is wrong, and it took a mail client to teach me why.&lt;/p&gt;

&lt;p&gt;Because "these are not the exact bytes I produced" and "this is a forgery" are &lt;em&gt;not the same sentence&lt;/em&gt;. I had been printing the second one every time I meant the first.&lt;/p&gt;

&lt;p&gt;If you tell someone holding a perfectly real contract that it's fake, you haven't built a security feature. You've built a way to ruin somebody's Tuesday.&lt;/p&gt;




&lt;h2&gt;
  
  
  Two checks, not one
&lt;/h2&gt;

&lt;p&gt;So the verdict comes from two completely independent checks, which fail for completely different reasons.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One.&lt;/strong&gt; Does the SHA-256 of this file match a document we finalized? That's the strongest answer there is: these are the exact bytes we made. This is the only check that needs a database row.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Two.&lt;/strong&gt; Is the certificate &lt;em&gt;inside&lt;/em&gt; the file authentically ours?&lt;/p&gt;

&lt;p&gt;That second one needs explaining. When a document is finalized, I stamp a small HMAC-signed payload into the PDF's &lt;code&gt;Keywords&lt;/code&gt; metadata. Who signed, when, the hash of the original, the document id. Base64url, because &lt;code&gt;Keywords&lt;/code&gt; is a flat PDF string and JSON's quotes and parentheses have to be escaped correctly by every reader that ever touches the file (one opaque token has nothing to get wrong).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MANIFEST_PREFIX&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;putmysign-certificate-v1:&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;encodeManifest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;manifest&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;CertificateManifest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&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;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;manifestPayload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;manifest&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;MANIFEST_PREFIX&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;}${&lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;`.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&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;The point of that: the document can speak for itself.&lt;/p&gt;

&lt;p&gt;My retention job deletes files on a schedule, by design. Someone downgrades, records age out, a mail client re-saves an attachment. In every one of those cases the byte-for-byte lookup finds nothing, and the only thing left in the world that can still say &lt;em&gt;we issued this, for this document, with these signers&lt;/em&gt; is the certificate riding along inside the file.&lt;/p&gt;

&lt;p&gt;A verifier that only recognises documents you still hold a row for will fail on everything you promised to delete.&lt;/p&gt;




&lt;h2&gt;
  
  
  Five outcomes
&lt;/h2&gt;

&lt;p&gt;So there are five, strongest first:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;verified&lt;/code&gt;: the bytes match a document we finalized. Nothing has changed.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;certified&lt;/code&gt;: the certificate is authentically ours, but the bytes aren't the ones we made. &lt;strong&gt;The signings happened. This particular file just isn't the artifact we issued.&lt;/strong&gt; This is my Outlook contract.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;self-signed&lt;/code&gt;: signed with the free browser-only tool. Nothing witnessed it, so the file's account of itself is all there is.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tampered&lt;/code&gt;: carries a certificate that does not check out.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;unknown&lt;/code&gt;: no certificate, no matching record. Not one of ours.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The middle three are the whole post. Collapsing any of them into "invalid" would be the wrong answer more often than the right one, and I have the receipts (one receipt, from my own inbox, but still).&lt;/p&gt;

&lt;p&gt;There's a smaller decision in there I'm oddly proud of. A certificate with &lt;em&gt;no signature at all&lt;/em&gt; is reported as &lt;code&gt;unknown&lt;/code&gt;, not &lt;code&gt;tampered&lt;/code&gt;, because that's what a document looks like when it was finalized while no signing key was configured. A signature that's present and wrong is a different matter entirely. Saying "forged" to someone holding a real contract is the worst mistake this endpoint could possibly make, so it only says it when it's actually sure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;verdict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;unknown&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;certificateIsAuthentic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;signature&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;verdict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;tampered&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;verdict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;certified&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;h2&gt;
  
  
  The self-signed one
&lt;/h2&gt;

&lt;p&gt;The free tool on the landing page signs a PDF entirely in the browser. The file never touches my server. That's the promise, and I'm not breaking it to make my verifier's life easier.&lt;/p&gt;

&lt;p&gt;Which means: nothing witnessed it. Nothing &lt;em&gt;could&lt;/em&gt; have witnessed it.&lt;/p&gt;

&lt;p&gt;I still write a certificate into it. Unsigned, marked &lt;code&gt;kind: "self"&lt;/code&gt;, and worth exactly nothing as proof.&lt;/p&gt;

&lt;p&gt;But it means &lt;code&gt;/verify&lt;/code&gt; can say "this was signed with Putmysign, by this name, on this date, and we were deliberately not involved", instead of reporting my own product's output as a stranger.&lt;/p&gt;

&lt;p&gt;A claim about a file isn't proof, but it beats a shrug.&lt;/p&gt;

&lt;p&gt;(The verify page keeps the same promise, by the way. Your PDF is read with &lt;code&gt;File.arrayBuffer()&lt;/code&gt; and never uploaded. What goes to the server is a 32-byte one-way digest and the certificate that was already in the file. Neither of those is content.)&lt;/p&gt;




&lt;h2&gt;
  
  
  The bit I'd do differently
&lt;/h2&gt;

&lt;p&gt;HMAC was the cheap choice. One environment variable, no key distribution, no ceremony.&lt;/p&gt;

&lt;p&gt;The cost is that the check has to run on my server. A sceptical counterparty, the exact person this feature exists for, cannot verify a certificate without asking me to confirm my own honesty, which is a hilarious thing to build into a trust product.&lt;/p&gt;

&lt;p&gt;A public key fixes it. That's the better end state and I haven't done it yet (I will, right after the eleven other things).&lt;/p&gt;




&lt;p&gt;Anyway. My actual take, one sentence:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A verifier that can only say yes or no will spend most of its life saying the wrong one.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Have you had a boolean like this in your own code, a flag that quietly collapsed two very different failures into one answer? I'd genuinely like to hear about it, mostly so I feel less alone about the mail client thing.&lt;/p&gt;

</description>
      <category>security</category>
      <category>webdev</category>
      <category>typescript</category>
      <category>showdev</category>
    </item>
    <item>
      <title>PDF Rendering in browsers</title>
      <dc:creator>Akbar Ali</dc:creator>
      <pubDate>Mon, 31 Aug 2026 14:02:55 +0000</pubDate>
      <link>https://dev.to/akbrews/pdf-rendering-in-browsers-1jfd</link>
      <guid>https://dev.to/akbrews/pdf-rendering-in-browsers-1jfd</guid>
      <description>&lt;h2&gt;
  
  
  Why was the certificate blank?
&lt;/h2&gt;

&lt;p&gt;A user sent me a screenshot of a signed document.&lt;/p&gt;

&lt;p&gt;The signature was there. The certificate page behind it was empty. Not broken, not garbled. Just a clean white page with nothing on it, like the PDF gave up halfway through.&lt;/p&gt;

&lt;p&gt;Worked fine on my machine, obviously. It always does.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the preview works
&lt;/h2&gt;

&lt;p&gt;You can't hand a browser a raw PDF and call it a day (not when the file is private and every page needs an access check). So each page gets rendered on the server into a JPEG.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pdftoppm&lt;/code&gt; does the render. One page, scaled to a fixed width, quality 82, into a temp dir that gets torn down in a finally block. The result goes into object storage under a cache key, and that write is best effort. If the cache write fails you still get your page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cached&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cached&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;cached&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;pdf&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetchPdfAsset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&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;jpeg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;renderPageWithPoppler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Simple enough. That part was never the problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem was Alpine
&lt;/h2&gt;

&lt;p&gt;PDFs have fourteen "base" fonts. Helvetica, Times, Courier and friends. The spec says every viewer must have them, so writing tools don't bother embedding them. Why ship a font everyone already has?&lt;/p&gt;

&lt;p&gt;Everyone, except a &lt;code&gt;node:24-alpine&lt;/code&gt; container.&lt;/p&gt;

&lt;p&gt;Alpine ships with basically no fonts (that's the whole point of Alpine). So &lt;code&gt;pdftoppm&lt;/code&gt; opens the PDF, reads "draw this in Helvetica", goes looking for something to substitute with, finds absolutely nothing, and draws nothing at all. Correctly, by its own logic.&lt;/p&gt;

&lt;p&gt;No error. No warning. Exit code &lt;code&gt;0&lt;/code&gt;. A perfectly successful render of a blank page.&lt;/p&gt;

&lt;p&gt;That's the worst kind of bug. It didn't fail. It agreed with me.&lt;/p&gt;

&lt;p&gt;The fix is one line in the Dockerfile: install Liberation fonts. They're metric compatible with the base-14 families, so nothing shifts around.&lt;/p&gt;

&lt;h2&gt;
  
  
  The other font problem
&lt;/h2&gt;

&lt;p&gt;While I was in there I found something worse, and this one didn't crash either.&lt;/p&gt;

&lt;p&gt;The app shows typed signatures in Dancing Script, the loopy face that reads like handwriting. The generated PDF was drawing them in Helvetica Oblique.&lt;/p&gt;

&lt;p&gt;So you type your name, watch it appear in nice script, click sign, download the file, and get slanted Helvetica. Not wrong exactly. Just not what you agreed to.&lt;/p&gt;

&lt;p&gt;For an app whose entire pitch is "the document is exactly what you saw", that's not a cosmetic bug.&lt;br&gt;
So Dancing Script got vendored into the repo and embedded straight into the PDF with fontkit, subsetted so only the glyphs you actually used come along for the ride.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;embedSignatureFont&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pdf&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PDFDocument&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;PDFFont&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;pdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;registerFontkit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fontkit&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;pdf&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;embedFont&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;subset&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The font is inlined as a data URI at build time instead of read off disk. The production image only copies &lt;code&gt;build/&lt;/code&gt;, so a runtime file read would have found nothing (the blank page bug again, wearing a different hat).&lt;br&gt;
Embedding fixed both ends at once. The download matches the screen now, and the renderer doesn't need the font installed anywhere, because the font is sitting inside the file.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I actually learned
&lt;/h2&gt;

&lt;p&gt;Fonts are a deployment concern. I'd have filed them under "design" on any list you handed me, right up until a container with no fonts quietly produced legal documents with nothing written on them.&lt;/p&gt;

&lt;p&gt;And a renderer that exits &lt;code&gt;0&lt;/code&gt; will lie to you. Check the pixels, not the exit code.&lt;/p&gt;

&lt;p&gt;The certificate page got rebuilt in the same pass. Three big signer cards became compact rows, seven to a page, readable timestamps, a pointer to &lt;code&gt;/verify&lt;/code&gt; at the bottom. It's the page almost nobody opens, right until the one day somebody really needs it.&lt;/p&gt;

&lt;p&gt;That's most of what building &lt;a href="//putmysign.com"&gt;Putmysign&lt;/a&gt; has been, honestly. Polishing things people will hopefully never have to look at.&lt;/p&gt;

&lt;p&gt;Anyone else been bitten by a slim base image quietly dropping something? I want to hear the worse ones.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>I built an e-signature app that charges for what actually costs money</title>
      <dc:creator>Akbar Ali</dc:creator>
      <pubDate>Sat, 22 Aug 2026 14:06:54 +0000</pubDate>
      <link>https://dev.to/akbrews/i-built-an-e-signature-app-that-charges-you-for-storage-not-signatures-23g4</link>
      <guid>https://dev.to/akbrews/i-built-an-e-signature-app-that-charges-you-for-storage-not-signatures-23g4</guid>
      <description>&lt;h2&gt;
  
  
  The bill that made no sense
&lt;/h2&gt;

&lt;p&gt;A signature is about 400 bytes of JSON and a hash.&lt;/p&gt;

&lt;p&gt;Every big e-signature product will charge you somewhere between $1 and $4 for each document you send. They call it an "envelope". On top of that you pay per seat, so every colleague who needs to send a contract is another line on the invoice. Run out of envelopes on the 22nd of the month and your deal waits until the 1st. I have watched a real contract sit still because a counter hit zero.&lt;/p&gt;

&lt;p&gt;So I looked at what an e-signature product actually costs to run, and priced &lt;a href="https://putmysign.com" rel="noopener noreferrer"&gt;Putmysign&lt;/a&gt; around that.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually costs money
&lt;/h2&gt;

&lt;p&gt;Storage is the obvious guess, and it is wrong. Uploads are capped at 5 MB. A heavy account sends a few hundred documents a year, which is a few gigabytes. At object storage prices that is cents. Per year.&lt;/p&gt;

&lt;p&gt;The cost that actually grows with use is &lt;strong&gt;sending a document&lt;/strong&gt;: an email to every recipient, reminders, and the bandwidth when they open the PDF. Fractions of a cent each time, but it is the one number that scales.&lt;/p&gt;

&lt;p&gt;It is also the number customers already think in. Nobody knows how many gigabytes they store. Everyone knows roughly how many contracts they send.&lt;/p&gt;

&lt;p&gt;So Putmysign meters exactly one thing, documents sent per month:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Free:&lt;/strong&gt; 10 documents a month, up to 10 recipients each, signing links live up to 30 days&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pro, $19/month:&lt;/strong&gt; 100 documents a month, up to 50 recipients each, links live up to a year, automatic reminders&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Every plan:&lt;/strong&gt; files and audit trail kept forever, unlimited signatures, unlimited reusable templates, no seats, recipients never make an account&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Drafts don't count. The allowance resets on the 1st of the month (UTC), not on a rolling 30-day window, so you always know when it comes back.&lt;/p&gt;

&lt;p&gt;On Pro that works out to 19 cents a document, against $1 to $4 elsewhere.&lt;/p&gt;

&lt;p&gt;That one pricing decision shaped most of the technical choices underneath. That is the interesting part.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Keep the file, and keep the proof
&lt;/h2&gt;

&lt;p&gt;Every signed PDF is kept. Storage is cheap, so there is no reason to make anyone think about retention.&lt;/p&gt;

&lt;p&gt;The file alone is not proof, though. So every document records two hashes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;originalHash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;originalBytes&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// signatures applied, certificate page added&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;finalHash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sha256&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;finalBytes&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The original is hashed on upload, the final one after the signatures and the audit certificate page are applied. If you download your copy, you can hash it and check it against a record I cannot quietly change. Your proof does not depend on trusting my storage.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Recipients never make an account
&lt;/h2&gt;

&lt;p&gt;A signup wall in the middle of someone else's contract is the biggest reason paperwork stalls. It mostly exists so vendors can count seats.&lt;/p&gt;

&lt;p&gt;So the signing link is the login. It is an HMAC over the document and the recipient, built from a server-only secret. Only the hash of the token is stored:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;secret&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SIGNING_TOKEN_SECRET&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SESSION_SECRET&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;must be at least 32 characters&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;createHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sha256&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hex&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;Store the hash, not the token. If my database leaks tomorrow, nobody gets a working signing link out of it. Same idea as hashing passwords, applied to a URL.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Whatever you meter, check what slips past it
&lt;/h2&gt;

&lt;p&gt;If you count documents, a single document with unlimited recipients is an unmetered mailing list. Someone on the free plan sent one document to 664 addresses, every one of them an email from my domain.&lt;/p&gt;

&lt;p&gt;So there is a per-document recipient cap: 10 on Free, 50 on Pro. Real approval rounds rarely go past a handful of people, and the cap closes the gap the meter left open.&lt;/p&gt;

&lt;p&gt;Any usage-based price has a hole like this. Find it before someone else does.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. One row per document
&lt;/h2&gt;

&lt;p&gt;A signing session is a burst of tiny writes. Opened. Viewed page 3. Signed field 2. IP recorded. All of it against one document that is only ever read as a whole.&lt;/p&gt;

&lt;p&gt;Splitting that across six tables buys me joins I never run. So each document is one JSONB column holding everything, with the fields I actually filter on copied out into real indexed columns: owner, status, updated date. A GIN index on the JSON handles signing-link and "shared with me" lookups.&lt;/p&gt;

&lt;p&gt;The real risk with a single row is two people signing at the same moment. Last write wins, one signature disappears. I fixed it the boring way, with &lt;code&gt;SELECT ... FOR UPDATE&lt;/code&gt; inside a transaction.&lt;/p&gt;

&lt;p&gt;Postgres has been a fine document database for years, and it still hands you row locks when you need them.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Thumbnails without a thumbnail service
&lt;/h2&gt;

&lt;p&gt;Page previews come from &lt;code&gt;pdftoppm&lt;/code&gt;, part of poppler. It is a 25 year old C program. I shell out to it, render the page when someone asks for it, and cache the image back into the bucket next to the PDF.&lt;/p&gt;

&lt;p&gt;No render service, no queue, no third party API with a per page price on it. When your price is per document, anything that adds a per-page cost underneath eats straight into it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The stack, quickly
&lt;/h2&gt;

&lt;p&gt;React Router 7, TypeScript, Postgres with Prisma, any S3 compatible bucket (MinIO locally, so the whole thing runs on a laptop with one &lt;code&gt;docker compose up&lt;/code&gt;), Firebase Auth for owners only, Resend for email with bounce handling, Paddle for billing.&lt;/p&gt;

&lt;p&gt;Nothing fancy. The interesting decisions are all about what is missing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Things worth stealing
&lt;/h2&gt;

&lt;p&gt;You do not have to care about e-signatures to use any of this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Charge for what actually costs you money, and check the bill before deciding what that is.&lt;/strong&gt; It is often not what you'd guess.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Meter the unit customers already think in.&lt;/strong&gt; Documents sent, not gigabytes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Whatever you meter, look for what slips past it.&lt;/strong&gt; Count documents and someone will put 664 people on one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep checkable proof, not just the file.&lt;/strong&gt; Hashes are tiny and they make your records verifiable without trusting you.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A signup wall in someone else's workflow is a tax on your own customer.&lt;/strong&gt; They pay it in deals that stall.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;JSONB, plus a few copied-out indexed columns, plus &lt;code&gt;FOR UPDATE&lt;/code&gt;&lt;/strong&gt; covers a lot of "we need a document database" situations.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It is live at &lt;strong&gt;&lt;a href="https://putmysign.com" rel="noopener noreferrer"&gt;putmysign.com&lt;/a&gt;&lt;/strong&gt;. Free tier, no card. Upload a PDF you already have and send it to someone who will never make an account.&lt;/p&gt;

&lt;p&gt;If you can break the signing link logic, I would genuinely like to hear about it in the comments.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What is the most backwards pricing unit you have run into? I will start: per seat pricing on a read only viewer.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>showdev</category>
      <category>postgres</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Translate apps in one command</title>
      <dc:creator>Akbar Ali</dc:creator>
      <pubDate>Wed, 13 Aug 2025 06:04:17 +0000</pubDate>
      <link>https://dev.to/akbrews/translate-apps-in-one-command-3mll</link>
      <guid>https://dev.to/akbrews/translate-apps-in-one-command-3mll</guid>
      <description>&lt;p&gt;Check out the package first:&lt;br&gt;
&lt;a href="https://www.npmjs.com/package/@pulimoodan/localiser" rel="noopener noreferrer"&gt;@pulimoodan/localiser&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Translate i18n locales with one command&lt;/li&gt;
&lt;li&gt;Translate only required languages (only the ones we changed)&lt;/li&gt;
&lt;li&gt;Translate only the required namespaces (yeah, same as above: only the ones we changed)&lt;/li&gt;
&lt;li&gt;Translate any language, with any iso codes: like pt or pt-BR (because, we're using AI. Not a flex btw)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Concerns to be addressed
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Optimise the Open AI requests with the only the required keys, instead of whole json file&lt;/li&gt;
&lt;li&gt;An init command to generate the project configuration &lt;/li&gt;
&lt;li&gt;Doesn't support different types of folder structures, only: lang/namespace.json&lt;/li&gt;
&lt;li&gt;Configure different types of models&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Contribute (or detract)
&lt;/h2&gt;

&lt;p&gt;If you've got an idea to improve or play around with this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fork the repo &lt;/li&gt;
&lt;li&gt;Work your magic, cook well&lt;/li&gt;
&lt;li&gt;Slam a PR&lt;/li&gt;
&lt;li&gt;And I will think about it (kidding, we need those contributions)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Link to GitHub repo: &lt;a href="https://github.com/pulimoodan/localiser" rel="noopener noreferrer"&gt;Localiser&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>ai</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Code pollution</title>
      <dc:creator>Akbar Ali</dc:creator>
      <pubDate>Sun, 03 Aug 2025 06:04:11 +0000</pubDate>
      <link>https://dev.to/akbrews/code-pollution-49fp</link>
      <guid>https://dev.to/akbrews/code-pollution-49fp</guid>
      <description>&lt;p&gt;The list of &lt;code&gt;PR&lt;/code&gt;s to be reviewed, getting long and long each day in numbers.&lt;/p&gt;

&lt;p&gt;The dashboard, looking at me with that weird devil laugh every time I open the Github.&lt;/p&gt;

&lt;p&gt;It's not me, being lazy and stubborn to keep reviewing everything consistently (maybe).&lt;/p&gt;

&lt;p&gt;It's some dry dreaded lines of waste, of code changes being written in those files that make the stubborn I am.&lt;/p&gt;

&lt;p&gt;The AI slop, waiting on those changes to get refuge in our codebases are becoming a nightmare day by day.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Cause
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Developers being lazy to write code. They're not here for passion (or they lost it), just for money.&lt;/li&gt;
&lt;li&gt;They don't care about what they (or the agent) write, they're just focused on completing the task due to work pressure.&lt;/li&gt;
&lt;li&gt;They're not good at this, maybe they're some product managers who focus on the app but not technology under the hood. &lt;/li&gt;
&lt;li&gt;The FOMO of using AI to get tasks move quickly and wasting time (most of the medias are responsible for this, the me before a few months would assure this).&lt;/li&gt;
&lt;li&gt;People being developers instead of poets (The art of beautiful code is long gone).&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Solution
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Review PRs effectively (I used to rewrite some my own, instead of educating the PR author)&lt;/li&gt;
&lt;li&gt;Talk to your teammates and motivate them (there had been one of my friends who quoted about a fellow's code: I got goosebumps reading it)&lt;/li&gt;
&lt;li&gt;As a developer, think about a problem before asking it to the agent. Share your thoughts and get feedback.&lt;/li&gt;
&lt;li&gt;Try to use AI for automation, not to be dumb day by day (The ability to think will fade away, aware of that).&lt;/li&gt;
&lt;li&gt;Proofread the code written by the agent, and instruct how to make it well written.&lt;/li&gt;
&lt;li&gt;Sometimes writing with your own hands feels like greatness being achieved (don't listen to the FOMO creators).&lt;/li&gt;
&lt;li&gt;Learn to code or learn to prompt (be good at it, sometimes it is easier to express in code than in natural language).&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;I could have become a project manager 20 years ago if I didn’t care to write code myself and I just wanted outcomes.&lt;br&gt;
-- DHH&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>productivity</category>
      <category>programming</category>
    </item>
    <item>
      <title>Common JS library import in ES Module</title>
      <dc:creator>Akbar Ali</dc:creator>
      <pubDate>Mon, 21 Jul 2025 07:15:13 +0000</pubDate>
      <link>https://dev.to/akbrews/common-js-library-import-in-es-module-57i7</link>
      <guid>https://dev.to/akbrews/common-js-library-import-in-es-module-57i7</guid>
      <description>&lt;p&gt;Spent a lot of time debugging this.&lt;/p&gt;

&lt;p&gt;I was using react color library from NPM in a project and my project was in ES module scope.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;reactColor&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-color&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;TypeError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Cannot&lt;/span&gt; &lt;span class="nx"&gt;read&lt;/span&gt; &lt;span class="nx"&gt;properties&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nf"&gt;undefined &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;reading&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;displayName&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;h2&gt;
  
  
  Fix
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;reactColor&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-color&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;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="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SketchPicker&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;reactColor&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>javascript</category>
      <category>react</category>
      <category>debugging</category>
    </item>
    <item>
      <title>Prisma: Could not parse schema engine response</title>
      <dc:creator>Akbar Ali</dc:creator>
      <pubDate>Thu, 19 Dec 2024 08:13:48 +0000</pubDate>
      <link>https://dev.to/akbrews/prisma-could-not-parse-schema-engine-response-4502</link>
      <guid>https://dev.to/akbrews/prisma-could-not-parse-schema-engine-response-4502</guid>
      <description>&lt;h2&gt;
  
  
  Problem
&lt;/h2&gt;

&lt;p&gt;Recently encountered this error while deploying our app to production:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: Could not parse schema engine response: SyntaxError: Unexpected token E in JSON at position 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is no more explanation why this occurred, just this line. In the recent merge commit we had only changed a couple of locale keywords, that's it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Figuring out
&lt;/h2&gt;

&lt;p&gt;Searched all over the internet and found nothing.&lt;/p&gt;

&lt;p&gt;Then, I noticed a warning a couple lines of above the error, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;prisma:warn Prisma failed to detect the libssl/openssl version to use, and may not work as expected. Defaulting to "openssl-1.1.x".
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I searched about this on GitHub and found this:&lt;br&gt;
&lt;a href="https://github.com/prisma/prisma/issues/19729" rel="noopener noreferrer"&gt;Prisma fails to find openssl 3.0.x on new bookworm&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I realised that for some reasons, the new version of the alpine docker image I was using doesn't come with the openssl package - Which was required for Prisma engine.&lt;/p&gt;
&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;The issue discussion I shared above said that use node version 18.5.something and we can temporarily resolve this or use a different docker image.&lt;/p&gt;

&lt;p&gt;But, I don't have to use a different version of node and make conflicts to other packages.&lt;/p&gt;

&lt;p&gt;So, I should install the package manually.&lt;/p&gt;

&lt;p&gt;This is the docker image I was using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM node:18-alpine
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I added these lines to it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# install openssl
RUN apk update &amp;amp;&amp;amp; apk upgrade
RUN apk add --no-cache openssl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And, that solved the problem.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>typescript</category>
      <category>programming</category>
    </item>
    <item>
      <title>Importing from GitLab to GitHub</title>
      <dc:creator>Akbar Ali</dc:creator>
      <pubDate>Fri, 17 May 2024 07:19:17 +0000</pubDate>
      <link>https://dev.to/akbrews/importing-from-gitlab-to-github-3bcd</link>
      <guid>https://dev.to/akbrews/importing-from-gitlab-to-github-3bcd</guid>
      <description>&lt;h2&gt;
  
  
  In GitLab:
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Profile -&amp;gt; Preferences -&amp;gt; Access tokens -&amp;gt; Add new token&lt;/li&gt;
&lt;li&gt;Name it, select all permission that apply, click create. &lt;/li&gt;
&lt;li&gt;Now you get a token, copy it.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  In GitHub
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Create new repository. &lt;/li&gt;
&lt;li&gt;On the create page, right below the heading there is a text saying &lt;code&gt;import a repository&lt;/code&gt;. &lt;/li&gt;
&lt;li&gt;Click it, copy the repo url from Gitlab and paste it there.&lt;/li&gt;
&lt;li&gt;Add your gitlab username and the access token. &lt;/li&gt;
&lt;li&gt;Name the repository. &lt;/li&gt;
&lt;li&gt;Click &lt;code&gt;begin import&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;It will take some time, sit tight.&lt;/li&gt;
&lt;/ol&gt;

</description>
    </item>
    <item>
      <title>Object memory reference in JS</title>
      <dc:creator>Akbar Ali</dc:creator>
      <pubDate>Tue, 12 Mar 2024 20:05:52 +0000</pubDate>
      <link>https://dev.to/akbrews/object-memory-reference-in-js-2ne7</link>
      <guid>https://dev.to/akbrews/object-memory-reference-in-js-2ne7</guid>
      <description>&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;person1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Alex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Toronto&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;person2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;person1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;person2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;John&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;person1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What would be the output of the above code?&lt;/p&gt;

&lt;p&gt;Does that look like this:&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="err"&gt;name:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Alex"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;address:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Toronto"&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;You're wrong. It would look like this:&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="err"&gt;name:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"John"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;address:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Toronto"&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;Yes, that's the correct output. But that was not what we ought to do. We need the name of the person1 to stay like whatever it was before. We changed the name of person2.&lt;/p&gt;

&lt;p&gt;Then, where did it go wrong?&lt;/p&gt;

&lt;p&gt;The problem is with the JavaScript's object memory reference. We only assigned the &lt;code&gt;person1&lt;/code&gt; variable to the &lt;code&gt;person2&lt;/code&gt; variable. Javascript keeps the memory reference of the variable in the place for both of them. That's why when we changed the name of &lt;code&gt;person2&lt;/code&gt;, it affected &lt;code&gt;person1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;How to overcome this bug?&lt;/p&gt;

&lt;p&gt;Even, I had spent a full day debugging a commercial app and banged my keyboards due to frustration to find out it's just a memory reference bug.&lt;/p&gt;

&lt;p&gt;Here's a solution:&lt;br&gt;
&lt;strong&gt;Spread operator&lt;/strong&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;person1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Alex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Toronto&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;person2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;person1&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nx"&gt;person2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;John&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;person1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we are spreading the &lt;code&gt;person1&lt;/code&gt; variable to create a new one. Now it would work fine as we expected. But, it has an exception.&lt;/p&gt;

&lt;p&gt;What if our code was like this:&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;person1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;first&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Alex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;last&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Telles&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Toronto&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;person2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;person1&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="nx"&gt;person2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;first&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;John&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;person1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is our expected output:&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="err"&gt;name:&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;span class="err"&gt;first:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Alex"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;last:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Telles"&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;span class="err"&gt;address:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Toronto"&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;But, what we get:&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="err"&gt;name:&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;span class="err"&gt;first:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"John"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;last:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Telles"&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;span class="err"&gt;address:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Toronto"&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;The &lt;code&gt;person1&lt;/code&gt; also got changed even if we used spread operation to create a new object. &lt;/p&gt;

&lt;p&gt;This is because, the spread operator only affects the first level properties and it won't clone the deep level key values. So, the &lt;code&gt;first&lt;/code&gt; in the &lt;code&gt;name&lt;/code&gt; property still has the memory reference to its parent.&lt;/p&gt;

&lt;p&gt;Here is the solution to deep clone JS objects:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JSON stringify&lt;/strong&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;person2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;person1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Converting the object to string and then into JSON resolves the problem.&lt;/p&gt;

&lt;p&gt;But still, we have a problem.&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;function&lt;/span&gt; &lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&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="s2"&gt;This is a Test&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;person1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;first&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Alex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;last&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Telles&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Toronto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;person1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;f&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;person2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;person1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="nx"&gt;person2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code will return an error saying &lt;code&gt;TypeError: person2.f is not a function&lt;/code&gt;. This is because, we had a memory reference to the function in the parent object and when we converted it into string for cloning, that reference got lost. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;thanks to &lt;a class="mentioned-user" href="https://dev.to/efpage"&gt;@efpage&lt;/a&gt; for the feedback.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So, there are some ways to overcome this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Using loadash&lt;/strong&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;_&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;lodash&lt;/span&gt;&lt;span class="dl"&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;test&lt;/span&gt;&lt;span class="p"&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="s2"&gt;This is a Test&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;person1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;first&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Alex&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;last&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Telles&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;XYZ&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;person2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;_&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cloneDeep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;person1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;person2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;lodash library has a cloneDeep function which will help to deep clone object with all memory reference.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Structured clone function&lt;/strong&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;person2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;structuredClone&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;person1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;structuredClone&lt;/code&gt; function will be available in the new version of JavaScript and it will deep copy the parent object.&lt;/p&gt;

&lt;p&gt;Hope this articles helped you. Drop your feedbacks in comments.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>beginners</category>
      <category>programming</category>
    </item>
    <item>
      <title>This or that?</title>
      <dc:creator>Akbar Ali</dc:creator>
      <pubDate>Wed, 06 Dec 2023 09:07:49 +0000</pubDate>
      <link>https://dev.to/akbrews/this-or-that-162j</link>
      <guid>https://dev.to/akbrews/this-or-that-162j</guid>
      <description>&lt;p&gt;Have you ever ended up in a situation where you have many images of something but hard to choose the perfect one?&lt;/p&gt;

&lt;p&gt;Like, if we have two images, it's easy to choose the best one. But when it's 5 or 10 or 50 images, the process becomes more difficult. &lt;/p&gt;

&lt;p&gt;The solution is to take the first two pictures, find the best one. Now take the third picture and cross check with the best one and it continues.&lt;/p&gt;

&lt;p&gt;I made a simple web app which helps to do this. Simply, we upload a bunch of images and click start. The program shows us a pair of images and we have to choose the best one. It repeats until the best one finally being found. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/pulimoodan/dot-poll" rel="noopener noreferrer"&gt;Dot-poll&lt;/a&gt; is a simple web app with basic html and js which delivers this solution. See the live version here: &lt;a href="https://pulimoodan.github.io/dot-poll/" rel="noopener noreferrer"&gt;Demo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is a lot of features we can add to this app and I am looking for developers who are interested working on this. Leave a comment or a pull request if you find this worth a try.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>github</category>
      <category>opensource</category>
    </item>
    <item>
      <title>An alternative for Render?</title>
      <dc:creator>Akbar Ali</dc:creator>
      <pubDate>Tue, 05 Dec 2023 09:17:32 +0000</pubDate>
      <link>https://dev.to/akbrews/an-alternative-for-render-3l8o</link>
      <guid>https://dev.to/akbrews/an-alternative-for-render-3l8o</guid>
      <description>&lt;p&gt;I have been using &lt;a href="https://render.com/" rel="noopener noreferrer"&gt;Render&lt;/a&gt; for hosting my side projects. It's flawless, easy to connect GitHub projects and deploy with just a click.&lt;/p&gt;

&lt;p&gt;I switched to render because I was fed up with the hard and weird deployment process of Heroku. Render has some amazing features and easy to use.&lt;/p&gt;

&lt;p&gt;But recently, I faced this problem of services getting suspended if they are in free tier. Somehow, it's hard to afford some side projects in a paid plan. I share my side projects with my friends and they say it's not loading. I ask them to wait for couple of minutes and they usually stop trying after some time.&lt;/p&gt;

&lt;p&gt;Currently, I am searching for a better alternative which provide full time serving of the web app we deploy. I really liked the workflow of &lt;a href="https://render.com/" rel="noopener noreferrer"&gt;Render&lt;/a&gt; and hope to find something similar.&lt;/p&gt;

</description>
      <category>hosting</category>
      <category>webdev</category>
      <category>github</category>
    </item>
  </channel>
</rss>
