<?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: Ben Greenberg</title>
    <description>The latest articles on DEV Community by Ben Greenberg (@bengreenberg).</description>
    <link>https://dev.to/bengreenberg</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%2F29526%2Fab3873ff-b15d-48ee-90c2-0006c40df4a1.jpg</url>
      <title>DEV Community: Ben Greenberg</title>
      <link>https://dev.to/bengreenberg</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bengreenberg"/>
    <language>en</language>
    <item>
      <title>Quick tip: canonical URLs prevent silent SEO damage</title>
      <dc:creator>Ben Greenberg</dc:creator>
      <pubDate>Fri, 03 Apr 2026 11:00:16 +0000</pubDate>
      <link>https://dev.to/bengreenberg/quick-tip-canonical-urls-prevent-silent-seo-damage-3460</link>
      <guid>https://dev.to/bengreenberg/quick-tip-canonical-urls-prevent-silent-seo-damage-3460</guid>
      <description>&lt;p&gt;Quick tip on SEO:&lt;/p&gt;

&lt;p&gt;If your site is accessible at both &lt;code&gt;https://example.com&lt;/code&gt; and &lt;code&gt;https://www.example.com&lt;/code&gt;, Google sees two different sites and splits your ranking signals between them.&lt;/p&gt;

&lt;p&gt;Fix: add a canonical tag to every page:&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;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"canonical"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://example.com/page"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check if yours is set correctly — the free audit tool at &lt;a href="https://audit.hummusonrails.com/free" rel="noopener noreferrer"&gt;https://audit.hummusonrails.com/free&lt;/a&gt; checks canonical tags along with 4 other key issues.&lt;/p&gt;

</description>
      <category>seo</category>
      <category>webdev</category>
      <category>devtips</category>
    </item>
    <item>
      <title>Your backend code is a black box. It doesn't have to be.</title>
      <dc:creator>Ben Greenberg</dc:creator>
      <pubDate>Wed, 01 Apr 2026 09:07:54 +0000</pubDate>
      <link>https://dev.to/arbitrum/your-backend-code-is-a-black-box-it-doesnt-have-to-be-59bd</link>
      <guid>https://dev.to/arbitrum/your-backend-code-is-a-black-box-it-doesnt-have-to-be-59bd</guid>
      <description>&lt;p&gt;Your API takes inputs and returns outputs. The logic in between? Nobody outside your team can verify it. Regulators read documentation you wrote about it. Partners call your endpoint and trust the response. Users don't even get that.&lt;/p&gt;

&lt;p&gt;This has worked for decades. But there's a growing category of backend logic where "trust us" isn't enough. Scoring algorithms, compliance checks, eligibility rules, validation pipelines. Anywhere someone needs to independently confirm that your code does what you say it does.&lt;/p&gt;

&lt;p&gt;You don't have a code problem. You have a verifiability problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  The case for publicly auditable code
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvblmmb49iy7jrpp5e8h5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvblmmb49iy7jrpp5e8h5.png" alt="Black box vs verifiable compute" width="800" height="541"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Most backend engineers don't think about code auditability because most code doesn't need it. Your CRUD endpoints, your auth flows, your data transformations, those are internal concerns. Nobody outside your org needs to verify them.&lt;/p&gt;

&lt;p&gt;But some functions carry weight. A scoring algorithm that determines loan eligibility. A validation pipeline that checks regulatory compliance. A pricing engine that partners depend on. For these functions, the standard answer is documentation: you write a spec, maybe you share pseudocode, and everyone agrees to trust that the running code matches the paper.&lt;/p&gt;

&lt;p&gt;That's not verification. That's trust with extra steps.&lt;/p&gt;

&lt;p&gt;Publicly auditable code means anyone can call the same function with the same inputs and confirm they get the same outputs. The logic is visible. Execution is deterministic. And the code can't change silently after deployment.&lt;/p&gt;

&lt;p&gt;This isn't a new idea. Open source gives you code visibility. But open source doesn't prove that the code running in production is the same code in the repo. You need a runtime where the deployed code is the source of truth, and where every execution is independently reproducible.&lt;/p&gt;

&lt;p&gt;That runtime exists. And you don't need to learn a new language to use it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rust in, verifiable execution out
&lt;/h2&gt;

&lt;p&gt;When most engineers hear "deploy code onchain," they picture learning Solidity, memorizing gas optimization tricks, and rewriting working logic in an unfamiliar language. That's a reasonable reason to stop listening.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.arbitrum.io/stylus/gentle-introduction" rel="noopener noreferrer"&gt;Arbitrum Stylus&lt;/a&gt; skips that detour. You write Rust, compile to WASM, and deploy it to a blockchain that's optimized for low-cost computation. Your existing toolchain, your existing crates, your existing mental model. The contract runs alongside Solidity smart contracts with shared state, but you never need to touch Solidity yourself.&lt;/p&gt;

&lt;p&gt;This isn't about converting you to crypto. It's about giving you a deployment target where execution is publicly verifiable by default.&lt;/p&gt;

&lt;p&gt;Three properties matter here:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Every execution is independently auditable.&lt;/strong&gt; Anyone can call the contract with the same inputs and confirm they get the same outputs. No trust required.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The logic is immutable.&lt;/strong&gt; Once deployed, the code can't change silently. If you push an update, that's a new deployment with its own address and its own history. No "we patched it last Tuesday but forgot to tell you."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Execution is deterministic.&lt;/strong&gt; Same inputs, same outputs, every time, on every node. No environment-specific drift, no floating point surprises across hardware.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the architecture looks like
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frk0oqvh90439evj9hyur.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frk0oqvh90439evj9hyur.png" alt="Scoring engine architecture" width="800" height="494"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I built a &lt;a href="https://github.com/hummusonrails/stylus-scoring-engine" rel="noopener noreferrer"&gt;scoring engine&lt;/a&gt; to test this pattern. The architecture splits cleanly between what stays in your infrastructure and what moves onchain:&lt;/p&gt;

&lt;p&gt;Your backend stays exactly where it is. An Axum API server handles business rules, authentication, request routing. Normal backend work. The only difference is that when the API needs a computation verified, it calls an onchain contract instead of a local function.&lt;/p&gt;

&lt;p&gt;The onchain piece is a Stylus contract. It takes parameters, runs the calculation, and returns results. No state storage, just pure computation. Think of it as a function you deploy instead of host.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1w298ib5f7tb8xt2djjc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1w298ib5f7tb8xt2djjc.png" alt="Shared crate compilation" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A shared Rust crate holds the type definitions and compiles for both environments. The contract uses &lt;code&gt;no_std&lt;/code&gt; for WASM. The API server uses &lt;code&gt;std&lt;/code&gt; for native. Same types, both sides. The compiler guarantees they match. No serialization mismatches, no type drift between your API and your contract.&lt;/p&gt;

&lt;p&gt;One language across the entire stack. The chain boundary becomes a function call, not a language barrier.&lt;/p&gt;

&lt;h2&gt;
  
  
  The performance question
&lt;/h2&gt;

&lt;p&gt;The first objection any backend engineer raises: what's the overhead?&lt;/p&gt;

&lt;p&gt;Fair. Onchain computation has historically been expensive. That's the whole reason Solidity exists. It was designed to minimize execution cost on the EVM.&lt;/p&gt;

&lt;p&gt;Stylus changes the math. WASM execution on Arbitrum is dramatically cheaper than EVM execution for compute-heavy workloads. The scoring engine used over 90% less gas than the equivalent Solidity contract doing identical work:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Environment&lt;/th&gt;
&lt;th&gt;Score&lt;/th&gt;
&lt;th&gt;Gas Usage&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Offchain Rust&lt;/td&gt;
&lt;td&gt;953&lt;/td&gt;
&lt;td&gt;n/a&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Onchain Stylus (WASM)&lt;/td&gt;
&lt;td&gt;953&lt;/td&gt;
&lt;td&gt;76,048&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Onchain Solidity (EVM)&lt;/td&gt;
&lt;td&gt;810&lt;/td&gt;
&lt;td&gt;1,027,635&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Storage operations cost the same either way, but iterative math, weighted calculations, and loop-heavy logic is where WASM pulls ahead.&lt;/p&gt;

