<?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: Alberto Barrago</title>
    <description>The latest articles on DEV Community by Alberto Barrago (@albz).</description>
    <link>https://dev.to/albz</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1671227%2F272345a3-435a-4979-a8c6-27d9621e9b34.jpeg</url>
      <title>DEV Community: Alberto Barrago</title>
      <link>https://dev.to/albz</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/albz"/>
    <language>en</language>
    <item>
      <title>Telemaco: a headless Rust browser engine</title>
      <dc:creator>Alberto Barrago</dc:creator>
      <pubDate>Tue, 08 Sep 2026 07:39:51 +0000</pubDate>
      <link>https://dev.to/albz/telemaco-a-headless-rust-browser-engine-54op</link>
      <guid>https://dev.to/albz/telemaco-a-headless-rust-browser-engine-54op</guid>
      <description>&lt;p&gt;&lt;strong&gt;Telemaco&lt;/strong&gt; is a headless browser engine I've been building in Rust. It runs real JavaScript through V8, keeps a real DOM tree, owns its own layout and paint pipeline, and speaks the Chrome DevTools Protocol, so Puppeteer and Playwright connect to it out of the box. No Chromium, no WebView, no 300 MB download. A 70 MB binary that starts instantly and loads a page in ~85 ms.&lt;/p&gt;

&lt;p&gt;This is the story of how it came to be, why it exists, and the choices that shaped it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem: Chromium as a tax
&lt;/h2&gt;

&lt;p&gt;For years, "headless browser" has meant one thing: headless Chrome. It works, but it carries a cost you only notice when you scale it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The download.&lt;/strong&gt; Every CI runner, every fresh container, every teammate's laptop pulls a ~300 MB Chromium. Playwright and Puppeteer make this painless, which is exactly why nobody questions it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The memory.&lt;/strong&gt; A single headless Chrome instance idles at 200+ MB. Spin up a few for parallel scraping and you're budgeting RAM like it's 2010.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The startup.&lt;/strong&gt; Two seconds to launch before you've even loaded a page.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The fingerprint.&lt;/strong&gt; Headless Chrome is trivially detectable. If you're scraping anything that cares, you're already fighting an anti-bot arms race.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this is inherent to the job. A browser engine is a parser, a DOM, a layout engine, a paint pipeline, and a JS runtime. Chromium is one very heavy way to assemble those pieces. I wanted a lighter one.&lt;/p&gt;

&lt;h2&gt;
  
  
  The need that started it
&lt;/h2&gt;

&lt;p&gt;Two concrete needs drove the project.&lt;/p&gt;

