<?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>Forem: Drilon Hametaj</title>
    <description>The latest articles on Forem by Drilon Hametaj (@drilonhametaj25).</description>
    <link>https://forem.com/drilonhametaj25</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1261194%2Ff221dee0-549b-4075-8264-3bdec2319d50.jpeg</url>
      <title>Forem: Drilon Hametaj</title>
      <link>https://forem.com/drilonhametaj25</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/drilonhametaj25"/>
    <language>en</language>
    <item>
      <title>Hardhat vs Foundry in 2026: Which Should You Use for Smart Contract Development?</title>
      <dc:creator>Drilon Hametaj</dc:creator>
      <pubDate>Thu, 16 Apr 2026 16:02:28 +0000</pubDate>
      <link>https://forem.com/drilonhametaj25/hardhat-vs-foundry-in-2026-which-should-you-use-for-smart-contract-development-369k</link>
      <guid>https://forem.com/drilonhametaj25/hardhat-vs-foundry-in-2026-which-should-you-use-for-smart-contract-development-369k</guid>
      <description>&lt;p&gt;I've used both Hardhat and Foundry in production projects. Smart contracts on Ethereum, Polygon, and BSC. Here's an honest comparison based on real experience, not documentation screenshots.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hardhat: The JavaScript-Native Approach
&lt;/h2&gt;

&lt;p&gt;Hardhat is the incumbent. Mature ecosystem, extensive plugin library, huge community. If you've done any Solidity development, you've probably used Hardhat.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I like:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Plugin ecosystem (ethers.js integration, gas reporter, coverage, verify)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;console.log&lt;/code&gt; in Solidity during development — this alone saves hours&lt;/li&gt;
&lt;li&gt;TypeScript support for tests and scripts&lt;/li&gt;
&lt;li&gt;Hardhat Network for local testing with mainnet forking
&lt;strong&gt;What frustrates me:&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Tests in JavaScript/TypeScript are verbose for testing Solidity&lt;/li&gt;
&lt;li&gt;Compilation speed is noticeably slower than Foundry on large projects&lt;/li&gt;
&lt;li&gt;Configuration can get complex with many plugins
My Hardhat test for the &lt;a href="https://www.drilonhametaj.it/progetti/certificazione-blockchain" rel="noopener noreferrer"&gt;SBT project&lt;/a&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;expect&lt;/span&gt; &lt;span class="p"&gt;}&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;chai&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt; &lt;span class="p"&gt;}&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;hardhat&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SoulboundCertificate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;sbt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;recipient&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;beforeEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;function &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="nx"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;recipient&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSigners&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;SBT&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getContractFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SoulboundCertificate&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;sbt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;SBT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;deploy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Should block transfers between addresses&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sbt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;recipient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&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;ipfs://test-uri&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="p"&gt;[,&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;other&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSigners&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;sbt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;recipient&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;transferFrom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;recipient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;other&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="mi"&gt;0&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;revertedWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SBT: transfer not allowed&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Foundry: The Solidity-Native Approach
&lt;/h2&gt;

&lt;p&gt;Foundry (by Paradigm) is the challenger. Tests written in Solidity, blazing fast compilation, built-in fuzzing. It's what the cool kids use. But is it actually better?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What I like:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tests in Solidity — you test Solidity WITH Solidity. No context switching.&lt;/li&gt;
&lt;li&gt;Speed. Compilation and tests are 5-10x faster than Hardhat on the same project.&lt;/li&gt;
&lt;li&gt;Built-in fuzzing with &lt;code&gt;forge fuzz&lt;/code&gt; — essential for security&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;forge script&lt;/code&gt; for deployments is cleaner than Hardhat scripts&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;chisel&lt;/code&gt; REPL for quick experimentation
&lt;strong&gt;What frustrates me:&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Smaller plugin ecosystem&lt;/li&gt;
&lt;li&gt;Less documentation and fewer Stack Overflow answers&lt;/li&gt;
&lt;li&gt;Frontend integration requires more manual setup (no native ethers.js plugin)&lt;/li&gt;
&lt;li&gt;Steeper learning curve if you're coming from JavaScript
Same test in Foundry:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "forge-std/Test.sol";
import "../src/SoulboundCertificate.sol";