&lt;p&gt;This isn't a contrived benchmark. &lt;a href="https://redstone.finance/" rel="noopener noreferrer"&gt;RedStone&lt;/a&gt;, an oracle provider, &lt;a href="https://blog.arbitrum.io/how-redstone-is-advancing-oracle-capabilities-with-stylus" rel="noopener noreferrer"&gt;published similar results&lt;/a&gt; after porting their verification logic to Stylus. Compute-intensive work is where this architecture pays for itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where auditable code matters
&lt;/h2&gt;

&lt;p&gt;Not every function belongs onchain. But some backend logic sits in a category where "trust me" isn't good enough:&lt;/p&gt;

&lt;p&gt;Scoring and risk calculations, where regulators want to audit the exact computation, not a description of it. Eligibility determinations, where partners want to verify outcomes independently instead of trusting your API response. Compliance validation, where auditors want proof that the logic running today is the same logic approved last quarter. Data pipelines, where counterparties want to confirm their data passed through agreed-upon rules.&lt;/p&gt;

&lt;p&gt;If you've ever written documentation explaining how your algorithm works so someone else could trust it, that's the function that belongs onchain. Let them verify the code instead of reading your summary of the code.&lt;/p&gt;

&lt;p&gt;The scoring engine I built is one example of this pattern. But the pattern is the point, not the example. Any pure function where independent verification adds value is a candidate.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you'd actually do on a Monday morning
&lt;/h2&gt;

&lt;p&gt;If you write Rust, the learning curve is smaller than you think.&lt;/p&gt;

&lt;p&gt;Install &lt;a href="https://github.com/OffchainLabs/cargo-stylus" rel="noopener noreferrer"&gt;&lt;code&gt;cargo-stylus&lt;/code&gt;&lt;/a&gt;, which handles compilation and deployment. Write your function with &lt;code&gt;#[entrypoint]&lt;/code&gt; and &lt;code&gt;#[public]&lt;/code&gt; macros. Compile to WASM. Deploy to a testnet. Call it from your existing API using an RPC endpoint.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[entrypoint]&lt;/span&gt;
&lt;span class="nd"&gt;#[storage]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;ScoringEngine&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[public]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ScoringEngine&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;score&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;factors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Factor&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Rule&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;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Your computation logic here&lt;/span&gt;
        &lt;span class="c1"&gt;// Publicly verifiable, deterministic, immutable&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;Your database, your auth, your business logic, none of that moves. You add one function that runs somewhere verifiable instead of somewhere trusted. Everything else stays the same.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/hummusonrails/stylus-scoring-engine" rel="noopener noreferrer"&gt;scoring engine repo&lt;/a&gt; has working code, setup instructions, and benchmarks comparing three execution paths: onchain Stylus, onchain Solidity, and offchain Rust. It's a concrete starting point, but the approach applies anywhere you need auditable computation.&lt;/p&gt;
&lt;h2&gt;
  
  
  You're adding a deployment target, not adopting a lifestyle
&lt;/h2&gt;

&lt;p&gt;The shift is smaller than it sounds. The same way you might deploy a function to Lambda for serverless execution or to a TEE for confidential computing, you deploy to Arbitrum for verifiable execution.&lt;/p&gt;

&lt;p&gt;Your Rust skills transfer directly. Your toolchain stays the same. The only thing that changes is who can verify your logic: everyone.&lt;/p&gt;

&lt;p&gt;If you've been dismissing onchain as irrelevant to backend work, spend thirty minutes with the &lt;a href="https://github.com/hummusonrails/stylus-scoring-engine" rel="noopener noreferrer"&gt;repo&lt;/a&gt;.&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/hummusonrails" rel="noopener noreferrer"&gt;
        hummusonrails
      &lt;/a&gt; / &lt;a href="https://github.com/hummusonrails/stylus-scoring-engine" rel="noopener noreferrer"&gt;
        stylus-scoring-engine
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Verifiable credit/risk scoring engine: Stylus (Rust/WASM) vs Solidity on Arbitrum with three-way gas benchmark
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;p&gt;
  &lt;a rel="noopener noreferrer" href="https://github.com/hummusonrails/stylus-scoring-engine/.github/banner.svg"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fhummusonrails%2Fstylus-scoring-engine%2FHEAD%2F.github%2Fbanner.svg" alt="stylus-scoring-engine" width="100%"&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
  &lt;a href="https://github.com/hummusonrails/stylus-scoring-engine/LICENSE" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/942e017bf0672002dd32a857c95d66f28c5900ab541838c6c664442516309c8a/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c75652e7376673f7374796c653d666c61742d737175617265" alt="License"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/c609e2b41de6e411f523c403fb94432207ee58321549a51ad8398529273854eb/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f727573742d312e38382d6f72616e67652e7376673f7374796c653d666c61742d737175617265"&gt;&lt;img src="https://camo.githubusercontent.com/c609e2b41de6e411f523c403fb94432207ee58321549a51ad8398529273854eb/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f727573742d312e38382d6f72616e67652e7376673f7374796c653d666c61742d737175617265" alt="Rust"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/47a6086701dfe590e01cab365b8e6a5ef4fb5fbef9696ace98b09ee0d2133304/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7374796c75732d2d73646b2d302e31302e302d3132414146462e7376673f7374796c653d666c61742d737175617265"&gt;&lt;img src="https://camo.githubusercontent.com/47a6086701dfe590e01cab365b8e6a5ef4fb5fbef9696ace98b09ee0d2133304/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7374796c75732d2d73646b2d302e31302e302d3132414146462e7376673f7374796c653d666c61742d737175617265" alt="Stylus SDK"&gt;&lt;/a&gt;
  &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/d49b7b9955764e444eb46dfd7f8e2fd481ec8fba30e9b7578e8d7a6ebb1fb480/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6178756d2d302e382d707572706c652e7376673f7374796c653d666c61742d737175617265"&gt;&lt;img src="https://camo.githubusercontent.com/d49b7b9955764e444eb46dfd7f8e2fd481ec8fba30e9b7578e8d7a6ebb1fb480/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6178756d2d302e382d707572706c652e7376673f7374796c653d666c61742d737175617265" alt="Axum"&gt;&lt;/a&gt;
  &lt;a href="https://github.com/hummusonrails/stylus-scoring-engine/issues" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/25b3e6d0d42c98de74a98cbb4d149a1c09020cf6d1361993b72d7d5b8ffed363/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5052732d77656c636f6d652d627269676874677265656e2e7376673f7374796c653d666c61742d737175617265" alt="PRs Welcome"&gt;&lt;/a&gt;
&lt;/p&gt;
&lt;p&gt;
  &lt;strong&gt;Verifiable credit/risk scoring on Arbitrum with a three-way gas benchmark: offchain Rust vs onchain Stylus vs onchain Solidity.&lt;/strong&gt;
  &lt;br&gt;
  &lt;a href="https://github.com/hummusonrails/stylus-scoring-engine#quick-start" rel="noopener noreferrer"&gt;Quick Start&lt;/a&gt; · &lt;a href="https://github.com/hummusonrails/stylus-scoring-engine#architecture" rel="noopener noreferrer"&gt;Architecture&lt;/a&gt; · &lt;a href="https://github.com/hummusonrails/stylus-scoring-engine/issues" rel="noopener noreferrer"&gt;Report a Bug&lt;/a&gt;
&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What it does&lt;/h2&gt;
&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Benchmarks&lt;/strong&gt; the same scoring algorithm across three execution environments with real gas numbers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scores&lt;/strong&gt; entities against configurable weighted rules with iterative convergence and cross-factor correlation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Demonstrates&lt;/strong&gt; Stylus gas savings on compute-heavy workloads (over 90% cheaper than Solidity)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Shares&lt;/strong&gt; Rust types between the onchain contract and offchain API server from a single crate&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stores&lt;/strong&gt; scoring rules in SQLite with full CRUD via REST endpoints&lt;/li&gt;
&lt;/ul&gt;



&lt;p&gt;
  &lt;a rel="noopener noreferrer" href="https://github.com/hummusonrails/stylus-scoring-engine/.github/demo.gif"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fhummusonrails%2Fstylus-scoring-engine%2FHEAD%2F.github%2Fdemo.gif" alt="Deploy and benchmark demo" width="100%"&gt;&lt;/a&gt;
&lt;/p&gt;



&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Quick Start&lt;/h2&gt;
&lt;/div&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; clone and install&lt;/span&gt;
git clone https://github.com/hummusonrails/stylus-scoring-engine.git
&lt;span class="pl-c1"&gt;cd&lt;/span&gt; stylus-scoring-engine
pnpm install