&lt;p&gt;First, &lt;strong&gt;efficient web search and extraction&lt;/strong&gt;. I work with colleagues who spend their days pulling structured data out of the web, like Salesforce records, MDN docs, product pages. The tooling for that is either a raw HTTP client (which can't run JavaScript) or a full browser (which is overkill). There was no middle ground: something that runs real JS and a real DOM, but is small enough to treat as a utility, not an infrastructure project.&lt;/p&gt;

&lt;p&gt;Second, &lt;strong&gt;end-to-end tests without Chromium&lt;/strong&gt;. If you've ever run a Playwright suite in CI, you know the drill: download the browser, hope the sandbox flags are right, watch the memory climb. I wanted the same CDP automation surface, the same &lt;code&gt;page.goto&lt;/code&gt;, the same &lt;code&gt;page.evaluate&lt;/code&gt;, but backed by something that doesn't need a browser install at all. A single binary that &lt;em&gt;is&lt;/em&gt; the browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  Standing on the shoulders of many projects
&lt;/h2&gt;

&lt;p&gt;I didn't want to reinvent a browser from scratch, and I didn't have to. The Rust ecosystem has quietly assembled most of the pieces, and Telemaco is deliberately built on top of them.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Servo's components&lt;/strong&gt;: &lt;code&gt;html5ever&lt;/code&gt; for HTML parsing, &lt;code&gt;selectors&lt;/code&gt; for CSS matching, &lt;code&gt;cssparser&lt;/code&gt;, &lt;code&gt;servo_arc&lt;/code&gt;. The DOM and CSS machinery that Mozilla's research browser spent years hardening.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;taffy&lt;/code&gt;&lt;/strong&gt; for the layout engine, a pure-Rust flexbox/grid layout library.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;tiny-skia&lt;/code&gt;&lt;/strong&gt; for rasterization and &lt;strong&gt;&lt;code&gt;ab_glyph&lt;/code&gt;&lt;/strong&gt; for glyph rendering, the paint pipeline, also pure Rust.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;V8 through &lt;code&gt;deno_core&lt;/code&gt;&lt;/strong&gt; for JavaScript. Real V8, the same engine Chrome uses, so JS semantics are exactly what you expect.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;chromiumoxide&lt;/code&gt;&lt;/strong&gt; and the Chrome DevTools Protocol for the automation surface.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The project started as a fork of &lt;strong&gt;Obscura&lt;/strong&gt; (Apache-2.0), a headless browser engine by h4ckf0r0day. That's the honest origin: I didn't start from a blank page, I started from a solid foundation and took it in a different direction. The crate names changed, the CLI surface changed, the project identity changed, and the codebase was adapted and extended for a practical, scraping- and agent-oriented focus. The attribution is in the repo's &lt;code&gt;NOTICE&lt;/code&gt; file; this is a derivative work, and it says so.&lt;/p&gt;

&lt;h2&gt;
  
  
  The architecture
&lt;/h2&gt;

&lt;p&gt;Telemaco is a workspace of nine crates, one layer per crate, with cross-crate calls going through the layer above rather than sideways:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Crate&lt;/th&gt;
&lt;th&gt;Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;telemaco-cli&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;CLI: &lt;code&gt;fetch&lt;/code&gt;, &lt;code&gt;serve&lt;/code&gt; (CDP server), &lt;code&gt;scrape&lt;/code&gt;, &lt;code&gt;mcp&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;telemaco-cdp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Chrome DevTools Protocol server (WebSocket)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;telemaco-js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;V8/&lt;code&gt;deno_core&lt;/code&gt; runtime; DOM shim + JS/Rust bridge&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;telemaco-dom&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;DOM tree&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;telemaco-net&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;HTTP client, stealth client, cookie jar, robots cache, tracker blocklist&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;telemaco-browser&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The &lt;code&gt;Page&lt;/code&gt; type, navigation, JS evaluation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;telemaco-render&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Selector cascade, retained layout, paint, screenshots, PDF&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;telemaco-mcp&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stateful MCP automation tools&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;telemaco&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Embeddable Rust library API&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A few invariants shaped the whole design:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One V8 isolate per process.&lt;/strong&gt; V8 is &lt;code&gt;!Send&lt;/code&gt;, so all async runs on a &lt;code&gt;tokio&lt;/code&gt; &lt;code&gt;LocalSet&lt;/code&gt;, and every JS op goes through a single global lock. It serializes JS execution, which is fine for a browser engine; the DOM is single-threaded anyway.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One bad page must never hang a worker.&lt;/strong&gt; There's a V8 termination watchdog per page and a process-level hard deadline. A page that spins in an infinite loop gets killed, not the worker.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSRF by default.&lt;/strong&gt; Loopback, RFC1918, and link-local fetches are blocked unless you explicitly pass &lt;code&gt;--allow-private-network&lt;/code&gt;. A scraping tool that can reach your internal network is a liability; this closes it by default.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What it looks like
&lt;/h2&gt;

&lt;p&gt;Fetch a page and run real JavaScript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;telemaco fetch https://news.ycombinator.com &lt;span class="nt"&gt;--eval&lt;/span&gt; &lt;span class="s2"&gt;"document.title"&lt;/span&gt;
Hacker News
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Drive it like headless Chrome, from Puppeteer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;puppeteer&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;puppeteer-core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;browser&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;puppeteer&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="na"&gt;browserWSEndpoint&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ws://127.0.0.1:9222/devtools/browser&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&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;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newPage&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;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://news.ycombinator.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stories&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;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;evaluate&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.titleline &amp;gt; a&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;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;href&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;Scrape many URLs in parallel:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;telemaco scrape url1 url2 url3 &lt;span class="nt"&gt;--concurrency&lt;/span&gt; 25 &lt;span class="nt"&gt;--format&lt;/span&gt; json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And for AI agents, there's an MCP server that exposes the same browser to Claude Desktop, Cursor, or any MCP client:&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;"mcpServers"&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;"telemaco"&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;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"telemaco"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"args"&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="s2"&gt;"mcp"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The numbers
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Telemaco&lt;/th&gt;
&lt;th&gt;Headless Chrome&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Memory&lt;/td&gt;
&lt;td&gt;30 MB&lt;/td&gt;
&lt;td&gt;200+ MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Binary size&lt;/td&gt;
&lt;td&gt;70 MB&lt;/td&gt;
&lt;td&gt;300+ MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Page load&lt;/td&gt;
&lt;td&gt;85 ms&lt;/td&gt;
&lt;td&gt;~500 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Startup&lt;/td&gt;
&lt;td&gt;Instant&lt;/td&gt;
&lt;td&gt;~2 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Anti-detect&lt;/td&gt;
&lt;td&gt;Built-in&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Puppeteer / Playwright&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Roughly 12x faster page loads and 6x less memory on framework pages, with the same CDP automation surface.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why "Telemaco"
&lt;/h2&gt;

&lt;p&gt;The name is the son of Odysseus, the one who sets out to find news of his father. A searcher, by definition. It felt right for a tool whose whole job is to go out and bring back what's on the web.&lt;/p&gt;

&lt;h2&gt;
  
  
  Not a product, a direction
&lt;/h2&gt;

&lt;p&gt;Telemaco isn't trying to sell anything. It's a different vision of what a browser engine can be: small enough to be a utility, fast enough to be a tool, and honest about where it comes from. It's built on the work of a lot of respected projects and people, like Servo, taffy, tiny-skia, deno_core, Obscura, and it's open source under Apache-2.0, so anyone can take it and point it at their own problem.&lt;/p&gt;

&lt;p&gt;If you've ever wished your scraping or your e2e tests didn't require a browser install, that's the itch it scratches. Clone it, build it, drive it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Landing page: &lt;a href="https://albz.it/telemaco/" rel="noopener noreferrer"&gt;albz.it/telemaco&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Source: &lt;a href="https://github.com/AlbertoBarrago/telemaco" rel="noopener noreferrer"&gt;github.com/AlbertoBarrago/telemaco&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>rust</category>
      <category>browser</category>
      <category>webdev</category>
      <category>devtool</category>
    </item>
    <item>
      <title>I Built a CLI to Answer One Question: What Will This Change Break?</title>
      <dc:creator>Alberto Barrago</dc:creator>
      <pubDate>Thu, 27 Aug 2026 16:03:12 +0000</pubDate>
      <link>https://dev.to/albz/i-built-a-cli-to-answer-one-question-what-will-this-change-break-114o</link>
      <guid>https://dev.to/albz/i-built-a-cli-to-answer-one-question-what-will-this-change-break-114o</guid>
      <description>&lt;p&gt;We've all been there.&lt;/p&gt;

&lt;p&gt;You open a repository, change a file that looks completely harmless, run the tests, and open a pull request.&lt;/p&gt;

&lt;p&gt;Then someone comments:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Careful. That module is also used by the billing pipeline.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Or worse, nobody notices.&lt;/p&gt;

&lt;p&gt;The change gets merged.&lt;/p&gt;

&lt;p&gt;And something breaks.&lt;/p&gt;

&lt;p&gt;The problem isn't necessarily bad code. In large or unfamiliar codebases, understanding the &lt;strong&gt;blast radius of a change&lt;/strong&gt; is surprisingly difficult.&lt;/p&gt;

&lt;p&gt;So I started building &lt;strong&gt;Serval&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Serval is a local-first CLI that tries to answer one simple question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If I change this file, what am I likely to affect?&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;When I want to understand the impact of a change, I usually end up doing some combination of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rg &lt;span class="s2"&gt;"someModule"&lt;/span&gt;
git log
git blame
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then I inspect imports, search CI configuration, check which files usually change together, and rely on whatever knowledge I have about the repository.&lt;/p&gt;

&lt;p&gt;That works.&lt;/p&gt;

&lt;p&gt;But it is mostly manual.&lt;/p&gt;

&lt;p&gt;And on a repository you don't know well, a large part of the process becomes guesswork.&lt;/p&gt;

&lt;p&gt;I wanted something closer to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;serval inspect src/auth/token.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and get:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Target
  src/auth/token.ts

Direct impact
  src/auth/middleware.ts

Indirect impact
  src/api/client.ts

CI
  integration-auth.yml

Git history
  7 significant changes
  3 frequently co-changed modules

Risk
  HIGH: 82/100

  +28  14 downstream modules
  +20  critical path
  +14  high historical churn
  +12  frequently co-changed modules
  +8   CI workflow affected
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not a prediction.&lt;/p&gt;

&lt;p&gt;Not:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"AI thinks this change might be dangerous."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Evidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four signals instead of one
&lt;/h2&gt;

&lt;p&gt;Looking only at imports isn't enough.&lt;/p&gt;

&lt;p&gt;A dependency graph can tell me:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;A -&amp;gt; B -&amp;gt; C
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If I change &lt;code&gt;C&lt;/code&gt;, I know that &lt;code&gt;B&lt;/code&gt; and potentially &lt;code&gt;A&lt;/code&gt; are affected.&lt;/p&gt;

&lt;p&gt;Useful.&lt;/p&gt;

&lt;p&gt;But repositories contain more information than their dependency graph.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Dependency graph
&lt;/h3&gt;

&lt;p&gt;Serval scans the repository and builds a graph from language-native dependencies.&lt;/p&gt;

&lt;p&gt;It currently understands repositories containing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JavaScript / TypeScript&lt;/li&gt;
&lt;li&gt;Go&lt;/li&gt;
&lt;li&gt;Python&lt;/li&gt;
&lt;li&gt;Java&lt;/li&gt;
&lt;li&gt;C&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a target file, Serval walks the graph in the opposite direction and calculates its dependents.&lt;/p&gt;

&lt;p&gt;That gives us the structural blast radius.&lt;/p&gt;

&lt;p&gt;But structure is only the first signal.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Git history
&lt;/h3&gt;

&lt;p&gt;Git contains something surprisingly close to the memory of a codebase.&lt;/p&gt;

&lt;p&gt;If two files repeatedly change together, that's information.&lt;/p&gt;

&lt;p&gt;If a module has been modified constantly during the last few months, that's information too.&lt;/p&gt;

&lt;p&gt;So Serval looks at things like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;historical churn
co-change frequency
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Imagine two files with identical dependency graphs.&lt;/p&gt;

&lt;p&gt;One hasn't changed in two years.&lt;/p&gt;

&lt;p&gt;The other changed nine times during the last 90 days and frequently changes together with 15 other modules.&lt;/p&gt;

&lt;p&gt;Those files probably shouldn't receive the same risk score.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. CI configuration
&lt;/h3&gt;

&lt;p&gt;Then there is another graph hiding inside most repositories:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CI/CD.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A change can trigger integration tests, builds, deployments or validation pipelines depending on path filters.&lt;/p&gt;

&lt;p&gt;Serval currently understands configuration from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GitHub Actions
GitLab CI
Azure Pipelines
Jenkins
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives another useful piece of evidence:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Which automation does this change actually touch?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  4. Critical paths
&lt;/h3&gt;

&lt;p&gt;Some parts of a system simply deserve more attention.&lt;/p&gt;

&lt;p&gt;Authentication is not the same as changing a README.&lt;/p&gt;

&lt;p&gt;Payment logic is not the same as changing a CSS utility.&lt;/p&gt;

&lt;p&gt;Serval therefore allows repository-specific critical-path rules through &lt;code&gt;.serval.yml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Again, this isn't trying to prove that something will break.&lt;/p&gt;

&lt;p&gt;It is accumulating evidence that says:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You should probably look at this change more carefully.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Why the score is deterministic
&lt;/h2&gt;

&lt;p&gt;This became one of the most important design decisions in the project.&lt;/p&gt;

&lt;p&gt;It would have been extremely easy to send the repository context to an LLM and ask:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"How risky is this change from 0 to 100?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I deliberately didn't do that.&lt;/p&gt;

&lt;p&gt;The core score produced by Serval is deterministic.&lt;/p&gt;

&lt;p&gt;The same repository state and configuration produce the same result.&lt;/p&gt;

&lt;p&gt;More importantly, every point can be explained.&lt;/p&gt;

&lt;p&gt;Something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Risk
  MEDIUM: 38/100

  +4   2 downstream modules
  +14  high historical churn
  +12  91 frequently co-changed modules
  +8   2 CI workflows affected
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is no hidden reasoning behind &lt;code&gt;38&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can audit it.&lt;/p&gt;

&lt;p&gt;You can disagree with it.&lt;/p&gt;

&lt;p&gt;You can change the configuration.&lt;/p&gt;

&lt;p&gt;But you can understand where it came from.&lt;/p&gt;

&lt;p&gt;For engineering tooling, I think that property matters.&lt;/p&gt;

&lt;h2&gt;
  
  
  So... no AI?
&lt;/h2&gt;

&lt;p&gt;Not exactly.&lt;/p&gt;

&lt;p&gt;Serval does support AI explanations.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;serval inspect src/auth/token.ts &lt;span class="nt"&gt;--explain&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The interesting part is the boundary.&lt;/p&gt;

&lt;p&gt;The AI &lt;strong&gt;cannot change the score&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It only receives the deterministic analysis and explains it in natural language.&lt;/p&gt;

&lt;p&gt;And because Serval is local-first, the default provider can be a local Ollama instance.&lt;/p&gt;

&lt;p&gt;Other providers can be used through locally installed CLIs such as Claude, Codex or Gemini.&lt;/p&gt;

&lt;p&gt;I like this architecture much more than putting an LLM at the center of the system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;repository
    ↓
deterministic analysis
    ↓
risk model
    ↓
result
    ↓
optional AI explanation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The model explains evidence.&lt;/p&gt;

&lt;p&gt;It doesn't invent the evidence.&lt;/p&gt;

&lt;h2&gt;
  
  
  Local-first was intentional
&lt;/h2&gt;

&lt;p&gt;Serval doesn't require:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an account&lt;/li&gt;
&lt;li&gt;a SaaS backend&lt;/li&gt;
&lt;li&gt;uploading your repository&lt;/li&gt;
&lt;li&gt;an API key for its core functionality&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The analysis happens locally.&lt;/p&gt;

&lt;p&gt;That matters for developer tooling because the repository already contains everything needed for most of the analysis:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;source code
Git history
CI configuration
repository configuration
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why send all of that somewhere else if we can calculate the answer locally?&lt;/p&gt;

&lt;h2&gt;
  
  
  It can also become a CI gate
&lt;/h2&gt;

&lt;p&gt;The same analysis can run against your current diff:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;serval diff
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or produce machine-readable output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;serval diff &lt;span class="nt"&gt;--json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this makes another use case possible:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;serval diff &lt;span class="nt"&gt;--fail-on&lt;/span&gt; high
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If one of the changed files reaches HIGH risk, Serval exits with a non-zero status.&lt;/p&gt;

&lt;p&gt;That means the tool can move from:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"interesting information for the developer"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;to:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"a deterministic signal inside the delivery pipeline."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I'm particularly interested in exploring this direction.&lt;/p&gt;

&lt;p&gt;Not as another quality gate that randomly blocks developers, but as a way to make the &lt;strong&gt;expected impact of a change visible before merge&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing it
&lt;/h2&gt;

&lt;p&gt;Serval is written in Go and distributed through Homebrew.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;AlbertoBarrago/tap/serval
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, inside a Git repository:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;serval inspect path/to/file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or simply:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;serval path/to/file
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are also commands for inspecting the graph and history directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;serval graph path/to/file
serval &lt;span class="nb"&gt;history &lt;/span&gt;path/to/file
serval doctor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And because I have a soft spot for old-school CLI tooling:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;man serval
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;exists too.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'm trying to explore
&lt;/h2&gt;

&lt;p&gt;Serval is still young.&lt;/p&gt;

&lt;p&gt;I'm less interested in pretending that a &lt;code&gt;0–100&lt;/code&gt; score can magically predict whether software will break and more interested in a different question:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How much useful information about change risk is already sitting inside our repositories?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Dependency graphs tell us what is connected.&lt;/p&gt;

&lt;p&gt;Git tells us what historically moves together.&lt;/p&gt;

&lt;p&gt;CI tells us what operational machinery is affected.&lt;/p&gt;

&lt;p&gt;Repository rules tell us which areas deserve additional attention.&lt;/p&gt;

&lt;p&gt;None of these signals is particularly revolutionary on its own.&lt;/p&gt;

&lt;p&gt;Combining them into a small, local, explainable tool is the experiment.&lt;/p&gt;

&lt;p&gt;And perhaps that's also where AI fits best in this kind of developer tooling:&lt;/p&gt;

&lt;p&gt;not replacing deterministic analysis,&lt;/p&gt;

&lt;p&gt;but sitting on top of it when natural-language reasoning is useful.&lt;/p&gt;




&lt;p&gt;Serval is open source and MIT licensed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Project:&lt;/strong&gt; &lt;a href="https://albz.it/serval/" rel="noopener noreferrer"&gt;https://albz.it/serval/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Source:&lt;/strong&gt; &lt;a href="https://github.com/AlbertoBarrago/serval" rel="noopener noreferrer"&gt;https://github.com/AlbertoBarrago/serval&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you work on large repositories, monorepos, legacy systems, or CI-heavy projects, I'd be particularly interested in hearing what signals &lt;em&gt;you&lt;/em&gt; use before touching an unfamiliar part of the codebase.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>devops</category>
      <category>go</category>
      <category>opensource</category>
    </item>
    <item>
      <title>NEON INVADERS</title>
      <dc:creator>Alberto Barrago</dc:creator>
      <pubDate>Tue, 21 Jul 2026 16:34:36 +0000</pubDate>
      <link>https://dev.to/albz/neon-invaders-3o9o</link>
      <guid>https://dev.to/albz/neon-invaders-3o9o</guid>
      <description>&lt;p&gt;Enjoy&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/AlbertoBarrago" rel="noopener noreferrer"&gt;
        AlbertoBarrago
      &lt;/a&gt; / &lt;a href="https://github.com/AlbertoBarrago/neon-invaders" rel="noopener noreferrer"&gt;
        neon-invaders
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A synthwave arcade shooter built with strict TypeScript, HTML5 Canvas, and zero runtime dependencies.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Neon Invaders&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;A synthwave invasion. Four enemy classes. One ship. No mercy.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.typescriptlang.org/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/5076e7f744b45576da4450a3c24b43f20e9c2201f142893e299fb33f4bd4b7d5/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f547970655363726970742d7374726963742d3331373863363f6c6f676f3d74797065736372697074266c6f676f436f6c6f723d7768697465" alt="TypeScript"&gt;&lt;/a&gt;
&lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/944100f7c981ca1cb5b6fc5fd1ca0669ab2b43260ca454310532428c8661df80/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f48544d4c352d43616e7661732d6533346632363f6c6f676f3d68746d6c35266c6f676f436f6c6f723d7768697465" alt="Canvas"&gt;&lt;/a&gt;
&lt;a href="https://vite.dev/" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/4226d10ce35ec1a7a3ac44a0aaf3f0e68a12d8e39c3d20dc17a9bd9c5f4ca89c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f566974652d372d3634366366663f6c6f676f3d76697465266c6f676f436f6c6f723d7768697465" alt="Vite"&gt;&lt;/a&gt;
&lt;a href="https://github.com/AlbertoBarrago/neon-invaders/./package.json" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/ff84f31741434b89904e9203bc61e655b58b1705b41005a1e4d7c839db714e0e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f72756e74696d655f646570656e64656e636965732d302d353265616666" alt="Runtime dependencies"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A fast, fullscreen arcade shooter inspired by &lt;em&gt;Space Invaders&lt;/em&gt;, rebuilt around fluid movement, escalating enemy behavior, procedural audio, and excessive neon.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;&lt;a href="https://albertobarrago.github.io/neon-invaders/" rel="nofollow noopener noreferrer"&gt;Play Neon Invaders&lt;/a&gt;&lt;/h3&gt;
&lt;/div&gt;
&lt;a rel="noopener noreferrer" href="https://github.com/AlbertoBarrago/neon-invaders/./docs/images/neon-invaders-hero.webp"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fraw.githubusercontent.com%2FAlbertoBarrago%2Fneon-invaders%2FHEAD%2F.%2Fdocs%2Fimages%2Fneon-invaders-hero.webp" alt="Neon Invaders player ship firing a cyan laser through a synthwave enemy formation toward the Overseer boss" width="100%"&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Why it feels different&lt;/h2&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fluid formations&lt;/strong&gt; — enemies descend with layered sine-wave drift instead of rigid grid steps.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Four enemy classes&lt;/strong&gt; — grunts, armored tanks, kamikaze divers, and multi-pattern bosses.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Escalating pressure&lt;/strong&gt; — every wave increases movement speed, fire rate, formation size, and dive frequency.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Four power-ups&lt;/strong&gt; — triple shot, full-height laser, timed invulnerability, and five-second bullet time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Combo scoring&lt;/strong&gt; — chain kills to raise the multiplier; completely missing a volley breaks it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local arcade leaderboard&lt;/strong&gt; — enter three initials after a qualifying run and keep a persistent top 10.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Arcade-grade feedback&lt;/strong&gt; — hit-stop, screen shake, impact sparks, explosions, engine trails, glow, and subtle CRT scanlines.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Procedural soundtrack&lt;/strong&gt; — synthwave bass, arpeggios, drums…&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/AlbertoBarrago/neon-invaders" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


</description>
      <category>gamedev</category>
      <category>ai</category>
    </item>
    <item>
      <title>Introducing TimeLog: A Native Time Tracker for iOS and macOS</title>
      <dc:creator>Alberto Barrago</dc:creator>
      <pubDate>Thu, 25 Jun 2026 15:33:33 +0000</pubDate>
      <link>https://dev.to/albz/introducing-timelog-a-native-time-tracker-for-ios-and-macos-2a3</link>
      <guid>https://dev.to/albz/introducing-timelog-a-native-time-tracker-for-ios-and-macos-2a3</guid>
      <description>&lt;p&gt;TimeLog started from a very simple email at work:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Reminder to register working hours.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That was enough to make me think about the way I track my time every day.&lt;/p&gt;

&lt;p&gt;I work mostly in the Apple ecosystem, so I wanted something that could fit naturally into my daily workflow: immediate, quiet, and almost invisible.&lt;/p&gt;

&lt;p&gt;A tool that could accompany my work instead of interrupting it.&lt;/p&gt;

&lt;h2&gt;
  
  
  From Many Ideas to Fewer Features
&lt;/h2&gt;

&lt;p&gt;At the beginning, I had a lot of ideas.&lt;/p&gt;

&lt;p&gt;Advanced tracking, analytics, synchronization, automations, integrations, reports, smart workflows, and probably a few more things that sounded useful on paper.&lt;/p&gt;

&lt;p&gt;But over time, something interesting happened.&lt;/p&gt;

&lt;p&gt;Instead of adding more features, I kept removing them.&lt;/p&gt;

&lt;p&gt;And eventually, TimeLog became exactly that: a tool that does a few things, but tries to do them well.&lt;/p&gt;

&lt;h2&gt;
  
  
  What TimeLog Does
&lt;/h2&gt;

&lt;p&gt;TimeLog is a native time tracking app for iOS and macOS, designed around simplicity and daily use.&lt;/p&gt;

&lt;p&gt;It currently focuses on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;automatic tracking by client and project&lt;/li&gt;
&lt;li&gt;calendar-based consultation&lt;/li&gt;
&lt;li&gt;Pomodoro and stopwatch modes&lt;/li&gt;
&lt;li&gt;time usage statistics&lt;/li&gt;
&lt;li&gt;data sharing by email&lt;/li&gt;
&lt;li&gt;a native macOS experience&lt;/li&gt;
&lt;li&gt;a mobile version for iOS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is not to build the most complex time tracking platform possible.&lt;/p&gt;

&lt;p&gt;The goal is to make time tracking feel natural.&lt;/p&gt;

&lt;h2&gt;
  
  
  Built for the Apple Ecosystem
&lt;/h2&gt;

&lt;p&gt;TimeLog is built with SwiftUI and SwiftData.&lt;/p&gt;

&lt;p&gt;One of the things I cared about from the beginning was making the app feel native on each platform.&lt;/p&gt;

&lt;p&gt;The macOS version is not just a stretched mobile interface. It is designed around desktop workflows, with native macOS patterns and quick access from the system environment.&lt;/p&gt;

&lt;p&gt;The iOS version follows a more mobile-first interaction model, with a simpler and more direct flow.&lt;/p&gt;

&lt;p&gt;Both versions share the same core idea: tracking time should require as little friction as possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Simplicity as a Product Decision
&lt;/h2&gt;

&lt;p&gt;I think it is very easy to add complexity to software.&lt;/p&gt;

&lt;p&gt;It is much harder to remove things without losing value.&lt;/p&gt;

&lt;p&gt;That has probably been the most interesting part of building TimeLog so far.&lt;/p&gt;

&lt;p&gt;Every feature has to justify its presence. If it makes the app feel heavier, slower, or more distracting, it may not belong there.&lt;/p&gt;

&lt;p&gt;The best feature of TimeLog might be everything I decided not to add.&lt;/p&gt;

&lt;p&gt;At least for now.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;p&gt;Building TimeLog has been a useful reminder that product development is often more about subtraction than addition.&lt;/p&gt;

&lt;p&gt;Some lessons that stood out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;native platform conventions matter&lt;/li&gt;
&lt;li&gt;small tools should stay focused&lt;/li&gt;
&lt;li&gt;not every useful idea deserves to become a feature&lt;/li&gt;
&lt;li&gt;time tracking should be fast enough to disappear into the workflow&lt;/li&gt;
&lt;li&gt;simplicity is not the absence of design, it is a design constraint&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;I am considering future integrations with third-party services to push time entries into external systems.&lt;/p&gt;

&lt;p&gt;One possible example is Wethod, since it would make sense to connect TimeLog with existing work reporting workflows.&lt;/p&gt;

&lt;p&gt;I am also looking for more feedback, especially on the mobile version. It exists, but since it is not published on the App Store yet, I have not received much real-world feedback from iOS users.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;TimeLog started from a small reminder about logging work hours.&lt;/p&gt;

&lt;p&gt;It became an attempt to build a focused, native, Apple-platform time tracker that respects the way people actually work.&lt;/p&gt;

&lt;p&gt;You can check out the presentation page here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://albz.it/Timelog/" rel="noopener noreferrer"&gt;https://albz.it/Timelog/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I’ll keep sharing updates as the project evolves.&lt;/p&gt;

</description>
      <category>swift</category>
      <category>swiftui</category>
      <category>ios</category>
    </item>
    <item>
      <title>AI Doesn't Read Your Mind. It Reads Your Trail.</title>
      <dc:creator>Alberto Barrago</dc:creator>
      <pubDate>Wed, 24 Jun 2026 17:40:01 +0000</pubDate>
      <link>https://dev.to/albz/ai-doesnt-read-your-mind-it-reads-your-trail-1phc</link>
      <guid>https://dev.to/albz/ai-doesnt-read-your-mind-it-reads-your-trail-1phc</guid>
      <description>&lt;h1&gt;
  
  
  AI Doesn't Read Your Mind
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;It only feels that way because it's become incredibly good at predicting where your thoughts are going.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Every day I see more people approaching AI as if it were magic.&lt;/p&gt;

&lt;p&gt;They ask a question.&lt;/p&gt;

&lt;p&gt;The model responds with something surprisingly relevant.&lt;/p&gt;

&lt;p&gt;A few messages later, they start talking about AI as if there were a tiny genius hidden behind the screen.&lt;/p&gt;

&lt;p&gt;There isn't.&lt;/p&gt;

&lt;p&gt;And the truth is far more interesting.&lt;/p&gt;




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

&lt;p&gt;Large Language Models don't read minds.&lt;/p&gt;

&lt;p&gt;They don't know your intentions.&lt;/p&gt;

&lt;p&gt;They don't understand your emotions.&lt;/p&gt;

&lt;p&gt;They don't have access to your thoughts.&lt;/p&gt;

&lt;p&gt;What they do is something much simpler:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;They predict.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;They analyze patterns.&lt;/p&gt;

&lt;p&gt;They estimate probabilities.&lt;/p&gt;

&lt;p&gt;They generate the next most likely sequence of tokens based on the context you've provided.&lt;/p&gt;

&lt;p&gt;The result can be so convincing that it creates the illusion of understanding.&lt;/p&gt;

&lt;p&gt;But an illusion is not magic.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why It Feels Like Mind Reading
&lt;/h2&gt;

&lt;p&gt;Years ago I became fascinated by some concepts from Neuro-Linguistic Programming (NLP).&lt;/p&gt;

&lt;p&gt;One idea stood out to me.&lt;/p&gt;

&lt;p&gt;Some communicators seem capable of knowing what you're about to say before you've said it.&lt;/p&gt;

&lt;p&gt;At first glance it looks like mind reading.&lt;/p&gt;

&lt;p&gt;In reality, they're observing signals.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Body language&lt;/li&gt;
&lt;li&gt;Tone of voice&lt;/li&gt;
&lt;li&gt;Word choices&lt;/li&gt;
&lt;li&gt;Repeated patterns&lt;/li&gt;
&lt;li&gt;Emotional reactions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;They're not reading minds.&lt;/p&gt;

&lt;p&gt;They're reading clues.&lt;/p&gt;

&lt;p&gt;The digital world works in a surprisingly similar way.&lt;/p&gt;




&lt;h2&gt;
  
  
  AI Reads Your Trail
&lt;/h2&gt;

&lt;p&gt;Every interaction leaves traces behind.&lt;/p&gt;

&lt;p&gt;When you talk to an AI, you're constantly providing signals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The words you choose&lt;/li&gt;
&lt;li&gt;The questions you ask&lt;/li&gt;
&lt;li&gt;The corrections you make&lt;/li&gt;
&lt;li&gt;The examples you provide&lt;/li&gt;
&lt;li&gt;The assumptions you reveal&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The model isn't reading your mind.&lt;/p&gt;

&lt;p&gt;It's reading your trail.&lt;/p&gt;

&lt;p&gt;And modern models have become exceptionally good at predicting where that trail is heading.&lt;/p&gt;

&lt;p&gt;That's what often feels like intelligence.&lt;/p&gt;

&lt;p&gt;That's what sometimes feels like understanding.&lt;/p&gt;

&lt;p&gt;And that's what many people mistake for magic.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Skill Everyone Talks About
&lt;/h2&gt;

&lt;p&gt;Today everyone talks about prompting.&lt;/p&gt;

&lt;p&gt;Prompt engineering.&lt;/p&gt;

&lt;p&gt;Prompt frameworks.&lt;/p&gt;

&lt;p&gt;Prompt tricks.&lt;/p&gt;

&lt;p&gt;Prompt hacks.&lt;/p&gt;

&lt;p&gt;Prompting matters.&lt;/p&gt;

&lt;p&gt;But I don't think it's the most important skill of the AI era.&lt;/p&gt;

&lt;p&gt;Not even close.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Skill Nobody Talks About
&lt;/h2&gt;

&lt;p&gt;The real skill is becoming a better:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reviewer&lt;/li&gt;
&lt;li&gt;Orchestrator&lt;/li&gt;
&lt;li&gt;Architect&lt;/li&gt;
&lt;li&gt;Editor&lt;/li&gt;
&lt;li&gt;Decision maker&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The people who get the most value from AI won't be the people who trust every answer.&lt;/p&gt;

&lt;p&gt;They'll be the people capable of challenging answers.&lt;/p&gt;

&lt;p&gt;The people who can identify weak reasoning.&lt;/p&gt;

&lt;p&gt;The people who can separate confidence from correctness.&lt;/p&gt;

&lt;p&gt;The people who understand that generating information is not the same thing as generating value.&lt;/p&gt;




&lt;h2&gt;
  
  
  AI Is Still A Tool
&lt;/h2&gt;

&lt;p&gt;Throughout my career I've learned a simple lesson:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A tool remains a tool.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Linux is a tool.&lt;/p&gt;

&lt;p&gt;Git is a tool.&lt;/p&gt;

&lt;p&gt;Docker is a tool.&lt;/p&gt;

&lt;p&gt;Kubernetes is a tool.&lt;/p&gt;

&lt;p&gt;AI is a tool.&lt;/p&gt;

&lt;p&gt;An extraordinarily powerful one.&lt;/p&gt;

&lt;p&gt;Possibly the most powerful tool many of us have ever used.&lt;/p&gt;

&lt;p&gt;But still a tool.&lt;/p&gt;

&lt;p&gt;The danger begins when we stop using it and start worshipping it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Humans Still Matter
&lt;/h2&gt;

&lt;p&gt;People often ask whether humans will remain relevant.&lt;/p&gt;

&lt;p&gt;I think the question misses the point.&lt;/p&gt;

&lt;p&gt;The value of humans was never memory.&lt;/p&gt;

&lt;p&gt;It was never calculation speed.&lt;/p&gt;

&lt;p&gt;It was never information retrieval.&lt;/p&gt;

&lt;p&gt;The uniquely human contribution is something else:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Creative judgment.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Creativity is more than prediction.&lt;/p&gt;

&lt;p&gt;Creativity is deciding that the most probable answer isn't the most interesting one.&lt;/p&gt;

&lt;p&gt;It's connecting ideas that don't naturally belong together.&lt;/p&gt;

&lt;p&gt;It's challenging assumptions.&lt;/p&gt;

&lt;p&gt;It's imagining something that doesn't exist yet.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Future
&lt;/h2&gt;

&lt;p&gt;The future doesn't belong to people who compete with AI.&lt;/p&gt;

&lt;p&gt;The future belongs to people who learn how to direct it.&lt;/p&gt;

&lt;p&gt;To challenge it.&lt;/p&gt;

&lt;p&gt;To review it.&lt;/p&gt;

&lt;p&gt;To orchestrate it.&lt;/p&gt;

&lt;p&gt;To use it without becoming dependent on it.&lt;/p&gt;

&lt;p&gt;Because behind every meaningful invention there is still something no model can fully automate:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A human being deciding to create.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And that act is far more important than any prediction.&lt;/p&gt;




&lt;h2&gt;
  
  
  Back To Fundamentals
&lt;/h2&gt;

&lt;p&gt;Ironically, the rise of AI is pushing me back toward old-school engineering skills.&lt;/p&gt;

&lt;p&gt;Reading source code.&lt;/p&gt;

&lt;p&gt;Understanding systems.&lt;/p&gt;

&lt;p&gt;Questioning assumptions.&lt;/p&gt;

&lt;p&gt;Knowing how things work beneath the abstraction.&lt;/p&gt;

&lt;p&gt;The better AI becomes at generating code, the more valuable it becomes to understand whether that code is actually correct.&lt;/p&gt;

&lt;p&gt;We're not moving away from engineering fundamentals.&lt;/p&gt;

&lt;p&gt;We're moving back to them.&lt;/p&gt;

&lt;p&gt;Just with better tools.&lt;/p&gt;




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

&lt;p&gt;AI is not magic.&lt;/p&gt;

&lt;p&gt;AI is not consciousness.&lt;/p&gt;

&lt;p&gt;AI is not reading your mind.&lt;/p&gt;

&lt;p&gt;AI is the most sophisticated prediction engine humanity has ever built.&lt;/p&gt;

&lt;p&gt;What you do with those predictions is still up to you.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;The future belongs neither to AI nor to humans alone.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;It belongs to humans who know how to think while using AI.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>ai</category>
      <category>programming</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>Enjoy, and give me some feedback ^^</title>
      <dc:creator>Alberto Barrago</dc:creator>
      <pubDate>Thu, 16 Apr 2026 07:41:11 +0000</pubDate>
      <link>https://dev.to/albz/enjoy-and-give-me-some-feedback--4a7j</link>
      <guid>https://dev.to/albz/enjoy-and-give-me-some-feedback--4a7j</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/albz/i-built-my-own-diagramming-tool-no-framework-no-runtime-just-canvas-3cn1" class="crayons-story__hidden-navigation-link"&gt;I Built My Own Diagramming Tool&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/albz" class="crayons-avatar  crayons-avatar--l  "&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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1671227%2F272345a3-435a-4979-a8c6-27d9621e9b34.jpeg" alt="albz profile" class="crayons-avatar__image" width="800" height="1067"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/albz" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Alberto Barrago
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Alberto Barrago
                
                
              
              &lt;div id="story-author-preview-content-3477193" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/albz" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1671227%2F272345a3-435a-4979-a8c6-27d9621e9b34.jpeg" class="crayons-avatar__image" alt="" width="800" height="1067"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Alberto Barrago&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/albz/i-built-my-own-diagramming-tool-no-framework-no-runtime-just-canvas-3cn1" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Apr 9&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/albz/i-built-my-own-diagramming-tool-no-framework-no-runtime-just-canvas-3cn1" id="article-link-3477193"&gt;
          I Built My Own Diagramming Tool
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag crayons-tag--filled  " href="/t/showdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;showdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/javascript"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;javascript&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/sideprojects"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;sideprojects&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/webdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;webdev&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
            &lt;a href="https://dev.to/albz/i-built-my-own-diagramming-tool-no-framework-no-runtime-just-canvas-3cn1#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            1 min read
          &lt;/small&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>I Built My Own Diagramming Tool</title>
      <dc:creator>Alberto Barrago</dc:creator>
      <pubDate>Thu, 09 Apr 2026 14:57:26 +0000</pubDate>
      <link>https://dev.to/albz/i-built-my-own-diagramming-tool-no-framework-no-runtime-just-canvas-3cn1</link>
      <guid>https://dev.to/albz/i-built-my-own-diagramming-tool-no-framework-no-runtime-just-canvas-3cn1</guid>
      <description>&lt;p&gt;Hey! I’ve been working on Markasso, a lightweight drawing and diagramming tool. Built entirely from scratch with vanilla JS and Canvas 2D API. No frameworks, no libraries, nothing.&lt;/p&gt;

&lt;p&gt;Why I built it: In this AI era where you can generate anything in seconds, I wanted to actually build something myself from the ground up. Understand (or try to do it) every line, every pixel, every math formula behind it. &lt;/p&gt;

&lt;p&gt;My grandmother was a math teacher so I guess the stubbornness runs in the family, but I’ll be honest, Claude helped me a lot when the geometry got tricky and Lorenzo Cataldi with the UI.&lt;/p&gt;

&lt;p&gt;Everything is hand-rolled: pure Canvas API (no SVG), a Redux-like state management with immutable state and action-based updates, hit detection, bounding boxes, transform handles, undo/redo via action stack. Single user, runs entirely in the browser.&lt;/p&gt;

&lt;p&gt;It’s also packed with little things I cared about: full keyboard shortcuts, multi-language support, a magnetic grid for snapping, and custom format export. I wanted it to feel like a real tool, not just a tech demo.&lt;/p&gt;

&lt;p&gt;The hardest part was making resize handles feel natural when you’re doing all the geometry yourself.&lt;br&gt;
Once you try building this stuff from scratch you really start to appreciate what the Excalidraw team has done.&lt;/p&gt;

&lt;p&gt;&lt;a href="//Markasso.it"&gt;Make a trip ^^&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;a href="https://codeberg.org/alBz/Markasso" rel="noopener noreferrer"&gt;Source Code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Would love to hear your feedback, especially on UX and what features you’d want to see next!&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>showdev</category>
      <category>sideprojects</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Fix: Eliminating Double Async Validation in TanStack Form &amp; Zod</title>
      <dc:creator>Alberto Barrago</dc:creator>
      <pubDate>Tue, 17 Feb 2026 18:10:16 +0000</pubDate>
      <link>https://dev.to/albz/fix-eliminating-double-async-validation-in-tanstack-form-zod-2dc</link>
      <guid>https://dev.to/albz/fix-eliminating-double-async-validation-in-tanstack-form-zod-2dc</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;A practical pattern to prevent duplicate API calls and race conditions&lt;br&gt;
in complex React forms.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When building production-grade forms with &lt;strong&gt;TanStack Form&lt;/strong&gt; and &lt;strong&gt;Zod&lt;/strong&gt;,&lt;br&gt;
especially in flows involving &lt;strong&gt;side effects&lt;/strong&gt; (e.g., OTP generation,&lt;br&gt;
user verification), you may encounter an elusive bug:&lt;/p&gt;

&lt;p&gt;⚠️ &lt;strong&gt;Async validation running twice on submit&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This can lead to duplicated API calls, inconsistent state, and poor user&lt;br&gt;
experience.&lt;/p&gt;

&lt;p&gt;In this article, we explore: - Why this happens - How to fix it&lt;br&gt;
reliably - How to harden your form logic for real-world scale&lt;/p&gt;


&lt;h2&gt;
  
  
  🚨 The Problem: Double Async Execution
&lt;/h2&gt;

&lt;p&gt;A known issue in TanStack &lt;a href="https://github.com/TanStack/form/issues/1431" rel="noopener noreferrer"&gt;Issue #1431&lt;/a&gt; causes async&lt;br&gt;
validation (&lt;code&gt;superRefine&lt;/code&gt;) to execute multiple times during submission.&lt;/p&gt;
&lt;h3&gt;
  
  
  Typical Setup
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useForm&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;defaultValues&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="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;validators&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;myZodSchema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// async superRefine&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;onSubmit&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="nx"&gt;value&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="nf"&gt;sendOtp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// ❌ may be called twice&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;h3&gt;
  
  
  Why It's Dangerous
&lt;/h3&gt;

&lt;p&gt;In flows like OTP authentication: - Multiple requests generate&lt;br&gt;
&lt;strong&gt;different codes&lt;/strong&gt; - First code becomes invalid - Users get stuck&lt;/p&gt;

&lt;p&gt;👉 This is not just inefficiency --- it's a &lt;strong&gt;critical UX bug&lt;/strong&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  🧠 Root Cause
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  TanStack Form may trigger validation multiple times internally&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;superRefine&lt;/code&gt; contains &lt;strong&gt;side effects&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  Validation ≠ Pure function anymore&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 This breaks the expectation that validation is idempotent&lt;/p&gt;


&lt;h2&gt;
  
  
  ✅ The Solution: Take Back Control
&lt;/h2&gt;

&lt;p&gt;We fix the issue with &lt;strong&gt;3 architectural decisions&lt;/strong&gt;:&lt;/p&gt;


&lt;h2&gt;
  
  
  1. Manual Validation with &lt;code&gt;safeParseAsync&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Avoid relying on automatic validation during submission.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&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;myZodSchema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;safeParseAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✔ Prevents double execution&lt;br&gt;
✔ Gives full control over validation lifecycle&lt;/p&gt;


&lt;h2&gt;
  
  
  2. Prevent Re-entrancy with &lt;code&gt;useRef&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;React state is not always fast enough to block rapid interactions.&lt;/p&gt;

&lt;p&gt;Use a &lt;strong&gt;low-level semaphore&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isSubmittingRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  3. Decouple Side Effects
&lt;/h2&gt;

&lt;p&gt;Never trigger API calls inside validation.&lt;/p&gt;

&lt;p&gt;👉 Validation must remain pure&lt;br&gt;
👉 Side effects go inside controlled submit flow&lt;/p&gt;




&lt;h2&gt;
  
  
  🧩 Full Implementation
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isSubmittingRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&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;handleSubmit&lt;/span&gt; &lt;span class="o"&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isSubmittingRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;isSubmittingRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// 1. Manual validation&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&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;myZodSchema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;safeParseAsync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;form&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;success&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// map errors if needed&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// 2. Execute side effect ONCE&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;triggerOtpRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;finally&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;isSubmittingRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&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;
  
  
  🌐 Network Layer Optimization
&lt;/h2&gt;

&lt;p&gt;During testing, another issue emerged: 👉 &lt;strong&gt;unwanted re-fetching&lt;br&gt;
disrupting UX&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Fix your QueryClient config:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;queryClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;QueryClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;defaultOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;queries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;staleTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;refetchOnWindowFocus&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&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="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why it matters
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  Users switch tabs to check OTP&lt;/li&gt;
&lt;li&gt;  Returning triggers refetch&lt;/li&gt;
&lt;li&gt;  UI state resets unexpectedly&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 Disable it for critical flows&lt;/p&gt;




&lt;h2&gt;
  
  
  🏗️ Production Insights
&lt;/h2&gt;

&lt;p&gt;From a real-world system serving high traffic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Small validation bugs can scale into &lt;strong&gt;massive API waste&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  Race conditions are often &lt;strong&gt;invisible locally&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;  Libraries are not always safe for &lt;strong&gt;side-effect-heavy flows&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 Always design defensively&lt;/p&gt;




&lt;h2&gt;
  
  
  🔑 Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Validation must be pure&lt;/strong&gt;&lt;br&gt;
Avoid side effects inside &lt;code&gt;superRefine&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Control execution manually&lt;/strong&gt;&lt;br&gt;
Use &lt;code&gt;safeParseAsync&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Prevent race conditions&lt;/strong&gt;&lt;br&gt;
Use &lt;code&gt;useRef&lt;/code&gt; as a semaphore&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tune network behavior&lt;/strong&gt;&lt;br&gt;
Disable &lt;code&gt;refetchOnWindowFocus&lt;/code&gt; when needed&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  💬 Final Thoughts
&lt;/h2&gt;

&lt;p&gt;If your validation triggers APIs, you are no longer just validating &lt;br&gt;
you are orchestrating &lt;strong&gt;stateful workflows&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;👉 Treat it like backend logic, not just form validation.&lt;/p&gt;




&lt;h2&gt;
  
  
  🚀 Discussion
&lt;/h2&gt;

&lt;p&gt;Have you experienced similar issues with async validation or race&lt;br&gt;
conditions in React forms?&lt;/p&gt;

&lt;p&gt;Let's discuss 👇&lt;/p&gt;




</description>
      <category>react</category>
      <category>architecture</category>
      <category>frontend</category>
    </item>
    <item>
      <title>RefAIne: Transform Casual Prompts into Expert-Level AI Instructions</title>
      <dc:creator>Alberto Barrago</dc:creator>
      <pubDate>Mon, 05 Jan 2026 22:42:48 +0000</pubDate>
      <link>https://dev.to/albz/refaine-transform-casual-prompts-into-expert-level-ai-instructions-3d9c</link>
      <guid>https://dev.to/albz/refaine-transform-casual-prompts-into-expert-level-ai-instructions-3d9c</guid>
      <description>&lt;p&gt;A free, open-source FastAPI microservice that converts simple prompts into detailed, production-ready AI instructions. Supports multiple LLM providers including Claude, OpenAI, Ollama, and Groq.&lt;/p&gt;

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

&lt;p&gt;Getting AI assistants to understand exactly what you need often requires multiple iterations. When you ask to "create a REST API," you might get a basic example when you actually need authentication, error handling, validation, and production-ready code.&lt;/p&gt;

&lt;p&gt;This gap between brief prompts and detailed requirements costs developers significant time in refinement cycles.&lt;/p&gt;

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

&lt;p&gt;RefAIne is a FastAPI microservice (approximately 145 lines) that transforms casual prompts into comprehensive engineering instructions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example Input:&lt;/strong&gt; &lt;code&gt;"make a function to sort a list"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Example Output:&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Create a Python function that sorts a list with the following requirements:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Function signature: Accept a list of comparable elements as input&lt;/li&gt;
&lt;li&gt;Return a new sorted list (do not modify the original)&lt;/li&gt;
&lt;li&gt;Use Python's built-in sorting (efficient O(n log n) Timsort)&lt;/li&gt;
&lt;li&gt;Add type hints and docstrings with examples&lt;/li&gt;
&lt;li&gt;Include error handling for None or non-list inputs&lt;/li&gt;
&lt;li&gt;Handle edge cases: empty list, single element, already sorted&lt;/li&gt;
&lt;li&gt;Make it generic to work with any comparable types&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Provide clean, PEP 8 compliant code with tests.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;RefAIne automatically adds technical context, specificity, best practices, and edge case considerations.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  Free Testing Options
&lt;/h3&gt;

&lt;p&gt;Test locally with Ollama at no cost before using paid APIs. No credit card or API key required for initial testing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Multiple Provider Support
&lt;/h3&gt;

&lt;p&gt;Switch between LLM providers using a single environment variable:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ollama (free, local, private)&lt;/li&gt;
&lt;li&gt;Groq (free tier available)&lt;/li&gt;
&lt;li&gt;Anthropic Claude (paid)&lt;/li&gt;
&lt;li&gt;OpenAI (paid)&lt;/li&gt;
&lt;li&gt;Any OpenAI-compatible API&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Production Ready
&lt;/h3&gt;

&lt;p&gt;Includes Docker configuration, comprehensive documentation, and environment-based configuration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Focused Design
&lt;/h3&gt;

&lt;p&gt;Single-purpose service with one endpoint and minimal dependencies.&lt;/p&gt;

&lt;h2&gt;
  
  
  About the Project
&lt;/h2&gt;

&lt;p&gt;RefAIne was created by &lt;a href="https://github.com/AlbertoBarrago" rel="noopener noreferrer"&gt;Alberto Barrago&lt;/a&gt; to automate the prompt refinement process. The project is open-source under the MIT license, allowing use in commercial projects without restrictions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Supporting Development
&lt;/h2&gt;

&lt;p&gt;RefAIne is free to use and will remain so. However, maintaining open-source software requires ongoing investment:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Server costs for testing and demonstrations&lt;/li&gt;
&lt;li&gt;API credits for development across multiple providers&lt;/li&gt;
&lt;li&gt;Time for bug fixes, features, and support&lt;/li&gt;
&lt;li&gt;Documentation and example creation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If RefAIne has been useful in your work, consider supporting the project:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Star the repository: &lt;a href="https://github.com/AlbertoBarrago/RefAIne" rel="noopener noreferrer"&gt;github.com/AlbertoBarrago/RefAIne&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Contribute financially via GitHub Sponsors or donation platforms&lt;/li&gt;
&lt;li&gt;Share the project with other developers&lt;/li&gt;
&lt;li&gt;Report issues or contribute code improvements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Financial support helps maintain active development, add new provider integrations, improve refinement quality, and respond to community needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick Start
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Clone repository&lt;/span&gt;
git clone https://github.com/AlbertoBarrago/RefAIne.git
&lt;span class="nb"&gt;cd &lt;/span&gt;RefAIne

&lt;span class="c"&gt;# Install and configure Ollama (for free local testing)&lt;/span&gt;
curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://ollama.com/install.sh | sh
ollama pull llama3.1

&lt;span class="c"&gt;# Set up environment&lt;/span&gt;
&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; .env &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
LLM_PROVIDER=openai
OPENAI_BASE_URL=http://localhost:11434/v1
OPENAI_API_KEY=ollama
OPENAI_MODEL=llama3.1
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="c"&gt;# Install and run&lt;/span&gt;
uv pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; pyproject.toml
uvicorn main:app &lt;span class="nt"&gt;--reload&lt;/span&gt;

&lt;span class="c"&gt;# Test the endpoint&lt;/span&gt;
curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:8000/refine &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"prompt": "create a REST API"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Interactive API documentation is available at &lt;code&gt;http://localhost:8000/docs&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;GitHub Repository: &lt;a href="https://github.com/AlbertoBarrago/RefAIne" rel="noopener noreferrer"&gt;github.com/AlbertoBarrago/RefAIne&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Issue Tracker: Report bugs or request features&lt;/li&gt;
&lt;li&gt;Documentation: Included in repository README&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Contributions and feedback are welcome.&lt;/p&gt;

</description>
      <category>python</category>
      <category>fastapi</category>
      <category>ai</category>
    </item>
    <item>
      <title>WIR - What Is Running: A CLI Tool in C to Inspect Processes and Ports</title>
      <dc:creator>Alberto Barrago</dc:creator>
      <pubDate>Tue, 30 Dec 2025 20:28:59 +0000</pubDate>
      <link>https://dev.to/albz/wir-what-is-running-a-cli-tool-in-c-to-inspect-processes-and-ports-5hdi</link>
      <guid>https://dev.to/albz/wir-what-is-running-a-cli-tool-in-c-to-inspect-processes-and-ports-5hdi</guid>
      <description>&lt;p&gt;I recently released &lt;strong&gt;wir&lt;/strong&gt; (What Is Running), a command-line tool written in C to inspect what's running on specific ports and get detailed process information. A project born from a practical need that turned into an opportunity to explore system programming in C.&lt;/p&gt;

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

&lt;p&gt;How many times have you had a port occupied without knowing which process is using it? Or needed to trace a process hierarchy to understand who spawned what? We usually resort to combinations of &lt;code&gt;lsof&lt;/code&gt;, &lt;code&gt;netstat&lt;/code&gt;, and &lt;code&gt;ps&lt;/code&gt;, but why not have everything in a single command?&lt;/p&gt;

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

&lt;p&gt;&lt;code&gt;wir&lt;/code&gt; is a cross-platform tool (macOS and Linux) that allows you to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Discover which process is using a specific port&lt;/li&gt;
&lt;li&gt;Get detailed information about a PID&lt;/li&gt;
&lt;li&gt;Visualize the complete ancestry tree of a process&lt;/li&gt;
&lt;li&gt;List all running processes&lt;/li&gt;
&lt;li&gt;View a process's environment variables&lt;/li&gt;
&lt;li&gt;Output in normal, short, JSON, or tree format&lt;/li&gt;
&lt;li&gt;Receive security warnings for potentially risky configurations&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Practical Examples
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Who's using port 8080?&lt;/span&gt;
wir &lt;span class="nt"&gt;--port&lt;/span&gt; 8080

&lt;span class="c"&gt;# Info about a specific process&lt;/span&gt;
wir &lt;span class="nt"&gt;--pid&lt;/span&gt; 1234

&lt;span class="c"&gt;# Show the process ancestry tree&lt;/span&gt;
wir &lt;span class="nt"&gt;--pid&lt;/span&gt; 1234 &lt;span class="nt"&gt;--tree&lt;/span&gt;

&lt;span class="c"&gt;# JSON output for scripting&lt;/span&gt;
wir &lt;span class="nt"&gt;--port&lt;/span&gt; 3000 &lt;span class="nt"&gt;--json&lt;/span&gt;

&lt;span class="c"&gt;# List all processes (short format)&lt;/span&gt;
wir &lt;span class="nt"&gt;--all&lt;/span&gt; &lt;span class="nt"&gt;--short&lt;/span&gt;

&lt;span class="c"&gt;# Security warnings only&lt;/span&gt;
wir &lt;span class="nt"&gt;--port&lt;/span&gt; 8080 &lt;span class="nt"&gt;--warnings&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Architecture
&lt;/h2&gt;

&lt;p&gt;The project is structured in a modular way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Platform abstraction layer&lt;/strong&gt;: handles differences between Linux (&lt;code&gt;/proc&lt;/code&gt; parsing) and macOS (&lt;code&gt;libproc&lt;/code&gt; and &lt;code&gt;sysctl&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Output formatting&lt;/strong&gt;: supports multiple display modes without duplicating logic&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistent error handling&lt;/strong&gt;: every allocation is checked, every resource is freed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Strict memory management&lt;/strong&gt;: no leaks, no undefined behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What I Learned
&lt;/h2&gt;

&lt;p&gt;Writing &lt;code&gt;wir&lt;/code&gt; was an excellent opportunity to practice fundamental concepts:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;System programming&lt;/strong&gt;: interfacing with &lt;code&gt;/proc&lt;/code&gt;, system calls, process management&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cross-platform development&lt;/strong&gt;: conditional compilation and different APIs for each OS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Memory safety in C&lt;/strong&gt;: manual memory management without a garbage collector&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build systems&lt;/strong&gt;: Makefile with automatic platform detection&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;API design&lt;/strong&gt;: clean and composable interface&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  I Don't Memorize Commands
&lt;/h2&gt;

&lt;p&gt;As my approach goes: I'm not interested in memorizing the exact &lt;code&gt;lsof&lt;/code&gt; or &lt;code&gt;netstat&lt;/code&gt; commands. I prefer understanding the underlying architecture and building tools that solve the problem more elegantly. &lt;code&gt;wir&lt;/code&gt; isn't just a wrapper, it's an abstraction that hides the complexity of OS differences.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Future
&lt;/h2&gt;

&lt;p&gt;The project is open to extensions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;UDP port support&lt;/li&gt;
&lt;li&gt;Advanced process filtering&lt;/li&gt;
&lt;li&gt;Support for other OSes (BSD, etc.)&lt;/li&gt;
&lt;li&gt;Performance optimizations&lt;/li&gt;
&lt;li&gt;Additional output formats&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try It Out
&lt;/h2&gt;

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

&lt;p&gt;It's a learning project, so feel free to experiment and extend it. Building system tools in C is a great way to understand what's really happening under the hood.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Build and install&lt;/span&gt;
brew tap AlbertoBarrago/tap
brew &lt;span class="nb"&gt;install &lt;/span&gt;wir

&lt;span class="c"&gt;# Start using it&lt;/span&gt;
wir &lt;span class="nt"&gt;--port&lt;/span&gt; 3000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>c</category>
      <category>tooling</category>
      <category>cli</category>
    </item>
    <item>
      <title>Neovim + COQ + Mason + LSP: Mini Guide</title>
      <dc:creator>Alberto Barrago</dc:creator>
      <pubDate>Mon, 17 Nov 2025 22:16:29 +0000</pubDate>
      <link>https://dev.to/albz/neovim-coq-mason-lsp-mini-guide-246h</link>
      <guid>https://dev.to/albz/neovim-coq-mason-lsp-mini-guide-246h</guid>
      <description>&lt;p&gt;This guide explains how to properly set up &lt;strong&gt;COQ.nvim&lt;/strong&gt; autocomplete&lt;br&gt;
with &lt;strong&gt;Mason&lt;/strong&gt; and &lt;strong&gt;LSP servers&lt;/strong&gt; in Neovim. It includes the most&lt;br&gt;
common pitfalls and a working configuration.&lt;/p&gt;
&lt;h2&gt;
  
  
  🔧 1. Install Required Plugins
&lt;/h2&gt;

&lt;p&gt;Make sure you have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;code&gt;ms-jpq/coq_nvim&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;ms-jpq/coq.artifacts&lt;/code&gt; (optional, extra completions)&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;williamboman/mason.nvim&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;williamboman/mason-lspconfig.nvim&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;neovim/nvim-lspconfig&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example (lazy.nvim):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"ms-jpq/coq_nvim"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;branch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"coq"&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="s2"&gt;"ms-jpq/coq.artifacts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;branch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"artifacts"&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="s2"&gt;"williamboman/mason.nvim"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"williamboman/mason-lspconfig.nvim"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"neovim/nvim-lspconfig"&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;
  
  
  ⚡ 2. Enable COQ Auto-Start
&lt;/h2&gt;

&lt;p&gt;COQ does not start automatically unless configured.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="n"&gt;vim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;coq_settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;auto_start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'shut-up'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alternatively, use manually later:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:COQnow
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  🧠 3. Correct Working LSP + COQ Setup
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"mason"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;coq&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"coq"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"mason-lspconfig"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="n"&gt;ensure_installed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"pyright"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"jdtls"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"dockerls"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"elixirls"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"ts_ls"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="n"&gt;automatic_installation&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="n"&gt;handlers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;server_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"lspconfig"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="n"&gt;server_name&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;coq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lsp_ensure_capabilities&lt;/span&gt;&lt;span class="p"&gt;({})&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;end&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"elixirls"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"lspconfig"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;elixirls&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;coq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lsp_ensure_capabilities&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
                    &lt;span class="n"&gt;settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                        &lt;span class="n"&gt;flags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                            &lt;span class="n"&gt;debounce_text_changes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="p"&gt;},&lt;/span&gt;
                        &lt;span class="n"&gt;elixirLS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                            &lt;span class="n"&gt;dialyzerEnabled&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="n"&gt;fetchDeps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&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="p"&gt;})&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;end&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;
  
  
  ❗ 4. What &lt;em&gt;Not&lt;/em&gt; To Do
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ❌ Do NOT use omnifunc with COQ
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight viml"&gt;&lt;code&gt;autocmd &lt;span class="nb"&gt;FileType&lt;/span&gt; markdown &lt;span class="k"&gt;setlocal&lt;/span&gt; &lt;span class="nb"&gt;omnifunc&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;coq#&lt;span class="nb"&gt;complete&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remove this. COQ does not use omnifunc.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧪 5. Verification Steps
&lt;/h2&gt;