contract SBTTest is Test {
    SoulboundCertificate sbt;
    address recipient = makeAddr("recipient");
    address other = makeAddr("other");

    function setUp() public {
        sbt = new SoulboundCertificate();
    }

    function test_BlockTransfer() public {
        sbt.mint(recipient, "ipfs://test-uri");

        vm.prank(recipient);
        vm.expectRevert("SBT: transfer not allowed");
        sbt.transferFrom(recipient, other, 0);
    }

    // Fuzz test — Foundry runs this with random inputs
    function testFuzz_MintToAnyAddress(address to) public {
        vm.assume(to != address(0));
        sbt.mint(to, "ipfs://test-uri");
        assertEq(sbt.ownerOf(0), to);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the fuzz test at the bottom. Foundry automatically runs it with hundreds of random addresses. This catches edge cases that manual tests miss. This is the kind of testing I apply in every &lt;a href="https://www.drilonhametaj.it/blockchain/audit" rel="noopener noreferrer"&gt;smart contract audit&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Recommendation
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Use Foundry if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your project is primarily smart contracts&lt;/li&gt;
&lt;li&gt;Security is critical (DeFi, high-value assets)&lt;/li&gt;
&lt;li&gt;You want the fastest development cycle&lt;/li&gt;
&lt;li&gt;Your team is comfortable with Solidity
&lt;strong&gt;Use Hardhat if:&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;You're building a full-stack DApp with significant frontend&lt;/li&gt;
&lt;li&gt;Your team is stronger in TypeScript than Solidity&lt;/li&gt;
&lt;li&gt;You need specific plugins (e.g., hardhat-deploy, gas-reporter)&lt;/li&gt;
&lt;li&gt;You're working with less experienced developers who need more resources
&lt;strong&gt;What I actually do:&lt;/strong&gt; I use both. Foundry for smart contract development and testing (faster, better fuzzing). Hardhat for deployment scripts and frontend integration (better ethers.js support). The two can coexist in the same project.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Real Answer
&lt;/h2&gt;

&lt;p&gt;The framework matters less than: writing comprehensive tests, running static analysis (Slither), doing fuzz testing, and having a &lt;a href="https://www.drilonhametaj.it/blockchain/audit" rel="noopener noreferrer"&gt;proper audit&lt;/a&gt; before mainnet deployment.&lt;/p&gt;

&lt;p&gt;A well-tested contract in Hardhat beats a poorly-tested contract in Foundry every time.&lt;/p&gt;




&lt;p&gt;I'm Drilon, a &lt;a href="https://www.drilonhametaj.it/blockchain" rel="noopener noreferrer"&gt;blockchain and full-stack developer&lt;/a&gt; working with businesses on Ethereum, Polygon, BSC, and TON. More technical deep-dives on my &lt;a href="https://www.drilonhametaj.it/blog" rel="noopener noreferrer"&gt;blog&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>blockchain</category>
      <category>solidity</category>
      <category>ethereum</category>
    </item>
    <item>
      <title>3 Database Indexes Turned a 30-Second Query Into 0.4 Seconds</title>
      <dc:creator>Drilon Hametaj</dc:creator>
      <pubDate>Thu, 16 Apr 2026 16:01:35 +0000</pubDate>
      <link>https://forem.com/drilonhametaj25/3-database-indexes-turned-a-30-second-query-into-04-seconds-4n73</link>
      <guid>https://forem.com/drilonhametaj25/3-database-indexes-turned-a-30-second-query-into-04-seconds-4n73</guid>
      <description>&lt;p&gt;A client called me: "The order list takes 30 seconds to load." Their custom ERP was crawling. The hardware was fine. The problem was the code. Specifically, one query.&lt;/p&gt;

&lt;p&gt;Here's what happened and how I fixed it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Setup
&lt;/h2&gt;

&lt;p&gt;Stack: Node.js backend, PostgreSQL database, React frontend. The application was a &lt;a href="https://www.drilonhametaj.it/gestionali" rel="noopener noreferrer"&gt;custom ERP&lt;/a&gt; for a manufacturing company — orders, clients, products, invoicing.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;orders&lt;/code&gt; table had 2.1 million rows accumulated over 8 years. The query that powered the main order list page was doing a full table scan. Every. Single. Time.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Query (Before)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;company_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;vat_number&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;clients&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;client_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'pending'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'processing'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'shipped'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_date&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="s1"&gt;'2025-01-01'&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;order_date&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt; &lt;span class="k"&gt;OFFSET&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks innocent, right? Let's see what &lt;code&gt;EXPLAIN ANALYZE&lt;/code&gt; says:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Seq Scan on orders o  (cost=0.00..89543.21 rows=2100000)
  Filter: (status = ANY(...) AND order_date &amp;gt;= '2025-01-01')
  Rows Removed by Filter: 1987432
Planning Time: 0.2ms
Execution Time: 29847ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Sequential scan on 2.1 million rows.&lt;/strong&gt; No indexes on &lt;code&gt;status&lt;/code&gt;, &lt;code&gt;order_date&lt;/code&gt;, or &lt;code&gt;client_id&lt;/code&gt; (the foreign key).&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fix: 3 Indexes
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Index 1: composite index for the WHERE clause&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_orders_status_date&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order_date&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- Index 2: foreign key index (should ALWAYS exist)&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_orders_client_id&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;client_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- Index 3: covering index for the most common query pattern&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_orders_list_covering&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order_date&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;INCLUDE&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;client_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Index 3 is the secret weapon — a &lt;strong&gt;covering index&lt;/strong&gt;. PostgreSQL can satisfy the entire query from the index alone, without touching the table (an "index-only scan"). The &lt;code&gt;INCLUDE&lt;/code&gt; clause adds columns that aren't part of the search but ARE part of the SELECT.&lt;/p&gt;

&lt;h2&gt;
  
  
  After
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Index Only Scan using idx_orders_list_covering on orders o
  Index Cond: (status = ANY(...) AND order_date &amp;gt;= '2025-01-01')
Planning Time: 0.3ms
Execution Time: 0.4ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;From 29,847ms to 0.4ms.&lt;/strong&gt; Same data. Same server. Same query. Just proper indexing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Broader Pattern
&lt;/h2&gt;

&lt;p&gt;In my experience building &lt;a href="https://www.drilonhametaj.it/gestionali" rel="noopener noreferrer"&gt;management software for SMBs&lt;/a&gt;, slow queries are the #1 performance complaint. And 80% of the time, the fix is the same:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Run &lt;code&gt;EXPLAIN ANALYZE&lt;/code&gt;&lt;/strong&gt; on every slow query&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add indexes for your WHERE clauses&lt;/strong&gt; — if you filter by it, index it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Always index foreign keys&lt;/strong&gt; — PostgreSQL does NOT auto-index FKs (unlike MySQL)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consider covering indexes&lt;/strong&gt; for your most critical queries&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitor with &lt;code&gt;pg_stat_user_indexes&lt;/code&gt;&lt;/strong&gt; to see which indexes are actually used
## Common Mistakes I See&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Too many indexes:&lt;/strong&gt; every index slows down INSERT/UPDATE. Index what you query, not everything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Indexing low-cardinality columns alone:&lt;/strong&gt; a boolean &lt;code&gt;is_active&lt;/code&gt; column with 99% &lt;code&gt;true&lt;/code&gt; doesn't benefit from a standalone index. But it CAN benefit as part of a composite index.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Ignoring index order in composites:&lt;/strong&gt; &lt;code&gt;(status, order_date)&lt;/code&gt; is NOT the same as &lt;code&gt;(order_date, status)&lt;/code&gt;. Put the equality conditions first, range conditions last.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Never running VACUUM/ANALYZE:&lt;/strong&gt; PostgreSQL needs updated statistics to choose the right query plan. If your stats are stale, even good indexes get ignored.&lt;/p&gt;

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

&lt;p&gt;Before blaming the hardware, the framework, or the database engine — check your indexes. The most impactful performance fix I've ever shipped took 3 lines of SQL and 10 minutes of analysis.&lt;/p&gt;




&lt;p&gt;I build &lt;a href="https://www.drilonhametaj.it/gestionali" rel="noopener noreferrer"&gt;custom management software&lt;/a&gt; for Italian SMBs. Performance, security, and clean architecture are non-negotiable. &lt;a href="https://www.drilonhametaj.it/contatti" rel="noopener noreferrer"&gt;Let's connect&lt;/a&gt;.&lt;/p&gt;




</description>
      <category>database</category>
      <category>performance</category>
      <category>postgres</category>
    </item>
    <item>
      <title>Building a Soulbound Token (SBT) System on Polygon — Full Walkthrough</title>
      <dc:creator>Drilon Hametaj</dc:creator>
      <pubDate>Thu, 16 Apr 2026 16:00:17 +0000</pubDate>
      <link>https://forem.com/drilonhametaj25/building-a-soulbound-token-sbt-system-on-polygon-full-walkthrough-c3c</link>
      <guid>https://forem.com/drilonhametaj25/building-a-soulbound-token-sbt-system-on-polygon-full-walkthrough-c3c</guid>
      <description>&lt;p&gt;I built a credential certification platform using Soulbound Tokens on Polygon. Not a tutorial project — a production system with thousands of certificates issued. Here's the full technical breakdown.&lt;/p&gt;

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

&lt;p&gt;A client needed to issue professional certifications that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Could be verified by anyone, instantly, without calling the issuer&lt;/li&gt;
&lt;li&gt;Could never be falsified or duplicated&lt;/li&gt;
&lt;li&gt;Would persist even if the issuing company shut down&lt;/li&gt;
&lt;li&gt;Would belong to the recipient, not to a centralized database
PDFs fail all four criteria. A centralized database fails the last two. Blockchain solves all four.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Soulbound Tokens
&lt;/h2&gt;

&lt;p&gt;Regular NFTs (ERC-721) can be transferred. That's the whole point of NFTs — ownership and transferability. But for &lt;strong&gt;credentials&lt;/strong&gt;, transferability is a bug, not a feature. You don't want someone selling their medical license on OpenSea.&lt;/p&gt;

&lt;p&gt;Soulbound Tokens are non-transferable NFTs. Once minted to a wallet, they stay there forever. The concept was proposed by Vitalik Buterin in the "Decentralized Society" paper.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Polygon
&lt;/h2&gt;

&lt;p&gt;Gas costs. Deploying on Ethereum mainnet: $50-500 depending on congestion. Minting each certificate on Ethereum: $5-50. For a system that issues hundreds of certificates, that's unsustainable.&lt;/p&gt;

&lt;p&gt;Polygon: deploy cost ~$0.01. Mint cost ~$0.001. Same Solidity code. Same EVM. Same security model (anchored to Ethereum).&lt;/p&gt;

&lt;p&gt;For a deeper comparison, I wrote a &lt;a href="https://www.drilonhametaj.it/blog/polygon-vs-ethereum-quale-scegliere" rel="noopener noreferrer"&gt;full analysis of Polygon vs Ethereum&lt;/a&gt; for business use cases.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Smart Contract
&lt;/h2&gt;

&lt;p&gt;Here's the core of the SBT contract:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract SoulboundCertificate is ERC721, ERC721URIStorage, Ownable {
    uint256 private _nextTokenId;

    constructor() ERC721("SoulboundCertificate", "SBC") Ownable(msg.sender) {}

    function mint(address to, string memory uri) public onlyOwner returns (uint256) {
        uint256 tokenId = _nextTokenId++;
        _safeMint(to, tokenId);
        _setTokenURI(tokenId, uri);
        return tokenId;
    }

    // Block ALL transfers — this is what makes it "soulbound"
    function _update(
        address to,
        uint256 tokenId,
        address auth
    ) internal override returns (address) {
        address from = _ownerOf(tokenId);
        // Allow minting (from == address(0)) and burning (to == address(0))
        // Block all transfers between non-zero addresses
        if (from != address(0) &amp;amp;&amp;amp; to != address(0)) {
            revert("SBT: transfer not allowed");
        }
        return super._update(to, tokenId, auth);
    }

    // Required overrides
    function tokenURI(uint256 tokenId)
        public view override(ERC721, ERC721URIStorage) returns (string memory)
    {
        return super.tokenURI(tokenId);
    }

    function supportsInterface(bytes4 interfaceId)
        public view override(ERC721, ERC721URIStorage) returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Key decisions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OpenZeppelin base:&lt;/strong&gt; never roll your own ERC-721. OpenZeppelin is battle-tested and audited.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;_update&lt;/code&gt; override:&lt;/strong&gt; in OZ v5, this is the single function that controls all token movements. Override it once, block transfers everywhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;onlyOwner&lt;/code&gt; for minting:&lt;/strong&gt; only the issuing organization can create certificates. No public minting.
## Metadata on IPFS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Certificate data (name, course, date, score) is stored as JSON on IPFS, not on-chain. On-chain storage is expensive and unnecessary for metadata.&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;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Advanced Solidity Development"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Professional certification issued by [Organization]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"image"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ipfs://QmXxx.../certificate-image.png"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"attributes"&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;"trait_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Recipient"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Mario Rossi"&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;"trait_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Course"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Advanced Solidity"&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;"trait_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Date"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&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-01-15"&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;"trait_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Score"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"92/100"&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;The &lt;code&gt;tokenURI&lt;/code&gt; points to the IPFS hash. IPFS is content-addressed — the hash IS the content. If anyone changes a single byte, the hash changes. Tamper-proof by design.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Frontend
&lt;/h2&gt;

&lt;p&gt;Built with Next.js + Ethers.js. Two interfaces:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Admin Panel&lt;/strong&gt; (authenticated, for the issuer):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Form: recipient wallet address, course details, date&lt;/li&gt;
&lt;li&gt;Generates JSON metadata → uploads to IPFS → calls &lt;code&gt;mint()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Dashboard of all issued certificates
&lt;strong&gt;Public Verification&lt;/strong&gt; (open to anyone):&lt;/li&gt;
&lt;li&gt;Enter a wallet address → see all SBTs held by that address&lt;/li&gt;
&lt;li&gt;Each certificate shows the full metadata pulled from IPFS&lt;/li&gt;
&lt;li&gt;Link to the transaction on Polygonscan for on-chain proof
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Simplified verification logic&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;JsonRpcProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;POLYGON_RPC&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;contract&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;ethers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Contract&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;SBT_ADDRESS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ABI&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;provider&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;balance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;contract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;balanceOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;walletAddress&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;certificates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;balance&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&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;tokenId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;contract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tokenOfOwnerByIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;walletAddress&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&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;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;contract&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tokenURI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tokenId&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;metadata&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;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;uri&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ipfs://&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;IPFS_GATEWAY&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="nx"&gt;certificates&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;metadata&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&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;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;p&gt;Full test suite with Hardhat:&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="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SoulboundCertificate&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="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should mint to recipient&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sbt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;recipient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&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;ipfs://test&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sbt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ownerOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;equal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;recipient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should block transfers&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &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="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sbt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;recipient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&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;ipfs://test&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;sbt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;recipient&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;transferFrom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;recipient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;other&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;address&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;be&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;revertedWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SBT: transfer not allowed&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="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should allow burning by owner&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &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="c1"&gt;// Revocation mechanism — issuer can burn if needed&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;Plus Slither for static analysis and manual review for logic bugs. I wrote about my full &lt;a href="https://www.drilonhametaj.it/blockchain/audit" rel="noopener noreferrer"&gt;smart contract audit process&lt;/a&gt; separately.&lt;/p&gt;

&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Deployed on Polygon mainnet&lt;/li&gt;
&lt;li&gt;Thousands of certificates issued&lt;/li&gt;
&lt;li&gt;Verification time: ~3 seconds (vs days for traditional verification)&lt;/li&gt;
&lt;li&gt;Cost per certificate: &amp;lt; $0.01&lt;/li&gt;
&lt;li&gt;Zero downtime since launch
## When to Use This Pattern&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;SBTs make sense for: professional certifications, academic credentials, membership badges, compliance attestations, and any credential that should be &lt;strong&gt;permanent&lt;/strong&gt;, &lt;strong&gt;non-transferable&lt;/strong&gt;, and &lt;strong&gt;publicly verifiable&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;They don't make sense for: temporary access tokens, transferable assets, or anything where privacy is critical (blockchain is public — consider ZK proofs if privacy matters).&lt;/p&gt;




&lt;p&gt;I'm a &lt;a href="https://www.drilonhametaj.it/blockchain" rel="noopener noreferrer"&gt;blockchain developer&lt;/a&gt; building smart contracts, wallets, and DApps for businesses. If you're working on something similar, &lt;a href="https://www.drilonhametaj.it/contatti" rel="noopener noreferrer"&gt;let's talk&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>solidity</category>
      <category>webdev</category>
      <category>blockchain</category>
      <category>polygon</category>
    </item>
    <item>
      <title>Why Software Is Not a Technical Choice — It’s a Business Strategy</title>
      <dc:creator>Drilon Hametaj</dc:creator>
      <pubDate>Mon, 08 Dec 2025 12:17:27 +0000</pubDate>
      <link>https://forem.com/drilonhametaj25/why-software-is-not-a-technical-choice-its-a-business-strategy-cn6</link>
      <guid>https://forem.com/drilonhametaj25/why-software-is-not-a-technical-choice-its-a-business-strategy-cn6</guid>
      <description>&lt;p&gt;Most people think of software as a technical decision.&lt;br&gt;
Frameworks.&lt;br&gt;
Stacks.&lt;br&gt;
Performance.&lt;br&gt;
Databases.&lt;/p&gt;

&lt;p&gt;But after working with many companies, I’ve learned something very different:&lt;br&gt;
choosing software is not a technical choice.&lt;br&gt;
It’s a strategic one.&lt;/p&gt;




&lt;h2&gt;
  
  
  Growth Breaks First Behind the Scenes
&lt;/h2&gt;

&lt;p&gt;Many businesses grow by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;working more hours&lt;/li&gt;
&lt;li&gt;taking more clients&lt;/li&gt;
&lt;li&gt;moving faster&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At first it works.&lt;br&gt;
Then something breaks.&lt;/p&gt;

&lt;p&gt;Requests get lost.&lt;br&gt;
Deadlines are missed.&lt;br&gt;
Information is scattered.&lt;br&gt;
People start asking:&lt;br&gt;
“Who is doing what?”&lt;br&gt;
“Where is that file?”&lt;br&gt;
“Did anyone answer that client?”&lt;/p&gt;

&lt;p&gt;Growth usually doesn’t break in marketing.&lt;br&gt;
It breaks in operations.&lt;/p&gt;




&lt;h2&gt;
  
  
  Chaos Is Expensive (But Invisible)
&lt;/h2&gt;

&lt;p&gt;Operational chaos is dangerous because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it doesn’t show up immediately&lt;/li&gt;
&lt;li&gt;it slowly erodes margins&lt;/li&gt;
&lt;li&gt;it increases stress and errors&lt;/li&gt;
&lt;li&gt;it makes every decision harder&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And yet many companies accept it as “normal”.&lt;/p&gt;

&lt;p&gt;It shouldn’t be.&lt;/p&gt;




&lt;h2&gt;
  
  
  Software as a Business Lever
&lt;/h2&gt;

&lt;p&gt;Good software allows you to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;centralize information&lt;/li&gt;
&lt;li&gt;reduce manual errors&lt;/li&gt;
&lt;li&gt;automate repetitive actions&lt;/li&gt;
&lt;li&gt;make decisions based on data, not intuition&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bad software does the opposite:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;adds complexity&lt;/li&gt;
&lt;li&gt;creates friction&lt;/li&gt;
&lt;li&gt;slows people down&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That’s why software is strategy, not just technology.&lt;/p&gt;




&lt;h2&gt;
  
  
  My Work Starts Here
&lt;/h2&gt;

&lt;p&gt;My work doesn’t start with writing code.&lt;br&gt;
It starts with understanding:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;workflows&lt;/li&gt;
&lt;li&gt;bottlenecks&lt;/li&gt;
&lt;li&gt;decision points&lt;/li&gt;
&lt;li&gt;real business problems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Only after that does the technical part begin.&lt;/p&gt;

&lt;p&gt;If you want to see the type of projects I work on today, you can find them here:&lt;br&gt;
👉 &lt;a href="https://www.drilonhametaj.it" rel="noopener noreferrer"&gt;https://www.drilonhametaj.it&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thought
&lt;/h2&gt;

&lt;p&gt;You can have the best marketing in the world.&lt;br&gt;
But if your internal systems are weak,&lt;br&gt;
your growth will always be fragile.&lt;/p&gt;

&lt;p&gt;Strong business needs strong systems.&lt;/p&gt;

</description>
      <category>softwaredevelopment</category>
      <category>productivity</category>
      <category>startup</category>
      <category>development</category>
    </item>
    <item>
      <title>Building a Vertical SaaS for Construction Companies: What I Learned from the Real World</title>
      <dc:creator>Drilon Hametaj</dc:creator>
      <pubDate>Mon, 08 Dec 2025 12:15:56 +0000</pubDate>
      <link>https://forem.com/drilonhametaj25/building-a-vertical-saas-for-construction-companies-what-i-learned-from-the-real-world-17e</link>
      <guid>https://forem.com/drilonhametaj25/building-a-vertical-saas-for-construction-companies-what-i-learned-from-the-real-world-17e</guid>
      <description>&lt;p&gt;When people think about construction companies, they often imagine cranes, workers, concrete and noise.&lt;br&gt;
What they rarely think about is software.&lt;/p&gt;

&lt;p&gt;And yet, after talking with dozens of small and medium construction companies, I realized something surprising:&lt;br&gt;
most of their biggest problems are not on the construction site.&lt;br&gt;
They are behind a computer.&lt;/p&gt;

&lt;p&gt;Spreadsheets everywhere.&lt;br&gt;
Costs tracked manually.&lt;br&gt;
Invoices compared at the end of the job.&lt;br&gt;
Profit discovered… too late.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Excel Illusion
&lt;/h2&gt;

&lt;p&gt;Let’s be clear: Excel is a powerful tool.&lt;br&gt;
It works perfectly when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you have a single project&lt;/li&gt;
&lt;li&gt;one person manages everything&lt;/li&gt;
&lt;li&gt;data doesn’t need to be shared in real time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The problem starts when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;multiple construction sites run at the same time&lt;/li&gt;
&lt;li&gt;several people enter data&lt;/li&gt;
&lt;li&gt;costs change daily&lt;/li&gt;
&lt;li&gt;variations keep coming in&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At that point, Excel stops being a solution and becomes a risk.&lt;br&gt;
One wrong formula, one outdated file… and your financial data is no longer trustworthy.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Real Business Problem
&lt;/h2&gt;

&lt;p&gt;The real issue I kept seeing was always the same:&lt;br&gt;
construction companies often don’t know their real profit in real time.&lt;/p&gt;

&lt;p&gt;They know:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;how much the client should pay&lt;/li&gt;
&lt;li&gt;roughly how much workers cost
But they don’t have:&lt;/li&gt;
&lt;li&gt;live cost tracking&lt;/li&gt;
&lt;li&gt;automatic margins&lt;/li&gt;
&lt;li&gt;real-time profitability per project&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From a business standpoint, this is extremely dangerous.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why I Decided to Build a Vertical Tool
&lt;/h2&gt;

&lt;p&gt;This is why I decided to build a vertical SaaS specifically for construction companies.&lt;br&gt;
Not a generic ERP.&lt;br&gt;
Not yet another spreadsheet replacement.&lt;br&gt;
But a tool focused on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;estimates&lt;/li&gt;
&lt;li&gt;job progress (SAL)&lt;/li&gt;
&lt;li&gt;real costs&lt;/li&gt;
&lt;li&gt;workers&lt;/li&gt;
&lt;li&gt;suppliers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is simple:&lt;br&gt;
give small construction companies the same level of control that only large companies usually have.&lt;/p&gt;

&lt;p&gt;If you’re curious about the project itself, this is where it lives:&lt;br&gt;
👉 &lt;a href="https://www.ediliziami.pro" rel="noopener noreferrer"&gt;https://www.ediliziami.pro&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What I’m Learning as a Founder
&lt;/h2&gt;

&lt;p&gt;The biggest lesson so far is this:&lt;br&gt;
real-world businesses don’t behave like textbooks.&lt;/p&gt;

&lt;p&gt;Every company:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;uses different workflows&lt;/li&gt;
&lt;li&gt;has different habits&lt;/li&gt;
&lt;li&gt;resists change in different ways&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The technical challenge is easy.&lt;br&gt;
The human challenge is the real product.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Thought
&lt;/h2&gt;

&lt;p&gt;Digital transformation is not about AI, blockchain or buzzwords.&lt;br&gt;
It’s about solving boring, painful, daily problems better than before.&lt;/p&gt;

&lt;p&gt;And boring problems usually hide the best SaaS opportunities.&lt;/p&gt;

</description>
      <category>saas</category>
      <category>startup</category>
      <category>webdev</category>
      <category>entrepreneurship</category>
    </item>
    <item>
      <title>Come ho creato TrovaMi.pro con l’AI (in 48 ore)</title>
      <dc:creator>Drilon Hametaj</dc:creator>
      <pubDate>Thu, 10 Jul 2025 08:48:04 +0000</pubDate>
      <link>https://forem.com/drilonhametaj25/come-ho-creato-trovamipro-con-lai-in-48-ore-kbo</link>
      <guid>https://forem.com/drilonhametaj25/come-ho-creato-trovamipro-con-lai-in-48-ore-kbo</guid>
      <description>&lt;h1&gt;
  
  
  Come ho creato TrovaMi.pro con l’AI (in 48 ore)
&lt;/h1&gt;

&lt;p&gt;📌 &lt;strong&gt;TL;DR&lt;/strong&gt;: Ho creato &lt;a href="https://www.trovami.pro" rel="noopener noreferrer"&gt;TrovaMi.pro&lt;/a&gt; in 48 ore sfruttando l’intelligenza artificiale per aiutare freelance, agenzie e professionisti a trovare lead validi e analizzati. È un tool semplice ma potente, nato da un bisogno reale. E no, non è l’ennesimo CRM.&lt;/p&gt;




&lt;h2&gt;
  
  
  💡 L’idea
&lt;/h2&gt;

&lt;p&gt;Tutto è iniziato da una frustrazione. Ogni volta che dovevo trovare nuovi clienti, perdevo ore su Google, Google Maps, Pagine Gialle, forum, gruppi Facebook... sempre la solita storia: informazioni incomplete, siti non aggiornati, recapiti sbagliati.&lt;/p&gt;

&lt;p&gt;Mi sono chiesto:&lt;br&gt;&lt;br&gt;
&lt;strong&gt;"Ma se esistono tool per tutto, perché non esiste un tool che trova &lt;em&gt;per me&lt;/em&gt; potenziali clienti, li analizza e me li propone pronti per essere contattati?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Boom.&lt;br&gt;&lt;br&gt;
Nasce l’idea di &lt;strong&gt;TrovaMi.pro&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  ⚙️ Come funziona
&lt;/h2&gt;

&lt;p&gt;TrovaMi è una piattaforma web (Next.js + Supabase + Playwright/Puppeteer per lo scraping) che permette:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Trovare aziende con sito, social e presenza online
&lt;/li&gt;
&lt;li&gt;✅ Analizzarle automaticamente (SEO, performance, privacy, pixel Facebook, dominio, ecc.)
&lt;/li&gt;
&lt;li&gt;✅ Filtrare i risultati in base al proprio settore (es. agenzie marketing, web developer, consulenti, ecc.)
&lt;/li&gt;
&lt;li&gt;✅ Salvare e gestire i contatti con un piccolo CRM integrato (solo per piani PRO)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Il tutto con un’interfaccia ispirata a &lt;strong&gt;Apple&lt;/strong&gt; e &lt;strong&gt;Linear&lt;/strong&gt;: elegante, chiara, veloce.&lt;/p&gt;




&lt;h2&gt;
  
  
  🛠️ Tech stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Frontend&lt;/strong&gt;: Next.js + Tailwind + Shadcn&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend&lt;/strong&gt;: Supabase (Postgres + Auth + Edge Functions)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scraping Engine&lt;/strong&gt;: Playwright + Puppeteer, orchestrato da GitHub Actions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache&lt;/strong&gt;: sistema di crediti per limitare e bilanciare gli accessi&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  🧠 Il tocco dell’AI
&lt;/h2&gt;

&lt;p&gt;Gran parte del progetto è stato scritto (e riscritto) usando &lt;strong&gt;Copilot Agent 4&lt;/strong&gt; e &lt;strong&gt;Claude 4&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
Li ho usati per:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Generare lo scraping engine&lt;/li&gt;
&lt;li&gt;Scrivere analisi SEO/performance/cookie&lt;/li&gt;
&lt;li&gt;Creare UI eleganti senza perdere tempo sul CSS&lt;/li&gt;
&lt;li&gt;Avere sempre uno sparring partner tecnico per ragionare sulle architetture&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Onestamente, senza l’AI ci avrei messo settimane. Con l’AI: 2 giorni.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🎯 Per chi è
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;👨‍💻 Freelance stanchi di cercare clienti a mano
&lt;/li&gt;
&lt;li&gt;📈 Agenzie marketing che vogliono vendere servizi SEO, Ads o branding
&lt;/li&gt;
&lt;li&gt;🧑‍🏫 Professionisti locali (avvocati, commercialisti, coach) che cercano clienti nel proprio territorio
&lt;/li&gt;
&lt;li&gt;👀 Chi fa outreach, ma vuole farlo meglio, più mirato&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  💸 Modello freemium
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gratuito&lt;/strong&gt;: 2 crediti al mese
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;19€/mese&lt;/strong&gt;: 25 crediti
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;49€/mese&lt;/strong&gt;: 100 crediti + CRM&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In più, si possono acquistare &lt;strong&gt;pacchetti una tantum&lt;/strong&gt; (es. 30 crediti a 9€).&lt;/p&gt;




&lt;h2&gt;
  
  
  🚀 Il mio obiettivo
&lt;/h2&gt;

&lt;p&gt;Rendere la ricerca clienti &lt;strong&gt;semplice, veloce e umana&lt;/strong&gt;.&lt;br&gt;&lt;br&gt;
Non è l’ennesimo tool pieno di feature inutili: è uno strumento concreto, pensato per essere usato &lt;em&gt;ogni giorno&lt;/em&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🙌 Provalo e fammi sapere
&lt;/h2&gt;

&lt;p&gt;👉 &lt;a href="https://www.trovami.pro" rel="noopener noreferrer"&gt;www.trovami.pro&lt;/a&gt;&lt;br&gt;&lt;br&gt;
Se lo provi, scrivimi: ogni suggerimento può migliorarlo.&lt;br&gt;&lt;br&gt;
E se vuoi aiutarmi a farlo conoscere, condividilo con chi lavora online.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>leadgeneration</category>
      <category>freelance</category>
    </item>
    <item>
      <title>EdiliziaMi: Un gestionale moderno per la complessità dei cantieri</title>
      <dc:creator>Drilon Hametaj</dc:creator>
      <pubDate>Tue, 18 Feb 2025 15:32:12 +0000</pubDate>
      <link>https://forem.com/drilonhametaj25/ediliziami-un-gestionale-moderno-per-la-complessita-dei-cantieri-4lke</link>
      <guid>https://forem.com/drilonhametaj25/ediliziami-un-gestionale-moderno-per-la-complessita-dei-cantieri-4lke</guid>
      <description>&lt;p&gt;La gestione dei cantieri edilizi è notoriamente difficile: materiali che tardano, ordini mal comunicati, team frammentati. Eppure, in un mondo in cui la digitalizzazione sta semplificando processi in ogni settore, il panorama edile rimane spesso bloccato in metodi obsoleti. La domanda diventa quindi: come possiamo applicare le migliori pratiche del software engineering e del product design a un settore tradizionale come l’edilizia?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://ediliziami.pro" rel="noopener noreferrer"&gt;EdiliziaMi&lt;/a&gt;&lt;/strong&gt; nasce con l’intento di portare ordine in questo caos. Con un approccio orientato agli sviluppatori e tecnologie moderne, stiamo costruendo una piattaforma che trasforma la gestione del cantiere in un processo fluido e prevedibile.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tecnologie e architettura:&lt;/strong&gt;&lt;br&gt;
Alla base di EdiliziaMi c’è un’architettura a microservizi che garantisce scalabilità e modularità. La scelta di questa architettura è motivata dalla necessità di adattarsi rapidamente alle esigenze specifiche delle imprese edili, che variano per dimensione, tipi di progetti e workflow operativi. Alcuni dettagli tecnici:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Backend:&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;NestJS&lt;/em&gt;: utilizzato per creare un backend modulare e scalabile.&lt;br&gt;
&lt;em&gt;Database&lt;/em&gt;: un’architettura ibrida che combina un database relazionale per la contabilità e uno NoSQL per gestire dati semi-strutturati come ordini, note di cantiere e report.&lt;br&gt;
&lt;em&gt;Event-driven architecture&lt;/em&gt;: utilizziamo Kafka per gestire eventi critici, come aggiornamenti in tempo reale degli ordini e notifiche per gli utenti.&lt;br&gt;
&lt;strong&gt;Frontend&lt;/strong&gt;:&lt;br&gt;
&lt;em&gt;Flutter&lt;/em&gt;: scelto per il frontend, garantisce un’esperienza utente fluida e un’unica codebase per iOS, Android e web.&lt;br&gt;
&lt;em&gt;Material Design&lt;/em&gt;: implementato per offrire un’interfaccia familiare, pulita e altamente responsive.&lt;br&gt;
Sfide tecniche e soluzioni:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Integrazione di dati eterogenei:&lt;/strong&gt;&lt;br&gt;
I cantieri generano dati non strutturati (foto, note vocali, annotazioni su planimetrie) e dati altamente strutturati (ordini di acquisto, registri di ore lavorate). EdiliziaMi utilizza schemi JSON flessibili per raccogliere e organizzare i dati semi-strutturati, mentre i dati finanziari e contabili sono normalizzati in un RDBMS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sicurezza e affidabilità dei dati:&lt;/strong&gt;&lt;br&gt;
Con dati sensibili come piani di costruzione, informazioni finanziarie e ordini di materiali, la sicurezza è fondamentale. EdiliziaMi implementa un sistema di autenticazione basato su JWT con refresh token, crittografia dei dati.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Modularità per adattarsi a diverse imprese:&lt;/strong&gt;&lt;br&gt;
Non tutte le aziende edili hanno gli stessi processi. Il design modulare dei microservizi consente di attivare solo le funzionalità necessarie, evitando sovraccarichi di configurazione e rendendo la piattaforma accessibile anche alle piccole imprese.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Perché EdiliziaMi è diverso:&lt;/strong&gt;&lt;br&gt;
Il nostro approccio parte dall’idea che la gestione del cantiere non deve essere un peso. Pensiamo che applicare pattern moderni di sviluppo software, un’architettura robusta e strumenti scalabili possa trasformare radicalmente il settore edile. Non è solo un gestionale: è un ecosistema costruito con gli stessi principi che rendono le migliori piattaforme software affidabili, performanti e facili da usare.&lt;/p&gt;

&lt;p&gt;EdiliziaMi non è una semplice digitalizzazione di processi esistenti. È una reingegnerizzazione della gestione del cantiere, unendo le esigenze pratiche del settore edilizio con le migliori tecniche di sviluppo software. Se siete curiosi di scoprire come questo approccio può rivoluzionare il lavoro di chi opera nei cantieri, seguite il nostro progetto su GitHub e iscrivetevi alla nostra lista d’attesa per essere tra i primi a provare EdiliziaMi.&lt;br&gt;
&lt;a href="https://ediliziami.pro" rel="noopener noreferrer"&gt;https://ediliziami.pro&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>nestjs</category>
      <category>flutter</category>
    </item>
  </channel>
</rss>