&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; start the local arbitrum devnode&lt;/span&gt;
pnpm devnode

&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; in a new terminal, deploy both contracts&lt;/span&gt;
pnpm deploy:all

&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; start the api server (reads .env written by deploy)&lt;/span&gt;
pnpm api

&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; in a new terminal, run the three-way benchmark&lt;/span&gt;
pnpm benchmark&lt;/pre&gt;

&lt;/div&gt;

&lt;strong&gt;Prerequisites&lt;/strong&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://rustup.rs/" rel="nofollow noopener noreferrer"&gt;Rust&lt;/a&gt; 1.88+ with…&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/hummusonrails/stylus-scoring-engine" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;The gap between "interesting in theory" and "I could actually use this" is shorter than you expect.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>webassembly</category>
      <category>blockchain</category>
      <category>backend</category>
    </item>
    <item>
      <title>The readability scores your content tool is missing</title>
      <dc:creator>Ben Greenberg</dc:creator>
      <pubDate>Tue, 31 Mar 2026 06:00:28 +0000</pubDate>
      <link>https://dev.to/bengreenberg/the-readability-scores-your-content-tool-is-missing-39be</link>
      <guid>https://dev.to/bengreenberg/the-readability-scores-your-content-tool-is-missing-39be</guid>
      <description>&lt;h1&gt;
  
  
  The Readability Scores Your Content Tool Is Missing
&lt;/h1&gt;

&lt;p&gt;Most readability tooling stops at a single score. That is a problem if you are building a documentation pipeline, a content linter, or any system that needs to catch unreadable text before it ships.&lt;/p&gt;

&lt;p&gt;Here are the four metrics worth tracking, what each one actually measures, and what your targets should be.&lt;/p&gt;

&lt;h2&gt;
  
  
  Flesch-Kincaid Grade Level
&lt;/h2&gt;

&lt;p&gt;This score maps text to a U.S. school grade level based on two inputs: average sentence length and average word length in syllables. A score of 8 means a typical 13-year-old can read it without friction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Target: 6 to 9 for most technical docs.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If your score is above 12, sentences are too long or you are leaning on polysyllabic jargon. Users will skim past dense paragraphs instead of reading them. If your score is below 5, you are likely oversimplifying to the point where context is missing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Flesch Reading Ease
&lt;/h2&gt;

&lt;p&gt;This uses the same inputs as FK Grade Level but outputs an inverse score on a 0 to 100 scale. Higher means easier. It weights sentence length more heavily than syllable count, so it punishes run-on sentences hard.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Target: 60 to 70 for technical documentation.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Below 50 and you are in academic or legal territory. Most readers will not finish the section. Above 75 and you may be losing precision, which matters in technical writing where exact phrasing carries meaning. The 60 to 70 range is the practical sweet spot where clarity and accuracy coexist.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automated Readability Index (ARI)
&lt;/h2&gt;

&lt;p&gt;ARI takes a different approach. Instead of counting syllables, it counts characters per word. This makes it faster to compute and less sensitive to syllabification edge cases. It outputs a grade-level score similar to FK but often diverges on technical content, where long words are common but not necessarily difficult for the target audience.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Target: 7 to 10 for developer docs.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Where ARI earns its place is as a cross-check. If FK says grade 8 but ARI says grade 14, you likely have a cluster of long technical terms inflating the character count. That is worth reviewing even if the content reads fine to a subject matter expert, because new users will not have that context.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sentence Length Variance
&lt;/h2&gt;

&lt;p&gt;Most tools report average sentence length and stop there. Variance is the signal they miss. Text where every sentence is roughly the same length reads as monotonous and is harder to parse. Alternating short and long sentences creates rhythm, which keeps readers oriented.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Target: Standard deviation of 8 to 15 words across your sentence lengths.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Below 5 means your writing is flat. Above 20 means sentence structure is inconsistent in a way that will confuse automated parsers and human readers alike. This is especially relevant in procedural docs where scannable short sentences should anchor longer explanatory ones.&lt;/p&gt;




&lt;p&gt;I built a TextAnalytics API that returns all of these scores in a single call -- useful if you are building a linter, a CMS plugin, or just want to quality-gate your content pipeline: &lt;a href="https://rapidapi.com/ben-eI6jno4PU/api/textanalytics" rel="noopener noreferrer"&gt;https://rapidapi.com/ben-eI6jno4PU/api/textanalytics&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>writing</category>
      <category>api</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Quick tip: Flesch-Kincaid Grade Level 8 is your target</title>
      <dc:creator>Ben Greenberg</dc:creator>
      <pubDate>Fri, 27 Mar 2026 11:00:05 +0000</pubDate>
      <link>https://dev.to/bengreenberg/quick-tip-flesch-kincaid-grade-level-8-is-your-target-ci7</link>
      <guid>https://dev.to/bengreenberg/quick-tip-flesch-kincaid-grade-level-8-is-your-target-ci7</guid>
      <description>&lt;p&gt;Quick tip on writing docs and content:&lt;/p&gt;

&lt;p&gt;Flesch-Kincaid Grade Level 8 = readable by a 13-year-old. That's your target for most technical documentation.&lt;/p&gt;

&lt;p&gt;Not because your readers are 13 — but because shorter sentences and common words reduce cognitive load. Even senior engineers read faster at grade 8.&lt;/p&gt;

&lt;p&gt;Most enterprise docs sit at grade 14-16. That's why people skim them.&lt;/p&gt;

&lt;p&gt;The TextAnalytics API returns FK grade level (plus 4 other readability scores) in a single call: &lt;a href="https://rapidapi.com/ben-eI6jno4PU/api/textanalytics" rel="noopener noreferrer"&gt;https://rapidapi.com/ben-eI6jno4PU/api/textanalytics&lt;/a&gt;&lt;/p&gt;

</description>
      <category>writing</category>
      <category>productivity</category>
      <category>devtips</category>
      <category>api</category>
    </item>
    <item>
      <title>I built a link preview API — here's what I learned about Open Graph</title>
      <dc:creator>Ben Greenberg</dc:creator>
      <pubDate>Tue, 24 Mar 2026 07:01:41 +0000</pubDate>
      <link>https://dev.to/bengreenberg/i-built-a-link-preview-api-heres-what-i-learned-about-open-graph-2j99</link>
      <guid>https://dev.to/bengreenberg/i-built-a-link-preview-api-heres-what-i-learned-about-open-graph-2j99</guid>
      <description>&lt;h2&gt;
  
  
  I Built a Link Preview API — Here's What I Learned About Open Graph
&lt;/h2&gt;

&lt;p&gt;Link previews seem simple until you actually build something that generates them reliably. I spent weeks digging into how platforms parse Open Graph metadata, and I kept running into the same category of problems: missing images, wrong fallbacks, cached bad data. Here is what surprised me.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Link Previews Actually Are (And Why They Break)
&lt;/h2&gt;

&lt;p&gt;When you paste a URL into Slack or Twitter, the platform fetches that page, reads the &lt;code&gt;&amp;lt;meta&amp;gt;&lt;/code&gt; tags in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;, and renders a card. The Open Graph protocol, originally developed by Facebook, defines the standard tags most platforms follow: &lt;code&gt;og:title&lt;/code&gt;, &lt;code&gt;og:description&lt;/code&gt;, &lt;code&gt;og:image&lt;/code&gt;, and &lt;code&gt;og:url&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The reason previews break so often comes down to a few recurring patterns:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The &lt;code&gt;og:image&lt;/code&gt; tag is missing entirely&lt;/li&gt;
&lt;li&gt;The image URL is relative instead of absolute&lt;/li&gt;
&lt;li&gt;The image dimensions are wrong and the platform rejects it silently&lt;/li&gt;
&lt;li&gt;The metadata exists but the page blocks crawlers with a bad &lt;code&gt;robots.txt&lt;/code&gt; or a JavaScript render wall&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That last one is brutal. If your content is rendered client-side and you have no SSR or prerendering, most crawlers will fetch an empty shell and your preview will be blank.&lt;/p&gt;

&lt;h2&gt;
  
  
  The og:image Requirements Most Devs Skip
&lt;/h2&gt;