&lt;h3&gt;
  
  
  ✔ Check if LSPs are connected:
&lt;/h3&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:LspInfo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  ✔ Check COQ status:
&lt;/h3&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:COQnow
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  ✔ Test autocomplete
&lt;/h3&gt;

&lt;p&gt;Open a file and type --- completion should appear.&lt;/p&gt;
&lt;h2&gt;
  
  
  🐛 6. Common Issues &amp;amp; Fixes
&lt;/h2&gt;
&lt;h3&gt;
  
  
  ❗ Autocomplete doesn't show up
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  COQ not started → &lt;code&gt;:COQnow&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  LSP not attached → &lt;code&gt;:LspInfo&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  Missing capabilities → use &lt;code&gt;coq.lsp_ensure_capabilities()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;  Remove delayed init like &lt;code&gt;vim.defer_fn&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  ❗ Markdown has no completion
&lt;/h3&gt;

&lt;p&gt;Install a Markdown LSP:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="n"&gt;ensure_installed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"marksman"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  🎉 Done!
&lt;/h1&gt;

&lt;p&gt;You now have a clean Neovim setup using &lt;strong&gt;COQ + Mason + LSP&lt;/strong&gt;.&lt;/p&gt;

</description>
      <category>neovim</category>
      <category>tooling</category>
      <category>cli</category>
      <category>coolthing</category>
    </item>
    <item>
      <title>Level Up Your Terminal Workflow: Helix + Yazi</title>
      <dc:creator>Alberto Barrago</dc:creator>
      <pubDate>Tue, 04 Nov 2025 14:35:00 +0000</pubDate>
      <link>https://dev.to/albz/level-up-your-terminal-workflow-helix-yazi-1e4</link>
      <guid>https://dev.to/albz/level-up-your-terminal-workflow-helix-yazi-1e4</guid>
      <description>&lt;p&gt;If you're looking for a modern, efficient terminal-based development setup, let me introduce you to a powerful combination: &lt;strong&gt;Helix&lt;/strong&gt; (a post-modern text editor) and &lt;strong&gt;Yazi&lt;/strong&gt; (a blazingly fast terminal file manager).&lt;/p&gt;

&lt;h2&gt;
  
  
  Why This Combo?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Helix&lt;/strong&gt; is a Kakoune-inspired editor with built-in LSP support, multiple cursors, and tree-sitter integration. It's fast, has sane defaults, and no plugin configuration needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Yazi&lt;/strong&gt; is a terminal file manager written in Rust that's incredibly fast and intuitive. Think of it as a visual way to navigate your projects before diving into code.&lt;/p&gt;

&lt;p&gt;Together, they create a lightweight alternative to heavy IDEs while keeping you productive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Moving Away from JetBrains
&lt;/h2&gt;

&lt;p&gt;I've been reflecting on my development tools lately, and I'm not particularly proud of relying on JetBrains products anymore. Their direction seems increasingly Microsoft-addicted - just look at Rider becoming primarily a .NET IDE focused on game development, while languages like Erlang and other niche but important ecosystems are left out of the scene. &lt;/p&gt;

&lt;p&gt;The bloat, the subscription model, and the narrowing focus pushed me to explore lighter, more universal alternatives. That's how I discovered this setup.&lt;/p&gt;

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

&lt;h3&gt;
  
  
  1. Install Both Tools
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# macOS&lt;/span&gt;
brew &lt;span class="nb"&gt;install &lt;/span&gt;helix yazi