&lt;p&gt;I see this constantly. Developers add an &lt;code&gt;og:image&lt;/code&gt; tag, the preview still looks broken, and they cannot figure out why. The requirements are stricter than the documentation implies:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Dimensions should be 1200x630 pixels.&lt;/strong&gt; Facebook and LinkedIn will downscale, but if you go too small (under 200x200) they ignore the image completely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;File size should stay under 1MB.&lt;/strong&gt; Larger images may time out during the crawl window.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The URL must be absolute.&lt;/strong&gt; &lt;code&gt;/images/preview.png&lt;/code&gt; will not work. &lt;code&gt;https://yourdomain.com/images/preview.png&lt;/code&gt; will.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No SVGs.&lt;/strong&gt; Almost no platform will render an SVG as a preview image. Use PNG or JPEG.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  How Twitter Cards Differ From Open Graph
&lt;/h2&gt;

&lt;p&gt;Twitter does read OG tags as a fallback, but it has its own system called Twitter Cards, and the &lt;code&gt;twitter:card&lt;/code&gt; type changes how everything renders.&lt;/p&gt;

&lt;p&gt;The two types you actually care about are &lt;code&gt;summary&lt;/code&gt; and &lt;code&gt;summary_large_image&lt;/code&gt;. If you use &lt;code&gt;summary&lt;/code&gt;, Twitter shows a small thumbnail beside the text. If you use &lt;code&gt;summary_large_image&lt;/code&gt;, you get the full-width banner image. Most developers want the large image but forget to set the tag, so they get the small thumbnail and wonder why it looks wrong compared to LinkedIn.&lt;/p&gt;

&lt;p&gt;You also need &lt;code&gt;twitter:title&lt;/code&gt; and &lt;code&gt;twitter:description&lt;/code&gt; even if you already have the OG equivalents. Twitter will use OG as a fallback, but being explicit is more reliable.&lt;/p&gt;

&lt;h2&gt;
  
  
  The og:description Fallback Chain
&lt;/h2&gt;

&lt;p&gt;When &lt;code&gt;og:description&lt;/code&gt; is missing, platforms do not just leave it blank. The fallback chain typically goes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;og:description&lt;/code&gt; meta tag&lt;/li&gt;
&lt;li&gt;Standard &lt;code&gt;meta name="description"&lt;/code&gt; tag&lt;/li&gt;
&lt;li&gt;First paragraph of visible body text&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Knowing this means you can usually ensure something reasonable shows up even on pages that have not been fully optimized.&lt;/p&gt;

&lt;h2&gt;
  
  
  The One Gotcha That Bit Me Hard
&lt;/h2&gt;

&lt;p&gt;If &lt;code&gt;og:url&lt;/code&gt; does not match the canonical URL of the page, platforms will cache the preview against the wrong URL. I had a staging URL leak into production metadata once and spent an embarrassing amount of time wondering why the preview was showing old content. Always set &lt;code&gt;og:url&lt;/code&gt; explicitly to the canonical version.&lt;/p&gt;




&lt;p&gt;Parsing all of this reliably across different platforms, redirect chains, and malformed HTML is exactly the kind of work that sounds quick and turns into a week of edge cases. I use what I built in production at LinkPreview API. It handles the messy parts of parsing OG data so you do not have to: &lt;a href="https://rapidapi.com/ben-eI6jno4PU/api/linkpreview1" rel="noopener noreferrer"&gt;https://rapidapi.com/ben-eI6jno4PU/api/linkpreview1&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>api</category>
      <category>javascript</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>5 things your website is getting wrong (and how to check for free)</title>
      <dc:creator>Ben Greenberg</dc:creator>
      <pubDate>Sun, 22 Mar 2026 21:51:15 +0000</pubDate>
      <link>https://dev.to/bengreenberg/5-things-your-website-is-getting-wrong-and-how-to-check-for-free-22fm</link>
      <guid>https://dev.to/bengreenberg/5-things-your-website-is-getting-wrong-and-how-to-check-for-free-22fm</guid>
      <description>&lt;p&gt;Most websites fail basic technical hygiene checks. Not because developers don't care, but because these things are easy to miss when you're focused on shipping features. Here are five common issues worth fixing today.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Missing or Wrong Security Headers
&lt;/h2&gt;

&lt;p&gt;Headers like &lt;code&gt;Content-Security-Policy&lt;/code&gt;, &lt;code&gt;X-Frame-Options&lt;/code&gt;, and &lt;code&gt;Strict-Transport-Security&lt;/code&gt; (HSTS) protect your users from clickjacking, XSS attacks, and protocol downgrade attacks. Skipping them leaves real attack surface open. Browsers and security scanners will flag these absences, and some enterprise clients actively check before integrating with your API.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to check:&lt;/strong&gt; Run &lt;code&gt;curl -I https://yourdomain.com&lt;/code&gt; and scan the response headers. Or paste your URL into &lt;a href="https://securityheaders.com" rel="noopener noreferrer"&gt;securityheaders.com&lt;/a&gt; for a free graded report.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Open Graph Tags That Break Link Previews
&lt;/h2&gt;

&lt;p&gt;When someone shares your link on Slack, LinkedIn, or Twitter, the platform reads your Open Graph meta tags to build the preview card. If &lt;code&gt;og:title&lt;/code&gt;, &lt;code&gt;og:image&lt;/code&gt;, or &lt;code&gt;og:description&lt;/code&gt; are missing or misconfigured, the preview looks broken or empty. This tanks click-through rates on content you spent real time creating.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to check:&lt;/strong&gt; Use the &lt;a href="https://www.opengraph.xyz" rel="noopener noreferrer"&gt;OpenGraph.xyz&lt;/a&gt; previewer. Paste your URL and see exactly what Slack or LinkedIn will render. Fix any missing tags in your &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. No Canonical URL
&lt;/h2&gt;

&lt;p&gt;If your page is reachable at both &lt;code&gt;https://example.com/page&lt;/code&gt; and &lt;code&gt;https://example.com/page?ref=newsletter&lt;/code&gt;, search engines may treat these as separate pages competing against each other. Over time this splits your ranking signals and can suppress both versions. A canonical tag tells crawlers which URL is the one that counts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to check:&lt;/strong&gt; Open DevTools in Chrome, go to Elements, and search for &lt;code&gt;canonical&lt;/code&gt; in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;. You should find something like &lt;code&gt;&amp;lt;link rel="canonical" href="https://example.com/page" /&amp;gt;&lt;/code&gt;. If it is missing or pointing to the wrong URL, fix it in your template.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Images Without Alt Text
&lt;/h2&gt;

&lt;p&gt;Alt text is not optional. Screen readers rely on it for users with visual impairments, and search engine crawlers use it to understand image content. A page full of images with empty or missing &lt;code&gt;alt&lt;/code&gt; attributes is both an accessibility failure and a missed SEO opportunity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to check:&lt;/strong&gt; Run &lt;code&gt;document.querySelectorAll('img:not([alt])')&lt;/code&gt; in the browser console. Any results mean you have untagged images. Axe DevTools (free browser extension) will also flag these automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Missing Viewport Meta Tag
&lt;/h2&gt;

&lt;p&gt;Without &lt;code&gt;&amp;lt;meta name="viewport" content="width=device-width, initial-scale=1"&amp;gt;&lt;/code&gt; in your &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt;, mobile browsers will render your page at desktop width and then scale it down. The result is a tiny, unreadable layout that frustrates users and tanks your Core Web Vitals scores.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How to check:&lt;/strong&gt; Open DevTools, toggle the device toolbar (Ctrl+Shift+M), and load your page. If it renders like a shrunken desktop site, the viewport tag is probably missing. Check the Elements panel to confirm.&lt;/p&gt;




&lt;p&gt;These five checks take maybe 15 minutes to run manually. If you want to do all of them in one go, I built a free tool that runs 5 key checks instantly, no signup, no waiting: &lt;a href="https://audit.hummusonrails.com/free" rel="noopener noreferrer"&gt;https://audit.hummusonrails.com/free&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>seo</category>
      <category>security</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Quick tip: your og:image should be 1200x630px</title>
      <dc:creator>Ben Greenberg</dc:creator>
      <pubDate>Fri, 20 Mar 2026 12:00:10 +0000</pubDate>
      <link>https://dev.to/bengreenberg/quick-tip-your-ogimage-should-be-1200x630px-4m27</link>
      <guid>https://dev.to/bengreenberg/quick-tip-your-ogimage-should-be-1200x630px-4m27</guid>
      <description>&lt;p&gt;Quick tip about link previews:&lt;/p&gt;

&lt;p&gt;Your &lt;code&gt;og:image&lt;/code&gt; should be &lt;strong&gt;1200x630px&lt;/strong&gt;. Most sites get this wrong — either wrong dimensions, an SVG (which many platforms reject), or a relative URL instead of absolute.&lt;/p&gt;

&lt;p&gt;Check yours:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://yoursite.com | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s1"&gt;'og:image'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're building something that reads og:image from other sites, the LinkPreview API handles all the edge cases: &lt;a href="https://rapidapi.com/ben-eI6jno4PU/api/linkpreview1" rel="noopener noreferrer"&gt;https://rapidapi.com/ben-eI6jno4PU/api/linkpreview1&lt;/a&gt;&lt;/p&gt;

</description>
      <category>seo</category>
      <category>webdev</category>
      <category>devtips</category>
      <category>opengraph</category>
    </item>
    <item>
      <title>Quick tip: check your security headers with curl</title>
      <dc:creator>Ben Greenberg</dc:creator>
      <pubDate>Wed, 18 Mar 2026 21:51:11 +0000</pubDate>
      <link>https://dev.to/bengreenberg/quick-tip-check-your-security-headers-with-curl-1n94</link>
      <guid>https://dev.to/bengreenberg/quick-tip-check-your-security-headers-with-curl-1n94</guid>
      <description>&lt;p&gt;Quick tip for checking your security headers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-I&lt;/span&gt; https://yoursite.com | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'x-frame\|content-security\|strict-transport\|x-content-type'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you get no output, you're missing all of them. HSTS is the most important one to add first — it forces HTTPS on all future visits.&lt;/p&gt;

&lt;p&gt;Want a full breakdown? Run a free check at &lt;a href="https://audit.hummusonrails.com/free" rel="noopener noreferrer"&gt;https://audit.hummusonrails.com/free&lt;/a&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>webdev</category>
      <category>devtips</category>
      <category>cli</category>
    </item>
    <item>
      <title>I got tired of building the same link preview function, so I made it an API</title>
      <dc:creator>Ben Greenberg</dc:creator>
      <pubDate>Sun, 15 Mar 2026 18:01:45 +0000</pubDate>
      <link>https://dev.to/bengreenberg/i-got-tired-of-building-the-same-link-preview-function-so-i-made-it-an-api-47bg</link>
      <guid>https://dev.to/bengreenberg/i-got-tired-of-building-the-same-link-preview-function-so-i-made-it-an-api-47bg</guid>
      <description>&lt;p&gt;Every app I've built in the last few years has needed the same thing: paste a URL, show a preview card. Slack does it. Discord does it. Every CMS does it. And every time, I end up writing the same cheerio scraping code, handling the same edge cases with Open Graph tags, and debugging the same issue where Twitter Cards use &lt;code&gt;name&lt;/code&gt; instead of &lt;code&gt;property&lt;/code&gt; and half the internet gets it wrong.&lt;/p&gt;

&lt;p&gt;A little while ago I finally extracted all of that into a standalone API and put it on RapidAPI. Figured other people are writing the same code too.&lt;/p&gt;

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

&lt;p&gt;You pass a URL. You get back structured metadata across six layers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Open Graph (title, description, images with dimensions, article metadata)&lt;/li&gt;
&lt;li&gt;Twitter Cards (card type, site, creator; only tags that are actually present, no fallback guessing)&lt;/li&gt;
&lt;li&gt;HTML meta (title tag, meta description, canonical, theme color)&lt;/li&gt;
&lt;li&gt;Icons (auto-selects the highest quality favicon with a priority chain)&lt;/li&gt;
&lt;li&gt;Feeds (discovers RSS, Atom, and JSON Feed links)&lt;/li&gt;
&lt;li&gt;JSON-LD (parses all script blocks, prefers Article/Product over BreadcrumbList)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The response gives you both a merged top-level view (where the title comes from whichever source has it; OG first, then Twitter, then the title tag) and the raw parsed layers, so you can do your own logic.&lt;/p&gt;

&lt;p&gt;Here's what GitHub returns:&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;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GitHub · Build and ship software on a single, collaborative platform"&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;"Join the world's most widely adopted..."&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="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://github.githubassets.com/images/modules/site/social-cards/campaign-social.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;"width"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"height"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;630&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;"favicon"&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://github.githubassets.com/favicons/favicon.svg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"siteName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GitHub"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"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;"website"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"themeColor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"#1e2327"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"openGraph"&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="err"&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;"twitter"&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="err"&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;"feeds"&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;"jsonLd"&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;"@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;"WebSite"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&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;"responseTime"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;234&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;h2&gt;
  
  
  The parts that were annoying to get right
&lt;/h2&gt;

&lt;p&gt;A few things I ran into while building this that I suspect anyone writing their own scraper will hit too:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OG tags use &lt;code&gt;property&lt;/code&gt;, Twitter uses &lt;code&gt;name&lt;/code&gt;.&lt;/strong&gt; The Open Graph spec says &lt;code&gt;&amp;lt;meta property="og:title"&amp;gt;&lt;/code&gt;. Twitter Cards say &lt;code&gt;&amp;lt;meta name="twitter:card"&amp;gt;&lt;/code&gt;. But a surprising number of sites swap them. The parser checks both attributes for both prefixes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Multiple &lt;code&gt;og:image&lt;/code&gt; tags are valid.&lt;/strong&gt; The OG spec supports arrays by repeating the tag. Structured properties like &lt;code&gt;og:image:width&lt;/code&gt; apply to the most recently declared &lt;code&gt;og:image&lt;/code&gt;. Most scrapers just grab the first one and ignore the rest.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JSON-LD blocks are a mess.&lt;/strong&gt; A typical news article page has three or four JSON-LD blocks: a BreadcrumbList, an Organization, and then the actual Article buried in the third one. You need to parse all of them and pick the right one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Favicons have a priority order.&lt;/strong&gt; Apple touch icons at 180x180 are usually the highest quality. Then standard icons at 32x32, then the generic &lt;code&gt;/favicon.ico&lt;/code&gt; fallback. Most implementations just grab the first &lt;code&gt;&amp;lt;link rel="icon"&amp;gt;&lt;/code&gt; they find.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Relative URLs everywhere.&lt;/strong&gt; OG images and feed links are often relative paths. You need the effective URL (after redirects) as the base to resolve them correctly.&lt;/p&gt;

&lt;h2&gt;
  
  
  The technical approach
&lt;/h2&gt;

&lt;p&gt;It's a Fastify server running on a VPS, using cheerio for HTML parsing. No headless browser, no Puppeteer, just fetch the HTML and parse it. This keeps response times under 500ms for cache misses and under 5ms for cache hits.&lt;/p&gt;

&lt;p&gt;SSRF protection was the part I spent the most time on. Since the API accepts arbitrary URLs from users, you need to prevent people from using it to probe internal networks. The server resolves the hostname first, checks the IP against a blocklist of private ranges, then connects directly to the resolved IP to prevent DNS rebinding attacks.&lt;/p&gt;

&lt;p&gt;I also built a text analytics API while I was at it, using the same infrastructure but for a different purpose. Pass in text, get back readability scores (Flesch-Kincaid, Coleman-Liau, SMOG, and three others), keyword density, bigrams, trigrams, and reading time. All pure math on strings, sub-10ms responses. Useful for content optimization tools and writing assistants.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try them
&lt;/h2&gt;

&lt;p&gt;Both APIs are on RapidAPI with a free tier (500 requests/month):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://rapidapi.com/ben-eI6jno4PU/api/linkpreview1" rel="noopener noreferrer"&gt;LinkPreview&lt;/a&gt; — URL metadata extraction&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://rapidapi.com/ben-eI6jno4PU/api/textanalytics" rel="noopener noreferrer"&gt;TextAnalytics&lt;/a&gt; — readability scores, keyword density, text metrics&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The free tier is enough to test and prototype with. &lt;/p&gt;

&lt;p&gt;If you run into any edge cases the parser doesn't handle well, I'd genuinely like to know. Parsing the wild HTML of the internet is a forever project.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>api</category>
      <category>productivity</category>
    </item>
    <item>
      <title>What winning Arbitrum Open House teams do differently</title>
      <dc:creator>Ben Greenberg</dc:creator>
      <pubDate>Tue, 17 Feb 2026 14:13:22 +0000</pubDate>
      <link>https://dev.to/arbitrum/what-winning-arbitrum-open-house-teams-do-differently-18f8</link>
      <guid>https://dev.to/arbitrum/what-winning-arbitrum-open-house-teams-do-differently-18f8</guid>
      <description>&lt;p&gt;&lt;strong&gt;tl;dr You can also watch a quick 60-second animated video covering all these details:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;