&lt;span class="c"&gt;# Linux (check your distro's package manager)&lt;/span&gt;
&lt;span class="c"&gt;# Arch&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;pacman &lt;span class="nt"&gt;-S&lt;/span&gt; helix yazi

&lt;span class="c"&gt;# Other distros - check helix-editor.com and yazi-rs.github.io&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Configure Yazi to Open Files in Helix
&lt;/h3&gt;

&lt;p&gt;Create &lt;code&gt;~/.config/yazi/yazi.toml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[opener]&lt;/span&gt;
&lt;span class="py"&gt;edit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="err"&gt;{&lt;/span&gt; &lt;span class="py"&gt;run&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'hx "$@"'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;block&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="err"&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;h3&gt;
  
  
  3. Customize Your Helix Theme (Optional)
&lt;/h3&gt;

&lt;p&gt;Create a custom dark theme at &lt;code&gt;~/.config/helix/themes/my-theme.toml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="py"&gt;"ui.background"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;bg&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"#1e1e1e"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="py"&gt;"ui.text"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;fg&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"#d4d4d4"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="py"&gt;"ui.selection"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;bg&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"#264f78"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="py"&gt;"ui.cursor"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;fg&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"#1e1e1e"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;bg&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"#aeafad"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="py"&gt;"ui.linenr"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;fg&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"#858585"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="py"&gt;"ui.linenr.selected"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;fg&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"#c6c6c6"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="py"&gt;"comment"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;fg&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"#6a9955"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="py"&gt;modifiers&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;"italic"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="py"&gt;"keyword"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;fg&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"#569cd6"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="py"&gt;"function"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;fg&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"#dcdcaa"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="py"&gt;"string"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;fg&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"#ce9178"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="py"&gt;"variable"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;fg&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"#9cdcfe"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="py"&gt;"type"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="py"&gt;fg&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"#4ec9b0"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then set it in &lt;code&gt;~/.config/helix/config.toml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="py"&gt;theme&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"my-theme"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Workflow
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Navigate with Yazi:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   &lt;span class="nb"&gt;cd&lt;/span&gt; ~/your-project
   yazi
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Browse your project structure&lt;/strong&gt; using arrow keys or &lt;code&gt;hjkl&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Press Enter&lt;/strong&gt; on any file to open it in Helix&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Edit in Helix&lt;/strong&gt; with LSP support, syntax highlighting, and multiple cursors&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Save and exit&lt;/strong&gt; (&lt;code&gt;Ctrl+s&lt;/code&gt;, then &lt;code&gt;:q&lt;/code&gt;) to return to Yazi&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Repeat&lt;/strong&gt; - navigate to next file&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Helix Quick Tips
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Space + f&lt;/code&gt; - Fuzzy file finder&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Space + b&lt;/code&gt; - Buffer (open files) picker
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Space + /&lt;/code&gt; - Global search in project&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mf&lt;/code&gt; - Select function, &lt;code&gt;md&lt;/code&gt; - select around&lt;/li&gt;
&lt;li&gt;Multiple cursors: &lt;code&gt;C&lt;/code&gt; to add cursor, &lt;code&gt;Alt+C&lt;/code&gt; to remove&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Not Just Use Helix's Built-in Picker?
&lt;/h2&gt;

&lt;p&gt;You absolutely can! Helix has excellent fuzzy finding (&lt;code&gt;Space + f&lt;/code&gt;). But Yazi gives you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Visual tree structure&lt;/li&gt;
&lt;li&gt;Quick file operations (copy, move, rename)&lt;/li&gt;
&lt;li&gt;Image previews&lt;/li&gt;
&lt;li&gt;Directory sizes&lt;/li&gt;
&lt;li&gt;A complementary tool for different tasks&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This setup gives you the speed and simplicity of terminal tools with the power of modern editing features. No Electron bloat, no complex plugin configurations - just fast, efficient coding.&lt;/p&gt;

&lt;p&gt;Give it a try and let me know what you think!&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Resources:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://helix-editor.com" rel="noopener noreferrer"&gt;Helix Editor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://yazi-rs.github.io" rel="noopener noreferrer"&gt;Yazi File Manager&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.helix-editor.com/themes.html" rel="noopener noreferrer"&gt;Helix Themes Documentation&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>helix</category>
      <category>terminal</category>
      <category>productivity</category>
      <category>tooling</category>
    </item>
  </channel>
</rss>