&lt;iframe class="tweet-embed" id="tweet-2023756279282589847-592" src="https://platform.twitter.com/embed/Tweet.html?id=2023756279282589847"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-2023756279282589847-592');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=2023756279282589847&amp;amp;theme=dark"
  }





&lt;/p&gt;

&lt;p&gt;Most hackathon judging is a black box. You submit, you wait, you get a number. Maybe feedback, maybe not. Open House does it differently, and if you're applying, knowing the framework gives you an edge.&lt;/p&gt;

&lt;p&gt;We evaluate every Open House submission on execution, problem-solution fit, and traction. No mystery categories, no subjective vibes. Here's what the judges are looking at and why it matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Execution is the first filter
&lt;/h2&gt;

&lt;p&gt;The first thing judges open is your repo and your demo. Not your pitch deck. Not your Twitter bio.&lt;/p&gt;

&lt;p&gt;A strong execution score means you shipped a functional MVP with decent code structure, some docs and tests, and a demo that actually works. That's the baseline for being taken seriously. Below that, you're showing an idea, not a product. Above that, you're showing a team that can build under pressure and keep building after the event ends.&lt;/p&gt;

&lt;p&gt;In India, the first Open House cohort, here's what the team that won the highest execution score did. They implemented a high-precision math layer in Stylus using Q96.48 fixed-point arithmetic, added trade segmentation for large orders, and had the whole thing running on Arbitrum. That's not a weekend sketch. That's a team demonstrating they can ship production-grade work on a deadline.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F45egi514n3xvg1y0mi8m.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F45egi514n3xvg1y0mi8m.jpeg" alt="Developers at the India Founder House"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What judges are actually checking: Does the repo match the demo? Is the code structured like someone plans to maintain it? Is there a deployed contract on an Arbitrum chain? Does the team's background (GitHub history, prior work) support the claim that they can keep going?&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem and solution is where most teams lose points
&lt;/h2&gt;

&lt;p&gt;This is the criterion where most submissions land in the middle of the pack. Most teams can identify a problem. Fewer can explain why their solution is the right one, and fewer still can articulate why it needs to exist on Arbitrum specifically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scoring well here requires strong differentiation or an ecosystem-first approach, plus a roadmap that supports continued execution.&lt;/strong&gt; Generic DeFi dashboards and "blockchain for X" pitches without clear user value sit near the bottom.&lt;/p&gt;

&lt;p&gt;Right now, the sectors where we see the strongest builder momentum and clearest Arbitrum fit are payments and stablecoins, DeFi, RWAs, privacy, consumer products, and Arbitrum-native tooling around Stylus and Timeboost. Judges know what good looks like in each of these areas, and the bar is specific.&lt;/p&gt;

&lt;p&gt;In DeFi, for instance, we care about user-facing financial products, not standalone protocols. In payments, we want products where stablecoins feel like local money, not products that expose crypto complexity to end users. In privacy, we're looking for privacy-enabled products people would actually adopt, not abstract primitives.&lt;/p&gt;

&lt;p&gt;The difference between a mid-range and a top score here? A top score is a first-of-its-kind idea for Arbitrum with a well-articulated case for why the problem, the solution, and the chain all fit together.&lt;/p&gt;

&lt;h2&gt;
  
  
  Traction or potential is the tiebreaker
&lt;/h2&gt;

&lt;p&gt;Two teams with similar execution and problem-solution scores get separated here. This criterion looks at whether there's evidence the project will continue to exist after the event.&lt;/p&gt;

&lt;p&gt;Judges look at GitHub activity beyond the hackathon commits, social presence, community engagement, and whether there's a defined path forward. A roadmap alone isn't enough. Judges want to see signals that the team is building something they intend to ship to users, not just something that wins a prize.&lt;/p&gt;

&lt;p&gt;In India, the first Open House cohort, many of the winning teams advanced to the mentorship program after the Founder House. Several of those teams are continuing their progress toward mainnet launch. That's the kind of trajectory this criterion is trying to predict.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5cwc8vcpv3ji8gvg6zgf.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5cwc8vcpv3ji8gvg6zgf.jpeg" alt="Builders pitching their projects"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At the bottom of the range: dormant repo, no socials, no roadmap. At the top: demonstrated adoption or strong momentum with an engaged community, partnerships, repeat builders, or a credible launch plan.&lt;/p&gt;

&lt;h2&gt;
  
  
  The eligibility rule nobody should miss
&lt;/h2&gt;

&lt;p&gt;Before any scoring happens, there's one binary check: your project must be deployed on an Arbitrum chain. Not "plans to deploy." Not "could work on Arbitrum." Deployed. This eliminates a surprising number of submissions.&lt;/p&gt;

&lt;p&gt;That chain can be Arbitrum One, Arbitrum Sepolia, or your own Arbitrum chain. If you haven't explored launching your own, the &lt;a href="https://docs.arbitrum.io/launch-arbitrum-chain/a-gentle-introduction" rel="noopener noreferrer"&gt;gentle introduction&lt;/a&gt; is a good starting point. And if you want to test on infrastructure purpose-built for builders, Robinhood recently launched a testnet on Arbitrum and backed Open House with a $1 million investment in supporting builders in the ecosystem.&lt;/p&gt;

&lt;p&gt;

&lt;iframe class="tweet-embed" id="tweet-2021600209524969824-289" src="https://platform.twitter.com/embed/Tweet.html?id=2021600209524969824"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-2021600209524969824-289');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=2021600209524969824&amp;amp;theme=dark"
  }





&lt;/p&gt;

&lt;h2&gt;
  
  
  What this means for your project
&lt;/h2&gt;

&lt;p&gt;The NYC buildathon is live right now. The NYC Founder House is coming up and registration is open. Dubai registration is open too, with the buildathon running April 23 to May 14 and the Founder House May 28 to 31. Wherever you are in the pipeline, the scoring framework tells you where to spend your time.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg1iosaj8bm57asq6dzv6.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fg1iosaj8bm57asq6dzv6.jpeg" alt="Join either the NYC or Dubai program"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ship working software. A polished pitch deck with a broken demo loses to a rough pitch deck with a working MVP every time. Get your contracts deployed on Arbitrum Sepolia at minimum, Arbitrum One if you can.&lt;/p&gt;

&lt;p&gt;Know what good looks like in your sector. If you're building in DeFi, that means a user-facing product with clear financial utility, not another protocol wrapper. If you're building in payments, that means an experience where stablecoins feel invisible to the end user. Judges evaluate submissions against specific expectations for each category, so make sure your project speaks directly to what matters in yours.&lt;/p&gt;

&lt;p&gt;Show evidence of continued building. Active GitHub commits, a defined roadmap, social engagement, and pilot users all contribute to the traction score. If your repo goes silent after submission, judges notice.&lt;/p&gt;

&lt;p&gt;Build on the Arbitrum stack with intention. Using Stylus, leveraging Timeboost, composing with existing Arbitrum DeFi protocols, or scoping an Arbitrum chain for your use case are all signals that you understand the ecosystem, not just the EVM.&lt;/p&gt;

&lt;p&gt;Open House is designed to find teams that will keep building. The scoring reflects that. If you're serious about shipping a product on Arbitrum, the evaluation criteria work in your favor.&lt;/p&gt;

&lt;p&gt;The NYC buildathon is live, the NYC Founder House kicks off soon, and Dubai registration is open. If you're building in payments, DeFi, RWAs, privacy, consumer, or Arbitrum-native tooling, there's an entry point for you right now at &lt;a href="https://openhouse.arbitrum.io" rel="noopener noreferrer"&gt;openhouse.arbitrum.io&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>hackathon</category>
      <category>productivity</category>
      <category>career</category>
      <category>blockchain</category>
    </item>
    <item>
      <title>OpenClaw Is Incredible. Setting It Up Shouldn't Require a CS Degree.</title>
      <dc:creator>Ben Greenberg</dc:creator>
      <pubDate>Mon, 09 Feb 2026 17:29:54 +0000</pubDate>
      <link>https://dev.to/bengreenberg/openclaw-is-incredible-setting-it-up-shouldnt-require-a-cs-degree-36nk</link>
      <guid>https://dev.to/bengreenberg/openclaw-is-incredible-setting-it-up-shouldnt-require-a-cs-degree-36nk</guid>
      <description>&lt;p&gt;If you've heard of &lt;a href="https://openclaw.ai" rel="noopener noreferrer"&gt;OpenClaw&lt;/a&gt;, you know it's one of the most capable personal AI assistants available. It's open source. It runs on your own infrastructure. It's proactive: it reaches out to you with reminders, follow-ups, and check-ins instead of waiting for you to type a prompt. It has persistent memory, autonomous background tasks, and a growing skill ecosystem.&lt;/p&gt;

&lt;p&gt;It's also a pain to set up if you're not technical.&lt;/p&gt;

&lt;p&gt;Cloning repos. Managing API keys. Configuring Docker. Setting up Telegram bots. Provisioning a VPS. Maintaining security updates. For developers, that's a weekend project. For everyone else, it's a wall.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gap between capability and accessibility
&lt;/h2&gt;

&lt;p&gt;OpenClaw's target audience is technical users comfortable with CLI environments. That makes sense for an open-source project. But the people who would benefit most from a proactive AI assistant are often the people least equipped to deploy one.&lt;/p&gt;

&lt;p&gt;Busy professionals who need an AI that checks in on their schedule. Freelancers who want accountability nudges. People who keep paying for ChatGPT but forget to open it. These aren't power users. They're the mainstream.&lt;/p&gt;

&lt;h2&gt;
  
  
  TapnClaw: managed OpenClaw in 5 minutes
&lt;/h2&gt;

&lt;p&gt;I built &lt;a href="https://tapnclaw.com" rel="noopener noreferrer"&gt;TapnClaw&lt;/a&gt; to bridge that gap. It's a managed deployment layer for OpenClaw that reduces the entire setup to three steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Sign in with Google, pick a plan&lt;/li&gt;
&lt;li&gt;Choose your AI model (Claude or ChatGPT)&lt;/li&gt;
&lt;li&gt;Connect Telegram&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Five minutes later, you have a dedicated server running OpenClaw with your chosen model, communicating through Telegram. No terminal. No config files. No documentation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Everything OpenClaw does, zero infrastructure work
&lt;/h2&gt;

&lt;p&gt;Your TapnClaw instance is a full OpenClaw deployment. That means you get:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Proactive messaging&lt;/strong&gt;: This is OpenClaw's killer feature. Your assistant doesn't just respond to you. It reaches out on its own. Mention needing to book a flight? It checks in the next day. Tell it about a deadline? It follows up with an offer to help. It uses OpenClaw's heartbeat and cron system to learn your patterns and act on them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Persistent memory&lt;/strong&gt;: It remembers what you've talked about across every conversation. Context doesn't reset. It builds a profile of your preferences, goals, and patterns over time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Autonomous tasks&lt;/strong&gt;: Background task execution means it can work on things even when you're not actively chatting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Private infrastructure&lt;/strong&gt;: Each user gets an isolated, dedicated server. Not multi-tenant. Your conversations never leave your machine. Daily backups, automatic security updates, and full data deletion on cancellation.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the experience actually looks like
&lt;/h2&gt;

&lt;p&gt;Here's a real interaction pattern:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Assistant&lt;/strong&gt;: You mentioned wanting to call the dentist this week. Remind you tomorrow?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You&lt;/strong&gt;: Yes please, 10am!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Assistant&lt;/strong&gt;: Done! I'll message you at 9:45am so you have time to prepare. Also, your flight to Denver is in 3 days. Check you in?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That proactive follow-up happens because OpenClaw's memory system retains context and its scheduling engine acts on it. TapnClaw just makes sure you never have to think about the infrastructure running it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it doesn't do
&lt;/h2&gt;

&lt;p&gt;Being direct about boundaries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Telegram only&lt;/strong&gt; for now. Discord, WhatsApp, and Slack are on the roadmap.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Personal use&lt;/strong&gt;, not designed for teams.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Managed hosting&lt;/strong&gt; means no SSH access and no self-hosting option. If you want full control, deploy OpenClaw directly.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Pricing
&lt;/h2&gt;

&lt;p&gt;$19.99/month or $14.99/month on quarterly billing. No AI markup. Setup fee waived on quarterly.&lt;/p&gt;

&lt;h2&gt;
  
  
  The thesis
&lt;/h2&gt;

&lt;p&gt;OpenClaw proved that a proactive, memory-rich AI assistant is transformative. TapnClaw's bet is that removing the setup barrier makes that transformation accessible to everyone, not just the people comfortable with Docker and shell scripts.&lt;/p&gt;

&lt;p&gt;Five minutes. Three steps. Your own private AI assistant.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tapnclaw.com" rel="noopener noreferrer"&gt;tapnclaw.com&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;TapnClaw is powered by &lt;a href="https://openclaw.ai" rel="noopener noreferrer"&gt;OpenClaw&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>showdev</category>
    </item>
    <item>
      <title>How I Built a Claude Code Skill That Scaffolds Complete Arbitrum dApps</title>
      <dc:creator>Ben Greenberg</dc:creator>
      <pubDate>Thu, 05 Feb 2026 10:11:48 +0000</pubDate>
      <link>https://dev.to/arbitrum/how-i-built-a-claude-code-skill-that-scaffolds-complete-arbitrum-dapps-2njl</link>
      <guid>https://dev.to/arbitrum/how-i-built-a-claude-code-skill-that-scaffolds-complete-arbitrum-dapps-2njl</guid>
      <description>&lt;p&gt;There is a gap in Web3 developer tooling that has always frustrated me. On one side, you have hello-world tutorials that deploy a counter contract and stop there. On the other, you have production codebases with thousands of lines of configuration that took months to evolve. The space in between -- where you need a working dApp with contracts, a frontend, a local chain, and a deployment pipeline -- is where most developers get stuck.&lt;/p&gt;

&lt;p&gt;I work in DevRel at Arbitrum, and I kept seeing the same pattern: developers would ask Claude Code to help them build on Arbitrum, and it would produce plausible-looking code that used outdated SDK versions, wrong RPC endpoints, or patterns that simply do not work on Stylus. The model has general Solidity knowledge, but it lacks the specific, opinionated context needed to scaffold a real Arbitrum project from scratch.&lt;/p&gt;

&lt;p&gt;So I built a Claude Code skill to fix that.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Are Claude Code Skills?
&lt;/h2&gt;

&lt;p&gt;Claude Code skills are structured markdown files that you drop into &lt;code&gt;~/.claude/skills/&lt;/code&gt;. When Claude Code starts a session, it reads these files and treats them as domain-specific knowledge. Think of a skill as a system prompt, but modular and version-controlled.&lt;/p&gt;

&lt;p&gt;A skill has two parts. First, a &lt;code&gt;SKILL.md&lt;/code&gt; file at the root that defines the core knowledge: what tools to use, what decisions to make, and what patterns to follow. Second, a &lt;code&gt;references/&lt;/code&gt; directory containing deeper documentation that Claude loads on demand. This two-tier approach keeps the context window lean -- Claude reads the decision tree up front and only pulls in detailed reference material when the conversation heads in that direction.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcv4e7jvl33864mz9t47v.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcv4e7jvl33864mz9t47v.webp" alt="Skill and Context Window from the Anthropic Blog"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The key insight is that skills are not code generators. They do not template out files. Instead, they give Claude the structured knowledge it needs to generate correct code itself, tailored to whatever the developer actually asks for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Architecture of the Skill
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://github.com/hummusonrails/arbitrum-dapp-skill" rel="noopener noreferrer"&gt;arbitrum-dapp-skill&lt;/a&gt; is organized around a decision tree and six reference documents.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3f9omkcj84p75e9022mg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3f9omkcj84p75e9022mg.png" alt="Decision Tree"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The decision tree lives at the top of &lt;code&gt;SKILL.md&lt;/code&gt; and handles the first question any Arbitrum project faces: Stylus Rust, Solidity, or both?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Need maximum performance and lower gas costs? Route to Stylus Rust, load &lt;code&gt;references/stylus-rust-contracts.md&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Need broad tooling compatibility and rapid prototyping? Route to Solidity, load &lt;code&gt;references/solidity-contracts.md&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Hybrid project? Use both. Stylus and Solidity contracts are fully interoperable on Arbitrum -- they share the same address space, storage model, and ABI encoding.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Below the decision tree, the skill defines an opinionated project structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-arbitrum-dapp/
├── apps/
│   ├── frontend/            # React / Next.js app
│   ├── contracts-stylus/    # Rust Stylus contracts
│   ├── contracts-solidity/  # Foundry Solidity contracts
│   └── nitro-devnode/       # Local dev chain (git submodule)
├── pnpm-workspace.yaml
└── package.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a pnpm monorepo. The &lt;code&gt;apps/&lt;/code&gt; directory separates concerns cleanly. The &lt;code&gt;nitro-devnode&lt;/code&gt; directory is a git submodule pointing to the official Offchain Labs devnode, which gives you a local Arbitrum chain running in Docker on &lt;code&gt;localhost:8547&lt;/code&gt; with pre-funded accounts ready for deployment.&lt;/p&gt;

&lt;p&gt;The six reference documents cover the full development lifecycle:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;stylus-rust-contracts.md&lt;/code&gt; -- Stylus SDK patterns, &lt;code&gt;sol_storage!&lt;/code&gt; macros, &lt;code&gt;#[public]&lt;/code&gt; methods, events, error handling&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;solidity-contracts.md&lt;/code&gt; -- Solidity 0.8.x on Arbitrum with Foundry workflows&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;frontend-integration.md&lt;/code&gt; -- viem + wagmi setup, hydration safety, contract reads and writes&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;local-devnode.md&lt;/code&gt; -- Docker setup, pre-funded accounts, CORS proxy for browser access&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;deployment.md&lt;/code&gt; -- Testnet (Arbitrum Sepolia) and mainnet (Arbitrum One) deployment&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;testing.md&lt;/code&gt; -- Unit testing strategies for both Stylus and Solidity contracts&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why These Tech Choices
&lt;/h2&gt;

&lt;p&gt;Every choice in the stack is deliberate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stylus SDK v0.10+&lt;/strong&gt; because earlier versions had breaking API differences in storage macros and entrypoint attributes. Pinning to 0.10+ means Claude generates code that actually compiles.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Foundry over Hardhat&lt;/strong&gt; for Solidity because Foundry's &lt;code&gt;forge&lt;/code&gt; is faster, its testing framework uses Solidity itself (no JavaScript test wrappers), and &lt;code&gt;cast&lt;/code&gt; provides a clean CLI for chain interaction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;viem over ethers.js&lt;/strong&gt; because viem provides strict TypeScript types for contract interaction. When Claude generates a &lt;code&gt;readContract&lt;/code&gt; call with the wrong function name, TypeScript catches it at compile time rather than failing silently at runtime.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;wagmi&lt;/strong&gt; because it wraps viem in React hooks with built-in state management for wallet connections, transaction confirmations, and contract reads. The skill includes hydration safety patterns for Next.js, which is a common stumbling block.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;pnpm&lt;/strong&gt; because workspaces let the frontend import ABIs directly from the contract directories, and pnpm's strict dependency resolution avoids the phantom dependency issues that plague npm in monorepos.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Concrete Walkthrough
&lt;/h2&gt;

&lt;p&gt;Here is what it looks like in practice. You install the skill, start Claude Code, and type:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Build me an NFT contract using Stylus Rust with a React frontend that lets users mint tokens.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Claude reads the skill, routes to the Stylus path, and starts scaffolding. It creates the monorepo structure, generates a Stylus contract using &lt;code&gt;sol_storage!&lt;/code&gt; for the token state, implements &lt;code&gt;mint&lt;/code&gt; and &lt;code&gt;balance_of&lt;/code&gt; as &lt;code&gt;#[public]&lt;/code&gt; methods, and emits a &lt;code&gt;Transfer&lt;/code&gt; event using &lt;code&gt;alloy_sol_types&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;sol_storage!&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;#[entrypoint]&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;NftContract&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;mapping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uint256&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;owners&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nf"&gt;mapping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;address&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;uint256&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;balances&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;uint256&lt;/span&gt; &lt;span class="n"&gt;total_supply&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="nd"&gt;#[public]&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;NftContract&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;mint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;U256&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;token_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.total_supply&lt;/span&gt;&lt;span class="nf"&gt;.get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nn"&gt;U256&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.total_supply&lt;/span&gt;&lt;span class="nf"&gt;.set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.owners&lt;/span&gt;&lt;span class="nf"&gt;.setter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;token_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.balances&lt;/span&gt;&lt;span class="nf"&gt;.setter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="nf"&gt;.set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.balances&lt;/span&gt;&lt;span class="nf"&gt;.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nn"&gt;U256&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nn"&gt;evm&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Transfer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ZERO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;token_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
        &lt;span class="n"&gt;token_id&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;On the frontend side, Claude sets up the wagmi provider, creates a mint button using &lt;code&gt;useWriteContract&lt;/code&gt;, and wires up a display component with &lt;code&gt;useReadContract&lt;/code&gt; -- all with proper hydration guards for Next.js. It exports the ABI from &lt;code&gt;cargo stylus export-abi&lt;/code&gt; and places it where the frontend can import it.&lt;/p&gt;

&lt;p&gt;The entire project is configured to run against &lt;code&gt;nitro-devnode&lt;/code&gt; out of the box. You start Docker, run the devnode script, deploy with &lt;code&gt;cargo stylus deploy&lt;/code&gt;, and the frontend connects through a CORS proxy route.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install It
&lt;/h2&gt;

&lt;p&gt;One command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bash &amp;lt;&lt;span class="o"&gt;(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://raw.githubusercontent.com/hummusonrails/arbitrum-dapp-skill/main/install.sh&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This clones the skill into &lt;code&gt;~/.claude/skills/arbitrum-dapp-skill&lt;/code&gt;. The next time you start Claude Code, it picks it up automatically. You can also install via &lt;a href="https://clawhub.ai/hummusonrails/arbitrum-dapp-skill" rel="noopener noreferrer"&gt;ClawHub&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx clawhub@latest &lt;span class="nb"&gt;install &lt;/span&gt;arbitrum-dapp-skill
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or if you prefer manual installation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/hummusonrails/arbitrum-dapp-skill.git ~/.claude/skills/arbitrum-dapp-skill
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To see it in action before installing, there is a &lt;a href="https://youtu.be/vsejiaOTmJA" rel="noopener noreferrer"&gt;demo video on YouTube&lt;/a&gt; that walks through the full workflow. &lt;/p&gt;

&lt;p&gt;

  &lt;iframe src="https://www.youtube.com/embed/vsejiaOTmJA"&gt;
  &lt;/iframe&gt;


&lt;/p&gt;

&lt;p&gt;I also wrote a &lt;a href="https://x.com/hummusonrails/status/2019335992902095183" rel="noopener noreferrer"&gt;deeper technical thread on X&lt;/a&gt; covering the design decisions in more detail.&lt;/p&gt;

&lt;p&gt;

&lt;iframe class="tweet-embed" id="tweet-2019337368033992833-712" src="https://platform.twitter.com/embed/Tweet.html?id=2019337368033992833"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-2019337368033992833-712');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=2019337368033992833&amp;amp;theme=dark"
  }





&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;The skill covers the core development lifecycle, but there is more to build. Token bridging patterns between L1 and Arbitrum, integration with Orbit chains, and more advanced Stylus patterns like precompile access are all on the roadmap.&lt;/p&gt;

&lt;p&gt;If you are building on Arbitrum and run into a pattern the skill does not cover well, open an issue or submit a PR on the &lt;a href="https://github.com/hummusonrails/arbitrum-dapp-skill" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt;. The whole point of packaging this as a skill rather than a tutorial is that it evolves with the ecosystem. As the Stylus SDK ships new versions, as viem adds new features, the skill updates and every developer who installed it gets the improvements on their next &lt;code&gt;git pull&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Skills are a new primitive for developer tooling. Instead of writing documentation that developers read and then translate into code, you write structured knowledge that an AI agent consumes directly. The developer describes what they want, and the agent applies the knowledge to generate a working implementation. I think this pattern is going to reshape how we think about developer education in Web3 and beyond.&lt;/p&gt;

&lt;p&gt;You can find the full source, documentation, and installation instructions at &lt;a href="https://hummusonrails.github.io/arbitrum-dapp-skill/" rel="noopener noreferrer"&gt;hummusonrails.github.io/arbitrum-dapp-skill&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>claudeai</category>
      <category>web3</category>
      <category>rust</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
