<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged showdev</title>
<link>https://coderlegion.com/tag/showdev</link>
<description>Powered by Question2Answer</description>
<item>
<title>What I've Been Improving Behind the Scenes</title>
<link>https://coderlegion.com/33205/what-ive-been-improving-behind-the-scenes</link>
<description>&lt;p&gt;Over the past few weeks, I've been spending more time refining Galaxy Movies rather than adding entirely new features.&lt;/p&gt;
&lt;p&gt;For anyone who hasn't come across it yet, Galaxy Movies is my movie and TV discovery project. It helps people find titles and check where they're streaming.&lt;/p&gt;
&lt;p&gt;A few recent changes have made a noticeable difference to the experience.&lt;/p&gt;
&lt;h4 id=&quot;giving-the-watchlist-a-better-starting-point&quot;&gt;Giving the watchlist a better starting point&lt;/h4&gt;
&lt;p&gt;One thing I wanted to improve was the empty watchlist experience.&lt;/p&gt;
&lt;p&gt;Previously, a new user could open the app and immediately be greeted with an empty page. Now the empty state includes a trending section with titles that can be added directly to the watchlist.&lt;/p&gt;
&lt;p&gt;The basic idea is to give users something to explore before they've saved anything.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-tsx&quot;&gt;{watchlist.length === 0 ? (
  &amp;lt;WatchlistEmptyState&amp;gt;
    &amp;lt;TrendingRail
      title=&quot;Start Here&quot;
      onAdd={addToWatchlist}
    /&amp;gt;
  &amp;lt;/WatchlistEmptyState&amp;gt;
) : (
  &amp;lt;WatchlistGrid titles={watchlist} /&amp;gt;
)}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It's a small change, but it gives people a much better starting point.&lt;/p&gt;
&lt;h4 id=&quot;making-navigation-feel-smoother&quot;&gt;Making navigation feel smoother&lt;/h4&gt;
&lt;p&gt;I added a progress bar for page transitions so there's better feedback when navigating around the app.&lt;/p&gt;
&lt;p&gt;I also fixed an issue with streaming provider filters where search results could briefly appear before the provider lookup finished.&lt;/p&gt;
&lt;p&gt;The important part is keeping the loading state visible until the provider lookup is complete.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-tsx&quot;&gt;if (isLoadingProviders) {
  return &amp;lt;SearchLoadingState /&amp;gt;;
}

return (
  &amp;lt;MovieGrid movies={filteredMovies} /&amp;gt;
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This prevents users from seeing results that haven't been filtered yet.&lt;/p&gt;
&lt;h4 id=&quot;spending-more-time-on-the-details&quot;&gt;Spending more time on the details&lt;/h4&gt;
&lt;p&gt;I've also been consolidating repeated UI elements into shared components. It's not the most exciting work to show off, but it makes future changes easier and keeps the interface more consistent.&lt;/p&gt;
&lt;p&gt;Instead of maintaining separate versions of the same card rail across the app, shared components can handle the common layout.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-tsx&quot;&gt;&amp;lt;CardRail
  title=&quot;Trending Now&quot;
  items={trendingTitles}
  renderItem={(title) =&amp;gt; (
    &amp;lt;MovieCard
      key={title.id}
      movie={title}
    /&amp;gt;
  )}
/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On the SEO side, I revisited the sitemap to focus on pages that are more useful to search engines.&lt;/p&gt;
&lt;p&gt;I also added a yearly streaming changes accumulator to preserve arrivals and departures by provider. This will make it possible to build yearly streaming recaps using data collected throughout the year.&lt;/p&gt;
&lt;h4 id=&quot;what-s-next-&quot;&gt;What's next?&lt;/h4&gt;
&lt;p&gt;I'm still working on improving discovery and making Galaxy Movies more useful without adding unnecessary complexity.&lt;/p&gt;
&lt;p&gt;Building a product independently means constantly deciding where time is best spent. Sometimes the most useful improvements aren't new features. They're the smaller fixes that make everything work better.&lt;/p&gt;
&lt;p&gt;You can check out the latest version - &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.galaxymovies.app&quot;&gt;https://www.galaxymovies.app&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Source code is available - &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/NickFasulo/galaxy-movies&quot;&gt;https://github.com/NickFasulo/galaxy-movies&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you're building a side project too, I'm curious how much of your development time goes toward new features versus improving what already exists.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/33205/what-ive-been-improving-behind-the-scenes</guid>
<pubDate>Sat, 31 Oct 2026 07:26:42 +0000</pubDate>
</item>
<item>
<title>Building Knemora: An Open-source, Local-first AI (SI) Second Brain</title>
<link>https://coderlegion.com/33114/building-knemora-an-open-source-local-first-ai-si-second-brain</link>
<description>&lt;p&gt;I’m Federico, a visual designer and full-stack developer.&lt;/p&gt;
&lt;p&gt;About six months ago, I had an idea I kept coming back to: could I turn documents and datasets into connected knowledge, explore the relationships visually, and ask questions without sending my material to a cloud inference service?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why I built Knemora?&lt;/strong&gt;&lt;br&gt;
After a lot of research, architecture decisions, late nights, and interface iterations, that idea became Knemora.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How it works?&lt;/strong&gt;&lt;br&gt;
It’s an open-source, local-first AI (SI) Second Brain. You bring your documents and datasets; Knemora compiles knowledge into a portable Markdown wiki, connects pages, and makes that knowledge available through search, a native graph, and chat with source citations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Architecture:&lt;/strong&gt;&lt;br&gt;
The diagrams below show the system architecture and the knowledge/inference workflow.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/luuyx8m950opmqn57egz.png&quot; alt=&quot;System architecture&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/pa4tcxcrntaoaacx19ai.png&quot; alt=&quot;Core AI architecture&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A few choices behind the architecture:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Portable knowledge: Markdown pages with frontmatter and explicit links. The graph is a rebuildable view of those connections.&lt;/li&gt;
&lt;li&gt;Local inference: llama.cpp with Qwen3-8B Q3_K_M as the default model.&lt;/li&gt;
&lt;li&gt;Retrieval: BM25 search and bounded graph navigation, without a vector database.&lt;/li&gt;
&lt;li&gt;Traceability: original documents are preserved, and generated claims carry source references.&lt;/li&gt;
&lt;li&gt;Dataset analysis: Polars performs structured calculations, keeping numerical results grounded in computation.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The stack includes Vue 3, FastAPI, Google ADK, SQLite, and Sigma.js/Graphology.&lt;/p&gt;
&lt;p&gt;The initial alpha targets &lt;strong&gt;macOS Apple Silicon&lt;/strong&gt;. Node.js 24 with npm is required; the launcher manages the remaining runtimes and model downloads. Initial setup needs internet access, while the core knowledge and inference workflow runs locally afterward. There’s also an isolated demo with interconnected sample knowledge.&lt;/p&gt;
&lt;p&gt;As a designer, I care about making the interface clear and the graph useful. As a developer, I care about keeping the underlying knowledge portable and understandable.&lt;/p&gt;
&lt;p&gt;Open-source software has helped me throughout my career, and I’m proud to contribute something of my own. This is an early alpha, with plenty left to improve.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Code, documentation, and setup:&lt;/strong&gt;&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/federicolaterra/knemora&quot;&gt;github.com/federicolaterra/knemora&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Website:&lt;/strong&gt;&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://knemora.space&quot;&gt;knemora.space&lt;/a&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/33114/building-knemora-an-open-source-local-first-ai-si-second-brain</guid>
<pubDate>Fri, 30 Oct 2026 15:23:16 +0000</pubDate>
</item>
<item>
<title>I made my portfolio site audit its own security headers, live, in front of you.</title>
<link>https://coderlegion.com/32700/i-made-my-portfolio-site-audit-its-own-security-headers-live-in-front-of-you</link>
<description>&lt;p&gt;My portfolio has a section that runs &lt;code&gt;curl -I&lt;/code&gt; on itself while you watch. Not a screenshot of headers I pasted in last year and forgot to update. The page fetches its own URL, reads the response headers off it, and prints each one ON or missing, right there in the section. If I ever ship a build that drops a header, the page snitches on me the next time someone loads it.&lt;/p&gt;
&lt;p&gt;I want to walk through how it works, and then the part that made me rewrite it: the real Content-Security-Policy is not in one place. It is split across two. Showing only the header would have told half the truth, on a page whose whole point is not doing that.&lt;/p&gt;
&lt;h3 id=&quot;the-setup&quot;&gt;The setup&lt;/h3&gt;
&lt;p&gt;The site is built with Astro, output fully static, served from Cloudflare. There is a section on the page titled like a shell command. Under it, a list of the security headers I expect the edge to send. The expected list is not hardcoded into the script, it comes from the component:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-astro&quot;&gt;const expected = [
  'Content-Security-Policy',
  'Strict-Transport-Security',
  'X-Content-Type-Options',
  'Referrer-Policy',
  'Permissions-Policy',
];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Same origin, so the browser is allowed to read the response headers back. That is the whole trick. A &lt;code&gt;fetch&lt;/code&gt; to any other site would get its headers hidden by CORS, but a page is allowed to look at itself.&lt;/p&gt;
&lt;h3 id=&quot;how-can-a-website-audit-its-own-security-headers-live-in-the-page-itself-&quot;&gt;How can a website audit its own security headers live, in the page itself?&lt;/h3&gt;
&lt;p&gt;The page runs a HEAD &lt;code&gt;fetch&lt;/code&gt; against its own URL. Same origin, so the browser lets it read the response headers back, and the card prints which ones are there and which are missing while you watch.&lt;/p&gt;
&lt;p&gt;The client script, in full:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;fetch(window.location.href, { method: 'HEAD' })
  .then((res) =&amp;gt; {
    box.replaceChildren();
    expected.forEach((name) =&amp;gt; box.appendChild(row(name, res.headers.get(name))));
    box.appendChild(row('Content-Security-Policy (meta)', metaCsp &amp;amp;&amp;amp; foldHashes(metaCsp)));
  })
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;HEAD, not GET, because I only care about the headers and there is no reason to pull the body again. For each expected header it calls &lt;code&gt;res.headers.get(name)&lt;/code&gt;. If the value is there, the row renders ON and prints it. If it is null, the row goes missing and gets a different style. No allowlist of &quot;good&quot; values, no grading. It shows what came back.&lt;/p&gt;
&lt;p&gt;One detail I care about more than it probably deserves: every cell is built with &lt;code&gt;document.createElement&lt;/code&gt; and &lt;code&gt;textContent&lt;/code&gt;, never &lt;code&gt;innerHTML&lt;/code&gt;. This is a section about security headers. If I XSS my own security section by piping a header value straight into the DOM as HTML, I have earned every bit of the embarrassment. So the header values are text, and text only.&lt;/p&gt;
&lt;p&gt;The site also scored A+ on Mozilla's HTTP Observatory, and the hero links straight to that scan so you can re-run it yourself instead of taking my word for the badge. The live card and the external scanner are checking the same thing from two sides.&lt;/p&gt;
&lt;h3 id=&quot;why-does-a-hash-based-csp-have-to-live-in-a-meta-tag-instead-of-the-response-headers-&quot;&gt;Why does a hash-based CSP have to live in a meta tag instead of the response headers?&lt;/h3&gt;
&lt;p&gt;Because only the build knows the hashes, and the headers file is static. Astro computes the &lt;code&gt;sha256-&lt;/code&gt; values at build time and writes them into a &lt;code&gt;&amp;lt;meta http-equiv&amp;gt;&lt;/code&gt;; if the same policy also sat in the headers, the browser would apply both as an intersection, and a &lt;code&gt;script-src 'self'&lt;/code&gt; written there would cancel out the hashes in the meta.&lt;/p&gt;
&lt;p&gt;This is where I had to go back and change the card. When I first wrote it, it read the five headers off the response and stopped. Clean, done. Except the meaningful part of my policy, the &lt;code&gt;script-src&lt;/code&gt; full of &lt;code&gt;sha256-&lt;/code&gt; hashes, lives in the HTML, not in the header the card was reading.&lt;/p&gt;
&lt;p&gt;Why not just put the whole CSP in the Cloudflare &lt;code&gt;_headers&lt;/code&gt; file and be done? Because then the browser applies both policies as an intersection, and they fight. My &lt;code&gt;_headers&lt;/code&gt; file says exactly this, in a comment I left for future me:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;# La CSP non sta qui: la genera Astro (`security.csp` in astro.config.mjs) come
# &amp;lt;meta http-equiv&amp;gt;, perché solo in build può calcolare gli hash dei propri script.
# Se una CSP vivesse anche qui, le due policy verrebbero applicate entrambe come
# intersezione: un `script-src 'self'` in questo file annullerebbe gli hash del meta
# e rimetterebbe il sito offline.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A &lt;code&gt;script-src 'self'&lt;/code&gt; in the header would intersect with the hash-based &lt;code&gt;script-src&lt;/code&gt; in the meta, and the result blocks the very scripts the hashes were meant to allow. Site offline.&lt;/p&gt;
&lt;p&gt;So there is exactly one CSP directive that has to live in the header, and it is the one a &lt;code&gt;&amp;lt;meta&amp;gt;&lt;/code&gt; tag is not allowed to express: &lt;code&gt;frame-ancestors&lt;/code&gt;. Per spec, &lt;code&gt;frame-ancestors&lt;/code&gt; inside a &lt;code&gt;&amp;lt;meta&amp;gt;&lt;/code&gt; is ignored, so it has to be a real response header. That is the whole content of the CSP header at the edge:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-http&quot;&gt;Content-Security-Policy: frame-ancestors 'none'
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Which means my original card, reading only headers, would have proudly printed a Content-Security-Policy of &lt;code&gt;frame-ancestors 'none'&lt;/code&gt; and called it a day. Technically true. Also a lie by omission, because it hid the part of the policy that does most of the work.&lt;/p&gt;
&lt;p&gt;So the card reads the meta too. It pulls the &lt;code&gt;&amp;lt;meta http-equiv=&quot;content-security-policy&quot;&amp;gt;&lt;/code&gt; content out of the DOM and prints it as a second CSP row. The hash list is dozens of entries long and useless to look at, so it folds each run of hashes into a count:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const foldHashes = (csp) =&amp;gt;
  csp.replace(/(?:'sha\d{3}-[A-Za-z0-9+/=]+'\s*)+/g, (run) =&amp;gt; {
    const n = run.match(/'sha\d{3}-/g)?.length ?? 0;
    return `${n} hash `;
  });
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The count is read from the real policy on the page, not a number I typed. If Astro emits one more inline style tomorrow, the number goes up on its own.&lt;/p&gt;
&lt;h3 id=&quot;what-this-cost-me-honestly&quot;&gt;What this cost me, honestly&lt;/h3&gt;
&lt;p&gt;I am mid-level. I did not know going in that &lt;code&gt;frame-ancestors&lt;/code&gt; was meta-blind, or that two CSPs intersect instead of one winning. I learned both by breaking the site. There is a manual hash in my Astro config for one inline script, the anti-flash theme script that runs before first paint, because Astro leaves &lt;code&gt;is:inline&lt;/code&gt; scripts alone and will not hash them for me:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;scriptDirective: {
  resources: [&quot;'self'&quot;, 'https://challenges.cloudflare.com'],
  hashes: ['sha256-WV81hIAeXjEdgj/cFIXtOf53g8pIquCjmXQuCHOehlw='],
},
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If I touch that script and forget the hash, &lt;code&gt;npm run test:csp&lt;/code&gt; fails and tells me the new hash to paste. That test exists because I shipped the mismatch once and the theme script got blocked in production.&lt;/p&gt;
&lt;p&gt;The card is not clever. It is a HEAD request, five &lt;code&gt;.get()&lt;/code&gt; calls, and one &lt;code&gt;querySelector&lt;/code&gt;. What I like about it is that it cannot drift. A README claiming &quot;we set strict security headers&quot; ages the moment someone edits a config. This section re-derives the claim from the live response every time the page loads, and it reads both halves of a policy that lives in two files, because reading one half would make the page a small liar about the one topic it is supposedly honest about.&lt;/p&gt;
&lt;p&gt;If you want to try the pattern: same-origin HEAD, read the headers, read the meta CSP too if you have one, build the DOM with &lt;code&gt;textContent&lt;/code&gt;. That is the entire thing.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/32700/i-made-my-portfolio-site-audit-its-own-security-headers-live-in-front-of-you</guid>
<pubDate>Tue, 27 Oct 2026 21:03:46 +0000</pubDate>
</item>
<item>
<title>How I Built My Own Opus Clip Alternative With Claude Code</title>
<link>https://coderlegion.com/32660/how-i-built-my-own-opus-clip-alternative-with-claude-code</link>
<description>&lt;p&gt;Opus Clip, Submagic and Vidyo all work the same way: you upload a long video, the AI scores every moment for &quot;virality&quot;, and you get the clips it likes. That is fine until you want something specific. When I am clipping a podcast for a client I don't want &quot;the most viral 30 seconds&quot;. I want &quot;every time they talk about pricing&quot; or &quot;the strongest hook in the first ten minutes&quot;.&lt;/p&gt;
&lt;p&gt;So I built my own Opus Clip alternative, called Hive Clips AI, with Claude Code. You give it a long video and a plain English prompt, and it transcribes the video, uses an LLM to find the moments that match your prompt, reframes each clip to keep the speaker in shot, burns in word-by-word captions and renders vertical clips ready to post. The prompt drives the selection, not a fixed score. That is the whole difference.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://danishsofi.in/images/blog/hive-clips/home.webp&quot; alt=&quot;Hive Clips AI home screen with a video upload area and a YouTube URL field&quot;&gt;&lt;br&gt;
&lt;em&gt;The Hive Clips AI home screen. Drop in a long video or paste a YouTube link.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Here is how it works, stage by stage, and what I would tell you before you try building one.&lt;/p&gt;
&lt;h3 id=&quot;what-does-an-ai-clipping-tool-actually-do-&quot;&gt;What does an AI clipping tool actually do?&lt;/h3&gt;
&lt;p&gt;Strip the marketing away and every clipping tool is the same nine-step pipeline. I built mine so each step runs on its own and saves its result, which matters later.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Stage &lt;/th&gt;&lt;th&gt;What happens &lt;/th&gt;&lt;th&gt;What I used&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;1. Ingest &lt;/td&gt;&lt;td&gt;Upload a file or paste a YouTube link &lt;/td&gt;&lt;td&gt;yt-dlp, FFmpeg&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;2. Transcribe &lt;/td&gt;&lt;td&gt;Speech to text with a timestamp on every word &lt;/td&gt;&lt;td&gt;Whisper API, or faster-whisper locally&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;3. Select &lt;/td&gt;&lt;td&gt;Find moments that match your prompt &lt;/td&gt;&lt;td&gt;GPT-4o with structured JSON output&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;4. Review &lt;/td&gt;&lt;td&gt;You pick which candidates to keep &lt;/td&gt;&lt;td&gt;Next.js UI&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;5. Configure &lt;/td&gt;&lt;td&gt;Choose aspect ratio and caption style &lt;/td&gt;&lt;td&gt;Next.js UI&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;6. Reframe &lt;/td&gt;&lt;td&gt;Track the face and move a virtual camera &lt;/td&gt;&lt;td&gt;OpenCV, PySceneDetect&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;7. Caption &lt;/td&gt;&lt;td&gt;Build animated word-level captions &lt;/td&gt;&lt;td&gt;ASS subtitles, libass&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;8. Render &lt;/td&gt;&lt;td&gt;Crop, burn captions, encode &lt;/td&gt;&lt;td&gt;FFmpeg&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;9. Deliver &lt;/td&gt;&lt;td&gt;Play, download or regenerate &lt;/td&gt;&lt;td&gt;FastAPI + Next.js&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The backend is Python with a FastAPI wrapper. The frontend is Next.js 15 with TypeScript and Tailwind. There is also a command line version, which is where I proved the whole pipeline worked before touching the UI.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://danishsofi.in/images/blog/hive-clips/pipeline.webp&quot; alt=&quot;Diagram of the nine Hive Clips AI pipeline stages from ingest to deliver&quot;&gt;&lt;br&gt;
&lt;em&gt;The nine stages. Select and Reframe are where most of the work went.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&quot;how-does-the-ai-pick-the-right-moments-&quot;&gt;How does the AI pick the right moments?&lt;/h3&gt;
&lt;p&gt;This is the part people assume is magic. It is mostly careful plumbing.&lt;/p&gt;
&lt;p&gt;A one hour podcast transcript is tens of thousands of tokens. Sending all of it in one request gives you worse picks, so the transcript is split into 9 minute windows with a 15 second overlap. The overlap is there so a great moment that sits across a window boundary doesn't get cut in half.&lt;/p&gt;
&lt;p&gt;Each window goes to GPT-4o with the creator's prompt and a strict instruction: return 2 to 5 clips, each between 20 and 90 seconds, and only clips that genuinely match the intent. The model also rates each clip's hook from 1 to 10, judged on the first three seconds, because that is what decides whether someone stops scrolling.&lt;/p&gt;
&lt;p&gt;The output is forced into a JSON schema, so I never have to parse loose text:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;clips&quot;: [
    {
      &quot;start_time&quot;: 412.3,
      &quot;end_time&quot;: 468.9,
      &quot;title&quot;: &quot;Why he stopped charging hourly&quot;,
      &quot;reasoning&quot;: &quot;Direct answer about pricing with a strong opening line&quot;,
      &quot;hook_strength&quot;: 8
    }
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After every window is done, the candidates get merged and re-ranked, and you see them in the review screen. Nothing renders until you pick. That review step is the thing I missed most in the paid tools.&lt;/p&gt;
&lt;h3 id=&quot;why-is-auto-reframing-the-hardest-part-&quot;&gt;Why is auto-reframing the hardest part?&lt;/h3&gt;
&lt;p&gt;Turning a 16:9 video into a 9:16 clip means cropping a narrow window out of a wide frame and moving that window to follow the speaker. The obvious way is to detect the face on every frame and centre the crop on it.&lt;/p&gt;
&lt;p&gt;That looks terrible. Face detection is noisy, so the crop shakes constantly, and when the video cuts to a different camera the crop slides across the cut like a drunk camera operator.&lt;/p&gt;
&lt;p&gt;The fix is to treat the crop as a virtual camera with physics, not a box that snaps to the face:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;A critically damped spring.&lt;/strong&gt; The camera moves toward the face but never overshoots. I ported Unity's SmoothDamp function for this. Smooth time is 0.55 seconds, which feels calm without lagging behind.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A deadzone.&lt;/strong&gt; If the face moves less than 6% of the crop width, the camera ignores it. This alone kills most of the jitter.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A speed limit.&lt;/strong&gt; Pans have a maximum speed, so a false detection can't whip the frame across the screen.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Scene cut resets.&lt;/strong&gt; PySceneDetect finds the hard cuts. At a cut the camera drops its speed and repositions instead of carrying a pan across the edit. Between cuts it must move smoothly.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Headroom.&lt;/strong&gt; The subject sits about 10% above centre, the way a real camera operator would frame a person.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The camera path becomes a crop track that FFmpeg applies during the render. Face detection itself is a basic OpenCV detector that needs no model download. I kept the smoothing separate from detection on purpose, so I can swap in a better detector later without touching the part that makes it look good.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://danishsofi.in/images/blog/hive-clips/chart.webp&quot; alt=&quot;Chart comparing noisy face detection points with the smooth virtual camera path&quot;&gt;&lt;br&gt;
&lt;em&gt;Real output from the Hive Clips smoother on noisy test data. The camera ignores small jitter and makes one calm pan when the speaker moves.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://danishsofi.in/images/blog/hive-clips/canvas.webp&quot; alt=&quot;Hive Clips AI configure screen with output canvas options 9:16, 1:1, 4:5, 16:9 and 4:3&quot;&gt;&lt;br&gt;
&lt;em&gt;Picking the output canvas. 9:16 for Reels, Shorts and TikTok, with a live preview on the right.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;If you build one of these, spend your time on the smoothing, not the detector. The smoothing is what makes it look professional.&lt;/p&gt;
&lt;h3 id=&quot;how-do-the-word-by-word-captions-work-&quot;&gt;How do the word-by-word captions work?&lt;/h3&gt;
&lt;p&gt;Most tutorials use SRT subtitles. SRT can't do the captions people actually want on Shorts and Reels, where each word lights up as it is spoken.&lt;/p&gt;
&lt;p&gt;I used ASS subtitles instead. ASS supports per-word timing, colours and animation, and FFmpeg burns it into the video through libass. Because the transcript has a timestamp for every word, each word can change colour exactly when it is said.&lt;/p&gt;
&lt;p&gt;The tool ships with 24 caption presets. The original 8 cover the basics: a karaoke style, a minimal style, a news bar and an outline style. Then I added 16 styles based on what is working on short-form right now, including a Hormozi-style highlight where the full line shows and the active word recolours, a one-word-at-a-time punch style, and a word reveal where words pop in and stay.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://danishsofi.in/images/blog/hive-clips/captions.webp&quot; alt=&quot;Hive Clips AI caption style presets including Bold Pop, Karaoke Classic, Hive Yellow and Hormozi Green&quot;&gt;&lt;br&gt;
&lt;em&gt;Part of the caption preset grid. Each preset is a starting point you can fine tune per clip.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&quot;what-broke-while-building-it-&quot;&gt;What broke while building it?&lt;/h3&gt;
&lt;p&gt;A few honest notes, because build logs that only show the happy path aren't useful.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The web app broke when the CLI didn't.&lt;/strong&gt; When I added caption overrides (custom colours and sizes per clip), the command line kept working but the web API failed. The database was missing the new column. The fix was a one-line migration, but it taught me to test both entry points every time the data model changes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Saving every stage paid off.&lt;/strong&gt; Each of the nine stages writes its result and a job record to the database. So when a render fails, I re-run the render only. I don't wait for the transcription again or pay for the AI selection twice. On a long podcast that is the difference between seconds and many minutes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Mock mode made testing free.&lt;/strong&gt; Without an OpenAI key, the tool runs with a stand-in transcript and a simple moment picker, but reframing, captions and rendering are fully real. I could test the hardest parts of the pipeline all day without spending anything on API calls. The UI labels mock mode clearly so stand-in output is never mistaken for real picks.&lt;/p&gt;
&lt;h3 id=&quot;how-did-claude-code-fit-in-&quot;&gt;How did Claude Code fit in?&lt;/h3&gt;
&lt;p&gt;I wrote a product spec first: the nine stages, the five screens, and the rules for each step. Then I built it with Claude Code one stage at a time, starting from the command line and proving each stage worked before moving on.&lt;/p&gt;
&lt;p&gt;Two habits made the biggest difference:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Interfaces before infrastructure.&lt;/strong&gt; Storage, the job queue, the database and the AI provider each sit behind a simple interface. Right now it runs on SQLite, local files and a local queue. Moving to Postgres, Cloudflare R2 and a proper job queue means swapping one piece at a time, not rewriting the app.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pure maths in its own file.&lt;/strong&gt; The camera smoother has no video code in it at all. That made it fast to test and easy for Claude Code to reason about, which is exactly where you want precision.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you are new to building with Claude Code, my &lt;a rel=&quot;nofollow&quot; href=&quot;https://danishsofi.in/blog/how-to-use-claude-code-to-build-app-website&quot;&gt;beginner's guide to building an app with Claude Code&lt;/a&gt; covers the setup, and &lt;a rel=&quot;nofollow&quot; href=&quot;https://danishsofi.in/blog/claude-code-pricing-free-options-2026&quot;&gt;Claude Code pricing and free options&lt;/a&gt; covers what it costs to run.&lt;/p&gt;
&lt;h3 id=&quot;what-is-next-for-the-tool-&quot;&gt;What is next for the tool?&lt;/h3&gt;
&lt;p&gt;The current build is the full pipeline plus the web UI. On the list:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sharper cut handling. Right now the reposition at a cut uses the last face position from before the cut, so on a hard camera change the frame catches up a beat late. The fix is to look ahead to the first detection after the cut.&lt;/li&gt;
&lt;li&gt;Live progress updates instead of polling every 1.5 seconds&lt;/li&gt;
&lt;li&gt;Regenerating only the captions or only the reframe, instead of the whole clip&lt;/li&gt;
&lt;li&gt;Speaker detection, so two-person podcasts can get a split-screen layout&lt;/li&gt;
&lt;li&gt;Running transcription and selection fully locally, with no API costs&lt;/li&gt;
&lt;li&gt;Auto-posting finished clips to TikTok, Instagram and YouTube Shorts&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;frequently-asked-questions&quot;&gt;Frequently asked questions&lt;/h3&gt;
&lt;h4 id=&quot;is-there-a-free-opus-clip-alternative-&quot;&gt;Is there a free Opus Clip alternative?&lt;/h4&gt;
&lt;p&gt;Yes, if you are willing to run it yourself. Open-source pieces like Whisper (transcription), FFmpeg (rendering) and PySceneDetect (scene cuts) are free, and that is most of a clipping tool. The paid part is usually the AI moment selection, and you can run that on a local model to bring the cost close to zero.&lt;/p&gt;
&lt;h4 id=&quot;how-does-opus-clip-choose-which-moments-to-clip-&quot;&gt;How does Opus Clip choose which moments to clip?&lt;/h4&gt;
&lt;p&gt;Opus Clip and similar tools score moments with their own virality model and give you the top picks. You can't tell them what to look for beyond basic settings. A prompt-driven tool like mine lets you describe the moment you want in plain English.&lt;/p&gt;
&lt;h4 id=&quot;can-gpt-4o-pick-good-clips-from-a-transcript-&quot;&gt;Can GPT-4o pick good clips from a transcript?&lt;/h4&gt;
&lt;p&gt;Yes, if you give it structure. Split long transcripts into overlapping windows, ask for a fixed number of clips with strict start and end times, and force a JSON schema. Then review the picks yourself before rendering. It is good at finding topics, less reliable at judging delivery, which is why the review step matters.&lt;/p&gt;
&lt;h4 id=&quot;what-is-the-hardest-part-of-building-an-ai-clipping-tool-&quot;&gt;What is the hardest part of building an AI clipping tool?&lt;/h4&gt;
&lt;p&gt;Reframing. Detecting a face is easy. Making the crop move smoothly, without jitter, without overshooting and without sliding across scene cuts, is where most of the work goes.&lt;/p&gt;
&lt;h4 id=&quot;do-i-need-a-gpu-to-run-it-&quot;&gt;Do I need a GPU to run it?&lt;/h4&gt;
&lt;p&gt;No. Local transcription uses faster-whisper, which runs on a CPU, and the face detector is lightweight. A GPU makes encoding faster, and the renderer will use one if you have it.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;If you want a clipping pipeline like this built for your channel or agency, &lt;a rel=&quot;nofollow&quot; href=&quot;https://danishsofi.in/contact&quot;&gt;get in touch&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Originally published on &lt;a rel=&quot;nofollow&quot; href=&quot;https://danishsofi.in/blog/build-opus-clip-alternative-claude-code&quot;&gt;danishsofi.in&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/32660/how-i-built-my-own-opus-clip-alternative-with-claude-code</guid>
<pubDate>Tue, 27 Oct 2026 10:03:19 +0000</pubDate>
</item>
<item>
<title>Launching a fancy text converter: 451 styles, and the Unicode edge cases behind them</title>
<link>https://coderlegion.com/32655/launching-a-fancy-text-converter-451-styles-and-the-unicode-edge-cases-behind-them</link>
<description>&lt;p&gt;I'm Shawn Sun, a front-end engineer, and I just put a fancy text converter online. This is a developer community, so here are the parts that were fun to build instead of a feature list.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=9770236220749453047&quot; alt=&quot;The converter's home page: one text box, category chips, and a grid of styled previews&quot;&gt;&lt;/p&gt;
&lt;p&gt;You type once and get 451 copy-and-paste styles. Tap one to copy it, and the last 8 you copied come back under Recent. No sign-up. None of these are fonts: every style is real Unicode characters, and that caused most of the work below.&lt;/p&gt;
&lt;h3 id=&quot;1-a-style-is-mostly-a-lookup-table&quot;&gt;1. A style is mostly a lookup table&lt;/h3&gt;
&lt;p&gt;Bold, italic, script, Fraktur and the rest live in the Mathematical Alphanumeric Symbols block, so most styles are &quot;offset of A, plus the letter's index&quot;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=2226421321765363481&quot; alt=&quot;Lookup table: A U+0041 maps to bold A U+1D400, a U+0061 to U+1D41A, with the offset formula&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;2-the-block-has-holes&quot;&gt;2. The block has holes&lt;/h3&gt;
&lt;p&gt;Some letters were encoded earlier elsewhere, so their slot is empty. A naive &lt;code&gt;base + index&lt;/code&gt; gives you a box, so each style carries a small exceptions table.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=14870203655612914539&quot; alt=&quot;Italic &amp;quot;the&amp;quot;: the naive code point U+1D455 is unassigned and shows a box; the fix uses U+210E PLANCK CONSTANT&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;3-accents-decompose-first&quot;&gt;3. Accents: decompose first&lt;/h3&gt;
&lt;p&gt;There is no bold é. I split it with NFD, style the e and keep the accent after it. Whether the accent then sits on top depends on the font.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=8443502707233179983&quot; alt=&quot;é decomposes into e plus U+0301; only the e is styled; two fonts render the result differently&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;4-don-t-split-emoji&quot;&gt;4. Don't split emoji&lt;/h3&gt;
&lt;p&gt;Walk a string by code point and 👩💻 falls apart. I cut input into grapheme clusters with &lt;code&gt;Intl.Segmenter&lt;/code&gt; and only style plain letters and digits.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=9433061209713490169&quot; alt=&quot;By code point, the woman technologist emoji is three pieces; Intl.Segmenter keeps it as one cluster&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;5-long-input-preview-first-convert-on-copy&quot;&gt;5. Long input: preview first, convert on copy&lt;/h3&gt;
&lt;p&gt;Rendering every style live is fine for a username and slow for a pasted paragraph. Past 200 UTF-16 code units, each card previews only the start, and the full text is converted when you copy.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=354969249241440925&quot; alt=&quot;Only the first 200 code units are converted for previews; the tapped style converts the whole text on copy&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;honest-limits&quot;&gt;Honest limits&lt;/h3&gt;
&lt;p&gt;Conversion runs in your browser. I count anonymous usage events, such as which style was copied, but your text isn't part of them.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=8032254886286468851&quot; alt=&quot;Limits table: missing glyphs, screen readers and search, usernames, digits that stay plain&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;i-d-like-your-feedback&quot;&gt;I'd like your feedback&lt;/h3&gt;
&lt;p&gt;Which category is missing, and do the style names make sense? Try your own text on &lt;a rel=&quot;nofollow&quot; href=&quot;https://fancy-text-generator.app/&quot;&gt;the live site&lt;/a&gt; and tell me in the comments.&lt;/p&gt;
&lt;p&gt;— Shawn Sun&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/32655/launching-a-fancy-text-converter-451-styles-and-the-unicode-edge-cases-behind-them</guid>
<pubDate>Tue, 27 Oct 2026 09:22:36 +0000</pubDate>
</item>
<item>
<title>I built PinOps to turn a published URL into useful Pinterest pins</title>
<link>https://coderlegion.com/30959/i-built-pinops-to-turn-a-published-url-into-useful-pinterest-pins</link>
<description>&lt;p&gt;I kept losing afternoons after an article was already live.&lt;/p&gt;
&lt;p&gt;Pinterest still needed useful pins from that URL: more than one angle, vertical creatives that match the destination, titles that stay honest to the link, and a queue that does not dump twenty pins in a day. The calendar was never the hard part.&lt;/p&gt;
&lt;p&gt;So I built PinOps.&lt;/p&gt;
&lt;p&gt;Paste a published URL. Get useful pins. Edit what needs a tweak. Queue what you keep.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://pinops.app&quot;&gt;https://pinops.app&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Freemium. Bootstrapped. I built it because I was tired of rebuilding that path every time a post went live.&lt;/p&gt;
&lt;p&gt;If you have tried to turn a blog into a Pinterest system, I want to hear where the time actually goes.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/30959/i-built-pinops-to-turn-a-published-url-into-useful-pinterest-pins</guid>
<pubDate>Tue, 13 Oct 2026 15:47:07 +0000</pubDate>
</item>
<item>
<title>I Tried to Build Persistent Compute on an Xbox SSD. The Failed Tests Changed the Architecture.</title>
<link>https://coderlegion.com/30863/tried-to-build-persistent-compute-on-an-xbox-ssd-the-failed-tests-changed-the-architecture</link>
<description>&lt;p&gt;The Xbox wrote 8 GiB of persistent data.&lt;/p&gt;
&lt;p&gt;I still couldn't call the experiment successful.&lt;/p&gt;
&lt;p&gt;The bytes were there, but the operation failed because a completely different part of the system had timed out. In another test, the worker correctly refused to allocate more storage even though its configuration technically allowed a much larger workload.&lt;/p&gt;
&lt;p&gt;Eventually, the experiment passed. But getting there required much more than writing files to an SSD.&lt;/p&gt;
&lt;p&gt;I had to investigate how Xbox processes share resources, build a separate coordination component, introduce a recoverable page store, survive real application restarts, and learn which failures were bugs and which were legitimate platform boundaries.&lt;/p&gt;
&lt;p&gt;This is one of the experiments behind XCP that I hadn't explored in the previous articles.&lt;/p&gt;
&lt;p&gt;The &lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/danielecangi/series/44781&quot;&gt;five-part XCP series&lt;/a&gt; ended with the platform becoming open source. This bonus chapter goes back into the original Xbox research to explain how I approached persistent computation inside the Developer Mode sandbox.&lt;/p&gt;
&lt;h3 id=&quot;the-problem-wasn-t-just-memory-capacity&quot;&gt;The problem wasn't just memory capacity&lt;/h3&gt;
&lt;p&gt;In the earlier experiments, XCP measured an application memory budget of 5 GiB in one Xbox Series X Developer Mode configuration.&lt;/p&gt;
&lt;p&gt;That was useful. It gave us an actual resource boundary for the worker rather than a number inferred from the console's hardware specifications.&lt;/p&gt;
&lt;p&gt;But it also raised another question.&lt;/p&gt;
&lt;p&gt;What happens when the work I want to execute is larger than the memory I can keep resident?&lt;/p&gt;
&lt;p&gt;I wasn't trying to bypass the sandbox or convince the Xbox that its SSD was additional physical RAM.&lt;/p&gt;
&lt;p&gt;I wanted to build something different: an execution system that could keep data and completed work in persistent storage, bring bounded pieces into memory, process them, and continue from a valid checkpoint after an interruption.&lt;/p&gt;
&lt;p&gt;Before building the storage layer, though, I had another hypothesis to investigate.&lt;/p&gt;
&lt;p&gt;Could a second process help?&lt;/p&gt;
&lt;h3 id=&quot;first-i-investigated-the-xbox-process-model&quot;&gt;First, I investigated the Xbox process model&lt;/h3&gt;
&lt;p&gt;XCP already had a foreground worker capable of running CPU and GPU workloads.&lt;/p&gt;
&lt;p&gt;I wanted to understand whether another process inside the same UWP package could perform useful background coordination without consuming the worker's entire resource budget.&lt;/p&gt;
&lt;p&gt;The candidate was an out-of-process App Service.&lt;/p&gt;
&lt;p&gt;On desktop Windows, creating another process might seem straightforward. On Xbox Developer Mode, I wasn't going to assume that the desktop behavior applied unchanged.&lt;/p&gt;
&lt;p&gt;So I started measuring.&lt;/p&gt;
&lt;p&gt;The first live probes established that the worker and an auxiliary App Service could run with distinct process and activation identities. They exchanged messages, maintained independent heartbeat activity and retained touched, verified memory during overlapping execution windows.&lt;/p&gt;
&lt;p&gt;That was already useful.&lt;/p&gt;
&lt;p&gt;But it did not prove that we had discovered a second independent 5 GiB memory budget.&lt;/p&gt;
&lt;p&gt;To investigate properly, I built an eleven-phase matrix covering CPU execution, verified GPU execution, simultaneous activity, progressive memory pressure, bidirectional storage visibility, suspension, termination, restart, redeployment and cleanup.&lt;/p&gt;
&lt;p&gt;And this is where the testing started getting complicated.&lt;/p&gt;
&lt;h4 id=&quot;the-first-failures-weren-t-even-storage-failures&quot;&gt;The first failures weren't even storage failures&lt;/h4&gt;
&lt;p&gt;Worker &lt;code&gt;.150&lt;/code&gt; reached the Xbox but exposed incomplete measurement data. It could report IPC latency summaries, yet the acceptance contract required the underlying distribution, including median, p95 and bidirectional throughput.&lt;/p&gt;
&lt;p&gt;There was no legitimate way to reconstruct missing measurements afterward.&lt;/p&gt;
&lt;p&gt;So that run remained diagnostic.&lt;/p&gt;
&lt;p&gt;Worker &lt;code&gt;.151&lt;/code&gt; produced another interesting failure: &lt;strong&gt;79 of 81 preflight checks passed&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The two failures came from heartbeat validation.&lt;/p&gt;
&lt;p&gt;The foreground heartbeat ran at 25 ms intervals. The auxiliary broker used a 100 ms timer. During an approximately 935 ms observation window, the broker produced nine ticks.&lt;/p&gt;
&lt;p&gt;The test incorrectly expected 32 because it had reused the foreground count.&lt;/p&gt;
&lt;p&gt;The broker wasn't failing to run. Our expectation was wrong.&lt;/p&gt;
&lt;p&gt;That candidate stayed failed, and the next version corrected the measurement contract.&lt;/p&gt;
&lt;p&gt;Worker &lt;code&gt;.152&lt;/code&gt; passed the preflight, but memory-pressure testing exposed another gap. The Xbox refused a bounded allocation, yet the error didn't carry enough structured information to establish the responsible component, observed headroom, applied reserve and cleanup outcome.&lt;/p&gt;
&lt;p&gt;A text error saying that memory allocation failed wasn't sufficient to classify the platform behavior.&lt;/p&gt;
&lt;p&gt;Worker &lt;code&gt;.153&lt;/code&gt; added the missing observations.&lt;/p&gt;
&lt;p&gt;The completed matrix passed all eleven phases, retaining fourteen actual attempts in its evidence history.&lt;/p&gt;
&lt;p&gt;The separate classification step selected &lt;strong&gt;Case B: a distinct but resource-limited useful process&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;That result changed the design.&lt;/p&gt;
&lt;p&gt;I didn't have evidence for a second large compute process. But I had enough evidence for a small coordination service.&lt;/p&gt;
&lt;p&gt;That was what I built.&lt;/p&gt;
&lt;h3 id=&quot;the-second-process-became-a-coordinator&quot;&gt;The second process became a coordinator&lt;/h3&gt;
&lt;p&gt;Instead of creating another general-purpose worker, I gave the auxiliary process a narrower responsibility.&lt;/p&gt;
&lt;p&gt;It could participate in lifecycle observation, heartbeat management and storage reservations.&lt;/p&gt;
&lt;p&gt;The main worker still controlled execution admission, semantic verification, recovery authorization and final result publication.&lt;/p&gt;
&lt;p&gt;This separation mattered because the broker could disappear and restart independently.&lt;/p&gt;
&lt;p&gt;A reservation could not simply be assumed valid because some old record said it existed.&lt;/p&gt;
&lt;p&gt;The worker had to validate the reservation identity, generation and storage plan before accepting new work.&lt;/p&gt;
&lt;p&gt;Worker &lt;code&gt;.154&lt;/code&gt; tested this arrangement on the physical Xbox.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;55 of 55 checks passed, with zero violations.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The useful result wasn't more RAM.&lt;/p&gt;
&lt;p&gt;It was an operational coordination mechanism on which persistent computation could be built.&lt;/p&gt;
&lt;h3 id=&quot;next-i-needed-storage-that-could-survive-failure&quot;&gt;Next, I needed storage that could survive failure&lt;/h3&gt;
&lt;p&gt;Writing a file is easy compared with deciding whether that file represents completed computation.&lt;/p&gt;
&lt;p&gt;Imagine a workload producing hundreds of output pages.&lt;/p&gt;
&lt;p&gt;The application writes a page, updates some metadata, and then disappears.&lt;/p&gt;
&lt;p&gt;After restarting, which pages can be trusted?&lt;/p&gt;
&lt;p&gt;Did a page finish writing? Does its recorded hash match the bytes? Was its journal entry committed? Can the computation resume from that point without silently skipping or duplicating work?&lt;/p&gt;
&lt;p&gt;For XCP, I built the storage system around content-addressed data and an append-only journal.&lt;/p&gt;
&lt;p&gt;Each committed page had an identity derived from its content. The journal recorded ordered transitions, with records linked through hashes.&lt;/p&gt;
&lt;p&gt;A page was not considered committed just because its file existed.&lt;/p&gt;
&lt;p&gt;The system had to verify the relevant content and metadata before including it in the committed sequence.&lt;/p&gt;
&lt;p&gt;Recovery worked from the longest valid committed prefix. Anything beyond that boundary could not silently become trusted state.&lt;/p&gt;
&lt;p&gt;That approach allowed the runtime to distinguish a completed checkpoint from bytes left behind by an interrupted operation.&lt;/p&gt;
&lt;h4 id=&quot;then-i-tried-breaking-it&quot;&gt;Then I tried breaking it&lt;/h4&gt;
&lt;p&gt;Worker &lt;code&gt;.155&lt;/code&gt; exercised the page store and recovery system on Xbox.&lt;/p&gt;
&lt;p&gt;The tests included truncated journal entries, corrupted hashes, missing pages, incomplete root manifests, conflicting page identities, exhausted quotas, stale reservations and repeated cleanup.&lt;/p&gt;
&lt;p&gt;The live campaign also tested recovery after worker restart, auxiliary restart and package redeployment.&lt;/p&gt;
&lt;p&gt;One deployment experiment uncovered an unexpected problem.&lt;/p&gt;
&lt;p&gt;An in-place installation using the same package version left the application running, but Xbox Network Isolation silently dropped the inbound worker connection.&lt;/p&gt;
&lt;p&gt;The correction was not to pretend the storage operation had succeeded.&lt;/p&gt;
&lt;p&gt;We used a clean uninstall with application data preserved, reinstalled the exact package, restored connectivity and verified that the committed storage root had survived.&lt;/p&gt;
&lt;p&gt;The complete &lt;code&gt;.155&lt;/code&gt; campaign eventually passed &lt;strong&gt;248 of 248 checks&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;At this point I had a storage substrate capable of preserving committed data through the tested failure and lifecycle scenarios.&lt;/p&gt;
&lt;p&gt;But I still hadn't demonstrated useful persistent computation.&lt;/p&gt;
&lt;p&gt;That required the next experiment.&lt;/p&gt;
&lt;h3 id=&quot;running-computation-through-persistent-storage&quot;&gt;Running computation through persistent storage&lt;/h3&gt;
&lt;p&gt;For the first workload, I reused an existing XCP test called &lt;em&gt;Provable Worlds&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;It produces a deterministic field of 1024 × 1024 unsigned 32-bit values.&lt;/p&gt;
&lt;p&gt;The complete result is only 4 MiB.&lt;/p&gt;
&lt;p&gt;That may sound small for an SSD experiment, but this test wasn't intended to prove beyond-RAM scale yet.&lt;/p&gt;
&lt;p&gt;I wanted to establish that computation could be divided into independently checked units, committed in order, interrupted and resumed correctly.&lt;/p&gt;
&lt;p&gt;The field was divided into &lt;strong&gt;256 tiles of 16 KiB each&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Each tile had a known global position in the output. The signed CPU Capsule could compute its bounded portion, and XCP compared that result against the CPU reference.&lt;/p&gt;
&lt;p&gt;The runtime used bounded output buffers rather than requiring the complete result to stay resident.&lt;/p&gt;
&lt;p&gt;The important condition was that the durable cursor advanced only after a page had been committed successfully.&lt;/p&gt;
&lt;h4 id=&quot;the-first-uninterrupted-runs-passed-restart-failed-&quot;&gt;The first uninterrupted runs passed. Restart failed.&lt;/h4&gt;
&lt;p&gt;Worker &lt;code&gt;.156&lt;/code&gt; completed two uninterrupted executions.&lt;/p&gt;
&lt;p&gt;Then I tested the restart path.&lt;/p&gt;
&lt;p&gt;Both real-restart resume attempts failed.&lt;/p&gt;
&lt;p&gt;The problem was the reservation lifecycle.&lt;/p&gt;
&lt;p&gt;After the foreground interruption, the system attempted to continue using the old coordinator lease. But that lease belonged to the previous activation.&lt;/p&gt;
&lt;p&gt;This contradicted what the earlier process-topology experiments had already shown.&lt;/p&gt;
&lt;p&gt;Persistent data could survive the interruption.&lt;/p&gt;
&lt;p&gt;A live process reservation could not be treated the same way.&lt;/p&gt;
&lt;p&gt;So I separated those two concepts.&lt;/p&gt;
&lt;p&gt;The committed journal retained its original storage identity and generation. A resumed worker had to obtain a &lt;strong&gt;fresh live reservation&lt;/strong&gt;, with a newer generation, and reauthorize the persisted work before continuing.&lt;/p&gt;
&lt;p&gt;Worker &lt;code&gt;.157&lt;/code&gt; implemented that correction.&lt;/p&gt;
&lt;p&gt;The test deliberately committed the first 128 tiles, computed the next tile without committing it, and interrupted execution.&lt;/p&gt;
&lt;p&gt;After the real restart, the worker verified the committed prefix, skipped those first 128 tiles and computed the remaining 128.&lt;/p&gt;
&lt;p&gt;It reproduced the expected final field hash.&lt;/p&gt;
&lt;p&gt;The uninterrupted tests, restart/resume path, cleanup and same-package regression checks passed.&lt;/p&gt;
&lt;p&gt;XCP admitted that specific bounded persistent-compute capability.&lt;/p&gt;
&lt;p&gt;One distinction is worth keeping: the tiled execution used the signed CPU Capsule, not a newly implemented GPU-tiled engine. The existing GPU path was checked separately. Nor did this test demonstrate performance gains from asynchronous I/O overlap.&lt;/p&gt;
&lt;p&gt;It demonstrated correct bounded computation and recovery.&lt;/p&gt;
&lt;p&gt;Now I wanted to investigate the storage scale.&lt;/p&gt;
&lt;h3 id=&quot;eight-gigabytes-and-another-failed-experiment&quot;&gt;Eight gigabytes, and another failed experiment&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/qnntqurx0dxo2jvtwvkc.png&quot; alt=&quot;Conceptual illustration of XCP persistent storage research on Xbox Series X, showing an 8 GiB SSD experiment, verified data pages, recovery tests and a successful result.&quot;&gt;&lt;/p&gt;
&lt;p&gt;For the scale test, I moved to a different page geometry.&lt;/p&gt;
&lt;p&gt;Instead of the 16 KiB computation tiles, the storage characterization used &lt;strong&gt;4 MiB pages&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The target was 8 GiB: exactly 2,048 pages.&lt;/p&gt;
&lt;p&gt;I also defined a progression through 256 MiB, 512 MiB, 1 GiB, 2 GiB, 4 GiB and finally 8 GiB.&lt;/p&gt;
&lt;p&gt;The data was generated deterministically, with page-specific content, so the test could verify distinct pages without treating a single repeated blob as proof of physical growth.&lt;/p&gt;
&lt;p&gt;And there was a safety condition: the operation had to preserve &lt;strong&gt;16 GiB of reported available storage&lt;/strong&gt; as a reserve.&lt;/p&gt;
&lt;p&gt;Then came worker &lt;code&gt;.158&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The worker materialized the full 8 GiB.&lt;/p&gt;
&lt;p&gt;But the operation failed.&lt;/p&gt;
&lt;p&gt;After more than 163 seconds of storage work, the coordinator attempted to release its reservation using an IPC deadline that had been established at the beginning of the operation.&lt;/p&gt;
&lt;p&gt;That deadline was only 30 seconds.&lt;/p&gt;
&lt;p&gt;The data had been written, but the protocol could no longer complete the operation correctly.&lt;/p&gt;
&lt;p&gt;This was not an accepted result.&lt;/p&gt;
&lt;p&gt;I corrected the lease-release timeout handling, added an explicit cleanup path for the failed run and tested again.&lt;/p&gt;
&lt;p&gt;Worker &lt;code&gt;.159&lt;/code&gt; successfully committed all 2,048 pages.&lt;/p&gt;
&lt;p&gt;The measured seed operation took approximately 164 seconds.&lt;/p&gt;
&lt;p&gt;That is an end-to-end operation time, including page generation, hashing, storage and journal work. It isn't a raw SSD throughput benchmark.&lt;/p&gt;
&lt;p&gt;But &lt;code&gt;.159&lt;/code&gt; was not the final success either.&lt;/p&gt;
&lt;h4 id=&quot;the-64-gib-attempt-was-refused&quot;&gt;The 64 GiB attempt was refused&lt;/h4&gt;
&lt;p&gt;The broader storage policy allowed much larger declared reservations, so I also tested what happened when the system attempted to continue beyond the 8 GiB seed.&lt;/p&gt;
&lt;p&gt;The remaining planned growth was 56 GiB.&lt;/p&gt;
&lt;p&gt;The platform-reported filesystem space was nowhere near sufficient for that growth plus the required 16 GiB safety reserve.&lt;/p&gt;
&lt;p&gt;The worker returned:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;STORAGE_SCALE_PLATFORM_HEADROOM_REFUSED&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;And crucially, it refused &lt;strong&gt;before writing additional data&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The existing workspace remained unchanged. Cleanup subsequently removed the 8 GiB seed and a second cleanup confirmed that the namespace was already absent.&lt;/p&gt;
&lt;p&gt;This result did not establish that the Xbox SSD had a 64 GiB limit, or that the entire physical SSD was visible to the application.&lt;/p&gt;
&lt;p&gt;The application had observed a much smaller storage filesystem, approximately 32.2 billion bytes of reported capacity in that test.&lt;/p&gt;
&lt;p&gt;The configured storage quota was not the same as actual platform capacity.&lt;/p&gt;
&lt;p&gt;The refusal was correct.&lt;/p&gt;
&lt;p&gt;Instead of trying to force a larger allocation, I froze the demonstrated 8 GiB size as the target for the final characterization.&lt;/p&gt;
&lt;h3 id=&quot;the-result-worker-0-1-160-0&quot;&gt;The result: worker 0.1.160.0&lt;/h3&gt;
&lt;p&gt;Worker &lt;code&gt;.160&lt;/code&gt; was built and signed as a new exact package and tested on the physical Xbox.&lt;/p&gt;
&lt;p&gt;This time, the process completed the full storage lifecycle.&lt;/p&gt;
&lt;p&gt;It committed 2,048 distinct 4 MiB pages, totaling 8 GiB, then released the reservation.&lt;/p&gt;
&lt;p&gt;I returned to the Xbox Home screen and resumed the worker without power-cycling the console.&lt;/p&gt;
&lt;p&gt;The resumed worker obtained a fresh reservation generation, recovered the persisted namespace, reread and rehashed the complete 8 GiB, and committed the ordered root without allocating another storage rung.&lt;/p&gt;
&lt;p&gt;A separate verification operation then read the complete data again under another fresh lease and reproduced the same root identity.&lt;/p&gt;
&lt;p&gt;Finally, cleanup removed the 2,048 unreferenced blobs and all 8 GiB of their content.&lt;/p&gt;
&lt;p&gt;A second cleanup found nothing left to remove.&lt;/p&gt;
&lt;p&gt;The complete &lt;code&gt;.160&lt;/code&gt; lifecycle passed its checks.&lt;/p&gt;
&lt;p&gt;That was the accepted storage-scale result.&lt;/p&gt;
&lt;p&gt;Not 64 GiB. Not the console's full SSD. Not virtual RAM.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Eight GiB of bounded, recoverable, content-verified persistent storage, measured through the actual Xbox worker lifecycle.&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;what-this-proves-and-what-it-doesn-t&quot;&gt;What this proves, and what it doesn't&lt;/h3&gt;
&lt;p&gt;The completed experiments establish two related capabilities.&lt;/p&gt;
&lt;p&gt;First, XCP demonstrated a persistent-compute workflow in which deterministic tiled execution could survive a real worker interruption, verify previous committed work and resume without recomputing the valid prefix.&lt;/p&gt;
&lt;p&gt;Second, it demonstrated an 8 GiB persistent storage namespace with recovery, full-root verification and cleanup on the physical Xbox.&lt;/p&gt;
&lt;p&gt;These were separate tests.&lt;/p&gt;
&lt;p&gt;The 4 MiB computation experiment did not prove that an arbitrary dataset larger than RAM could be processed end to end.&lt;/p&gt;
&lt;p&gt;The 8 GiB storage experiment did not prove an 8 GiB GPU or CPU workload, transparent paging, unrestricted SSD access or a performance-optimized streaming engine.&lt;/p&gt;
&lt;p&gt;In fact, the recorded streaming runtime exposed no measured prefetch/compute overlap advantage. The bounded buffers, ordered commits and recovery path were the capabilities being established.&lt;/p&gt;
&lt;p&gt;Combining those pieces into larger real workloads is further engineering work, not something I can claim was already completed.&lt;/p&gt;
&lt;p&gt;But the distinction between demonstrated components and future integration doesn't make the result less interesting.&lt;/p&gt;
&lt;p&gt;It tells me which parts of the problem are actually solved.&lt;/p&gt;
&lt;h3 id=&quot;why-the-failed-tests-mattered-more-than-the-number&quot;&gt;Why the failed tests mattered more than the number&lt;/h3&gt;
&lt;p&gt;Looking back, there wasn't one dramatic failure.&lt;/p&gt;
&lt;p&gt;There were several small, independent ones.&lt;/p&gt;
&lt;p&gt;The auxiliary process existed, but didn't prove an independent memory budget.&lt;/p&gt;
&lt;p&gt;The heartbeat worked, but the test expected the wrong number of ticks.&lt;/p&gt;
&lt;p&gt;Memory pressure produced a refusal, but not enough structured evidence to classify it.&lt;/p&gt;
&lt;p&gt;The storage layer survived, but an installation sequence disrupted the worker connection.&lt;/p&gt;
&lt;p&gt;Computation succeeded uninterrupted, but couldn't resume under the old lease.&lt;/p&gt;
&lt;p&gt;Eight gigabytes were written, but the IPC timeout invalidated the operation.&lt;/p&gt;
&lt;p&gt;And a larger storage request was refused because the available space could not satisfy the reserve.&lt;/p&gt;
&lt;p&gt;Each result forced a different correction.&lt;/p&gt;
&lt;p&gt;Some changed the implementation. Some changed the measurement contract. One changed the resource-topology decision. Another changed the scale we were prepared to claim.&lt;/p&gt;
&lt;p&gt;I think this is one of the most interesting aspects of XCP.&lt;/p&gt;
&lt;p&gt;It didn't become more capable by assuming the Xbox restrictions were irrelevant.&lt;/p&gt;
&lt;p&gt;It became more capable by discovering which capabilities could be combined reliably inside those restrictions.&lt;/p&gt;
&lt;p&gt;The result was not a magic way to make SSD behave like RAM.&lt;/p&gt;
&lt;p&gt;It was a storage and recovery architecture that allows computation to have a durable history independent of the process that produced it.&lt;/p&gt;
&lt;h3 id=&quot;the-code-is-now-open-source&quot;&gt;The code is now open source&lt;/h3&gt;
&lt;p&gt;The original Xbox experiments were performed in the historical XCP research environment.&lt;/p&gt;
&lt;p&gt;The public repository contains reconstructed reference implementations and the source needed to inspect the design, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Daniele-Cangi/xcp-xbox/blob/main/reference/xcompute-probe/src/XComputeProbe/runtime/WorkerContentAddressedStoreRecovery.cpp&quot;&gt;Content-addressed storage and recovery&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Daniele-Cangi/xcp-xbox/blob/main/reference/xcompute-probe/src/XComputeProbe/runtime/WorkerProvableWorldsStreamingRuntime.cpp&quot;&gt;Persistent tiled compute runtime&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Daniele-Cangi/xcp-xbox/blob/main/reference/xcompute-probe/src/XComputeProbe/runtime/WorkerStorageScaleCharacterizationRuntime.cpp&quot;&gt;8 GiB storage characterization runtime&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The distinction between the original historical results and the newly reconstructed development packages remains important.&lt;/p&gt;
&lt;p&gt;The public source lets developers inspect and rebuild the relevant components. The historical hardware results belong to their original package versions and evidence records.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Newly reconstructed packages do not automatically inherit those physical-Xbox results.&lt;/strong&gt; They require their own device validation.&lt;/p&gt;
&lt;p&gt;That boundary is explained in the &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Daniele-Cangi/xcp-xbox/blob/main/docs/RECONSTRUCTION_AUDIT.md&quot;&gt;public reconstruction audit&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&quot;beyond-the-ssd-experiment&quot;&gt;Beyond the SSD experiment&lt;/h3&gt;
&lt;p&gt;XCP started as an attempt to make an Xbox Series X a programmable execution target.&lt;/p&gt;
&lt;p&gt;The project eventually expanded into a virtual machine, CPU/GPU execution, agent-driven development, a desktop studio and tools for adapting and building software.&lt;/p&gt;
&lt;p&gt;The SSD experiments explored another necessary part of that picture.&lt;/p&gt;
&lt;p&gt;A useful execution target needs more than the ability to run a program once.&lt;/p&gt;
&lt;p&gt;It needs a way to preserve work, recover from interruption and continue under a new execution context without pretending that the old one still exists.&lt;/p&gt;
&lt;p&gt;The most valuable result of this experiment wasn't the eight-gigabyte figure by itself.&lt;/p&gt;
&lt;p&gt;It was the realization that &lt;strong&gt;the lifetime of a computation does not have to be identical to the lifetime of the process executing it&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;On this particular Xbox, we demonstrated some concrete parts of that idea.&lt;/p&gt;
&lt;p&gt;And as with the rest of XCP, the next meaningful step is to take those capabilities into a more demanding workload and find out where they stop being sufficient.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;XCP is open source&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;GitHub: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Daniele-Cangi/xcp-xbox&quot;&gt;https://github.com/Daniele-Cangi/xcp-xbox&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;XCP Studio: &lt;a rel=&quot;nofollow&quot; href=&quot;https://xcpstudio.com/&quot;&gt;https://xcpstudio.com/&lt;/a&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/30863/tried-to-build-persistent-compute-on-an-xbox-ssd-the-failed-tests-changed-the-architecture</guid>
<pubDate>Mon, 12 Oct 2026 21:54:26 +0000</pubDate>
</item>
<item>
<title>I turned strings into letter arrays and started rebuilding URLs with index ranges</title>
<link>https://coderlegion.com/30523/i-turned-strings-into-letter-arrays-and-started-rebuilding-urls-with-index-ranges</link>
<description>&lt;p&gt;I was just playing with a tiny idea:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;What if every string was just an array of single characters… and I could pull pieces out by index ranges to form words, paths, or API links?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That experiment became &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.npmjs.com/package/string-range-tokens&quot;&gt;string-range-tokens&lt;/a&gt;&lt;/strong&gt; — a zero-dependency ESM package that does exactly that.&lt;/p&gt;
&lt;h3 id=&quot;the-core-idea&quot;&gt;The core idea&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;import { toTokens, sub, build } from 'string-range-tokens';

const t = toTokens('https://api.example.com/v1/users');

sub(t, 0, 4);                 // &quot;https&quot;
sub(t, 23, 25, [26, 31]);     // &quot;/v1/users&quot;
sub(t, -5, -1);               // &quot;users&quot;   ← negative indexes work
build(t, [0, 4], [23, 25]);   // &quot;https/v1&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;toTokens&lt;/code&gt; turns any string into a proper array of characters (Unicode-safe).&lt;br&gt;
&lt;code&gt;sub&lt;/code&gt; and &lt;code&gt;build&lt;/code&gt; let you reassemble any combination of inclusive ranges.&lt;/p&gt;
&lt;h3 id=&quot;why-bother-&quot;&gt;Why bother?&lt;/h3&gt;
&lt;p&gt;Sometimes you want to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Assemble API paths or query fragments from a shared token pool&lt;/li&gt;
&lt;li&gt;Avoid writing the full string literal in one place&lt;/li&gt;
&lt;li&gt;Pick substrings by position without regex&lt;/li&gt;
&lt;li&gt;Build tiny string “templates” from a fixed alphabet&lt;/li&gt;
&lt;li&gt;Teach indexing, ranges, and a bit of recursion in a playful way&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It’s not encryption (never hide real secrets with it). It’s just a clean, lightweight way to work with strings as letter arrays.&lt;/p&gt;
&lt;h3 id=&quot;extra-powers&quot;&gt;Extra powers&lt;/h3&gt;
&lt;h4 id=&quot;plan-find-the-ranges-that-rebuild-a-target&quot;&gt;&lt;code&gt;plan&lt;/code&gt; – find the ranges that rebuild a target&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const pool = toTokens('the quick brown fox');
const ranges = plan(pool, 'brew');
build(pool, ...ranges); // &quot;brew&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;scatter-gather-seeded-shuffle-reconstruct&quot;&gt;&lt;code&gt;scatter&lt;/code&gt; / &lt;code&gt;gather&lt;/code&gt; – seeded shuffle + reconstruct&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const { pool, key } = scatter('/api/orders', 7);
gather(pool, key); // &quot;/api/orders&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Useful for deterministic scrambling when you want both the shuffled tokens and a way to put them back together.&lt;/p&gt;
&lt;h3 id=&quot;install&quot;&gt;Install&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm i string-range-tokens
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or just copy the source — it’s a single file with no dependencies.&lt;/p&gt;
&lt;p&gt;Works in Node 18+, Deno, Bun, and the browser.&lt;br&gt;
Both JavaScript and TypeScript implementations are included.&lt;/p&gt;
&lt;p&gt;CDN:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;import { toTokens, sub, build, plan, scatter, gather } 
  from 'https://cdn.jsdelivr.net/npm/string-range-tokens@1.0.0/src/index.js';
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;links&quot;&gt;Links&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;npm: &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.npmjs.com/package/string-range-tokens&quot;&gt;string-range-tokens&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Figsh/srTokens&quot;&gt;Figsh/srTokens&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;If you end up using it for something fun (or weird), I’d love to hear about it.&lt;/p&gt;
</description>
<category>Discussions</category>
<guid isPermaLink="true">https://coderlegion.com/30523/i-turned-strings-into-letter-arrays-and-started-rebuilding-urls-with-index-ranges</guid>
<pubDate>Fri, 09 Oct 2026 22:09:28 +0000</pubDate>
</item>
<item>
<title>I Added a Chatbot to My Movie Discovery App</title>
<link>https://coderlegion.com/30350/i-added-a-chatbot-to-my-movie-discovery-app</link>
<description>&lt;p&gt;I recently added a chatbot feature to Galaxy Movies, my movie discovery app.&lt;/p&gt;
&lt;p&gt;The idea came from a problem I run into all the time: there are too many movies to choose from, and browsing doesn’t always help.&lt;/p&gt;
&lt;p&gt;You can scroll through trending lists, genres, ratings, and streaming options, but sometimes what you really want is to ask a simple question: “What should I watch tonight?”&lt;/p&gt;
&lt;p&gt;So I built Galaxy Bot.&lt;/p&gt;
&lt;p&gt;Galaxy Bot is a conversational movie assistant that helps users find movies based on what they actually want in the moment. Instead of only relying on filters, users can describe their mood, who they’re watching with, how much time they have, or what kind of movie they’re trying to avoid.&lt;/p&gt;
&lt;p&gt;Some example prompts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;“Recommend a feel-good movie under two hours.”&lt;/li&gt;
&lt;li&gt;“I want a sci-fi movie that isn’t too confusing.”&lt;/li&gt;
&lt;li&gt;“What should I watch with my family?”&lt;/li&gt;
&lt;li&gt;“Give me something tense but not horror.”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The main thing I focused on was making the chatbot feel useful without getting in the way. It should make discovery faster, not make the app feel heavier.&lt;/p&gt;
&lt;p&gt;This first version is about helping users get unstuck when they don’t know what to watch. Over time, I’d like to make it better at understanding taste, saved movies, ratings, and streaming availability.&lt;/p&gt;
&lt;p&gt;You can check it out here: &lt;a rel=&quot;nofollow&quot; href=&quot;https://galaxymovies.app&quot;&gt;Galaxy Movies&lt;/a&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/30350/i-added-a-chatbot-to-my-movie-discovery-app</guid>
<pubDate>Thu, 08 Oct 2026 22:45:14 +0000</pubDate>
</item>
<item>
<title>XCP: From an Xbox Experiment to an Open Development Platform</title>
<link>https://coderlegion.com/30262/xcp-from-an-xbox-experiment-to-an-open-development-platform</link>
<description>&lt;p&gt;XCP — Chapter 5&lt;br&gt;
In the previous chapters, I wrote about turning an Xbox Series X into a programmable execution target, building a virtual machine inside its sandbox, connecting AI coding agents to real hardware, and measuring the memory and GPU capabilities available in Developer Mode.&lt;/p&gt;
&lt;p&gt;Those articles explained individual parts of XCP. This final chapter is about what they were all leading toward.&lt;/p&gt;
&lt;p&gt;The goal was never simply to run AI-generated code on an Xbox. The goal was to build a development platform around the console, one that independent developers could eventually extend with familiar tools, engines and frameworks.&lt;/p&gt;
&lt;p&gt;That distinction matters because XCP now contains considerably more than the compute worker that started the experiment.&lt;/p&gt;
&lt;p&gt;It includes a programmable runtime, a desktop development environment, an execution and evidence system, Godot adaptation research, a native graphics pipeline and procedural worlds generated from WorldLoop.&lt;/p&gt;
&lt;p&gt;Each of those components grew out of a practical experiment. Together, they suggest a different way of approaching console development.&lt;/p&gt;
&lt;p&gt;From hardware research to a development platform&lt;br&gt;
The first question was relatively simple: how much useful computation could we perform on an Xbox Series X without bypassing its security model?&lt;/p&gt;
&lt;p&gt;XCP began inside the supported Developer Mode application sandbox. We explored memory allocation, persistent storage, CPU execution, GPU compute, graphics rendering and communication with a development PC.&lt;/p&gt;
&lt;p&gt;Those experiments produced measurable results, including a 5 GiB application memory budget in one tested configuration and more than 7.7 TFLOP/s in a bounded FP32 shader workload. We also built XVM, a verifiable execution environment capable of running admitted programs and returning structured execution evidence.&lt;/p&gt;
&lt;p&gt;But the more interesting possibility emerged after those foundations were working.&lt;/p&gt;
&lt;p&gt;We already had a console that could receive structured work, execute it, report its state and preserve a history of what happened. The next logical step was to use that infrastructure for complete software projects rather than isolated compute workloads.&lt;/p&gt;
&lt;p&gt;That decision changed XCP from a hardware research project into a creative-platform project.&lt;/p&gt;
&lt;p&gt;Three Godot games, three different problems&lt;br&gt;
One of the most important parts of the original XCP research was adapting existing Godot projects into bounded, playable experiences on the Xbox.&lt;/p&gt;
&lt;p&gt;Rather than using a single demonstration designed specifically for the platform, we worked with three substantially different games.&lt;/p&gt;
&lt;p&gt;Blokki — a 2D puzzle&lt;br&gt;
Blokki gave us a compact starting point for testing scene interpretation, grid interaction, controller input and gameplay feedback.&lt;/p&gt;
&lt;p&gt;The adaptation translated pointer-based interactions into a controller-driven grid, replaced some presentation elements with procedural drawing and preserved a bounded playable puzzle loop.&lt;/p&gt;
&lt;p&gt;The Xbox playability checks covered rendering, controller response, valid and invalid action feedback, and the usability of the interaction loop.&lt;/p&gt;
&lt;p&gt;Minedot — a 3D voxel-style sandbox&lt;br&gt;
Minedot introduced a different class of problems: a 3D environment, camera navigation, spatial interaction and mining or placing blocks.&lt;/p&gt;
&lt;p&gt;Its playable reconstruction used the left stick for movement, the right stick for looking around and bounded controller actions for interacting with the world.&lt;/p&gt;
&lt;p&gt;Some source dependencies were incomplete, so the Xbox implementation used declared substitutions rather than pretending that the original game had been reproduced completely.&lt;/p&gt;
&lt;p&gt;The resulting bounded experience was tested on physical hardware, including movement, camera control, interaction feedback and visual usability.&lt;/p&gt;
&lt;p&gt;Open RTS — a more complex 3D strategy game&lt;br&gt;
Open RTS pushed the experiment further.&lt;/p&gt;
&lt;p&gt;This time the adaptation involved a strategy environment with unit selection, movement, combat, interface visibility and a victory condition.&lt;/p&gt;
&lt;p&gt;The first physical test exposed presentation problems. Rather than simply accepting a successful launch, we corrected the interface behavior and performed another test.&lt;/p&gt;
&lt;p&gt;The revised version passed its bounded playability checks, including selection, movement, combat and victory feedback.&lt;/p&gt;
&lt;p&gt;Across these three projects, the research recorded 3/3 bounded playable reconstructions, 15/15 human playability checks and 17 captures.&lt;/p&gt;
&lt;p&gt;These results do not establish full source equivalence or universal Godot compatibility. The reconstructions used explicit substitutions, and additional work remains before an arbitrary Godot project can pass through the system without project-specific adaptation.&lt;/p&gt;
&lt;p&gt;What they do establish is that the approach was exercised across a 2D puzzle, a voxel-style 3D sandbox and a more complex 3D strategy game on real Xbox hardware.&lt;/p&gt;
&lt;p&gt;That is a useful foundation for developing reusable engine adapters instead of continuing to create isolated ports.&lt;/p&gt;
&lt;p&gt;Forge: when WorldLoop met Xbox&lt;br&gt;
Godot adaptation was not the only creative experiment.&lt;/p&gt;
&lt;p&gt;Another branch of the original research connected XCP with WorldLoop, my procedural CAD and world-generation project.&lt;/p&gt;
&lt;p&gt;WorldLoop was originally developed around procedural construction and scene generation. Buildings, structural relationships and larger environments could be generated from structured rules rather than manually assembled one object at a time.&lt;/p&gt;
&lt;p&gt;I wanted to see whether that same approach could produce content for a completely different execution environment.&lt;/p&gt;
&lt;p&gt;This led to Forge Protocol, a native Xbox graphics and gameplay experiment using Direct3D 11.&lt;/p&gt;
&lt;p&gt;The important architectural decision was to keep generation on the development PC. WorldLoop produced structured environments, which were then compiled into bounded native assets that the Xbox application could load and render.&lt;/p&gt;
&lt;p&gt;The console did not need to run the original generator or its development dependencies.&lt;/p&gt;
&lt;p&gt;The pipeline was approximately:&lt;/p&gt;
&lt;p&gt;WorldLoop procedural generation&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;         |
         v
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  Structured world + geometry&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;         |
         v
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;   PC-side asset compilation&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;         |
         v
Forge scene bundle
         |
         v
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;   Xbox native D3D11 runtime&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;         |
         v
Rendering + gameplay
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The experiments progressed beyond simple geometry.&lt;/p&gt;
&lt;p&gt;Orbital Forge Alpha demonstrated a WorldLoop-generated scene on Xbox, while Orbital Forge Beta expanded the environment into thousands of render parts, multiple placed structures, roads, structural relationships and a larger native scene bundle.&lt;/p&gt;
&lt;p&gt;The Beta world contained 2,364 render parts and compiled into 56,736 vertices and 28,368 triangles. It was installed and launched on the physical Xbox, with further iterations addressing scene scale, composition and gameplay readability.&lt;/p&gt;
&lt;p&gt;This mattered for a reason beyond the visual result.&lt;/p&gt;
&lt;p&gt;The same console execution target was receiving content from a procedural world generator that had not originally been designed for Xbox.&lt;/p&gt;
&lt;p&gt;Forge was not proof that every procedural engine could automatically run on the platform. It demonstrated one workable boundary between external generation and a native console runtime.&lt;/p&gt;
&lt;p&gt;For XCP, that boundary is important. Different authoring environments should not necessarily require completely separate execution infrastructures.&lt;/p&gt;
&lt;p&gt;XCP Studio: one project, shared by humans and agents&lt;br&gt;
The next part of the platform was the development experience.&lt;/p&gt;
&lt;p&gt;XCP Studio was designed as a Windows desktop environment where a developer, a visual interface and an AI coding agent can operate against the same project representation.&lt;/p&gt;
&lt;p&gt;Rather than giving each interface its own interpretation of the software, the project has a shared structure and explicit contracts.&lt;/p&gt;
&lt;p&gt;A project can declare its modules, assets, required capabilities and runtime configuration. XCP validates these declarations against the capabilities actually exposed by the execution target.&lt;/p&gt;
&lt;p&gt;The development cycle is:&lt;/p&gt;
&lt;p&gt;Developer / coding agent&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;       |
       v
Editable XCP project
       |
       v
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;   Validate + build bundle&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;       |
       v
 Install on Xbox
       |
       v
Launch + interact
       |
       v
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;   Observe + capture evidence&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;       |
       v
 Revise or rollback
       |
       +------&amp;gt; Next iteration
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The desktop environment is not merely a dashboard for a worker.&lt;/p&gt;
&lt;p&gt;It is intended to become the place where software is created, adapted, tested and evolved.&lt;/p&gt;
&lt;p&gt;The original research has already measured an end-to-end XCP Studio Technical Preview workflow on physical Xbox hardware, including project validation, installation, activation, launch, interaction and capture.&lt;/p&gt;
&lt;p&gt;The packaged preview still requires further clean-machine validation and release preparation. It should not be confused with a generally available finished product.&lt;/p&gt;
&lt;p&gt;Nevertheless, the essential development loop has been exercised.&lt;/p&gt;
&lt;p&gt;Why this could matter commercially&lt;br&gt;
There is a practical audience for a platform like this: independent developers, small studios, technical artists, researchers and people who want to experiment with interactive software on real console hardware.&lt;/p&gt;
&lt;p&gt;For many of them, the difficult part is not necessarily writing the first prototype. It is managing the gap between a familiar development environment and the constraints of a different execution target.&lt;/p&gt;
&lt;p&gt;A useful platform could reduce that gap.&lt;/p&gt;
&lt;p&gt;Imagine creating a small project with an existing engine, adapting it through a community-maintained integration, running it on an Xbox in Developer Mode and receiving structured feedback from the actual device.&lt;/p&gt;
&lt;p&gt;The developer could then revise the same project and repeat the cycle without rebuilding a separate workflow for every experiment.&lt;/p&gt;
&lt;p&gt;That would be particularly useful during early prototyping, when ideas change frequently and fast iteration is often more valuable than elaborate release infrastructure.&lt;/p&gt;
&lt;p&gt;There are already official development paths for engines such as Godot and Unreal Engine. XCP is not intended to replace Microsoft's commercial publishing programs, nor does it grant access to restricted console capabilities.&lt;/p&gt;
&lt;p&gt;Its proposed value is different: an open, reusable prototyping and execution environment built around the capabilities available to it.&lt;/p&gt;
&lt;p&gt;The long-term ambition is to support increasingly complex projects and established frameworks through reusable adapters and runtime components.&lt;/p&gt;
&lt;p&gt;Godot is a natural starting point because we have already conducted adaptation experiments. Unreal Engine and other major frameworks represent possible future integration work, not compatibility that XCP can currently claim.&lt;/p&gt;
&lt;p&gt;And I do not think this requires waiting until every feature of a major engine is supported before the platform becomes useful.&lt;/p&gt;
&lt;p&gt;A carefully defined subset that developers can actually build, execute and test may already provide value.&lt;/p&gt;
&lt;p&gt;Why I made XCP open source&lt;br&gt;
The original repository grew into a laboratory containing hardware probes, multiple generations of runtime experiments, creative projects, integration prototypes, successful tests and failed approaches.&lt;/p&gt;
&lt;p&gt;It was useful for research, but it was not a particularly good starting point for external contributors.&lt;/p&gt;
&lt;p&gt;That is why I extracted a separate public XCP repository around the reusable components: project contracts, XVM, source adapters, semantic infrastructure, execution evidence, conformance tests and development tools.&lt;/p&gt;
&lt;p&gt;The public repository is not a complete copy of the original laboratory. Third-party game sources and assets are excluded, and historical Xbox results must not be presented as fresh verification of newly reconstructed binaries.&lt;/p&gt;
&lt;p&gt;The next stage also cannot depend entirely on me building every integration.&lt;/p&gt;
&lt;p&gt;For XCP to become a genuine platform, the community needs to be able to extend it.&lt;/p&gt;
&lt;p&gt;That could mean implementing another source adapter, contributing rendering functionality, improving Godot support, building conformance tests, introducing a different runtime or exploring another execution target.&lt;/p&gt;
&lt;p&gt;The project deliberately separates source interpretation, execution contracts and target-specific capabilities because those are the areas in which independent contributions could eventually make the largest difference.&lt;/p&gt;
&lt;p&gt;I am not assuming that every abstraction is already general enough. In fact, one of the reasons to involve outside developers is to discover where the architecture still contains assumptions inherited from the original Xbox experiments.&lt;/p&gt;
&lt;p&gt;A platform becomes interesting when people can build things its original author did not anticipate.&lt;/p&gt;
&lt;p&gt;What remains to be done&lt;br&gt;
The historical research demonstrates several important capabilities, but turning those capabilities into a broadly usable development platform requires another kind of engineering.&lt;/p&gt;
&lt;p&gt;The public toolchain needs reproducible validation against the hardware. The desktop application needs a straightforward installation and onboarding experience. Framework adapters need to become more general, and new projects must be tested without relying on prior knowledge of their structure.&lt;/p&gt;
&lt;p&gt;That last point is especially important.&lt;/p&gt;
&lt;p&gt;We have demonstrated bounded playable reconstructions of three Godot games. The next significant result would be an independent developer taking a previously unseen project through the adaptation and execution process, with a clear record of which features work, which require substitutions and which remain unsupported.&lt;/p&gt;
&lt;p&gt;That would measure something different from the historical experiments: the usefulness of XCP as an extensible development tool.&lt;/p&gt;
&lt;p&gt;There is also substantial room for the community to explore beyond games. Interactive scenes, simulations, procedural environments, educational software and GPU-driven experiments all fit the same general direction.&lt;/p&gt;
&lt;p&gt;The platform should grow according to what can actually be implemented and measured, rather than a fixed list of promises.&lt;/p&gt;
&lt;p&gt;The bigger idea behind XCP&lt;br&gt;
Looking back, the development of XCP followed a somewhat unusual path.&lt;/p&gt;
&lt;p&gt;It started with an Xbox and a question about its computational capabilities.&lt;/p&gt;
&lt;p&gt;That led to memory and GPU experiments, a virtual machine, verified execution, AI-agent integration, a creative runtime, Godot adaptation, procedural worlds and finally a desktop development environment.&lt;/p&gt;
&lt;p&gt;None of those individual components tells the whole story.&lt;/p&gt;
&lt;p&gt;XVM is not the final product. GPU compute is not the final product. Forge and the Godot experiments are not separate destinations.&lt;/p&gt;
&lt;p&gt;They are different parts of a larger effort to make real hardware available as a practical, programmable and verifiable development target.&lt;/p&gt;
&lt;p&gt;The long-term vision is an open platform where developers can bring familiar tools, the community can expand the supported capabilities, and AI agents can participate in development without replacing the developer's authority over the project.&lt;/p&gt;
&lt;p&gt;The Xbox Series X is the first physical target because that is where the experiment began and where the current evidence exists.&lt;/p&gt;
&lt;p&gt;It does not have to be the last.&lt;/p&gt;
&lt;p&gt;And that is why I think XCP has a meaningful future beyond its original hardware research.&lt;/p&gt;
&lt;p&gt;Not because it already supports every engine or solves every problem in console development, but because it has established several working pieces of an environment that independent developers could help turn into something substantially larger.&lt;/p&gt;
&lt;p&gt;The next interesting result will not be another benchmark produced by me. It will be something built by somebody else using XCP.&lt;/p&gt;
&lt;p&gt;That is what I would like to see the project become.&lt;/p&gt;
&lt;p&gt;XCP is open source&lt;br&gt;
GitHub: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Daniele-Cangi/xcp-xbox&quot;&gt;https://github.com/Daniele-Cangi/xcp-xbox&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;XCP Studio: &lt;a rel=&quot;nofollow&quot; href=&quot;https://xcpstudio.com/&quot;&gt;https://xcpstudio.com/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The public repository contains the extracted platform, reference implementations, contracts and verification tooling. The original Xbox research provides historical hardware evidence, while new builds require validation against their own exact artifacts.&lt;/p&gt;
&lt;p&gt;Contributions, experiments and technical discussion are welcome.&lt;/p&gt;
&lt;p&gt;This is the final chapter of the XCP introduction series. The development itself continues.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/30262/xcp-from-an-xbox-experiment-to-an-open-development-platform</guid>
<pubDate>Thu, 08 Oct 2026 10:54:36 +0000</pubDate>
</item>
<item>
<title>I’m in High School and I Built VBX: A Modern, Compiled Successor to Visual Basic 6</title>
<link>https://coderlegion.com/29980/im-in-high-school-and-i-built-vbx-a-modern-compiled-successor-to-visual-basic-6</link>
<description>&lt;p&gt;Hey DEV Community! &lt;/p&gt;
&lt;p&gt;Remember the 90s and early 2000s? You'd open a simple program, drag a button onto a form, write a couple of readable lines of code, hit Run, and a real, working &lt;code&gt;.exe&lt;/code&gt; popped up instantly on your screen.&lt;/p&gt;
&lt;p&gt;No 500MB of &lt;code&gt;node_modules&lt;/code&gt;. No broken Python virtual environments. No 40GB Visual Studio installation. Just pure, unadulterated programming joy.&lt;/p&gt;
&lt;p&gt;Microsoft killed Visual Basic 6 back in 2008. But as a high school student who fell in love with that retro, productive paradigm, I decided to spend my nights building a modern answer for 2026: &lt;strong&gt;Meet VBX (Visual Basic X)&lt;/strong&gt; ⚡.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-what-is-vbx-&quot;&gt;️ What is VBX?&lt;/h3&gt;
&lt;p&gt;VBX is &lt;strong&gt;not&lt;/strong&gt; a toy interpreter running inside Python. &lt;/p&gt;
&lt;p&gt;It’s an open-source, high-performance &lt;strong&gt;compiler written in pure Go&lt;/strong&gt; that translates clean, human-readable Basic syntax directly into standard &lt;strong&gt;C99&lt;/strong&gt;. The generated C code is then compiled via GCC or Clang with &lt;code&gt;-O2&lt;/code&gt; optimizations into a standalone native binary with &lt;strong&gt;zero runtime dependencies&lt;/strong&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;.vbx Source Code ➔ [ VBX Parser (Go) ] ➔ Standard C99 Code ➔ [ GCC/Clang -O2 ] ➔ Native Binary (.exe)
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;a-quick-taste-of-vbx&quot;&gt;A Quick Taste of VBX&lt;/h3&gt;
&lt;p&gt;Here’s an interactive program in VBX that takes user input, shows native OS popups, and writes data to disk:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-vbx&quot;&gt;' Interactive App in VBX
Dim userName = InputBox(&quot;Please enter your name:&quot;, &quot;Registration&quot;)

' Display native OS alert (Windows, Mac, or Linux)
MsgBox &quot;Hello, &quot; &amp;amp; userName &amp;amp; &quot;! Saving your data...&quot;, &quot;Welcome&quot;

' Native File I/O
Dim logFile = &quot;user_data.txt&quot;
File.Write logFile, &quot;Registered User: &quot; &amp;amp; userName

' Read it back and verify
Dim savedContent = File.Read(logFile)
MsgBox &quot;Verified from file: &quot; &amp;amp; savedContent, &quot;Success&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You run it with:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;vbx run app.vbx
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or compile a release binary ready to distribute:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;vbx build app.vbx -o my_app
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-under-the-hood-the-engineering-&quot;&gt;⚡ Under the Hood (The Engineering)&lt;/h3&gt;
&lt;p&gt;As someone passionate about systems programming, I wanted VBX to be &lt;strong&gt;rock-solid&lt;/strong&gt;:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Zero External Dependencies in Compiler:&lt;/strong&gt; The Go compiler relies strictly on Go's standard library (&lt;code&gt;bufio&lt;/code&gt;, &lt;code&gt;strings&lt;/code&gt;, &lt;code&gt;regexp&lt;/code&gt;, &lt;code&gt;os/exec&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Memory Safety (No Heap Leaks):&lt;/strong&gt; Instead of naive &lt;code&gt;malloc&lt;/code&gt; calls for string concatenation, the generated C runtime uses a static &lt;strong&gt;ring-buffer memory pool (&lt;code&gt;vbx_alloc_str&lt;/code&gt;)&lt;/strong&gt;—a technique borrowed from classic game engines (like &lt;em&gt;Doom&lt;/em&gt;). It chews through 10,000 loop iterations with flat, zero-leak memory usage.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Multi-Pass Type Inference:&lt;/strong&gt; Evaluates expression types across AST passes to emit clean C function signatures and format specifiers (&lt;code&gt;long long&lt;/code&gt;, &lt;code&gt;double&lt;/code&gt;, &lt;code&gt;const char*&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;42 Test Suites:&lt;/strong&gt; Tested end-to-end with unit, integration, and nested-block stress tests.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GPLv3 with a Runtime Exception:&lt;/strong&gt; The compiler remains free software forever, but applications compiled with VBX are 100% yours to distribute or sell under any license you choose.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id=&quot;meet-our-mascot-basic-chan-&quot;&gt;Meet Our Mascot: BASIC-Chan!&lt;/h3&gt;
&lt;p&gt;Every great language needs a soul. We designed &lt;strong&gt;BASIC-Chan&lt;/strong&gt;—a cyberpunk anime tech girl holding a glowing holographic floppy disk and a floating retro &quot;Click Me&quot; button, matching our neon cyan and purple aesthetic!&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;tooling-amp-vs-code-extension&quot;&gt;Tooling &amp;amp; VS Code Extension&lt;/h3&gt;
&lt;p&gt;We didn't want to stop at the CLI. We’ve already built a full multi-editor ecosystem:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;Official VS Code Extension&lt;/strong&gt; is already live on the &lt;a rel=&quot;nofollow&quot; href=&quot;https://marketplace.visualstudio.com/items?itemName=M5Dev.vbx-language&quot;&gt;Visual Studio Marketplace&lt;/a&gt; (works in the browser on &lt;code&gt;vscode.dev&lt;/code&gt; too!).&lt;/li&gt;
&lt;li&gt;Syntax highlighting packages available for &lt;strong&gt;JetBrains IDEs, Sublime Text, Notepad++, Vim, Neovim, Emacs, and Micro&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;what-s-next--1&quot;&gt;What's Next?&lt;/h3&gt;
&lt;p&gt;Our next big milestone is &lt;strong&gt;VBX Studio&lt;/strong&gt;: a dedicated, lightweight, cross-platform visual IDE with a true drag-and-drop form designer, just like the classic 90s RAD tools we all miss.&lt;/p&gt;
&lt;p&gt;As Linus Torvalds famously once said: &lt;em&gt;&quot;Just a hobby, won't be big and professional...&quot;&lt;/em&gt; &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;⭐ &lt;strong&gt;GitHub Repository:&lt;/strong&gt; [https://github.com/M5Devs/vbx]&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Editors Repository:&lt;/strong&gt; [https://github.com/M5Devs/vbx-editors]&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I'd genuinely love to hear your thoughts, feedback, and what features you'd like to see in VBX! Let me know in the comments below! ❤️&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/29980/im-in-high-school-and-i-built-vbx-a-modern-compiled-successor-to-visual-basic-6</guid>
<pubDate>Tue, 06 Oct 2026 11:31:00 +0000</pubDate>
</item>
<item>
<title>I wanted Git to show me what would happen before changing history, so I built it</title>
<link>https://coderlegion.com/29970/i-wanted-git-to-show-me-what-would-happen-before-changing-history-so-i-built-it</link>
<description>&lt;p&gt;I use Git every day, but one thing has always bothered me:&lt;/p&gt;
&lt;p&gt;when you do something that changes history, a lot of Git tools still basically ask you to understand the command first and trust that the result is what you expected.&lt;/p&gt;
&lt;p&gt;I wanted something more visual.&lt;/p&gt;
&lt;p&gt;So while building &lt;strong&gt;RepoBeam&lt;/strong&gt;, I added a before/after preview for operations like going back to an older commit.&lt;/p&gt;
&lt;p&gt;Instead of clicking the action and hoping I understood it correctly, RepoBeam first shows me what the repository will look like afterwards.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/rx5b0flb4q1km0dt0ugp.png&quot; alt=&quot;RepoBeam showing a Before and After commit-history preview for a “Back to this commit” operation, so the user can see exactly how the Git history will change before confirming.&quot;&gt;&lt;/p&gt;
&lt;p&gt;For example, when going back to a commit I can choose whether I want to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;restore the files as normal unstaged changes&lt;/li&gt;
&lt;li&gt;restore them already staged&lt;/li&gt;
&lt;li&gt;create a new restore commit when rewriting published history would be unsafe&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And RepoBeam shows the expected history before I confirm anything.&lt;/p&gt;
&lt;p&gt;The idea is simple:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;show me what Git is going to do before it does it.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;RepoBeam actually started for a different reason.&lt;/p&gt;
&lt;p&gt;I used &lt;strong&gt;Fork&lt;/strong&gt; for a long time on Windows. When I started working more on Linux, I tried GitKraken, Sublime Merge, SmartGit, GitButler and a bunch of smaller clients, but I never found one that really felt like the Git GUI I wanted to use every day.&lt;/p&gt;
&lt;p&gt;So I started building my own.&lt;/p&gt;
&lt;p&gt;Over time it turned into a full Git client with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;local changes and staging&lt;/li&gt;
&lt;li&gt;split/unified diffs&lt;/li&gt;
&lt;li&gt;commit history and graph&lt;/li&gt;
&lt;li&gt;branches and remotes&lt;/li&gt;
&lt;li&gt;stashes&lt;/li&gt;
&lt;li&gt;guided conflict resolution&lt;/li&gt;
&lt;li&gt;before/after Git previews&lt;/li&gt;
&lt;li&gt;Coding Sessions&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;coding-sessions&quot;&gt;Coding Sessions&lt;/h4&gt;
&lt;p&gt;This is another part I use a lot now.&lt;/p&gt;
&lt;p&gt;When I start working with Codex or Claude, I start a Coding Session in RepoBeam first.&lt;/p&gt;
&lt;p&gt;RepoBeam then organizes everything that changes during that session.&lt;/p&gt;
&lt;p&gt;I can review the files afterwards, undo one change, bring it back, keep the rest, or undo the entire session.&lt;/p&gt;
&lt;p&gt;It does not replace Git or create some separate version-control system. It works with normal Git repositories.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;RepoBeam currently runs on &lt;strong&gt;Linux and Windows&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Linux builds are available as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.deb&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;AppImage&lt;/li&gt;
&lt;li&gt;Snap&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Private repositories work, no RepoBeam account is required, and the app is free to use including for work.&lt;/p&gt;
&lt;p&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://repobeam.com&quot;&gt;https://repobeam.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/jnapyun31qyc4pii98np.png&quot; alt=&quot;RepoBeam’s “Back to this commit” preview showing three safe restore options: restore as unstaged changes, restore staged, or create a restore commit while keeping published history intact.&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/l3y2vsbn8rpocdm2106b.png&quot; alt=&quot;RepoBeam’s guided conflict resolver showing Local and Server versions side by side, with options to keep Local, keep Server, keep both, or manually edit the final result before continuing the merge.&quot;&gt;&lt;/p&gt;
&lt;p&gt;I released &lt;strong&gt;1.8.3&lt;/strong&gt; this week and this is the first time I'm seriously putting RepoBeam in front of developers I don't know.&lt;/p&gt;
&lt;p&gt;The thing I'm most curious about:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Would a before/after preview like this actually make you more comfortable with Git history operations, or do you prefer seeing the underlying Git command directly?&lt;/strong&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/29970/i-wanted-git-to-show-me-what-would-happen-before-changing-history-so-i-built-it</guid>
<pubDate>Tue, 06 Oct 2026 09:55:16 +0000</pubDate>
</item>
<item>
<title>Building CyberRef: A Single-Binary Offline Reference for Cybersecurity Practitioners</title>
<link>https://coderlegion.com/29754/building-cyberref-a-single-binary-offline-reference-for-cybersecurity-practitioners</link>
<description>&lt;p&gt;I built CyberRef to solve a problem I kept seeing in labs and on engagements: nobody remembers the exact syntax of a command under pressure.&lt;/p&gt;
&lt;p&gt;You know the command exists. You know roughly what it does. But it's 2 AM during an incident, or you're mid-engagement with a client watching, and the exact flags slip away. So you open twelve tabs of cheatsheets, dig through your notes app, ask Slack — and twenty minutes disappear on something you knew cold last week.&lt;/p&gt;
&lt;p&gt;CyberRef is my attempt to fix that. This article is a walkthrough of what it does, the design decisions behind it, and what I learned building it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What It Is&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A single binary you download and run. It opens a local web UI at &lt;a rel=&quot;nofollow&quot; href=&quot;http://localhost:8787&quot;&gt;http://localhost:8787&lt;/a&gt; with a searchable command reference and a set of guided lab scenarios.&lt;/p&gt;
&lt;p&gt;No install. No server. No internet required. Everything is embedded — the UI, the datasets, the assets.&lt;/p&gt;
&lt;p&gt;Written in Go for the backend, Alpine.js + vanilla CSS for the frontend, all served from one binary via embed.FS.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why Offline-First&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Most cheat-sheet tools are websites. That's fine until you're in an air-gapped lab, an isolated client environment, or a VM with no network egress. In those settings, an online tool is dead on arrival.&lt;/p&gt;
&lt;p&gt;Offline-first isn't a limitation — it's a constraint that shaped every decision:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Data is embedded at build time. YAML files are compiled into the binary via go:embed. No external file reads at runtime.

No network calls at startup. The binary doesn't phone home, doesn't check for updates, doesn't do telemetry.

Stateless by default. Nothing is written to disk unless the user explicitly exports something.

Single binary distribution. One .exe on Windows. Copy it to a USB stick, run it on any machine.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This means the tool works in the environments where cyber work actually happens — not just where developers are comfortable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why Not React&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The obvious frontend choice would have been React with Vite and a build pipeline. I went with vanilla HTML/CSS/JS + Alpine.js instead. Here's why:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;go build must be the only build command. A React pipeline adds Node, npm, a bundler, and version drift. Two toolchains for a lookup tool is overkill.

Alpine.js gives 80% of React's ergonomics in one &amp;lt;script&amp;gt; tag. Declarative bindings, reactivity, x-for, x-if — everything a searchable list needs.

No dist/ folder to embed. The HTML, CSS, and JS files are the final artifacts. go:embed picks them up directly.

Zero framework churn. Alpine v3 will keep working in five years. React 18 will be replaced by React 21 by then.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For a searchable, filterable, well-formatted reference tool, this is the right tradeoff. React would have been solving a problem I didn't have.&lt;br&gt;
The Placeholder Substitution Feature&lt;/p&gt;
&lt;p&gt;This is the feature that turned CyberRef from a nice-to-have into a tool people actually use.&lt;/p&gt;
&lt;p&gt;Commands like nmap -A -p- {TARGET} aren't directly paste-ready. {TARGET} is a placeholder that must be replaced. CyberRef scans each command for {TOKEN} patterns and renders an inline input for each one:&lt;br&gt;
A few things make this work well:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Values persist across sessions. Type your lab IP once, and every command using {TARGET} reuses it.

YAML defaults pre-fill inputs. When a scenario specifies placeholders: { TARGET: &quot;10.10.10.5&quot; }, the input starts populated.

Copy gives you the substituted version. Not the template. The ready-to-paste command.

Values are shared across commands and scenarios. Configure once for an engagement, use everywhere.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Twenty lines of Alpine state plus a localStorage wrapper. Small feature, huge UX impact.&lt;br&gt;
The Scenario Layer&lt;/p&gt;
&lt;p&gt;A command reference is table stakes. The real problem in cyber training isn't &quot;what's the command&quot; — it's &quot;what's the sequence, and why.&quot;&lt;/p&gt;
&lt;p&gt;In v0.3 I added a scenario layer. Each scenario is a guided workflow with:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Narrative overview — what the technique is, why it matters

Prerequisites — what you need before starting

Steps — ordered commands with rationale, expected output, and notes

Defense — hardening, detection, and response guidance

References — MITRE ATT&amp;amp;CK, OWASP, vendor docs
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The scenarios are mapped to the Cyber Kill Chain, MITRE ATT&amp;amp;CK, and the CIA triad. This is important — it turns a command list into a curriculum.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Blue-Team Content&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;In v0.4 I added the first blue-team scenario — Log Analysis &amp;amp; Detection Engineering — plus purple-team notes on every attack scenario.&lt;/p&gt;
&lt;p&gt;Purple team notes are the connective tissue. They answer the question: &quot;How do I run this attack and watch it get detected at the same time?&quot;&lt;/p&gt;
&lt;p&gt;For example, on the Kerberoasting scenario:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Detection is easiest when 4769 events are forwarded to a SIEM. Filter for RC4 (0x17) ticket encryptions — modern AD should only see AES in normal operations. Any RC4 TGS is worth investigating.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is different from the Defense panel. The Defense panel lists what to monitor. The Purple Team Notes explain how to actually run the exercise.&lt;/p&gt;
&lt;p&gt;Blue-team content also includes ready-to-run SIEM queries for Splunk, Elastic, and Wazuh. Copy, paste, run. No translation needed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What I Learned&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Constraints make the product better. Offline-first, single-binary, no-build-step — these weren't compromises. They were features. Every one of them made the tool faster, simpler, and more usable in the environments where it matters.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ship small, ship often. The progression was:&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;v0.2 — command reference with placeholders&lt;/li&gt;
&lt;li&gt;v0.3 — scenarios + export&lt;/li&gt;
&lt;li&gt;v0.3.1 — favorites &amp;amp; recents&lt;/li&gt;
&lt;li&gt;v0.3.2 — filters &amp;amp; command palette&lt;/li&gt;
&lt;li&gt;v0.4 — attack lifecycle depth + blue-team content&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each release solved one thing well. Nobody had to wait six months for a &quot;big update.&quot;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Users tell you what to build next. The favorites feature came from watching how I used the tool myself. The command palette came from friends who said &quot;We want to jump to a command without leaving the keyboard.&quot; Real friction beats planned roadmaps.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Some things don't need to be fancy. embed.FS + net/http + Alpine.js. No ORM, no state management library, no container. Just three external Go dependencies and one vendored JS file. The simplicity is the point.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;What's Next&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Planned for v0.5:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Windows / Active Directory — native commands and AD-focused scenarios&lt;/li&gt;
&lt;li&gt;CLI companion — cyberref search &quot;kerb&quot; from the terminal, reusing the same data&lt;/li&gt;
&lt;li&gt;PowerShell export — for scenarios, alongside the current bash export&lt;/li&gt;
&lt;li&gt;Light theme — for bright environments&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Signed binaries — to remove the Windows SmartScreen warning&lt;br&gt;
&lt;strong&gt;&lt;br&gt;
Longer term:&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Community scenario contribution format&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;Documentation site&lt;/li&gt;
&lt;li&gt;Multi-platform binaries (macOS, Linux)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Try It&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Download: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/toyosee/cyberref-releases/releases&quot;&gt;https://github.com/toyosee/cyberref-releases/releases&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Run: Double-click cyberref.exe. Browser opens. That's the whole setup.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note on Windows SmartScreen&lt;/strong&gt;: You'll likely see a &quot;Windows protected your PC&quot; warning. This is normal for unsigned binaries. Click More info → Run anyway. Code signing is on the roadmap.&lt;br&gt;
Feedback Welcome&lt;/p&gt;
&lt;p&gt;If you're a practitioner — red, blue, or somewhere in between — I'd love to hear:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What commands or scenarios are missing that you'd use?&lt;/li&gt;
&lt;li&gt;What friction points does the tool not solve yet?&lt;/li&gt;
&lt;li&gt;What would make it part of your daily workflow?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Open an issue on the releases repo, or find me on LinkedIn. - &lt;a rel=&quot;nofollow&quot; href=&quot;https://linkedin.com/in/elijahabolaji&quot;&gt;https://linkedin.com/in/elijahabolaji&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;CyberRef is written in Go, uses Alpine.js for the frontend, and ships as a single binary for Windows with macOS and Linux builds planned. Source is maintained privately; binaries are released publicly.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/29754/building-cyberref-a-single-binary-offline-reference-for-cybersecurity-practitioners</guid>
<pubDate>Sun, 04 Oct 2026 19:02:33 +0000</pubDate>
</item>
<item>
<title>Show a single Windows application window in any web browser on LAN</title>
<link>https://coderlegion.com/29696/show-a-single-windows-application-window-in-any-web-browser-on-lan</link>
<description>&lt;p&gt;Ever needed to show one app window on a tablet without any remote-desktop software? &lt;/p&gt;
&lt;p&gt;I built WindowStream for exactly that, and Ive just open-sourced it. &lt;/p&gt;
&lt;p&gt;Its a small Windows tray app that streams a chosen window as MJPEG over HTTP on your local network. On the tablet or other PC you just open a browser: nothing to install, and its strictly view-only. &lt;/p&gt;
&lt;p&gt;What Im happy with: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Any modern browser works as the viewer (Android, iOS, another PC), with pinch-zoom, panning and fullscreen. &lt;/li&gt;
&lt;li&gt;Two capture modes: a screen copy, or PrintWindow, which keeps streaming the window even when other windows cover it. &lt;/li&gt;
&lt;li&gt;Light on resources: a frame is encoded only when the picture has changed, and JPEG encoding goes through libjpeg-turbo. &lt;/li&gt;
&lt;li&gt;Starts with Windows and finds the window again even if its title changes. &lt;/li&gt;
&lt;li&gt;English and Russian interface, a single-exe build, unit tests, and user manuals in three languages. Python, MIT licensed. &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One caveat: there is no password or encryption, so its meant for trusted networks only, and the docs say so clearly. &lt;/p&gt;
&lt;p&gt;Code, a release with the ready-made exe, and documentation: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/eugenyh/WindowStream&quot;&gt;https://github.com/eugenyh/WindowStream&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Feedback and ideas are very welcome!&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/29696/show-a-single-windows-application-window-in-any-web-browser-on-lan</guid>
<pubDate>Sun, 04 Oct 2026 05:51:45 +0000</pubDate>
</item>
<item>
<title>CodeSage: Code Intelligence for AI Agents, and Why I Rebuilt Its Models</title>
<link>https://coderlegion.com/29515/codesage-code-intelligence-for-ai-agents-and-why-i-rebuilt-its-models</link>
<description>&lt;p&gt;Until this week, &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/iliaal/codesage&quot;&gt;CodeSage&lt;/a&gt; ran its two models exactly as they ship on Hugging Face: Jina's code embedder and the ms-marco MiniLM reranker. A full index of php-src took 1,782 seconds, just under half an hour, and I got tired of waiting.&lt;/p&gt;
&lt;p&gt;CodeSage 0.38 does the same full index in 373 seconds. Nothing was retrained. The weights are the same weights; what changed is the inference graph around them, a few things in the chunker, and one SQLite query that had been quietly eating most of the run. The two rebuilt models are now public on Hugging Face, along with the scripts that produce them byte-for-byte.&lt;/p&gt;
&lt;h3 id=&quot;what-codesage-gives-an-agent&quot;&gt;What CodeSage gives an agent&lt;/h3&gt;
&lt;p&gt;CodeSage is a code intelligence engine for AI coding agents: a structural graph (symbols, references, dependencies) plus semantic search (embedding retrieval with cross-encoder reranking), in one Rust binary that works as a CLI or an MCP server. An agent asks it questions grep can't answer, like &quot;what depends on this class&quot; or &quot;where does session handling happen&quot;, and gets file-and-line answers from an index instead of guesses from a context window.&lt;/p&gt;
&lt;p&gt;Parsing is tree-sitter, covering PHP, Python, C, C++, Java, Rust, JavaScript, TypeScript and Go. Each project gets one SQLite database at &lt;code&gt;.codesage/index.db&lt;/code&gt;, with sqlite-vec for KNN and FTS5 for literal matches. The MCP side is a stdio shim that starts or reuses a per-user daemon over a Unix socket, so concurrent agent sessions share one project cache, one model pool and one CUDA context. There is no HTTP listener.&lt;/p&gt;
&lt;p&gt;The MCP server exposes 22 tools. The ones agents lean on most:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;find_symbol&lt;/code&gt;, &lt;code&gt;find_references&lt;/code&gt;, &lt;code&gt;trace_call_path&lt;/code&gt;: where something is defined, who calls it, and the shortest call chain between two symbols.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;impact_analysis&lt;/code&gt;, &lt;code&gt;assess_risk&lt;/code&gt;: what a change to this file can break, scored from the graph plus git history.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;search&lt;/code&gt;: natural-language retrieval over ~50-line chunks, reranked.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;from_trace&lt;/code&gt;: maps a pasted stack trace or ASan report (Python, PHP with Xdebug, Rust, Java, Go, Node, gdb) onto indexed symbols.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;edit_check&lt;/code&gt;: diffs a proposed declaration against HEAD before the agent writes the file.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The graph is only as useful as its recall. In August, &lt;code&gt;impact_analysis&lt;/code&gt; reported that &lt;code&gt;AxiosError&lt;/code&gt; in axios had 0 dependents. It has 23. File-path imports like &lt;code&gt;./core/AxiosError.js&lt;/code&gt; never resolved; only Rust-style &lt;code&gt;::&lt;/code&gt; paths did. Fixing that, and counting &lt;code&gt;Foo.staticMethod()&lt;/code&gt; and &lt;code&gt;x instanceof Foo&lt;/code&gt; as usage, took mean dependent recall on axios from 0.37 to 0.66 at precision 0.85. An agent told &quot;0 dependents&quot; by a tool it trusts goes ahead and changes the class.&lt;/p&gt;
&lt;h3 id=&quot;the-index-has-to-keep-up-with-the-code&quot;&gt;The index has to keep up with the code&lt;/h3&gt;
&lt;p&gt;An index that lags the code feeds the agent stale answers with full confidence, so CodeSage re-indexes from git hooks on every commit, checkout and merge. That puts the indexer's fixed cost on the hot path dozens of times a day, which is why a lot of the work across 46 releases since April has gone into cost rather than features.&lt;/p&gt;
&lt;p&gt;The per-commit path got there first. In 0.23.0, a no-change incremental run stopped loading the ONNX model and mapping a CUDA context before checking whether any file had changed:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;No-change incremental &lt;code&gt;codesage index&lt;/code&gt; &lt;/th&gt;&lt;th&gt;Time &lt;/th&gt;&lt;th&gt;Peak memory&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;0.22.0 &lt;/td&gt;&lt;td&gt;2.7-4.5 s &lt;/td&gt;&lt;td&gt;1.16 GB&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;0.23.0 &lt;/td&gt;&lt;td&gt;0.08 s &lt;/td&gt;&lt;td&gt;150 MB&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The file watcher re-embeds only chunks whose text changed: an 8-save burst marks 90 chunks dirty and embeds 1.&lt;/p&gt;
&lt;p&gt;That leaves the full index, which nobody runs on every commit but everybody runs eventually: first onboarding, a model change, a chunker change. 0.38 itself forces one, because the embedder now reads up to 1,024 tokens per chunk instead of 512. When the full run takes half an hour on a large repo, people postpone it, and a postponed re-index is a stale index.&lt;/p&gt;
&lt;h3 id=&quot;same-weights-rebuilt-graphs&quot;&gt;Same weights, rebuilt graphs&lt;/h3&gt;
&lt;p&gt;The embedder was slow because of how it was exported, not because of what it computes. The upstream Jina v2 ONNX file is a decomposed opset-11 export. Its attention uses ALiBi plus a QK LayerNorm, a combination ONNX Runtime's transformer optimizer doesn't recognize, so nothing fuses. Every layer materializes the full &lt;code&gt;[batch, 12, seq, seq]&lt;/code&gt; score tensor several times over, and because the graph outputs per-token hidden states, every batch copies a &lt;code&gt;[batch, seq, 768]&lt;/code&gt; tensor from the GPU back to the host just so the caller can average it.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://huggingface.co/IA0x00/jina-embeddings-v2-base-code-codesage&quot;&gt;IA0x00/jina-embeddings-v2-base-code-codesage&lt;/a&gt; carries two graphs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;onnx/model.onnx&lt;/code&gt; (fp32, every execution provider): masked mean pooling and L2 normalization folded into the graph, so the output is the 768-dimension sentence embedding and nothing else crosses back to the host.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;onnx/model_cuda_fp16.onnx&lt;/code&gt; (CUDA only): the same pooling, plus attention fused by hand into &lt;code&gt;com.microsoft&lt;/code&gt; &lt;code&gt;MultiHeadAttention&lt;/code&gt;, with ALiBi and the key-padding mask passed as &lt;code&gt;attention_bias&lt;/code&gt;. Weights are fp16; the output stays fp32.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One fp16 detail: the attention mask filled padded positions with &lt;code&gt;-FLT_MAX&lt;/code&gt;, which overflows fp16. It's &lt;code&gt;-10000.0&lt;/code&gt; now, which is still negative enough to zero those positions after softmax.&lt;/p&gt;
&lt;p&gt;Measured on an RTX 4080 Laptop GPU with ONNX Runtime 1.24.4, 2,048 real chunks per corpus of up to 512 tokens each, compared against upstream fp32 with pooling done on the host:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Corpus &lt;/th&gt;&lt;th&gt;Upstream fp32 &lt;/th&gt;&lt;th&gt;Pooled fp32 &lt;/th&gt;&lt;th&gt;Fused fp16 &lt;/th&gt;&lt;th&gt;Min cosine (fp16) &lt;/th&gt;&lt;th&gt;Top-10 neighbour overlap&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;php-src (C) &lt;/td&gt;&lt;td&gt;59.7 chunks/s &lt;/td&gt;&lt;td&gt;89 &lt;/td&gt;&lt;td&gt;249-257 &lt;/td&gt;&lt;td&gt;0.99999 &lt;/td&gt;&lt;td&gt;0.994&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Laravel app (PHP) &lt;/td&gt;&lt;td&gt;67.2 &lt;/td&gt;&lt;td&gt;113 &lt;/td&gt;&lt;td&gt;301-317 &lt;/td&gt;&lt;td&gt;0.999996 &lt;/td&gt;&lt;td&gt;0.996&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;React app (TS/JS) &lt;/td&gt;&lt;td&gt;69.8 &lt;/td&gt;&lt;td&gt;109 &lt;/td&gt;&lt;td&gt;296-317 &lt;/td&gt;&lt;td&gt;0.999995 &lt;/td&gt;&lt;td&gt;0.997&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;A minimum cosine of 0.99999 across 2,048 chunks means the vectors are, for retrieval purposes, the same vectors. Those throughput figures are at the 512-token cap; CodeSage now runs Jina at 1,024, because real C in php-src runs a median of 611 tokens per 1,500-byte chunk and the old cap truncated more than half of them. So the production per-chunk rate is lower than this table, and the chunks carry more of the code.&lt;/p&gt;
&lt;p&gt;The reranker needed far less. &lt;a rel=&quot;nofollow&quot; href=&quot;https://huggingface.co/IA0x00/ms-marco-MiniLM-L6-v2-codesage&quot;&gt;IA0x00/ms-marco-MiniLM-L6-v2-codesage&lt;/a&gt; is a standard BERT, so ONNX Runtime's stock optimizer fuses it (embedding LayerNorm, six Attention nodes, skip LayerNorm, bias GELU) and the work was mostly converting to fp16. Scoring 50 (query, chunk) candidates went from a median 108.0 ms per query to 20.6 ms, with the same top result on 40 of 40 test queries, top-10 overlap of 0.9975, and a maximum logit change of 0.024.&lt;/p&gt;
&lt;p&gt;Both repositories are Apache-2.0, the upstream license, and the weights are unchanged apart from precision. CodeSage pins each one by Hugging Face commit and by the sha256 of every graph and tokenizer it loads, and &lt;code&gt;scripts/derive-jina-onnx.py&lt;/code&gt; and &lt;code&gt;scripts/derive-reranker-onnx.py&lt;/code&gt; in the CodeSage repo download the pinned upstream files, verify them, and write the published files byte-for-byte. You don't have to trust my upload; you can rebuild it. Your &lt;code&gt;config.toml&lt;/code&gt; keeps the upstream model names, and the pin table maps them to the derived repos, so there is nothing to change on upgrade.&lt;/p&gt;
&lt;h3 id=&quot;the-biggest-single-win-wasn-t-a-model&quot;&gt;The biggest single win wasn't a model&lt;/h3&gt;
&lt;p&gt;Most of the half hour was SQLite. When a file changes, its old chunks are deleted first, and &lt;code&gt;file_path&lt;/code&gt; lived in an auxiliary column of the sqlite-vec table and an &lt;code&gt;UNINDEXED&lt;/code&gt; column of the FTS5 table. Neither can be searched by index, so &lt;code&gt;DELETE ... WHERE file_path = ?&lt;/code&gt; scanned both tables for every file: about one second per file at php-src's 54,000 rows, roughly 20 of the 28 minutes a full index took when I measured it.&lt;/p&gt;
&lt;p&gt;The fix is a small sidecar table, &lt;code&gt;&amp;lt;chunk_table&amp;gt;_paths(id, file_path)&lt;/code&gt;, kept in step on every insert and delete and rebuilt if it goes missing or diverges. Deletes now resolve the path to rowids and remove by rowid: 3.2 ms per file.&lt;/p&gt;
&lt;p&gt;Two chunker changes ship in the same release. Chunks that are mostly digits and commas (lookup tables, timezone and Unicode data) are no longer embedded, since no natural-language query is looking for them, and batches are length-sorted and byte-bounded so a batch of short chunks isn't padded out to the length of its longest member.&lt;/p&gt;
&lt;p&gt;Before and after, all of it together:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;&lt;code&gt;codesage index --full&lt;/code&gt; &lt;/th&gt;&lt;th&gt;Before &lt;/th&gt;&lt;th&gt;0.38&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;php-src &lt;/td&gt;&lt;td&gt;1,782 s &lt;/td&gt;&lt;td&gt;373 s&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;private mobile backend &lt;/td&gt;&lt;td&gt;469 s &lt;/td&gt;&lt;td&gt;80 s&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;private frontend app &lt;/td&gt;&lt;td&gt;295 s &lt;/td&gt;&lt;td&gt;60 s&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;That is the combined effect of the fused fp16 graph, the 1,024-token cap, the dropped data chunks, the batching, and the delete fix. I don't have a clean per-change split beyond the delete scan's own estimate, so I'm not going to invent one. Retrieval held: recall@10 is unchanged on three private eval corpora and rose from 0.765 to 0.790 on a php-src session corpus.&lt;/p&gt;
&lt;h3 id=&quot;scores-that-say-what-they-measured&quot;&gt;Scores that say what they measured&lt;/h3&gt;
&lt;p&gt;Fast answers are worthless if the agent can't tell a measurement from a default, so several releases since August went into making CodeSage say what it didn't check. &lt;code&gt;impact_analysis&lt;/code&gt; reports a lower bound, because dynamic calls and unresolved imports can hide real dependents. &lt;code&gt;assess_risk&lt;/code&gt; returns &lt;code&gt;unscored&lt;/code&gt; for a file with no indexed git history instead of a low number. &lt;code&gt;find_references&lt;/code&gt; marks a count as a floor when the symbol name is ambiguous. An empty search result carries indexed-file counts per language, so &quot;not found&quot; and &quot;never indexed&quot; stop looking identical.&lt;/p&gt;
&lt;p&gt;The same rule applies to my own benchmarks. In August I withdrew a published retrieval number (recall@10 0.932, NDCG@10 0.788 over 602 queries) after finding the harness had indexed whole repos instead of each benchmark's root and scored crashed queries as 0.000. The replacement table in the README runs 663 queries across 33 repos in 9 languages, reports per-language NDCG@10 from 0.7455 (C) to 0.9434 (JavaScript), and says it was measured on a modified 0.27.0 build. It hasn't been re-run on 0.38 yet.&lt;/p&gt;
&lt;h3 id=&quot;where-it-falls-short&quot;&gt;Where it falls short&lt;/h3&gt;
&lt;p&gt;These are the limits I know about, all of them in the README:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Nine languages. On the public benchmark, 47% of queries are in languages CodeSage doesn't parse (Ruby, Kotlin, Swift, Scala) and get no recall at all.&lt;/li&gt;
&lt;li&gt;Agents don't reach for it on their own. With CodeSage and &lt;code&gt;Grep&lt;/code&gt; both available in Claude Code, agents picked CodeSage on code-identifier queries 1.1% of the time over 30 days of sessions, and 0 of 10 on a controlled harness (measured in April, not re-measured since). That is why the &lt;code&gt;codesage-tools&lt;/code&gt; Claude Code plugin serves a &lt;code&gt;codesage brief&lt;/code&gt; inline before an Edit or Write in an onboarded project instead of waiting to be asked. It stays silent when there is nothing worth saying.&lt;/li&gt;
&lt;li&gt;Every speed number in this post is CUDA. CPU and Apple's CoreML get the pooled fp32 graph but not the fused fp16 one, and I haven't measured the CPU side.&lt;/li&gt;
&lt;li&gt;No prebuilt binaries and no documented Windows support. You build it with cargo.&lt;/li&gt;
&lt;li&gt;Read-only. It doesn't edit code and doesn't query across repositories.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;try-it&quot;&gt;Try it&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;cargo build --release -p codesage --features cuda   # drop --features cuda for CPU / CoreML
cd /path/to/your/project
codesage init
codesage index
codesage install-hooks
claude mcp add --scope user codesage -- codesage mcp
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Codex users run &lt;code&gt;codesage install codex --global&lt;/code&gt; instead of the last line. If you're on Claude Code, &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/iliaal/whetstone&quot;&gt;whetstone&lt;/a&gt; is the other half: CodeSage tells the agent what the code is, whetstone covers how to investigate, review and ship. The first index downloads the models: about 370 MB on CUDA, about 736 MB for the fp32 graphs on CPU or CoreML.&lt;/p&gt;
&lt;p&gt;Nothing about CodeSage 0.38 required a better model. It required looking at what the existing one did between input and output, and at what the database did on every file delete. Most of that half hour was overhead around the model, not the model itself.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/iliaal/codesage&quot;&gt;github.com/iliaal/codesage&lt;/a&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/29515/codesage-code-intelligence-for-ai-agents-and-why-i-rebuilt-its-models</guid>
<pubDate>Fri, 02 Oct 2026 11:25:42 +0000</pubDate>
</item>
<item>
<title>Miraiclip: an open-source engine for building video editors in the browser</title>
<link>https://coderlegion.com/29060/miraiclip-an-open-source-engine-for-building-video-editors-in-the-browser</link>
<description>&lt;hr&gt;
&lt;p&gt;title: &quot;Miraiclip: an open-source engine for building video editors in the browser&quot;&lt;br&gt;
published: true&lt;br&gt;
description: &quot;A framework-agnostic TypeScript library where every edit is a command — undo/redo, collaboration, and AI control fall out of one design decision.&quot;&lt;/p&gt;
&lt;h3 id=&quot;tags-webdev-typescript-opensource-showdev&quot;&gt;tags: webdev, typescript, opensource, showdev&lt;/h3&gt;
&lt;p&gt;I've been building &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/comaniacs/miraiclip&quot;&gt;Miraiclip&lt;/a&gt;&lt;/strong&gt; — an open-source, framework-agnostic library for building video editors in the browser — and it's now on npm under MIT. This post covers what it is, the one design decision everything hangs on, and a few hard-won lessons from making video actually work in a browser.&lt;/p&gt;
&lt;h3 id=&quot;the-problem&quot;&gt;The problem&lt;/h3&gt;
&lt;p&gt;Every browser video editor ends up rebuilding the same difficult parts: frame-accurate playback, an undo/redo system that never corrupts state, and exports that match what the preview showed. These are engine problems, not product problems — but there's no engine to reach for, so every team pays for them again.&lt;/p&gt;
&lt;p&gt;Miraiclip is that engine. It is deliberately &lt;strong&gt;not&lt;/strong&gt; an editor app. There's no timeline UI, no buttons — the core is headless: project state, a microsecond-precision timeline, and a full command history, with rendering delivered as a separate layer. You bring the UI (React, Vue, Svelte, vanilla — the core has zero UI dependencies).&lt;/p&gt;
&lt;h3 id=&quot;one-design-decision-everything-is-a-command&quot;&gt;One design decision: everything is a command&lt;/h3&gt;
&lt;p&gt;Instead of calling imperative methods that mutate state, you dispatch descriptive commands. Every command is deterministic, validated against a schema, serializable, and invertible.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import { createProject } from &quot;@miraiclip/core&quot;;

const project = createProject({ width: 1920, height: 1080, fps: 30 });

project.dispatch({
  type: &quot;asset/add&quot;,
  payload: { id: &quot;intro&quot;, kind: &quot;video&quot;, src: &quot;/media/intro.mp4&quot;, durationUs: 12_000_000 },
});
project.dispatch({ type: &quot;track/add&quot;, payload: { id: &quot;video-1&quot;, kind: &quot;video&quot; } });
project.dispatch({
  type: &quot;clip/add&quot;,
  payload: {
    kind: &quot;video&quot;, id: &quot;clip-1&quot;, trackId: &quot;video-1&quot;, assetId: &quot;intro&quot;,
    startUs: 0,            // timeline position in microseconds
    durationUs: 5_000_000, // 5 seconds
  },
});

// Batch commands into one undoable transaction
project.transaction(() =&amp;gt; {
  project.dispatch({ type: &quot;clip/split&quot;, payload: { clipId: &quot;clip-1&quot;, atUs: 2_000_000, newClipId: &quot;clip-1b&quot; } });
  project.dispatch({ type: &quot;clip/move&quot;, payload: { clipId: &quot;clip-1&quot;, startUs: 1_000_000 } });
});

project.undo();
project.redo();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Three things fall out of this decision without being bolted on:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Undo/redo&lt;/strong&gt; — commands are invertible, so history is exact, replayable, and inspectable.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Collaboration&lt;/strong&gt; — every state change is emitted as granular RFC-6902 JSON patches. Command and patch streams are the substrate for multiplayer editing:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;project.events.on(&quot;patches&quot;, ({ patches }) =&amp;gt; {
  // [{ op: &quot;replace&quot;, path: &quot;/clips/clip-1/startUs&quot;, value: 1000000 }]
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;AI control&lt;/strong&gt; — commands are plain descriptive data with a published catalog and JSON schemas. An LLM can read project state and generate valid edit sequences; the command catalog doubles as a tool definition. A human clicking a UI, an LLM planning an edit, and a sync layer replaying a collaborator's changes all speak the same language.&lt;/p&gt;
&lt;h3 id=&quot;playback-and-export&quot;&gt;Playback and export&lt;/h3&gt;
&lt;p&gt;The rendering layer (&lt;code&gt;@miraiclip/renderer&lt;/code&gt;) is a WebCodecs decode pipeline plus a WebGL compositor (PixiJS), with an audio-master clock:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import {
  createPixiBackend, createPlayer, exportProject,
  openMediabunnyDemuxer, createWebCodecsDecoderFactory,
  createWebAudioOutput, openMediabunnyAudio,
} from &quot;@miraiclip/renderer&quot;;

const backend = await createPixiBackend({ canvas, width: 1280, height: 720 });
const player = createPlayer(project, {
  backend,
  openDemuxer: openMediabunnyDemuxer,
  createDecoder: createWebCodecsDecoderFactory({ maxOutputDimensionPx: 1920 }), // proxy preview
  audioOutput: createWebAudioOutput(),
  openAudio: openMediabunnyAudio,
});
player.play();

// Offline export, faster than realtime — same compositor as the preview
const bytes = await exportProject(project, { format: &quot;mp4&quot;, quality: &quot;high&quot; });
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The export renders through the &lt;strong&gt;same compositor&lt;/strong&gt; as the preview, so preview/export parity holds by construction rather than by testing luck. Output goes to MP4 or WebM, and it can stream straight into a file (&lt;code&gt;showSaveFilePicker&lt;/code&gt; writable) so long exports don't accumulate in memory. There's also &lt;code&gt;@miraiclip/server-export&lt;/code&gt;, which runs the identical pipeline in headless Chrome from Node:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;miraiclip-export project.json --out final.mp4 --quality high
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Keyframe animations (cubic-bézier easings), effects (color adjust, blur, chroma key), transitions with equal-power audio crossfades, and karaoke-style captions with SRT/VTT and ASR word-timestamp import are built in — and they render identically in preview, browser export, and server export, because it's one compositor everywhere.&lt;/p&gt;
&lt;h3 id=&quot;things-the-browser-made-me-learn&quot;&gt;Things the browser made me learn&lt;/h3&gt;
&lt;p&gt;A few lessons that shaped the engine, in case you're building anything media-heavy:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&quot;Frame-accurate&quot; means presented frames, not scheduled ones.&lt;/strong&gt; Callbacks tell you a frame was &lt;em&gt;scheduled&lt;/em&gt;; only reading pixels back tells you what the user actually saw. Miraiclip's test fixtures encode each frame's index as its color, so tests assert on what's really on the canvas — that's how several seek and playback bugs were caught that callback-based tests would have blessed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Export memory must be independent of timeline length.&lt;/strong&gt; Mixing an hour of audio in one &lt;code&gt;OfflineAudioContext&lt;/code&gt; buffer costs ~1.4 GB; encoding into an in-memory buffer costs the whole file. Miraiclip mixes audio in bounded sequential chunks interleaved with the frame walk and streams encoded output to disk — a demonstrated one-hour export (86,400 frames plus a full hour of audio) peaked at 165 MB of JS heap, running at 1.6× realtime on a dev laptop.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Trust nothing you can't verify with a second decoder.&lt;/strong&gt; Exports are validated by a corpus that re-decodes every file with ffmpeg — an independent decoder — checking every frame's timestamp and pixels, audio gain, and A/V sync (measured offset: 0 ms at both ends of the file). A muxer bug can't hide by round-tripping through the encoder that produced it.&lt;/p&gt;
&lt;h3 id=&quot;status&quot;&gt;Status&lt;/h3&gt;
&lt;p&gt;Miraiclip is pre-1.0: the core model is stable in shape, but expect API changes between minor versions. Current packages: &lt;code&gt;@miraiclip/core&lt;/code&gt;, &lt;code&gt;@miraiclip/renderer&lt;/code&gt;, and &lt;code&gt;@miraiclip/server-export&lt;/code&gt;. Chromium-family browsers are the target for rendering and export (WebCodecs-based); the core runs anywhere JS runs, including Node.&lt;/p&gt;
&lt;p&gt;If you're building anything with video in the browser — an editor, a clip tool, automated video generation — I'd genuinely value your eyes on the API before it hardens.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/comaniacs/miraiclip&quot;&gt;github.com/comaniacs/miraiclip&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Docs + live examples&lt;/strong&gt; (the code shown is the code that runs): &lt;a rel=&quot;nofollow&quot; href=&quot;https://comaniacs.github.io/miraiclip/&quot;&gt;comaniacs.github.io/miraiclip&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;npm:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.npmjs.com/package/@miraiclip/core&quot;&gt;&lt;code&gt;@miraiclip/core&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Questions and issues welcome — and if you try it, I'd love to hear what you build.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/29060/miraiclip-an-open-source-engine-for-building-video-editors-in-the-browser</guid>
<pubDate>Tue, 29 Sep 2026 12:28:18 +0000</pubDate>
</item>
<item>
<title>Guys I need your feedback for logo</title>
<link>https://coderlegion.com/29045/guys-i-need-your-feedback-for-logo</link>
<description>&lt;p&gt;Hey guys ;)&lt;br&gt;
This post is a bit unusual, but I really need your help and opinion. So right now I’m working on a new logo for Aster. And I really need to know is it good? And if it isn’t, then what’s wrong with it? I'll be glad to hear from all of you, thanks.&lt;br&gt;
Here it is&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;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%2Farticles%2Fo28il51h0e1q7xh0p1eh.jpg&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;More about idea of logo and other I'll tell in next post. So, yep bye bye and thanks for all of you.&lt;br&gt;
Oops, here is second version of logo. So, which is better?&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=12372411446401770292&quot; alt=&quot;Version 2&quot;&gt;&lt;/p&gt;
</description>
<category>Discussions</category>
<guid isPermaLink="true">https://coderlegion.com/29045/guys-i-need-your-feedback-for-logo</guid>
<pubDate>Tue, 29 Sep 2026 11:45:22 +0000</pubDate>
</item>
<item>
<title>I built a Pix library for Elixir (and one cent proved it works)</title>
<link>https://coderlegion.com/28983/i-built-a-pix-library-for-elixir-and-one-cent-proved-it-works</link>
<description>&lt;p&gt;Over the last few months I became a bit of a compulsive open source contributor. Translating Linux kernel documentation into Portuguese, a merged PR in &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/uutils/coreutils&quot;&gt;uutils/coreutils&lt;/a&gt;, a few fixes in Elixir libraries I had used before. Always the same routine: find a problem in someone else's project, understand it, fix it, wait for review.&lt;/p&gt;
&lt;p&gt;Then a different itch showed up: instead of fixing other people's libraries, build my own.&lt;/p&gt;
&lt;p&gt;This post is about that. How I built &lt;code&gt;pix_brcode&lt;/code&gt;, a Pix library for Elixir, over a weekend, tested it with real money (R$ 0.01, don't judge me) and published it on Hex. And about the bug that only showed up once a real bank got involved.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/p85i9j3exahlfhcidnfb.png&quot; alt=&quot;Github print&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;what-is-pix-and-why-a-library-for-it&quot;&gt;What is Pix, and why a library for it&lt;/h3&gt;
&lt;p&gt;If you're not from Brazil, you've probably never heard of Pix. It's the instant payment system created by Brazil's Central Bank in 2020, and it took over the country: it's free for individuals, settles in seconds, works 24/7, and today it's how Brazilians pay for everything from coffee to rent.&lt;/p&gt;
&lt;p&gt;Most Pix payments start with a QR code, or with its text version, the &lt;em&gt;&quot;copia e cola&quot;&lt;/em&gt; (copy and paste) code: a long string starting with &lt;code&gt;000201&lt;/code&gt; that you paste into your bank app. Everyone uses it every day, and almost nobody knows what's inside it.&lt;/p&gt;
&lt;p&gt;In the JavaScript world this is a solved problem: &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.npmjs.com/package/pix-utils&quot;&gt;&lt;code&gt;pix-utils&lt;/code&gt;&lt;/a&gt; on npm generates and parses Pix codes and gets over 60k downloads a month. I went looking for the equivalent on &lt;a rel=&quot;nofollow&quot; href=&quot;https://hex.pm&quot;&gt;Hex&lt;/a&gt;, Elixir's package manager, and couldn't find a maintained one.&lt;/p&gt;
&lt;p&gt;So I put the two things together: a big, mature library to use as a reference, and an empty spot in the ecosystem I wanted to fill. I decided to build a &quot;pix-utils&quot; for Hex, or, as I preferred to call it, &lt;code&gt;pix_brcode&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;what-s-inside-a-pix-code&quot;&gt;What's inside a Pix code&lt;/h3&gt;
&lt;p&gt;That string is a &lt;strong&gt;BR Code&lt;/strong&gt;: an international payment QR code standard (EMVCo's QR Code Specification, Merchant-Presented Mode) with Pix-specific fields.&lt;/p&gt;
&lt;p&gt;The structure is simpler than it looks. Everything is &lt;strong&gt;TLV&lt;/strong&gt;: a 2-digit ID, a 2-digit length, then the value.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;00 02 01                       → payload format: &quot;01&quot;
26 58 0014br.gov.bcb.pix0136…  → Pix account (with subfields inside!)
52 04 0000                     → merchant category
53 03 986                      → currency: Brazilian real
54 04 0.01                     → amount
58 02 BR                       → country
59 04 Igor                     → receiver name
60 09 Sao Paulo                → city
62 07 0503***                  → txid (transaction id)
63 04 F6CE                     → CRC16
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Look at field &lt;code&gt;26&lt;/code&gt;: its value is &lt;strong&gt;another TLV&lt;/strong&gt;, holding the Pix identifier (&lt;code&gt;br.gov.bcb.pix&lt;/code&gt;) and the key. TLV inside TLV.&lt;/p&gt;
&lt;p&gt;And the last field, &lt;code&gt;63&lt;/code&gt;, is a &lt;strong&gt;CRC16&lt;/strong&gt;: a checksum over everything before it. If someone copies the code missing one character, the CRC doesn't match and the bank app rejects it.&lt;/p&gt;
&lt;p&gt;(Important side note: a CRC is not security. It catches accidental errors, not fraud. Anyone can recompute it.)&lt;/p&gt;
&lt;h3 id=&quot;the-decisions-that-made-the-weekend-worth-it&quot;&gt;The decisions that made the weekend worth it&lt;/h3&gt;
&lt;h4 id=&quot;zero-dependencies&quot;&gt;Zero dependencies&lt;/h4&gt;
&lt;p&gt;Installing &lt;code&gt;pix_brcode&lt;/code&gt; pulls in nothing else. That had a cost: Erlang only ships CRC32, so the CRC16 was written by hand in about 20 lines, using Elixir's binary pattern matching:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-elixir&quot;&gt;defp crc(&amp;lt;&amp;lt;byte, rest::binary&amp;gt;&amp;gt;, acc) do
  acc = Enum.reduce(1..8, bxor(acc, byte &amp;lt;&amp;lt;&amp;lt; 8), fn _, acc -&amp;gt; shift(acc &amp;lt;&amp;lt;&amp;lt; 1) end)
  crc(rest, acc)
end

defp crc(&amp;lt;&amp;lt;&amp;gt;&amp;gt;, acc), do: acc
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Adding a dependency for 20 lines would be trading a small problem for a permanent one.&lt;/p&gt;
&lt;h4 id=&quot;money-is-never-a-float&quot;&gt;Money is never a float&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;pix-utils&lt;/code&gt; takes the amount as a JavaScript &lt;code&gt;number&lt;/code&gt;, which is a float. And floats with money are how you end up seeing &lt;code&gt;0.1 + 0.2 = 0.30000000000000004&lt;/code&gt; on someone's bill.&lt;/p&gt;
&lt;p&gt;In &lt;code&gt;pix_brcode&lt;/code&gt;, the amount is either an &lt;strong&gt;integer in cents&lt;/strong&gt; (&lt;code&gt;1050&lt;/code&gt; becomes &lt;code&gt;&quot;10.50&quot;&lt;/code&gt;) or an exact string (&lt;code&gt;&quot;10.50&quot;&lt;/code&gt;). The conversion is done on text, with no division at all.&lt;/p&gt;
&lt;h4 id=&quot;strict-in-what-you-send-liberal-in-what-you-accept&quot;&gt;Strict in what you send, liberal in what you accept&lt;/h4&gt;
&lt;p&gt;When generating, the library is strict: a name longer than 25 characters is an error instead of being silently truncated (imagine the payer's bank app showing the wrong name). When parsing, it accepts whitespace and line breaks around the code, because real copy-and-paste comes dirty from WhatsApp.&lt;/p&gt;
&lt;p&gt;That principle has a name, Postel's law. And it was about to be tested sooner than I expected.&lt;/p&gt;
&lt;h3 id=&quot;the-moment-of-truth-real-money&quot;&gt;The moment of truth: real money&lt;/h3&gt;
&lt;p&gt;Tests passing, Credo clean, docs generated. Only one test left that really matters: &lt;strong&gt;a bank app accepting the code.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The first try failed in all three banks I tested. Itaú, Nubank and Mercado Pago: &quot;Invalid QR Code&quot;.&lt;/p&gt;
&lt;p&gt;For a minute I thought the weekend was wasted. It wasn't: I had used a made-up Pix key. When you paste a static Pix code, the app looks the key up in the Central Bank's directory. Unknown key, rejected code. The test was wrong, not the library.&lt;/p&gt;
&lt;p&gt;I generated it again with my real Itaú random key and an amount of one cent:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-elixir&quot;&gt;PixBrcode.encode(%{key: &quot;my-real-key&quot;, amount: 1,
                   merchant_name: &quot;Igor&quot;, merchant_city: &quot;Sao Paulo&quot;})
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pasted it into Nubank. My name showed up, R$ 0.01. Paid. It landed in my Itaú account instantly.&lt;/p&gt;
&lt;p&gt;The best-invested cent of my life.&lt;/p&gt;
&lt;h3 id=&quot;the-bug-no-test-caught&quot;&gt;The bug no test caught&lt;/h3&gt;
&lt;p&gt;The reverse path was still missing: generate a code &lt;strong&gt;in the bank app&lt;/strong&gt; and parse it with the library. Pix &amp;gt; Receive on Nubank, copy, paste into the terminal:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-elixir&quot;&gt;iex&amp;gt; PixBrcode.decode(&quot;00020126580014BR.GOV.BCB.PIX0136…&quot;)
{:error, :invalid_gui}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;BR.GOV.BCB.PIX&lt;/code&gt;. In uppercase.&lt;/p&gt;
&lt;p&gt;The &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.bcb.gov.br/estabilidadefinanceira/pix&quot;&gt;Central Bank's manual&lt;/a&gt; writes &lt;code&gt;br.gov.bcb.pix&lt;/code&gt; in lowercase, and the library compared the exact text. Nubank sends it uppercase. So does Itaú. All my tests passed because they all followed the manual to the letter, and the banks don't.&lt;/p&gt;
&lt;p&gt;The fix was one line: compare case-insensitively when parsing, and keep generating lowercase, as the manual says. Postel again: strict in what you send, liberal in what you accept.&lt;/p&gt;
&lt;p&gt;The real codes showed other differences too: Nubank includes the postal code (field &lt;code&gt;61&lt;/code&gt;) and puts part of the account holder's tax ID in the receiver name; Itaú doesn't. The library's tests now use payloads with the same layout as the real ones, but with fake data. (Your partial tax ID in a public repo is not a great idea, and I learned that before making the mistake.)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The lesson:&lt;/strong&gt; the spec is the minimum. Real systems have variations that only show up with real data.&lt;/p&gt;
&lt;h3 id=&quot;a-second-opinion-before-publishing&quot;&gt;A second opinion before publishing&lt;/h3&gt;
&lt;p&gt;Before pushing to Hex, I had an AI agent do an independent code review: a fresh session, with no context from the development, reading everything from scratch. Nothing blocked the release, but it came back with four good findings:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the amount field didn't enforce the spec's 13-character limit;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;decode(nil)&lt;/code&gt; raised an exception, while the README promised always &lt;code&gt;{:ok, _}&lt;/code&gt; or &lt;code&gt;{:error, _}&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;text with invalid bytes also crashed the library;&lt;/li&gt;
&lt;li&gt;emoji in the name got through, and since each emoji takes several bytes, the length declared in the TLV was wrong for other parsers.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All four were fixed before v0.1.0. Name, city and description must now be ASCII after removing accents (&lt;code&gt;São João&lt;/code&gt; becomes &lt;code&gt;Sao Joao&lt;/code&gt;, and ☕ is an error).&lt;/p&gt;
&lt;h3 id=&quot;what-s-next&quot;&gt;What's next&lt;/h3&gt;
&lt;p&gt;v0.1.0 generates and parses static and dynamic Pix codes, validates keys (tax IDs, phone, e-mail, random key) and runs CI on three Elixir versions. Next on the list: typespecs, more tolerant key parsing and a CHANGELOG. And there's another idea in the queue: Brazilian &lt;em&gt;boleto&lt;/em&gt; bank slips, which also have no package on Hex.&lt;/p&gt;
&lt;p&gt;If you work with Elixir and Pix, try the library and tell me what breaks. Issues and PRs are very welcome:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Hex: &lt;a rel=&quot;nofollow&quot; href=&quot;https://hex.pm/packages/pix_brcode&quot;&gt;hex.pm/packages/pix_brcode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Docs: &lt;a rel=&quot;nofollow&quot; href=&quot;https://hexdocs.pm/pix_brcode&quot;&gt;hexdocs.pm/pix_brcode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/igorgbr/pix_brcode&quot;&gt;github.com/igorgbr/pix_brcode&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Fixing other people's libraries taught me to read code. Building my own taught me that the manual is only the start of the conversation.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This post was originally published in Portuguese on &lt;a rel=&quot;nofollow&quot; href=&quot;https://coisadedevacademy.com.br/biblioteca-pix-elixir-pix-brcode/&quot;&gt;Coisa de Dev&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/28983/i-built-a-pix-library-for-elixir-and-one-cent-proved-it-works</guid>
<pubDate>Tue, 29 Sep 2026 00:34:58 +0000</pubDate>
</item>
<item>
<title>Forensic Aviation Analytics: Auditing Fuel Bleeds &amp; Delay Cascades on a Regional A320neo Network</title>
<link>https://coderlegion.com/28910/forensic-aviation-analytics-auditing-fuel-bleeds-delay-cascades-regional-a320neo-network</link>
<description>&lt;p&gt;When evaluating airline performance, typical analytics dashboards rely blindly on passenger-facing &quot;On-Time&quot; arrival percentages. However, from an operational and financial engineering standpoint, these high-level metrics can easily mask millions of dollars in silent cash destruction. &lt;/p&gt;
&lt;p&gt;Due to strategic schedule padding, a short-haul flight can sit idling its engines on a congested tarmac for 20 minutes, yet still be registered as &quot;On-Time&quot; to the customer. The passenger is happy, but the airline’s finance department just watched pure margin vaporize into thin air.&lt;/p&gt;
&lt;p&gt;To expose these hidden friction layers, I engineered LCC-Ops: an end-to-end data warehouse auditing over 120,000 regional transaction legs modeled around the single-aisle network performance of flynas across key GCC hubs.&lt;/p&gt;
&lt;p&gt;Here is the technical architecture and the forensic database methodologies behind the pipeline.&lt;/p&gt;
&lt;h3 id=&quot;the-tech-stack-amp-architecture&quot;&gt;The Tech Stack &amp;amp; Architecture&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Database Layer:&lt;/strong&gt; MySQL Server 8.0 (Advanced CTEs, Mathematical Window Functions, Schema Migrations)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Business Intelligence:&lt;/strong&gt; Power BI Desktop (Data Type Optimization, Cross-Table Iterators, UI Design)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Core Fleet Constants:&lt;/strong&gt; Engineered around the Airbus A320neo (CFM LEAP-1A26 engine) utilizing official ICAO Engine Emissions Databank metrics. At a standard 7% ground idle taxi thrust, this engine burns exactly 0.0940 kg of fuel per second, per engine&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;the-database-engineering-breakdown&quot;&gt;The Database Engineering Breakdown&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Pillar 1&lt;/strong&gt;: Isolating Tarmac Cash Destruction (Domain A)To find the true cost of airport ground bottlenecks, the ingestion engine isolates rows inside the transactions table (fact_flights) where the Out-Gate to Runway Takeoff latency (taxi_out) exceeded a standard 4-minute engine stabilization and warming buffer.&lt;/p&gt;
&lt;p&gt;By pulling the real-world ICAO fuel coefficients down via relational keys, the calculation bypasses text-based padding and computes raw physical fuel destruction.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;SELECT 
    f.origin AS departure_hub,
    COUNT(f.tail_number) AS total_monitored_flights,
    ROUND(AVG(f.taxi_out), 2) AS avg_taxi_out_minutes,
    -- Formula: (Actual Taxi Time - 4 Min Buffer) * 60 Sec * ICAO Idle Flow Rate * 2 Engines
    ROUND(SUM((f.taxi_out - 4) * 60 * e.fuel_flow_idle_kg_sec * 2), 2) AS total_wasted_fuel_kg
FROM fact_flights f
JOIN dim_aircraft a ON f.tail_number = a.tail_number
JOIN dim_engine e ON a.engine_identification = e.engine_identification
WHERE f.reporting_airline = 'XY' AND f.taxi_out &amp;gt; 4
GROUP f.origin;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Pillar 2: Tracking Cross-Border Delay Cascades (Domain D)&lt;/strong&gt;&lt;br&gt;
When an international flight arriving from an airport like Karachi (KHI) experiences customs or airspace delays, that latency risks cascading into the aircraft’s subsequent domestic legs (e.g., RUH ➔ JED).&lt;/p&gt;
&lt;p&gt;To track this chronologically across individual daily airframe lifecycles, I utilized advanced LEAD() mathematical window functions&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;WITH ranked_daily_schedule AS (
    SELECT 
        flight_date, tail_number, origin, dest, carrier_delay AS leg_1_delay,
        LEAD(origin, 1) OVER (PARTITION BY tail_number, flight_date ORDER BY taxi_out DESC) AS leg_2_origin,
        LEAD(carrier_delay, 1) OVER (PARTITION BY tail_number, flight_date ORDER BY taxi_out DESC) AS leg_2_delay
    FROM fact_flights
    WHERE reporting_airline = 'XY'
)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Pillar 3: Menu Logistics &amp;amp; Hangar Risk Automation (Domains B &amp;amp; C)&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;Catering Spoilage Optimization:&lt;/em&gt; Short flight windows on regional tracks mean cabin crews lack the time to complete complex perishable sales loops. The data models validate flynas’s lean, shelf-stable buy-on-board menu model, proving that switching to fresh profiles would trigger an immediate &lt;strong&gt;54% inventory spoilage cash bleed&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt; &lt;em&gt;Automated Hangar Reorder Points (ROP):&lt;/em&gt; The warehouse tracks rolling airframe hours against specific tail numbers (HZ-NS1 to HZ-NS5) [Query-relevant Context]. If a component approaches its high-wear safety limit, the system triggers automated database alerts to protect the airline against catastrophic &lt;strong&gt;$25,000/day Aircraft on Ground (AOG) hangar penalties&lt;/strong&gt;&lt;/p&gt;
&lt;h3 id=&quot;ui-engineering-in-power-bi&quot;&gt;UI Engineering in Power BI&lt;/h3&gt;
&lt;p&gt;To present these data layers to corporate stakeholders, I built an interactive application-style interface directly inside Power BI using flynas’s signature branding tones.&lt;/p&gt;
&lt;p&gt;The biggest challenge during BI ingestion was data type conversions—empty strings or trailing spaces in the raw logs caused math functions to drop blank values (--). By implementing upfront string transformations (Uppercase/Trim) and embedding data validation wrappers like ISNUMBER(VALUE()) directly into row-level DAX iterators, the front-end dynamically scales across massive transaction volumes without data loss.&lt;/p&gt;
&lt;p&gt;The entire production code schema and scripts are live on my GitHub &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/hafsamotiwala/flynas-fleet-telemetry-analytics&quot;&gt;Link&lt;/a&gt;. I'd love to hear how other data engineers manage real-time fuel-burn parameters or multi-leg network routing in your pipelines!&lt;/p&gt;
</description>
<category>Discussions</category>
<guid isPermaLink="true">https://coderlegion.com/28910/forensic-aviation-analytics-auditing-fuel-bleeds-delay-cascades-regional-a320neo-network</guid>
<pubDate>Mon, 28 Sep 2026 11:41:49 +0000</pubDate>
</item>
<item>
<title>Marshal: Teaching an AI Copilot to Run a World Cup Stadium — Built with Google Antigravity</title>
<link>https://coderlegion.com/28636/marshal-teaching-an-ai-copilot-to-run-a-world-cup-stadium-built-with-google-antigravity</link>
<description>&lt;h3 id=&quot;the-problem-1&quot;&gt;The Problem&lt;/h3&gt;
&lt;p&gt;PromptWars Challenge 4 asked for a GenAI solution touching stadium &lt;br&gt;
navigation, crowd management, accessibility, transportation, &lt;br&gt;
sustainability, multilingual support, and operational intelligence &lt;br&gt;
during FIFA World Cup 2026 — for fans, organizers, volunteers, or &lt;br&gt;
venue staff.&lt;/p&gt;
&lt;p&gt;That's seven verticals in one sentence. Trying to solve all of them &lt;br&gt;
is how you end up with a shallow app that does nothing well.&lt;/p&gt;
&lt;h3 id=&quot;why-i-rejected-the-obvious-path&quot;&gt;Why I Rejected the Obvious Path&lt;/h3&gt;
&lt;p&gt;Most teams in this challenge will build a fan-facing app — a chatbot &lt;br&gt;
that helps you find your seat, or a navigation assistant. It's the &lt;br&gt;
first idea everyone has, which means the leaderboard will be full of &lt;br&gt;
near-identical submissions competing on polish alone.&lt;/p&gt;
&lt;p&gt;I went the other way: &lt;strong&gt;build for stadium staff, not fans.&lt;/strong&gt; A World &lt;br&gt;
Cup venue runs on volunteers and ops staff making fast decisions with &lt;br&gt;
incomplete information — where's the bottleneck, who do we move, in &lt;br&gt;
what language do we tell them. That's a harder, less obvious, and &lt;br&gt;
much less saturated problem. I named it &lt;strong&gt;Marshal&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Marshal combines three of the seven verticals — crowd management, &lt;br&gt;
real-time decision support, and multilingual support — and &lt;br&gt;
deliberately ignores the other four (navigation, accessibility, &lt;br&gt;
transportation, sustainability) for this build. Depth over breadth.&lt;/p&gt;
&lt;h3 id=&quot;architecture&quot;&gt;Architecture&lt;/h3&gt;
&lt;p&gt;Marshal runs as a 4-agent pipeline:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Signal Ingestor&lt;/strong&gt; — reads live gate density, transit delays, and &lt;br&gt;
incident feeds (simulated for this build)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Situation Analyzer&lt;/strong&gt; — detects anomalies and classifies severity &lt;br&gt;
(LOW/MEDIUM/HIGH) based on how fast a gate is approaching capacity&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Action Recommender&lt;/strong&gt; — calls a tool, &lt;code&gt;checkAlternateGateCapacity()&lt;/code&gt;, &lt;br&gt;
that checks the real-time density of every other gate and picks the &lt;br&gt;
least-congested one, then drafts a concrete staff action&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Multilingual Notifier&lt;/strong&gt; — generates real alert text in English, &lt;br&gt;
Spanish, French, and Hindi for staff, plus a simplified version for &lt;br&gt;
fan-facing signage&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The pipeline is visualized live in the UI — each stage lights up as &lt;br&gt;
it runs, and a reasoning console streams the AI's decision-making in &lt;br&gt;
real time, so a viewer can watch the logic unfold instead of just &lt;br&gt;
seeing a final answer appear.&lt;/p&gt;
&lt;h3 id=&quot;my-build-process-with-antigravity&quot;&gt;My Build Process with Antigravity&lt;/h3&gt;
&lt;p&gt;I built this in 8 sequential prompts rather than one giant spec — &lt;br&gt;
smaller, focused prompts held Antigravity's output closer to what I &lt;br&gt;
actually wanted at each stage.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Prompt 1&lt;/strong&gt; scaffolded the 3-panel dashboard and simulated gate-density &lt;br&gt;
generator. This came out clean on the first pass — proper HTML/CSS/JS &lt;br&gt;
separation, a distinctive dark control-room visual theme (glassmorphism, &lt;br&gt;
scanline overlay, amber/cyan glow accents) instead of a generic template.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What broke:&lt;/strong&gt; After the first few prompts, three real gaps showed up &lt;br&gt;
when I compared the output against what I'd actually asked for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &quot;Simulate Crowd Spike&quot; button had no click listener wired to &lt;br&gt;
it — the pipeline was instead running on a random 3-second timer, &lt;br&gt;
which meant I had zero control over when a demo-worthy event &lt;br&gt;
actually fired.&lt;/li&gt;
&lt;li&gt;The Action Recommender was a hardcoded lookup table &lt;br&gt;
(&lt;code&gt;{'Gate A': 'Deploy 2 tickets...'}&lt;/code&gt;) instead of a real tool call — &lt;br&gt;
it gave the same canned advice regardless of what was actually &lt;br&gt;
happening at the other gates.&lt;/li&gt;
&lt;li&gt;The Multilingual Notifier printed a console line &lt;em&gt;claiming&lt;/em&gt; it sent &lt;br&gt;
alerts in EN/ES/FR, but never actually generated any translated text.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;How I fixed it:&lt;/strong&gt; I sent three targeted follow-up prompts rather than &lt;br&gt;
re-prompting from scratch — each one named the exact gap:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Wire a real click handler to the button and force a controllable &lt;br&gt;
spike instead of relying on the timer&lt;/li&gt;
&lt;li&gt;Replace the lookup table with a real &lt;code&gt;checkAlternateGateCapacity()&lt;/code&gt; &lt;br&gt;
function that reads live gate state and picks the actual &lt;br&gt;
least-congested option, with the tool call and its result logged &lt;br&gt;
visibly in the console&lt;/li&gt;
&lt;li&gt;Generate real 4-language alert text tied to the specific gate and &lt;br&gt;
action from that run, displayed in the alert card itself&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This iteration loop — build, compare against the original prompt, name &lt;br&gt;
the specific gap, re-prompt — was more reliable than trying to get &lt;br&gt;
everything right in one shot.&lt;/p&gt;
&lt;h3 id=&quot;demo-walkthrough&quot;&gt;Demo Walkthrough&lt;/h3&gt;
&lt;p&gt;{% embed &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.loom.com/share/0523f68b722845489aad58bccdc66969&quot;&gt;https://www.loom.com/share/0523f68b722845489aad58bccdc66969&lt;/a&gt; %}&lt;/p&gt;
&lt;p&gt;Clicking &quot;Simulate Crowd Spike at Gate 3&quot; triggers the full pipeline: &lt;br&gt;
Gate 3 density jumps to 91%, the Signal Ingestor picks it up, the &lt;br&gt;
Analyzer classifies it as HIGH severity and explains why, the &lt;br&gt;
Recommender calls its tool and finds the least-crowded alternate gate, &lt;br&gt;
and the Notifier pushes a real multilingual alert — all visible in the &lt;br&gt;
console in under 15 seconds.&lt;/p&gt;
&lt;h3 id=&quot;quantified-impact&quot;&gt;Quantified Impact&lt;/h3&gt;
&lt;p&gt;Simulated staff decision latency drops from ~4 minutes of manual radio coordination to under 15 seconds of automated &lt;br&gt;
detection-to-alert time.&quot;  its a simulated benchmark, &lt;br&gt;
since this is a prototype, not a deployed system.&lt;/p&gt;
&lt;h3 id=&quot;what-i-d-build-with-more-time&quot;&gt;What I'd Build With More Time&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Real IoT/sensor integration instead of simulated feeds&lt;/li&gt;
&lt;li&gt;A predictive model (forecasting a crowd spike 10+ minutes ahead) &lt;br&gt;
rather than a reactive one&lt;/li&gt;
&lt;li&gt;Historical incident data to tune severity thresholds per venue&lt;/li&gt;
&lt;li&gt;Expanding to the accessibility and transportation verticals I &lt;br&gt;
deliberately left out of this build&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;try-it-1&quot;&gt;Try It&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Live demo: &lt;a rel=&quot;nofollow&quot; href=&quot;https://nittensharma16.github.io/marshal-stadium-copilot/*/&quot;&gt;https://nittensharma16.github.io/marshal-stadium-copilot/*/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Code: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nittensharma16/marshal-stadium-copilot&quot;&gt;https://github.com/nittensharma16/marshal-stadium-copilot&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Built for PromptWars Challenge 4, using Google Antigravity end to end.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/28636/marshal-teaching-an-ai-copilot-to-run-a-world-cup-stadium-built-with-google-antigravity</guid>
<pubDate>Fri, 25 Sep 2026 16:42:53 +0000</pubDate>
</item>
<item>
<title>Data Engineering Beyond &quot;Pretty Charts&quot;: How I Built an Aviation Telemetry Leakage Engine</title>
<link>https://coderlegion.com/28338/data-engineering-beyond-pretty-charts-how-i-built-an-aviation-telemetry-leakage-engine</link>
<description>&lt;h2 id=&quot;stop-tracking-on-time-flights-track-tarmac-fuel-destruction-instead-&quot;&gt;Stop Tracking &quot;On-Time&quot; Flights. Track Tarmac Fuel Destruction Instead.&lt;/h2&gt;
&lt;p&gt;Most aviation dashboards track gate-to-gate &quot;On-Time&quot; arrivals. The problem? &lt;strong&gt;It's a lagging indicator that completely masks why airlines silently hemorrhage millions.&lt;/strong&gt; Padded flight schedules disguise a massive bottleneck: chronic tarmac fuel destruction occurring right before takeoff.&lt;/p&gt;
&lt;p&gt;I built &lt;strong&gt;AeroOps&lt;/strong&gt;, an end-to-end telemetry and multi-domain operations leakage engine, to expose exactly where systems drop value between the hangar and the runway.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt; Repo &amp;amp; Technical Report:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/hafsamotiwala/aero-ops-forensic-audit&quot;&gt;[GitHub Link]&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt; Stack:&lt;/strong&gt; MySQL (Star Schema &amp;amp; CTEs) · Telemetry Data Modeling · Executive Dashboard Architecture · Power BI (DAX)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h4 id=&quot;the-architectural-amp-engineering-challenge&quot;&gt;The Architectural &amp;amp; Engineering Challenge&lt;/h4&gt;
&lt;p&gt;Handling flight telemetry means dealing with high-volume, disjointed datasets. To make this actionable, the engineering required:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Star Schema Data Warehousing:&lt;/strong&gt; Joined &lt;strong&gt;513,000+ flight legs&lt;/strong&gt; with precise technical burn parameters pulled directly from the &lt;em&gt;ICAO Aircraft Emissions Databank&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bypassing Relational Boundaries:&lt;/strong&gt; Formulated deduplicated DAX lookup models to handle wide-body propulsion penalties under sub-second memory constraints.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Algorithmic Baseline Filters:&lt;/strong&gt; Enforced a strict &lt;strong&gt;4-minute engine warm-up buffer&lt;/strong&gt; directly into the SQL logic. This stripped away safe, expected operational windows to isolate true, excess ground latency.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h4 id=&quot;the-bottom-line-impact-metrics&quot;&gt;The Bottom-Line Impact Metrics&lt;/h4&gt;
&lt;p&gt;By querying raw transaction logs and user event data instead of building passive, &quot;pretty&quot; charts, the engine surfaced massive operational friction:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt; $42.69M in Tarmac Cash Destruction:&lt;/strong&gt; Isolated and quantified the exact cost of ground traffic gridlocks at major hubs like Atlanta (ATL).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt; 54% to 57% Short-Haul Menu Spoilage:&lt;/strong&gt; Exposed a massive waste loop across major carriers (Delta/American) due to over-boarding. Transitioning the system to a 100% shelf-stable inventory model &lt;strong&gt;reclaimed $2.58M in simulated margin leaks&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt; $725K Legal Fine Exposure:&lt;/strong&gt; Isolated a hidden &lt;strong&gt;9% telemetry sync drop rate&lt;/strong&gt; specifically among regional commuter airframes caused by corrupted logs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt; Prevention of $1.50M Asset Groundings (AOG):&lt;/strong&gt; Coded an automated Reorder Point (ROP) trigger tool to insulate warehouses against catastrophic out-of-stock asset scenarios.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h4 id=&quot;the-takeaway&quot;&gt;The Takeaway&lt;/h4&gt;
&lt;p&gt;Data shouldn't just look nice in a presentation deck. If your queries aren't actively protecting business margins or exposing systemic infrastructure leaks, you're looking at the wrong metrics. &lt;/p&gt;
&lt;p&gt;If your team is scaling fast and dealing with messy relational pipelines or unoptimized telemetry data, let's connect and fix your backend infrastructure loops. &lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/28338/data-engineering-beyond-pretty-charts-how-i-built-an-aviation-telemetry-leakage-engine</guid>
<pubDate>Wed, 23 Sep 2026 12:10:54 +0000</pubDate>
</item>
<item>
<title>Reverse-Engineering Messy Databases: My End-to-End Data Audits Across 17,000+ Relational Schema Logs</title>
<link>https://coderlegion.com/28337/reverse-engineering-messy-databases-end-end-data-audits-across-000-relational-schema-logs</link>
<description>&lt;h3 id=&quot;from-raw-logs-to-business-strategy-how-i-taught-myself-data-engineering-amp-analytics&quot;&gt;From Raw Logs to Business Strategy: How I Taught Myself Data Engineering &amp;amp; Analytics&lt;/h3&gt;
&lt;p&gt;I didn’t take the traditional path into data. Over the past 12 months, I bypassed the university lecture halls to teach myself relational database architecture, pipeline optimization, and growth analytics by diving straight into raw datasets.&lt;/p&gt;
&lt;p&gt;I look at data through an engineering and business lens. Instead of building passive charts that just look pretty, I query transaction logs and user event data to find hidden operational friction, map out user behavior patterns, and isolate exactly where systems are dropping value. I build end-to-end data systems: raw SQL schemas, data sanitization scripts, interactive Power BI dashboards, and single-page executive reports.&lt;/p&gt;
&lt;p&gt;Below are the architectural breakdowns, technical challenges, and business impact metrics of the four core data infrastructure projects I have engineered from scratch.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;project-1-stockpulse-multi-regional-supply-chain-audit&quot;&gt;Project 1: StockPulse — Multi-Regional Supply Chain Audit&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Technical Stack:&lt;/strong&gt; MySQL (Mathematical Window Functions), Power BI, Advanced Excel&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GitHub Codebase &amp;amp; Dashboard Layout:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/hafsamotiwala/Enterprise-Procurement-Supply-Chain-Resilience-Pipeline&quot;&gt;[LINK]&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;the-operational-bottleneck&quot;&gt;The Operational Bottleneck&lt;/h4&gt;
&lt;p&gt;Horizon Marketplace faced shrinking profit margins from slow-moving warehouse stock, while simultaneous supplier shipping delays caused high-demand items to go out of stock on our mobile application. Traditional procurement tracking models failed because they relied on simple historical averages, which immediately miscalculate when data hits high seasonal sales volatility spikes.&lt;/p&gt;
&lt;h4 id=&quot;the-technical-implementation&quot;&gt;The Technical Implementation&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Audited 17,646 operational and sales ledger transactions&lt;/strong&gt; using MySQL window functions to calculate cumulative revenue contributions per SKU.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Coded a predictive restocking matrix script&lt;/strong&gt; utilizing sales volatility standard deviations and rolling manufacturer lead timelines.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Formulated dynamic Safety Stock buffers&lt;/strong&gt; and programmed automated database Reorder Point triggers to flag supplier order alerts before shelves hit zero.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;the-business-impact-metrics&quot;&gt;The Business Impact Metrics&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;ABC inventory segmentation&lt;/strong&gt; proved that exactly 23 Class A items drive 80% of total marketplace revenue, showing that the remaining catalog space was tying up critical capital.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Quantified a hidden revenue bleed of $63,897.26&lt;/strong&gt; caused by a single high-demand apparel product sitting entirely out of stock for 42 days due to upstream logistics bottlenecks.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Transitioned a multi-regional operation&lt;/strong&gt; from manual, time-intensive spreadsheets to an automated, predictive data framework.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;project-2-funnelopt-habitarcade-b2c-telemetry-audit&quot;&gt;Project 2: FunnelOpt — HabitArcade B2C Telemetry Audit&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Technical Stack:&lt;/strong&gt; MySQL (Session Overrides), Funnel Analysis, User Lifecycle Mapping&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GitHub Codebase &amp;amp; Database Logs:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/hafsamotiwala/-HabitArcade-Enterprise-B2C-SaaS-Funnel-Telemetry-Analysis&quot;&gt;[LINK]&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;the-operational-bottleneck-1&quot;&gt;The Operational Bottleneck&lt;/h4&gt;
&lt;p&gt;A B2C application tracking engine suffered from severe data execution drops where product behavioral logs completely failed to map user activation patterns. The relational schema dropped child telemetry records because they were importing prior to the absolute database finalization of the parent account index logs.&lt;/p&gt;
&lt;h4 id=&quot;the-technical-implementation-1&quot;&gt;The Technical Implementation&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Audited full-funnel growth logs&lt;/strong&gt; across 6 independent acquisition pipelines to analyze customer conversion latency and drop-offs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Structured specific MySQL session overrides&lt;/strong&gt; to temporarily bypass structural foreign key constraints.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Forced clean data synchronization&lt;/strong&gt; across child records to completely restore parent-child index synchronization without corrupting historical system logs.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;the-business-impact-metrics-1&quot;&gt;The Business Impact Metrics&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Isolated a systemic “Streak-Breaking Crash Tax”&lt;/strong&gt; where application sync errors wiped 30-day user milestones, triggering immediate 1-star reviews and an active 6% to 7% monthly subscriber bleed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Exposed an “Inverted Value Curve”&lt;/strong&gt; revealing that high-value annual checkout tiers underperformed monthly subscriptions due to silent gateway billing processor failures blocking top-priced subscription cash settlement.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;project-3-leakstop-olist-b2b-revenue-leakage-amp-onboarding-audit&quot;&gt;Project 3: LeakStop — Olist B2B Revenue Leakage &amp;amp; Onboarding Audit&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Technical Stack:&lt;/strong&gt; MySQL (Schema Engineering), Power BI, Business Intelligence&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GitHub Codebase &amp;amp; Analytical Readme:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/hafsamotiwala/B2B-Saas-Funnel-Optimization-Audit&quot;&gt;[LINK]&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;the-operational-bottleneck-2&quot;&gt;The Operational Bottleneck&lt;/h4&gt;
&lt;p&gt;Cross-vendor data duplication across digital marketplace ledgers was heavily skewing gross reporting metrics. Standard relational database joins double-counted total transactions when a customer order spanned multiple independent business vendors in a single checkout loop.&lt;/p&gt;
&lt;h4 id=&quot;the-technical-implementation-2&quot;&gt;The Technical Implementation&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Engineered robust MySQL ingestion and data sanitization scripts&lt;/strong&gt; to enforce strict primary key integrity down to individual item vectors, removing all reporting bias.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Processed user account lifecycles&lt;/strong&gt; to calculate onboarding completion latency across specific B2B verticals.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Programmed a real-time database filter&lt;/strong&gt; to flag slipping user accounts precisely on Day 16 of transaction inactivity.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;the-business-impact-metrics-2&quot;&gt;The Business Impact Metrics&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Isolated an annual revenue leakage of R$ 600,600&lt;/strong&gt; (approximately $119,175 USD) stemming from 462 merchant accounts that stalled out completely during the onboarding phase.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Exposed an invisible latency anomaly&lt;/strong&gt; where logistically complex business profiles stalled for an average of 112 days in domain routing and tax mapping phases.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Implemented a Day 16 early-warning database filter&lt;/strong&gt; to flag at-risk accounts before they reached a terminal, zero-recovery retention cliff.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;project-4-cohortpace-saas-behavioral-retention-engine&quot;&gt;Project 4: CohortPace — SaaS Behavioral Retention Engine&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Technical Stack:&lt;/strong&gt; MySQL (Subqueries &amp;amp; Relational Joins), Cohort Modeling, Data Analytics&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GitHub Codebase &amp;amp; Technical Report:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/hafsamotiwala/SaaS-User-Behavior-Analysis-Email-Creator-Platforms-&quot;&gt;[LINK]&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;the-operational-bottleneck-3&quot;&gt;The Operational Bottleneck&lt;/h4&gt;
&lt;p&gt;Initial application dashboards focused blindly on feature exploration depth, which completely masked why healthy-looking active user profiles were suddenly canceling subscriptions. Traditional active-user metrics acted as a lagging indicator, hiding friction because churning accounts remain active inside broken workflows right up to their exit timelines.&lt;/p&gt;
&lt;h4 id=&quot;the-technical-implementation-3&quot;&gt;The Technical Implementation&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Modeled simulated user cohort transactions&lt;/strong&gt; across multi-platform SaaS behavioral profiles to analyze the core drivers of long-term Monthly Recurring Revenue (MRR).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Used advanced MySQL subqueries and relational joins&lt;/strong&gt; to reconstruct full lifecycle interaction sequences.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mapped user interaction velocity trends&lt;/strong&gt; against subscription lifecycles to isolate clear behavioral churn signatures.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;the-business-impact-metrics-3&quot;&gt;The Business Impact Metrics&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Proved through data pacing&lt;/strong&gt; sending signals that churning users maintain consistent interaction velocity close to their exit date, showing churn is driven by late-stage workflow utility limits rather than gradual disengagement.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Demonstrated that long-term user monetization&lt;/strong&gt; is strictly dependent on habit formation within a single core workflow rather than broad, multi-feature platform exploration.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;conclusion-turning-code-into-business-strategy&quot;&gt;Conclusion: Turning Code Into Business Strategy&lt;/h3&gt;
&lt;p&gt;I don’t need a playbook to get started, and I don’t need a manager to hand me a static checklist. I find the question, ingest the data, clean the schemas, model the answer, and present the metric that directly protects business margins.&lt;/p&gt;
&lt;p&gt;I am actively available for analytical remote contract roles, standalone data audits, and freelance pipeline optimization. If your startup is scaling fast and dealing with messy relational pipelines, let’s connect and fix your backend infrastructure loops.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt; Interactive Portfolio Hub:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://lucky-bit-036.notion.site/HAFSA-5fd489cedd70459ca0237c36a168f30a&quot;&gt;[LINK]&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt; Contact:&lt;/strong&gt; motiwala603@gmail.com&lt;/li&gt;
&lt;li&gt;&lt;strong&gt; Connect with me:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.linkedin.com/in/hafsa-motiwala-a9608835b/&quot;&gt;[LinkedIn]&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;em&gt;Open to analyst remote roles across product, growth, and business analytics. Based in Pakistan, available worldwide.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/28337/reverse-engineering-messy-databases-end-end-data-audits-across-000-relational-schema-logs</guid>
<pubDate>Wed, 23 Sep 2026 11:56:39 +0000</pubDate>
</item>
<item>
<title>I Wanted a RAG System That Could Show Its Work, So I Built Alchimista</title>
<link>https://coderlegion.com/28173/i-wanted-a-rag-system-that-could-show-its-work-so-i-built-alchimista</link>
<description>&lt;p&gt;Most RAG demos stop at the answer.&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Daniele-Cangi/Alchimista&quot;&gt;Alchimista&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You upload a document, ask a question, get a plausible response, maybe see a few source chunks, and that's where the story ends.&lt;/p&gt;
&lt;p&gt;But I kept coming back to a different problem:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What happens when the answer itself isn't enough?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;What if I need to know which evidence was used, what happened to sensitive data before processing, which policy was active, whether two tenants could ever see each other's documents, and what the system actually did rather than what its architecture diagram claims it should do?&lt;/p&gt;
&lt;p&gt;That's where &lt;strong&gt;Alchimista&lt;/strong&gt; started.&lt;/p&gt;
&lt;h3 id=&quot;the-idea&quot;&gt;The idea&lt;/h3&gt;
&lt;p&gt;Alchimista is a self-hosted document intelligence and AI governance system built around a fairly simple principle:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;An AI answer should leave evidence behind.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The basic path looks like this:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;document → privacy processing → ingestion → retrieval → cited answer → decision evidence → audit&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;That sounds straightforward. Making all of those boundaries agree with each other was considerably less straightforward.&lt;/p&gt;
&lt;h3 id=&quot;rag-wasn-t-the-difficult-part&quot;&gt;RAG wasn't the difficult part&lt;/h3&gt;
&lt;p&gt;Retrieving chunks and generating an answer is only one piece of the system.&lt;/p&gt;
&lt;p&gt;The more interesting questions appeared around it.&lt;/p&gt;
&lt;p&gt;A document belongs to a tenant. Retrieval has to respect that boundary.&lt;/p&gt;
&lt;p&gt;An answer needs citations. Those citations need to point back to actual indexed evidence.&lt;/p&gt;
&lt;p&gt;Sensitive information may need to be detected or pseudonymized before it reaches an external model.&lt;/p&gt;
&lt;p&gt;If privacy processing fails, silently continuing with the original text is not an acceptable fallback.&lt;/p&gt;
&lt;p&gt;And if the system records an AI decision, the audit trail shouldn't accidentally become another place where sensitive data leaks.&lt;/p&gt;
&lt;p&gt;So Alchimista gradually became less of a &quot;RAG application&quot; and more of an infrastructure layer around the complete evidence path.&lt;/p&gt;
&lt;h3 id=&quot;privacy-as-part-of-the-pipeline&quot;&gt;Privacy as part of the pipeline&lt;/h3&gt;
&lt;p&gt;Alchimista currently has several privacy modes.&lt;/p&gt;
&lt;p&gt;At the strictest level, sensitive values are pseudonymized &lt;strong&gt;before chunking and embedding&lt;/strong&gt;, meaning the normal retrieval index never receives their cleartext representation.&lt;/p&gt;
&lt;p&gt;Reversible mappings live separately in an AES-256-GCM encrypted vault.&lt;/p&gt;
&lt;p&gt;More importantly, enabled privacy policies fail closed. If the privacy service is unavailable or produces an invalid result, processing stops instead of quietly bypassing the protection.&lt;/p&gt;
&lt;p&gt;That behavior matters more to me than adding another checkbox labelled &quot;privacy&quot;.&lt;/p&gt;
&lt;h3 id=&quot;local-first&quot;&gt;Local first&lt;/h3&gt;
&lt;p&gt;An earlier version of the project leaned heavily on cloud infrastructure.&lt;/p&gt;
&lt;p&gt;That worked, but eventually I wanted the core system to be independently runnable and inspectable.&lt;/p&gt;
&lt;p&gt;The current baseline needs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Docker with Compose&lt;/li&gt;
&lt;li&gt;Python 3.11+&lt;/li&gt;
&lt;li&gt;PostgreSQL&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;No GCP project, external vector database, Auth0 tenant, object store, or paid service is required.&lt;/p&gt;
&lt;p&gt;The cloud integrations still exist as optional adapters, but they are no longer prerequisites for proving that the system works.&lt;/p&gt;
&lt;p&gt;Starting it locally is intentionally boring:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git clone https://github.com/Daniele-Cangi/Alchimista.git
cd Alchimista
python scripts/init_local_env.py
docker compose up --detach --build --wait
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then the application is available locally with Documents, Ask, Privacy, Audit, Governance, and System in the same interface.&lt;/p&gt;
&lt;h3 id=&quot;i-wanted-a-proof-not-just-a-demo&quot;&gt;I wanted a proof, not just a demo&lt;/h3&gt;
&lt;p&gt;One thing I've become increasingly skeptical of in my own projects is a green UI that proves very little about the system underneath it.&lt;/p&gt;
&lt;p&gt;So Alchimista has an end-to-end self-hosted smoke test.&lt;/p&gt;
&lt;p&gt;It doesn't just check that the containers started.&lt;/p&gt;
&lt;p&gt;It exercises ingestion, privacy detection, pseudonymization and restoration, protected persistence, SQL retrieval, citations, tenant isolation, AI decision evidence, audit metadata, the encrypted vault, cleanup behavior, and persistence across a PostgreSQL restart.&lt;/p&gt;
&lt;p&gt;The successful end state is deliberately unexciting:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;SELF_HOSTED_SMOKE_OK
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But that line means much more to me than a screenshot of a chatbot answering a document question.&lt;/p&gt;
&lt;h3 id=&quot;some-deliberate-limitations&quot;&gt;Some deliberate limitations&lt;/h3&gt;
&lt;p&gt;There are also things I don't want Alchimista to pretend.&lt;/p&gt;
&lt;p&gt;It implements technical controls; it does &lt;strong&gt;not&lt;/strong&gt; magically certify GDPR, the EU AI Act, or any other regulation.&lt;/p&gt;
&lt;p&gt;PII detection cannot promise to identify every possible sensitive value.&lt;/p&gt;
&lt;p&gt;The default local embedding path is designed to make the complete system independently runnable and test retrieval mechanics. It isn't a claim that a deterministic offline embedder is the best semantic retrieval system available.&lt;/p&gt;
&lt;p&gt;And stronger pseudonymization can affect retrieval quality, which means strict privacy modes have to be evaluated against the actual domain rather than assumed to be free.&lt;/p&gt;
&lt;p&gt;I think being explicit about those boundaries makes the system more useful, not less.&lt;/p&gt;
&lt;h3 id=&quot;what-s-next-1&quot;&gt;What's next&lt;/h3&gt;
&lt;p&gt;Alchimista is now at the point where I'm less interested in adding features for the sake of feature count and more interested in attacking the boundaries.&lt;/p&gt;
&lt;p&gt;What breaks under larger document collections?&lt;/p&gt;
&lt;p&gt;How much retrieval quality is lost under different privacy strategies?&lt;/p&gt;
&lt;p&gt;Which pieces of AI decision evidence are genuinely useful during an investigation months later?&lt;/p&gt;
&lt;p&gt;Where should the trust boundary actually sit when an external model is introduced?&lt;/p&gt;
&lt;p&gt;Those are the experiments I'm interested in next.&lt;/p&gt;
&lt;p&gt;This is also my first proper post on DEV, so I'll probably use this space less as a release feed and more as a place to document those experiments — including the ones that don't work.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Alchimista is open source and available on GitHub.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Feedback on the architecture, privacy model, or places you'd try to break it is very welcome.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/28173/i-wanted-a-rag-system-that-could-show-its-work-so-i-built-alchimista</guid>
<pubDate>Tue, 22 Sep 2026 13:05:47 +0000</pubDate>
</item>
<item>
<title>Faker Doesn't Know Your Entities Are Related, So I Built Something That Does</title>
<link>https://coderlegion.com/28147/faker-doesnt-know-your-entities-are-related-so-i-built-something-that-does</link>
<description>&lt;h2 id=&quot;faker-doesn-t-know-your-entities-are-related-so-i-built-something-that-does&quot;&gt;Faker Doesn't Know Your Entities Are Related, So I Built Something That Does&lt;/h2&gt;
&lt;p&gt;You've added a second entity to the schema, wired up a &lt;code&gt;@ManyToOne&lt;/code&gt;, and gone back to your seed script to generate fifty more rows. Ninety seconds later, the app refuses to start: unique constraint violation, somewhere inside a loop you wrote three weeks ago at 11pm. You fix it. You restart. A different field breaks a different constraint.&lt;/p&gt;
&lt;p&gt;This is the exact moment every Spring Boot developer eventually meets the real limit of tools like Faker. They're brilliant at generating a name, an email, an address. They have no idea the &lt;code&gt;Payment&lt;/code&gt; sitting in front of them needs a &lt;code&gt;Counterparty&lt;/code&gt; to already exist.&lt;/p&gt;
&lt;p&gt;So you do what everyone does: hand-write the wiring. Create parents first. Hold onto their generated IDs. Wire them into children. Hope you didn't just violate a &lt;code&gt;@NotNull&lt;/code&gt; somewhere in the process. It works, for a while. Then the schema changes, and the script quietly stops matching reality until the next 3am debugging session finds out the hard way.&lt;/p&gt;
&lt;p&gt;I hit this enough times that I stopped patching the script and looked at the actual problem: the information needed to seed this correctly already exists. It's sitting right there in the entity, in the annotations you already wrote. &lt;code&gt;@ManyToOne&lt;/code&gt;, &lt;code&gt;@NotNull&lt;/code&gt;, &lt;code&gt;@Column(unique = true)&lt;/code&gt;, JPA already knows the shape of your data. Nothing should need to be told that twice.&lt;/p&gt;
&lt;p&gt;That became &lt;strong&gt;SynthForge&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;the-core-idea-1&quot;&gt;The core idea&lt;/h3&gt;
&lt;p&gt;Instead of writing a script that generates data, you annotate the entity:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-java&quot;&gt;@Entity @Seed(count = 50)
public class Counterparty { /* fields only */ }

@Entity @Seed(count = 200)
public class Payment {
    @ManyToOne(optional = false) private Counterparty counterparty;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Start the app in a dev profile. Both tables populate, correctly ordered, on every restart. No seed method. No calling code, anywhere. The entity is the seed script.&lt;/p&gt;
&lt;h3 id=&quot;what-s-actually-happening-underneath&quot;&gt;What's actually happening underneath&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Entity scanning.&lt;/strong&gt; SynthForge reads JPA-managed attributes through the &lt;code&gt;jakarta.persistence.metamodel.Metamodel&lt;/code&gt; API, not raw reflection. That distinction matters more than it sounds: only real persistent fields ever get touched, nothing that merely resembles one.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Relationship ordering.&lt;/strong&gt; From the owning-side &lt;code&gt;@ManyToOne&lt;/code&gt;/&lt;code&gt;@OneToOne&lt;/code&gt; relationships, it builds a dependency graph and topologically sorts it. Parent rows exist before a child is ever generated to reference them. This is precisely where hand-written scripts fail first: the moment a second level of nesting shows up, manual ID-tracking becomes its own source of bugs, the thing you're debugging instead of your actual feature.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Constraint-aware generation.&lt;/strong&gt; &lt;code&gt;@NotNull&lt;/code&gt;, &lt;code&gt;@Size&lt;/code&gt;, &lt;code&gt;@Email&lt;/code&gt;, and a set of field-name heuristics (&lt;code&gt;email&lt;/code&gt;, &lt;code&gt;iban&lt;/code&gt;, &lt;code&gt;amount&lt;/code&gt;, &lt;code&gt;country&lt;/code&gt;) drive realistic values through Datafaker. Hit a &lt;code&gt;@Column(unique = true)&lt;/code&gt; field, and instead of a constraint violation forty rows in, you get a bounded retry loop.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Idempotent restarts.&lt;/strong&gt; A table that already has rows gets skipped. Restart against a persistent database as many times as you want; nothing duplicates.&lt;/p&gt;
&lt;h3 id=&quot;-isn-t-this-just-instancio-&quot;&gt;&quot;Isn't this just Instancio?&quot;&lt;/h3&gt;
&lt;p&gt;Fair question, and worth answering directly rather than dodging it. Instancio, and its &lt;code&gt;instancio-jpa&lt;/code&gt; extension, gives you an API to call from inside a test: build a graph, persist it, for that one test. It's genuinely good at that job.&lt;/p&gt;
&lt;p&gt;SynthForge isn't called from anywhere. Annotate the entity, start the app in an enabled profile, and the database is already populated before your code runs a single line. Different tool, because it solves a different moment: application startup, not a single test method.&lt;/p&gt;
&lt;h3 id=&quot;where-it-stands&quot;&gt;Where it stands&lt;/h3&gt;
&lt;p&gt;Live on Maven Central:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-xml&quot;&gt;&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;io.github.thembatman0&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;synthforge-spring&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;0.1.0&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Repo: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/ThembaTman0/synthforge&quot;&gt;github.com/ThembaTman0/synthforge&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It won't fix your architecture and it won't make decisions for you. But the next time you add a foreign key at 11pm, there's a decent chance it means you get to go to bed instead.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/28147/faker-doesnt-know-your-entities-are-related-so-i-built-something-that-does</guid>
<pubDate>Tue, 22 Sep 2026 11:22:33 +0000</pubDate>
</item>
<item>
<title>I Got Tired of Building Backends for Forms, So I Built FORX</title>
<link>https://coderlegion.com/28093/i-got-tired-of-building-backends-for-forms-so-i-built-forx</link>
<description>&lt;p&gt;&lt;strong&gt;I Got Tired of Building Backends for Forms, So I Built FORX&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I don't know if I'm the only developer who does this.&lt;/p&gt;
&lt;p&gt;You need a simple contact form.&lt;/p&gt;
&lt;p&gt;So you create the HTML.&lt;/p&gt;
&lt;p&gt;Then you realize:&lt;/p&gt;
&lt;p&gt;“Okay, where does this form data actually go?”&lt;/p&gt;
&lt;p&gt;And suddenly your tiny contact form needs a backend.&lt;/p&gt;
&lt;p&gt;You start thinking about API routes, databases, validation, authentication, email notifications, spam protection, deployment...&lt;/p&gt;
&lt;p&gt;All that.&lt;/p&gt;
&lt;p&gt;For a form.&lt;/p&gt;
&lt;p&gt;That was basically the reason I started building FORX.&lt;/p&gt;
&lt;p&gt;The problem&lt;/p&gt;
&lt;p&gt;Let's say I'm building a portfolio.&lt;/p&gt;
&lt;p&gt;I want a simple form where someone can enter:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Name&lt;/li&gt;
&lt;li&gt;Email&lt;/li&gt;
&lt;li&gt;Message&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That's it.&lt;/p&gt;
&lt;p&gt;I don't necessarily want to create a database.&lt;/p&gt;
&lt;p&gt;I don't want to build an API.&lt;/p&gt;
&lt;p&gt;I don't want to deploy another backend.&lt;/p&gt;
&lt;p&gt;I just want the submissions to go somewhere.&lt;/p&gt;
&lt;p&gt;And I realized this isn't only useful for contact forms.&lt;/p&gt;
&lt;p&gt;The same thing applies to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Waitlists&lt;/li&gt;
&lt;li&gt;Feedback forms&lt;/li&gt;
&lt;li&gt;Surveys&lt;/li&gt;
&lt;li&gt;Job applications&lt;/li&gt;
&lt;li&gt;Event registrations&lt;/li&gt;
&lt;li&gt;Portfolio inquiries&lt;/li&gt;
&lt;li&gt;Simple data collection&lt;/li&gt;
&lt;li&gt;Landing page forms&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A lot of these projects don't need a full backend.&lt;/p&gt;
&lt;p&gt;They just need a place to send the data.&lt;/p&gt;
&lt;p&gt;So I started thinking:&lt;/p&gt;
&lt;p&gt;What if the backend was already there?&lt;/p&gt;
&lt;p&gt;That's FORX&lt;/p&gt;
&lt;p&gt;FORX is basically forms without the backend.&lt;/p&gt;
&lt;p&gt;You create an endpoint with FORX, connect your form to it, and whenever someone submits the form, FORX receives the data.&lt;/p&gt;
&lt;p&gt;Your architecture can look something like this:&lt;/p&gt;
&lt;p&gt;Your website&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; |
 | POST request
 v
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  FORX endpoint&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; |
 v
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Submission stored&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; |
 v
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;   FORX dashboard&lt;/p&gt;
&lt;p&gt;That's the idea.&lt;/p&gt;
&lt;p&gt;No database setup.&lt;/p&gt;
&lt;p&gt;No API route.&lt;/p&gt;
&lt;p&gt;No backend to deploy.&lt;/p&gt;
&lt;p&gt;Just your form.&lt;/p&gt;
&lt;p&gt;The simplest example&lt;/p&gt;
&lt;p&gt;Imagine I have this contact form:&lt;/p&gt;
&lt;p&gt;&amp;lt; form action=&quot;YOUR_FORX_ENDPOINT&quot; method=&quot;POST&quot;&amp;gt;&lt;br&gt;
  &amp;lt; input&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type=&quot;text&quot;
name=&quot;name&quot;
placeholder=&quot;Your name&quot;
required
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  /&amp;gt;&lt;/p&gt;
&lt;p&gt;  &amp;lt; input&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type=&quot;email&quot;
name=&quot;email&quot;
placeholder=&quot;Your email&quot;
required
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  /&amp;gt;&lt;/p&gt;
&lt;p&gt;  &amp;lt; textarea&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;name=&quot;message&quot;
placeholder=&quot;Your message&quot;
required
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  &amp;gt; &amp;lt;/ textarea &amp;gt;&lt;/p&gt;
&lt;p&gt;  &amp;lt; button type=&quot;submit&quot;&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Send message
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  &amp;lt;/ button &amp;gt;&lt;br&gt;
&amp;lt;/ form &amp;gt;&lt;/p&gt;
&lt;p&gt;Instead of writing my own API endpoint, I point the form's &quot;action&quot; to my FORX endpoint.&lt;/p&gt;
&lt;p&gt;That's basically it.&lt;/p&gt;
&lt;p&gt;When someone submits the form, FORX receives the POST request and handles the submission.&lt;/p&gt;
&lt;p&gt;Your frontend doesn't really care where the backend is.&lt;/p&gt;
&lt;p&gt;It just sends the request.&lt;/p&gt;
&lt;p&gt;Why not just build a backend?&lt;/p&gt;
&lt;p&gt;You absolutely can.&lt;/p&gt;
&lt;p&gt;That's actually the point.&lt;/p&gt;
&lt;p&gt;If you're building something complex, you probably should build your own backend.&lt;/p&gt;
&lt;p&gt;FORX isn't trying to replace that.&lt;/p&gt;
&lt;p&gt;But sometimes you're building something like:&lt;/p&gt;
&lt;p&gt;portfolio.html&lt;br&gt;
landing-page.html&lt;br&gt;
contact.html&lt;/p&gt;
&lt;p&gt;And you just need:&lt;/p&gt;
&lt;p&gt;POST /contact&lt;/p&gt;
&lt;p&gt;You don't want to spend an afternoon setting up infrastructure for it.&lt;/p&gt;
&lt;p&gt;I know I don't.&lt;/p&gt;
&lt;p&gt;I wanted FORX to feel stupidly simple&lt;/p&gt;
&lt;p&gt;One thing I've been thinking about while building FORX is that infrastructure products can sometimes become more complicated than the problem they're solving.&lt;/p&gt;
&lt;p&gt;I didn't want:&lt;/p&gt;
&lt;p&gt;“Welcome to FORX. Before you create your first form, please configure seventeen things.”&lt;/p&gt;
&lt;p&gt;I wanted something closer to:&lt;/p&gt;
&lt;p&gt;Create endpoint&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  ↓
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Copy endpoint&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  ↓
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Put it in your form&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  ↓
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Receive submissions&lt;/p&gt;
&lt;p&gt;That's the experience I'm aiming for.&lt;/p&gt;
&lt;p&gt;But it's not just contact forms&lt;/p&gt;
&lt;p&gt;This is probably the part I'm most interested in.&lt;/p&gt;
&lt;p&gt;FORX can be used anywhere you need to collect data from a frontend without wanting to build the backend yourself.&lt;/p&gt;
&lt;p&gt;For example, imagine you're launching a small project and want to collect emails.&lt;/p&gt;
&lt;p&gt;You could have:&lt;/p&gt;
&lt;p&gt;&amp;lt;  form action=&quot;YOUR_FORX_ENDPOINT&quot; method=&quot;POST&quot;&amp;gt;&lt;br&gt;
  &amp;lt;  input&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;type=&quot;email&quot;
name=&quot;email&quot;
placeholder=&quot;you@example.com&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  /&amp;gt;&lt;/p&gt;
&lt;p&gt;  &amp;lt; button type=&quot;submit&quot;&amp;gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Join the waitlist
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  &amp;lt;/ button &amp;gt;&lt;br&gt;
&lt;/p&gt;
&lt;p&gt;Or maybe you're building a portfolio and want people to contact you.&lt;/p&gt;
&lt;p&gt;Or you're making a small survey.&lt;/p&gt;
&lt;p&gt;Or you want to collect feedback from users.&lt;/p&gt;
&lt;p&gt;The frontend stays yours.&lt;/p&gt;
&lt;p&gt;FORX just handles the boring part behind it.&lt;/p&gt;
&lt;p&gt;One feature I really wanted: domain restrictions&lt;/p&gt;
&lt;p&gt;There's also something I think is important when you're accepting submissions from the public.&lt;/p&gt;
&lt;p&gt;You don't necessarily want your endpoint being used by random websites.&lt;/p&gt;
&lt;p&gt;So FORX lets you configure allowed domains for an endpoint.&lt;/p&gt;
&lt;p&gt;That means you can say:&lt;/p&gt;
&lt;p&gt;This endpoint should only accept submissions from my website.&lt;/p&gt;
&lt;p&gt;It's a small feature, but it's one of those things that becomes important once you actually put a form on the internet.&lt;/p&gt;
&lt;p&gt;The developer in me still wanted an endpoint&lt;/p&gt;
&lt;p&gt;I also didn't want FORX to feel like one of those form builders where you're forced into a specific UI.&lt;/p&gt;
&lt;p&gt;Your form can still be completely yours.&lt;/p&gt;
&lt;p&gt;You can use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Plain HTML&lt;/li&gt;
&lt;li&gt;React&lt;/li&gt;
&lt;li&gt;Next.js&lt;/li&gt;
&lt;li&gt;Vue&lt;/li&gt;
&lt;li&gt;Astro&lt;/li&gt;
&lt;li&gt;Svelte&lt;/li&gt;
&lt;li&gt;Basically anything that can make an HTTP request&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;FORX doesn't need to know what your frontend looks like.&lt;/p&gt;
&lt;p&gt;It just needs to receive the request.&lt;/p&gt;
&lt;p&gt;That's what I like about the idea.&lt;/p&gt;
&lt;p&gt;Why I built it&lt;/p&gt;
&lt;p&gt;This project started from one of those “surely someone has already solved this” moments.&lt;/p&gt;
&lt;p&gt;I was thinking about forms and backend infrastructure, and I realized there was an interesting gap between:&lt;/p&gt;
&lt;p&gt;“I need a backend.”&lt;/p&gt;
&lt;p&gt;and&lt;/p&gt;
&lt;p&gt;“I need somewhere for this form submission to go.”&lt;/p&gt;
&lt;p&gt;Those aren't always the same problem.&lt;/p&gt;
&lt;p&gt;And maybe developers shouldn't have to build an entire backend every time they want to collect a few pieces of data.&lt;/p&gt;
&lt;p&gt;That's basically what I'm trying to solve with FORX.&lt;/p&gt;
&lt;p&gt;Not every form needs a backend.&lt;/p&gt;
&lt;p&gt;Sometimes the backend should just exist.&lt;/p&gt;
&lt;p&gt;FORX is still evolving&lt;/p&gt;
&lt;p&gt;I'm still building it.&lt;/p&gt;
&lt;p&gt;There are a lot of things I want to improve and add.&lt;/p&gt;
&lt;p&gt;I'm also still figuring out exactly how far I want to take the product.&lt;/p&gt;
&lt;p&gt;Because honestly, one of the fun parts of building something from scratch is discovering what people actually use it for.&lt;/p&gt;
&lt;p&gt;Maybe someone will use FORX for a portfolio contact form.&lt;/p&gt;
&lt;p&gt;Maybe someone will use it for a waitlist.&lt;/p&gt;
&lt;p&gt;Maybe someone will do something I didn't even think about.&lt;/p&gt;
&lt;p&gt;That's kind of the point.&lt;/p&gt;
&lt;p&gt;Try it&lt;/p&gt;
&lt;p&gt;If you're building a portfolio, landing page, survey, waitlist, contact form, or basically anything that needs to collect data, you can check out FORX here:&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://forx.voryn.xyz&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Create an endpoint, connect it to your form, and see how it feels.&lt;/p&gt;
&lt;p&gt;I'm especially interested in hearing what you would use it for.&lt;/p&gt;
&lt;p&gt;Because at the end of the day, I built FORX for a pretty simple reason:&lt;/p&gt;
&lt;p&gt;I don't want to build another backend just to receive a form submission.&lt;/p&gt;
&lt;p&gt;And maybe you don't either. &lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/28093/i-got-tired-of-building-backends-for-forms-so-i-built-forx</guid>
<pubDate>Tue, 22 Sep 2026 04:12:24 +0000</pubDate>
</item>
<item>
<title>I Built Lens Browser Because the Internet Never Forgets</title>
<link>https://coderlegion.com/27990/i-built-lens-browser-because-the-internet-never-forgets</link>
<description>&lt;p&gt;Every time we open a website, something happens behind the screen.&lt;/p&gt;
&lt;p&gt;A request is sent.&lt;/p&gt;
&lt;p&gt;A connection is created.&lt;/p&gt;
&lt;p&gt;Scripts are loaded.&lt;/p&gt;
&lt;p&gt;Trackers start working.&lt;/p&gt;
&lt;p&gt;And quietly, information about us begins to leave.&lt;/p&gt;
&lt;p&gt;Most people never see it.&lt;/p&gt;
&lt;p&gt;They simply open a page, search for something, read an article, or watch a video — while invisible systems collect signals about their behavior.&lt;/p&gt;
&lt;p&gt;The modern internet is powerful.&lt;/p&gt;
&lt;p&gt;But that power often comes with a hidden cost.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Our data.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;your-data-is-not-free&quot;&gt;Your Data Is Not Free&lt;/h3&gt;
&lt;p&gt;Browsing history.&lt;/p&gt;
&lt;p&gt;Interests.&lt;/p&gt;
&lt;p&gt;Device information.&lt;/p&gt;
&lt;p&gt;Location signals.&lt;/p&gt;
&lt;p&gt;Online behavior.&lt;/p&gt;
&lt;p&gt;These things have value.&lt;/p&gt;
&lt;p&gt;Companies build products around this data. Advertisers use it to understand people. Analytics systems use it to measure behavior.&lt;/p&gt;
&lt;p&gt;But the question is:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Do we really know how much of ourselves we give away every day?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Privacy should not be something only available to people who understand complicated settings.&lt;/p&gt;
&lt;p&gt;It should be the default.&lt;/p&gt;
&lt;p&gt;That is why I started building Lens Browser.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;not-another-browser&quot;&gt;Not Another Browser&lt;/h3&gt;
&lt;p&gt;There are already many browsers.&lt;/p&gt;
&lt;p&gt;Some are fast.&lt;/p&gt;
&lt;p&gt;Some have beautiful interfaces.&lt;/p&gt;
&lt;p&gt;Some have millions of users.&lt;/p&gt;
&lt;p&gt;I did not build Lens Browser because the world needed another browser.&lt;/p&gt;
&lt;p&gt;I built it because I wanted a browser that starts from a different idea:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The user should be in control.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;A browser should be a window to the internet.&lt;/p&gt;
&lt;p&gt;Not a tool that quietly watches the person looking through that window.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;privacy-by-default&quot;&gt;Privacy by Default&lt;/h3&gt;
&lt;p&gt;Lens Browser was designed around a simple principle:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Browse privately. No compromise.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Dual-layer ad blocking using connection-level filtering and JavaScript interception&lt;/li&gt;
&lt;li&gt;Anti-fingerprinting protection through Privacy Mode&lt;/li&gt;
&lt;li&gt;Automatic deletion of history, cookies, and cached data when closing the app&lt;/li&gt;
&lt;li&gt;Threat detection before dangerous pages fully load&lt;/li&gt;
&lt;li&gt;Trusted and blocked domain management&lt;/li&gt;
&lt;li&gt;Lightweight design with no account required&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;No sign-up.&lt;/p&gt;
&lt;p&gt;No profile.&lt;/p&gt;
&lt;p&gt;No unnecessary tracking.&lt;/p&gt;
&lt;p&gt;Just open and browse.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;built-small-built-carefully&quot;&gt;Built Small, Built Carefully&lt;/h3&gt;
&lt;p&gt;Lens Browser is built with modern Android technology:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Kotlin&lt;/li&gt;
&lt;li&gt;Jetpack Compose&lt;/li&gt;
&lt;li&gt;MVVM architecture&lt;/li&gt;
&lt;li&gt;Android WebView with custom privacy layers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The app is lightweight, around 10 MB, and supports Android 6.0+.&lt;/p&gt;
&lt;p&gt;It is also open source under the MIT License.&lt;/p&gt;
&lt;p&gt;I believe privacy tools should be transparent.&lt;/p&gt;
&lt;p&gt;People should be able to understand what they use.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-internet-was-built-to-connect-people&quot;&gt;The Internet Was Built to Connect People&lt;/h3&gt;
&lt;p&gt;Technology should make life better.&lt;/p&gt;
&lt;p&gt;It should help people discover, communicate, and create.&lt;/p&gt;
&lt;p&gt;But as the internet evolves, we should not forget one important thing:&lt;/p&gt;
&lt;p&gt;Behind every device is a person.&lt;/p&gt;
&lt;p&gt;Not a data point.&lt;/p&gt;
&lt;p&gt;Not a profile.&lt;/p&gt;
&lt;p&gt;Not a target.&lt;/p&gt;
&lt;p&gt;A person.&lt;/p&gt;
&lt;p&gt;Lens Browser is my small contribution toward a more private internet.&lt;/p&gt;
&lt;p&gt;A reminder that privacy still matters.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;try-lens-browser&quot;&gt;Try Lens Browser&lt;/h3&gt;
&lt;p&gt;Lens Browser is available for Android.&lt;/p&gt;
&lt;p&gt;Download:&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://flagodna.com/lens-browser/&quot;&gt;https://flagodna.com/lens-browser/&lt;/a&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/27990/i-built-lens-browser-because-the-internet-never-forgets</guid>
<pubDate>Mon, 21 Sep 2026 17:26:51 +0000</pubDate>
</item>
<item>
<title>We Built Kolaboratr Because Finding the Right People to Build With Shouldn’t Be This Hard</title>
<link>https://coderlegion.com/27686/we-built-kolaboratr-because-finding-the-right-people-to-build-with-shouldnt-be-this-hard</link>
<description>&lt;p&gt;There’s a pattern we keep seeing across indie development and creative communities.&lt;/p&gt;
&lt;p&gt;One person posts:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;“I have a project, but I need an artist.”&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A programmer says:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;“I’m looking for a team.”&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A writer is looking for something worth joining.&lt;/p&gt;
&lt;p&gt;A composer wants a real project to score.&lt;/p&gt;
&lt;p&gt;A designer has an idea, but needs developers to help bring it to life.&lt;/p&gt;
&lt;p&gt;Sometimes these people are in the &lt;strong&gt;same community&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Sometimes they’re only a few posts apart.&lt;/p&gt;
&lt;p&gt;And somehow, they still never find each other.&lt;/p&gt;
&lt;p&gt;That problem is why we built &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://kolaboratr.com&quot;&gt;Kolaboratr&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;{% embed &lt;a rel=&quot;nofollow&quot; href=&quot;https://youtube.com/shorts/NFo2aRf9ZYw?si=XcF9ijJWNIWQr0hG&quot;&gt;https://youtube.com/shorts/NFo2aRf9ZYw?si=XcF9ijJWNIWQr0hG&lt;/a&gt; %}&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;what-is-kolaboratr-&quot;&gt;What Is Kolaboratr?&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Kolaboratr is a creator collaboration platform built to help people find projects, collaborators, and teams.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It’s designed for creators such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Developers&lt;/li&gt;
&lt;li&gt;Artists&lt;/li&gt;
&lt;li&gt;Writers&lt;/li&gt;
&lt;li&gt;Designers&lt;/li&gt;
&lt;li&gt;Composers&lt;/li&gt;
&lt;li&gt;Animators&lt;/li&gt;
&lt;li&gt;Project leads&lt;/li&gt;
&lt;li&gt;Other people trying to build something with a team&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Project leads can create a &lt;strong&gt;Kolab&lt;/strong&gt;, explain what they’re building, list the roles they need, describe the collaboration or compensation arrangement, and manage applications and invitations.&lt;/p&gt;
&lt;p&gt;Creators can build profiles around their:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Roles&lt;/li&gt;
&lt;li&gt;Skills&lt;/li&gt;
&lt;li&gt;Tools&lt;/li&gt;
&lt;li&gt;Experience&lt;/li&gt;
&lt;li&gt;Availability&lt;/li&gt;
&lt;li&gt;Portfolio&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The goal is simple:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Put the people looking for collaborators and the people looking for projects in the same place.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id=&quot;we-re-starting-at-zero-and-we-re-not-hiding-it&quot;&gt;We’re Starting at Zero — And We’re Not Hiding It&lt;/h3&gt;
&lt;p&gt;Kolaboratr is &lt;strong&gt;brand new&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;That means there aren't thousands of creator profiles already waiting inside.&lt;/p&gt;
&lt;p&gt;There aren't hundreds of projects filling every category.&lt;/p&gt;
&lt;p&gt;We could try to make the platform &lt;em&gt;look&lt;/em&gt; established.&lt;/p&gt;
&lt;p&gt;We'd rather tell people the truth.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;We're building the first community right now.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We’re looking for the first real:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Developers looking for projects&lt;/li&gt;
&lt;li&gt;Artists looking for teams&lt;/li&gt;
&lt;li&gt;Writers looking for collaborators&lt;/li&gt;
&lt;li&gt;Composers looking for games to score&lt;/li&gt;
&lt;li&gt;Designers looking for people to build with&lt;/li&gt;
&lt;li&gt;Project leads actively trying to form teams&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you join now, you aren't arriving after the community has already been established.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;You're helping establish it.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;finding-someone-is-only-the-beginning&quot;&gt;Finding Someone Is Only the Beginning&lt;/h3&gt;
&lt;p&gt;Anyone who has worked on a collaborative project knows the problems don't disappear once somebody says:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;em&gt;“Sure, I’ll join.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Then come the other questions.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Who owns the work?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Is the project paid, unpaid, or revenue share?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How is revenue divided?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Who gets credit?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Can contributors use their work in their portfolios?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What happens if somebody leaves?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What happens if the project gets cancelled?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;And perhaps the most dangerous question:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;“Wait... what did we actually agree to?”&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That’s why Kolaboratr also includes &lt;strong&gt;Kolaboratr Kontracts&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;kolaboratr-kontracts&quot;&gt;Kolaboratr Kontracts&lt;/h3&gt;
&lt;p&gt;Kontracts give collaborators a way to document important project terms, including:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Compensation&lt;/li&gt;
&lt;li&gt;Revenue sharing&lt;/li&gt;
&lt;li&gt;Existing asset ownership&lt;/li&gt;
&lt;li&gt;Ownership of new contributions&lt;/li&gt;
&lt;li&gt;Licensing&lt;/li&gt;
&lt;li&gt;Credit&lt;/li&gt;
&lt;li&gt;Portfolio rights&lt;/li&gt;
&lt;li&gt;Expected time commitments&lt;/li&gt;
&lt;li&gt;What happens if someone leaves&lt;/li&gt;
&lt;li&gt;What happens if the project is cancelled&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Both parties review and sign the &lt;strong&gt;same version&lt;/strong&gt; of the agreement.&lt;/p&gt;
&lt;p&gt;Once finalized, that version is preserved as a record of what everyone actually accepted.&lt;/p&gt;
&lt;p&gt;Kolaboratr Kontracts are &lt;strong&gt;not a substitute for professional legal advice&lt;/strong&gt; when an attorney is appropriate.&lt;/p&gt;
&lt;p&gt;They &lt;em&gt;are&lt;/em&gt; a way to prevent important collaboration terms from existing only inside scattered DMs, Discord messages, and forgotten conversations.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;built-by-vasken-interactive-studios&quot;&gt;Built by Vasken Interactive Studios&lt;/h3&gt;
&lt;p&gt;Kolaboratr is a product of &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://vaskeninteractivestudios.com&quot;&gt;Vasken Interactive Studios LLC&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;We’re an independent game and software studio creating original games, apps, and creator-focused tools.&lt;/p&gt;
&lt;p&gt;Our projects include games currently in development such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;House of Brats&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Last Haul&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Shadow Realms&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We're also building practical and creative software including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Trucker Suite&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PremiseForge&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Kolaboratr&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Different projects. Different audiences.&lt;/p&gt;
&lt;p&gt;But there's a common theme:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Take an idea and turn it into something real.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Kolaboratr grew directly out of one obstacle that keeps appearing when people try to do exactly that.&lt;/p&gt;
&lt;p&gt;Having ideas isn't usually the problem.&lt;/p&gt;
&lt;p&gt;Having work to do isn't the problem.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Finding the right people who actually want to build something with you can be.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-founding-kolaboratrs&quot;&gt;The Founding Kolaboratrs&lt;/h3&gt;
&lt;p&gt;Because we're building the initial community now, the &lt;strong&gt;first 1,000 members&lt;/strong&gt; receive:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;One full year of full access free&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No payment card required&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;A permanent &lt;strong&gt;Founding Kolaboratr&lt;/strong&gt; badge&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That Founding badge stays with the account permanently.&lt;/p&gt;
&lt;p&gt;It marks the creators who were there while the community was still being built.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;we-want-the-rough-edge-feedback&quot;&gt;We Want the Rough-Edge Feedback&lt;/h3&gt;
&lt;p&gt;This isn't a polished corporate announcement followed by:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;em&gt;“Thanks for using our product. Please submit feedback into the void.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We're still actively building.&lt;/p&gt;
&lt;p&gt;If you're one of the first people using Kolaboratr and something is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Confusing&lt;/li&gt;
&lt;li&gt;Awkward&lt;/li&gt;
&lt;li&gt;Broken&lt;/li&gt;
&lt;li&gt;Missing&lt;/li&gt;
&lt;li&gt;Unnecessary&lt;/li&gt;
&lt;li&gt;Harder than it should be&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Tell us.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;We especially want to hear from people who are genuinely trying to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Find a project&lt;/li&gt;
&lt;li&gt;Recruit collaborators&lt;/li&gt;
&lt;li&gt;Build an indie team&lt;/li&gt;
&lt;li&gt;Organize a creative project&lt;/li&gt;
&lt;li&gt;Work across different creative disciplines&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The first community has a real opportunity to influence how Kolaboratr develops.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;find-your-people&quot;&gt;Find Your People&lt;/h3&gt;
&lt;p&gt;If you're building something and missing the right person...&lt;/p&gt;
&lt;p&gt;Or you're the person looking for something worth building...&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Come help us build the first Kolaboratr community.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt; &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://kolaboratr.com&quot;&gt;Join Kolaboratr&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Find your people. Build something together.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;— &lt;strong&gt;Vasken Interactive Studios LLC&lt;/strong&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/27686/we-built-kolaboratr-because-finding-the-right-people-to-build-with-shouldnt-be-this-hard</guid>
<pubDate>Sat, 19 Sep 2026 02:44:16 +0000</pubDate>
</item>
<item>
<title>I built a real MNA circuit solver from scratch, then wrapped it in Three.js</title>
<link>https://coderlegion.com/27600/i-built-a-real-mna-circuit-solver-from-scratch-then-wrapped-it-in-three-js</link>
<description>&lt;p&gt;The problem with &quot;circuit simulators&quot; on the web&lt;/p&gt;
&lt;p&gt;Most browser-based circuit tools either fake it (animate current flow without real math) or wrap an existing SPICE engine. I wanted to actually implement the math myself — a Modified Nodal Analysis (MNA) solver, the same technique real SPICE tools use under the hood — and then give it a 3D interface instead of the usual flat 2D schematic.&lt;/p&gt;
&lt;p&gt;The result is CircuitForge: place components on a 3D breadboard, wire them up, and get real computed node voltages and branch currents — not an animation.&lt;/p&gt;
&lt;p&gt;The actual solver&lt;/p&gt;
&lt;p&gt;MNA builds a matrix from Kirchhoff's Current Law at every node, plus extra equations for voltage sources, then solves it. Stripped down, the core loop looks roughly like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const g = 1 / resistance;
matrix[nodeA][nodeA] += g;
matrix[nodeB][nodeB] += g;
matrix[nodeA][nodeB] -= g;
matrix[nodeB][nodeA] -= g;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Solve Ax = z for the node voltage vector, and you get exact values — not an approximation. I validated it against hand-calculated results on a few real circuits (simple divider, diode network, a series-parallel KCL cross-check) before trusting it in production. Full writeup with the actual worked numbers is here: luvaai.in/learn/circuit-simulator-accuracy-validation.&lt;/p&gt;
&lt;p&gt;Then the fun part: making it 3D&lt;/p&gt;
&lt;p&gt;Three.js handles the scene, camera, and raycasting for placing/wiring components. The tricky bit wasn't rendering — it was making wire connections feel physical: snapping to component pins, detecting valid connection points, and keeping the 3D positions in sync with the underlying circuit graph the solver actually operates on. Two completely separate data models (3D scene graph vs. circuit netlist) that have to stay perfectly consistent.&lt;/p&gt;
&lt;p&gt;What it can't do (yet)&lt;/p&gt;
&lt;p&gt;Being upfront: this is DC operating-point analysis only right now — no transient simulation, no AC frequency sweeps. If you need to see a capacitor charge curve over time, this isn't there yet. It's on the roadmap, but I'd rather ship an honestly-scoped DC solver than fake a transient one.&lt;/p&gt;
&lt;p&gt;Try it&lt;/p&gt;
&lt;p&gt;Live at luvaai.in — no signup needed, just start placing components. If you've built anything with raycasting-based node/wire editors, or have thoughts on extending MNA to transient analysis, I'd love to hear it in the comments.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/27600/i-built-a-real-mna-circuit-solver-from-scratch-then-wrapped-it-in-three-js</guid>
<pubDate>Fri, 18 Sep 2026 11:23:57 +0000</pubDate>
</item>
<item>
<title>Two years of pelicans on bicycles: 103 SVGs, 66 models, and what the guy who invented the benchmark says about it</title>
<link>https://coderlegion.com/27565/two-years-pelicans-bicycles-svgs-models-and-what-guy-who-invented-the-benchmark-says-about</link>
<description>&lt;p&gt;In October 2024, Simon Willison gave sixteen LLMs the same eight words:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Generate an SVG of a pelican riding a bicycle.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;He has been running it on every new model since. Twenty-three months later there are 103 of these drawings scattered across dozens of his posts, and he has written a section explaining why you should not use his benchmark to compare models.&lt;/p&gt;
&lt;p&gt;We pulled all of them into one page — &lt;a rel=&quot;nofollow&quot; href=&quot;https://pelicanzoo.ai&quot;&gt;pelicanzoo.ai&lt;/a&gt; — because reading them in order turns out to be a surprisingly good history of the last two years. This post is what we learned putting it together, and how the thing is built.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Everything here is Simon's work.&lt;/strong&gt; The images, the commentary and the judgement calls are his; we mirrored and organised them, and every specimen links back to the post it came from.&lt;/p&gt;
&lt;h3 id=&quot;the-prompt-is-deliberately-unfair&quot;&gt;The prompt is deliberately unfair&lt;/h3&gt;
&lt;p&gt;His stated reasons are mundane: he likes pelicans (he lives in Half Moon Bay, they're on the beach), and he was confident no SVG of a pelican on a bicycle existed in any training set.&lt;/p&gt;
&lt;p&gt;The reasons it &lt;em&gt;works&lt;/em&gt; are better. LLMs are text models that can't see, but they can write code, and SVG is code. So the task is to assemble a picture out of coordinates and radii with no feedback loop — the model never sees what it drew. Bicycles are hard for humans to draw from memory (try it: you'll get the frame wrong). And, in his words:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Pelicans can't ride bicycles. They don't have the right shape.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There's a bonus that matters more than it sounds: models leave comments in the SVG. &lt;code&gt;&amp;lt;!-- wheel --&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;!-- beak --&amp;gt;&lt;/code&gt;. You can read what it &lt;em&gt;meant&lt;/em&gt; to draw and compare it against what came out. The gap is usually the joke.&lt;/p&gt;
&lt;h3 id=&quot;2024-it-s-mostly-shapes&quot;&gt;2024: it's mostly shapes&lt;/h3&gt;
&lt;p&gt;The early attempts are bad in specific, funny ways. Claude 3.5 Sonnet produced two circles, a triangle, and a beak Simon described as a yellow banana smile. Llama 3.3 70B drew a small circle, a vertical line, and something shaped like a sink. Qwen2.5-Coder's bicycle is a pile of brown shapes that reads as a tractor. Mistral Small 3 delivered a squat white duck squatting on a barbell.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pelicanzoo.ai/specimen/ai-worlds-fair-2025-11.jpeg&quot; alt=&quot;Mistral Small 3's duck on a barbell&quot;&gt;&lt;/p&gt;
&lt;p&gt;Then DeepSeek R1 landed in January 2025, and shortly afterwards Nvidia lost about $600bn in market cap. Simon later singled its pelican out as &quot;the pelican that crashed the stock market&quot; — the best one so far: a recognisable bicycle, a bird next to it that's arguably a pelican, not actually riding it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pelicanzoo.ai/specimen/ai-worlds-fair-2025-10.jpeg&quot; alt=&quot;DeepSeek R1's pelican&quot;&gt;&lt;/p&gt;
&lt;p&gt;For that first year the drawings tracked real capability fairly well. Models that drew better pelicans were better at writing code. A joke about how silly model comparisons are started behaving like a real benchmark.&lt;/p&gt;
&lt;h3 id=&quot;the-560-match-tournament-that-cost-18-cents&quot;&gt;The 560-match tournament that cost 18 cents&lt;/h3&gt;
&lt;p&gt;By June 2025 he had 34 pelicans and wanted a ranking without looking at 34 images himself.&lt;/p&gt;
&lt;p&gt;So he had Claude write a tool that renders any two pelicans side by side and screenshots the pair. 34 images, every pairing, 560 matches. Each match went to GPT-4.1 mini with instructions to pick the better &quot;pelican riding a bicycle&quot; illustration and explain why. Elo scores out of the results.&lt;/p&gt;
&lt;p&gt;Total judging cost: &lt;strong&gt;about 18 cents.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pelicanzoo.ai/specimen/ai-worlds-fair-2025-32.jpeg&quot; alt=&quot;Tournament winner and loser&quot;&gt;&lt;/p&gt;
&lt;p&gt;Winner was a Gemini 2.5 Pro preview; last place, Llama 3.3 70B. The judge's reasoning for that matchup: the left image clearly depicts a pelican riding a bicycle, the right is a few minimal shapes that don't read as anything.&lt;/p&gt;
&lt;p&gt;The same talk includes the cost comparison that stuck with me. o1-pro: &lt;strong&gt;88.755 cents&lt;/strong&gt; for one pelican. Gemini 2.5 Pro: &lt;strong&gt;4.77 cents&lt;/strong&gt;. Gemini's was better.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pelicanzoo.ai/specimen/ai-worlds-fair-2025-18.jpeg&quot; alt=&quot;o1-pro at 88 cents&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;the-models-start-editorialising&quot;&gt;The models start editorialising&lt;/h3&gt;
&lt;p&gt;August 2025, Simon runs Qwen3-4B-Thinking locally. It declines to draw a bicycle. It returns a blue circle with red text across it:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;This is art - pelicans don't ride bikes!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;He made that the headline of the post.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pelicanzoo.ai/specimen/qwen3-4b-thinking-pelican.png&quot; alt=&quot;Qwen3-4B-Thinking: &amp;quot;This is art&amp;quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;In November, Gemini 3 at low reasoning effort gave its pelican a little hat, with an SVG comment reading &lt;code&gt;hat (optional fun detail)&lt;/code&gt;. At high effort the frame geometry was finally correct.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pelicanzoo.ai/specimen/gemini-3-pelican-low.png&quot; alt=&quot;Gemini 3, low effort, wearing a hat&quot;&gt;&lt;/p&gt;
&lt;p&gt;He decided the task had gotten too easy and escalated: a California brown pelican, bicycle with spokes and a correct frame, large gular pouch, visible feather texture, unambiguously pedalling, in breeding plumage. He attached a photo he took himself.&lt;/p&gt;
&lt;p&gt;Most models missed the detail that makes it a trick question — the California brown pelican isn't brown.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pelicanzoo.ai/specimen/gemini-3-breeding-pelican-high.png&quot; alt=&quot;Gemini 3 high effort on the harder prompt&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;the-laptop-model-that-beat-opus&quot;&gt;The laptop model that beat Opus&lt;/h3&gt;
&lt;p&gt;April 16, 2026: Qwen3.6-35B-A3B and Claude Opus 4.7 shipped the same day. Simon ran Qwen as a 20.9GB quantised model on his MacBook and Opus through the API.&lt;/p&gt;
&lt;p&gt;He gave it to Qwen. Opus got the frame geometry wrong, and got it wrong again at maximum reasoning effort.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pelicanzoo.ai/specimen/Qwen3.6-35B-A3B-UD-Q4_K_S-pelican.png&quot; alt=&quot;Qwen3.6-35B-A3B, running on a laptop&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pelicanzoo.ai/specimen/opus-4.7-pelican.png&quot; alt=&quot;Claude Opus 4.7 — look at the frame&quot;&gt;&lt;/p&gt;
&lt;p&gt;This result made him suspicious of his own benchmark. There's a persistent theory that labs train against his &quot;stupid test&quot;, and he'd been keeping a spare prompt in reserve for exactly this moment. He burned it: &lt;strong&gt;a flamingo riding a unicycle.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Qwen's flamingo wears sunglasses and a bow tie, appears to be smoking, and is flanked by heart emoji and the caption &quot;Flamingo on a Unicycle&quot;. The source comment says &lt;code&gt;Give the flamingo sunglasses!&lt;/code&gt;. Opus drew a competent, entirely unremarkable flamingo. That one went to Qwen as well.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pelicanzoo.ai/specimen/qwen-flamingo.png&quot; alt=&quot;Qwen's flamingo&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://pelicanzoo.ai/specimen/opus-flamingo.png&quot; alt=&quot;Opus's flamingo&quot;&gt;&lt;/p&gt;
&lt;p&gt;His own framing was careful: he has enormous respect for Qwen, and he does not believe a 21GB quantised model is more useful than Anthropic's latest flagship. But if the thing you want is an SVG of a pelican on a bicycle, the laptop won that day.&lt;/p&gt;
&lt;p&gt;That's roughly where pelican quality and model capability stop tracking each other.&lt;/p&gt;
&lt;h3 id=&quot;the-inventor-says-don-t-use-it&quot;&gt;The inventor says don't use it&lt;/h3&gt;
&lt;p&gt;Kimi K3, July 2026. Its pelican burned 13,241 reasoning tokens and cost 25 cents. Simon used the post to write a section on what the benchmark is still good for.&lt;/p&gt;
&lt;p&gt;His conclusion after 21 months: it was never a good benchmark. The correlation in year one was a surprise and it's basically gone now — GLM-5.2 draws a better pelican than GPT-5.6 or Claude Fable 5, and he doesn't think GLM is in that class. More importantly it tests nothing about what actually matters today, namely whether a model can hold a long conversation and call tools reliably. His words: &lt;strong&gt;don't use pelicans to compare models.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;So why keep going? Two reasons, both practical:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It forces him to actually run each new model. Posting a pelican is proof he got the thing working, rather than just reading the launch post.&lt;/li&gt;
&lt;li&gt;Even one prompt leaks information. On the Kimi K3 run he noticed his eight-word prompt was billed as 95 input tokens, and sending just &quot;hi&quot; cost 86 — implying about 85 tokens of hidden system prompt.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;September 1, 2026: Claude Fable 5.1, across five reasoning levels. Low and medium barely think and finish in about twenty seconds. Maximum ran &lt;strong&gt;13 minutes 54 seconds&lt;/strong&gt;, emitted 65,927 tokens, cost &lt;strong&gt;$3.30&lt;/strong&gt;, and produced a pelican in a blue cap with a fish in the basket. Best pelican he's seen from Anthropic.&lt;/p&gt;
&lt;p&gt;The benchmark has become a ritual rather than a measurement. It won't tell you which model is strongest. One pelican's invoice and reasoning trace will still tell you a lot about how a model works, and whether it'll decide on its own to put a hat on your bird.&lt;/p&gt;
&lt;h3 id=&quot;building-the-zoo&quot;&gt;Building the zoo&lt;/h3&gt;
&lt;p&gt;The drawings live across two years of posts, which makes reading them in sequence tedious. So: one page, 103 specimens, 66 models, 12 vendors, October 2024 to now. Each one is tagged with model and date and links back to Simon's original post.&lt;/p&gt;
&lt;p&gt;A few things that turned out to be more interesting than expected:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;24 of them are still vector.&lt;/strong&gt; Where the original SVG survives, the page serves the SVG — zoom in as far as you like, and four of them are animated (wheels turning, pelican bobbing). The rest exist only as screenshots from his conference slides, so they're labelled &lt;em&gt;print&lt;/em&gt; rather than &lt;em&gt;live&lt;/em&gt;. Making that distinction visible mattered more than we expected; a screenshot of an SVG and the SVG are not the same artifact, and the ones that are still code are the ones you can actually inspect.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The critic reads source, not pixels.&lt;/strong&gt; There's a button under each specimen that asks a model to review the drawing — and it's given the SVG source, not an image. Half of that is cost. The other half is that it's funnier and more specific: it can complain that the body is an ellipse of radius 104, or that the rear wheel is out of proportion with the frame. Using models to review models closes a loop that probably shouldn't be closed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Submissions have no backend.&lt;/strong&gt; You can paste in an SVG a model drew for you. The page sanitises it in the browser, then generates a pre-filled GitHub pull request. No server, no database — one pelican is one file. It's a static site on Cloudflare.&lt;/p&gt;
&lt;p&gt;The sanitiser is the part I'd actually defend in review. Model-generated SVG pasted in by strangers is untrusted markup, so it strips &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;foreignObject&amp;gt;&lt;/code&gt;, &lt;code&gt;iframe&lt;/code&gt;/&lt;code&gt;object&lt;/code&gt;/&lt;code&gt;embed&lt;/code&gt;, inline &lt;code&gt;on*&lt;/code&gt; handlers, &lt;code&gt;javascript:&lt;/code&gt; URLs, &lt;code&gt;@import&lt;/code&gt;, and any remote &lt;code&gt;href&lt;/code&gt;/&lt;code&gt;src&lt;/code&gt;/&lt;code&gt;url()&lt;/code&gt; reference. Data URIs stay — they're self-contained. Remote references go because they leak the visitor's IP to whoever hosts them and let someone swap the picture out later.&lt;/p&gt;
&lt;p&gt;Two design decisions in there I'd repeat:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// It reports what it removed instead of only returning a cleaned string.
// @returns {{ ok, svg, removed: string[], error }}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The same function runs in three places — the static build, the browser preview, and CI on incoming pull requests. Because the site strips &lt;em&gt;before&lt;/em&gt; it builds the PR, a submission that still arrives dirty was hand-crafted, so CI stops it for a human to read rather than silently cleaning it. A sanitiser that returns a clean string and nothing else throws away the signal that someone was trying.&lt;/p&gt;
&lt;p&gt;The other one is dull and bit us first:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// Percentages need something to be a percentage of; without a viewBox an SVG
// whose width/height we strip will stretch to fill whatever box it lands in.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We strip fixed &lt;code&gt;width&lt;/code&gt;/&lt;code&gt;height&lt;/code&gt; so specimens scale in a grid. Do that to an SVG with no &lt;code&gt;viewBox&lt;/code&gt; and it stretches to fill its container. So the sanitiser synthesises one from the original dimensions when it's missing.&lt;/p&gt;
&lt;p&gt;There's also a guessing game — you get a drawing and four model names, 73 rounds over 51 models. Distractors are picked from &lt;em&gt;different&lt;/em&gt; vendors on purpose. Three Gemini variants side by side isn't difficult, it's just unfair. The 2024 ones are easy. The 2026 ones are basically a coin flip, which is its own kind of result.&lt;/p&gt;
&lt;h3 id=&quot;credit&quot;&gt;Credit&lt;/h3&gt;
&lt;p&gt;The images and the original keeper's notes are Simon Willison's, collected from his posts, and the footer links back to &lt;a rel=&quot;nofollow&quot; href=&quot;https://simonwillison.net/tags/pelican-riding-a-bicycle/&quot;&gt;his pelican-riding-a-bicycle tag&lt;/a&gt;. We built the cage, not the birds.&lt;/p&gt;
&lt;p&gt;If you've got one that's especially good, or especially wrong, the submission box takes it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://pelicanzoo.ai&quot;&gt;pelicanzoo.ai&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h4 id=&quot;sources&quot;&gt;Sources&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://simonwillison.net/2024/Oct/25/pelicans-on-a-bicycle/&quot;&gt;Pelicans on a bicycle&lt;/a&gt; (2024-10-25)&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://simonwillison.net/2025/Jun/6/six-months-in-llms/&quot;&gt;The last six months in LLMs, illustrated by pelicans on bicycles&lt;/a&gt; (2025-06-06)&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://simonwillison.net/2025/Nov/18/gemini-3/&quot;&gt;Trying out Gemini 3 Pro with audio transcription and a new pelican benchmark&lt;/a&gt; (2025-11-18)&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://simonwillison.net/2026/Apr/16/qwen-beats-opus/&quot;&gt;Qwen3.6-35B-A3B on my laptop drew me a better pelican than Claude Opus 4.7&lt;/a&gt; (2026-04-16)&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://simonwillison.net/2026/Jul/16/kimi-k3/&quot;&gt;Kimi K3, and what we can still learn from the pelican benchmark&lt;/a&gt; (2026-07-16)&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://simonwillison.net/2026/Sep/1/claude-fable-5-1/&quot;&gt;Claude Fable 5.1 made me a really nice animated pelican&lt;/a&gt; (2026-09-01)&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/27565/two-years-pelicans-bicycles-svgs-models-and-what-guy-who-invented-the-benchmark-says-about</guid>
<pubDate>Fri, 18 Sep 2026 01:30:04 +0000</pubDate>
</item>
<item>
<title>Turning YouTube Videos Into Obsidian Notes — and What Shipping the Plugin Taught Me</title>
<link>https://coderlegion.com/27564/turning-youtube-videos-into-obsidian-notes-and-what-shipping-the-plugin-taught-me</link>
<description>&lt;p&gt;Every so often I'd watch a 90-minute conference talk, close the tab, and realise the only trace it left in my vault was a URL in a list called &quot;watch later (again)&quot;.&lt;/p&gt;
&lt;p&gt;So I built an Obsidian plugin that turns a video into an actual note. It's been in the community directory since September and is on 0.3.0 now. This is what it does, and — more usefully — the handful of decisions and review rules I'd want to know before building another one.&lt;/p&gt;
&lt;h3 id=&quot;what-lands-in-the-vault&quot;&gt;What lands in the vault&lt;/h3&gt;
&lt;p&gt;One command, one link, and you get a note:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://summarizevideototext.com/obsidian-plugin/note.png&quot; alt=&quot;A video note in Obsidian: embedded player, TL;DR callout and key insights&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;a TL;DR callout and key insights&lt;/li&gt;
&lt;li&gt;an embedded player at the top — timestamps seek it in place, without leaving the note&lt;/li&gt;
&lt;li&gt;chapters as timestamped bullets&lt;/li&gt;
&lt;li&gt;optional summary (several templates), quiz, and full transcript in a collapsed callout&lt;/li&gt;
&lt;li&gt;frontmatter with title, source, channel, language and tags, shaped for Dataview&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;https://summarizevideototext.com/obsidian-plugin/chapters.png&quot; alt=&quot;Key insights and chapters with timestamp links&quot;&gt;&lt;/p&gt;
&lt;p&gt;Plus a right-sidebar chat panel scoped to the note you're reading, and a command that turns the quiz into &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/st3v3nmw/obsidian-spaced-repetition&quot;&gt;Spaced Repetition&lt;/a&gt; cards.&lt;/p&gt;
&lt;p&gt;Now the parts that were actually decisions.&lt;/p&gt;
&lt;h3 id=&quot;the-plugin-holds-no-model-keys&quot;&gt;The plugin holds no model keys&lt;/h3&gt;
&lt;p&gt;An Obsidian plugin runs on the user's machine. You can't ship an API key in it, and asking every user to paste their own is both friction and a support queue you will personally staff forever.&lt;/p&gt;
&lt;p&gt;So the plugin generates nothing. It sends a video link and an account token to a server, and writes down what comes back. That's the whole client.&lt;/p&gt;
&lt;p&gt;The honest trade-off: this means the plugin needs an account, which is a real cost to the user and something I put in the README under a &lt;strong&gt;Before you install&lt;/strong&gt; heading rather than burying. A plugin that silently requires a signup is a plugin that gets one-starred.&lt;/p&gt;
&lt;h3 id=&quot;the-connect-handshake-and-the-one-line-that-makes-it-safe&quot;&gt;The connect handshake, and the one line that makes it safe&lt;/h3&gt;
&lt;p&gt;Settings → &lt;strong&gt;Connect&lt;/strong&gt; opens the browser at &lt;code&gt;/connect/obsidian?state=…&amp;amp;vault=…&lt;/code&gt;. You sign in, the server mints a token, and the page bounces you back via &lt;code&gt;obsidian://svt-connect?token=…&amp;amp;state=…&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;state&lt;/code&gt; is generated by the plugin and checked when the redirect comes home. Without that check, &lt;strong&gt;any web page could deep-link a token into your vault&lt;/strong&gt; — the &lt;code&gt;obsidian://&lt;/code&gt; scheme is open to anyone. It's four lines of code and it's the difference between a handshake and a hole.&lt;/p&gt;
&lt;p&gt;Server side the token is stored as a sha256 hash. Client side it lands in &lt;code&gt;.obsidian/plugins/&amp;lt;id&amp;gt;/data.json&lt;/code&gt;, which is worth saying out loud in your docs: if the user syncs their vault, that file goes with it.&lt;/p&gt;
&lt;h3 id=&quot;one-note-format-two-producers&quot;&gt;One note format, two producers&lt;/h3&gt;
&lt;p&gt;The website also has an &lt;em&gt;Export → Obsidian&lt;/em&gt; button. That's two code paths producing &quot;the same&quot; Markdown, which in my experience drift apart within about a week.&lt;/p&gt;
&lt;p&gt;So the Markdown is assembled in exactly one place — a server endpoint — and the plugin writes whatever it receives verbatim. The website button and the plugin produce identical notes because they are, literally, the same function call.&lt;/p&gt;
&lt;h3 id=&quot;re-running-a-command-must-not-eat-the-user-s-writing&quot;&gt;Re-running a command must not eat the user's writing&lt;/h3&gt;
&lt;p&gt;People add their own notes under the generated stuff. Refreshing a note has to be non-destructive or nobody will ever press it twice.&lt;/p&gt;
&lt;p&gt;The generated region is fenced:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-markdown&quot;&gt;%% svt:start %%
…everything the server produced…
%% svt:end %%

## My notes
whatever you wrote, untouched
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Refresh replaces only what's between the markers. Frontmatter is the subtler half: the server returns an &lt;code&gt;ownedKeys&lt;/code&gt; list alongside the note, so the plugin overwrites the keys it owns and leaves every key you added alone. Writes go through &lt;code&gt;vault.process&lt;/code&gt; rather than read-then-write, which is also what the community review expects.&lt;/p&gt;
&lt;h3 id=&quot;requesturl-doesn-t-stream&quot;&gt;&lt;code&gt;requestUrl&lt;/code&gt; doesn't stream&lt;/h3&gt;
&lt;p&gt;Obsidian's &lt;code&gt;requestUrl&lt;/code&gt; sidesteps CORS, which you want, because your plugin isn't an origin anyone will whitelist. What it doesn't do is stream.&lt;/p&gt;
&lt;p&gt;My summary endpoint is server-sent events. So the plugin waits for the whole response and parses the event stream after the fact. You lose the progressive typewriter effect; you keep mobile support and never fight a preflight. Worth knowing before you architect around a stream that won't arrive.&lt;/p&gt;
&lt;h3 id=&quot;flashcards-and-only-appending&quot;&gt;Flashcards, and only appending&lt;/h3&gt;
&lt;p&gt;0.3.0 turns the quiz into a card note: multi-line format, the right answer and its explanation on the back, and a timestamp link to the moment the question came from.&lt;/p&gt;
&lt;p&gt;Run it again after new questions appear and only the missing cards are appended. Existing cards, their &lt;code&gt;&amp;lt;!--SR:…--&amp;gt;&lt;/code&gt; review schedules, and any cards you wrote yourself are never touched. Rewriting a review schedule is the fastest way to lose a user who has been reviewing for six months.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://summarizevideototext.com/obsidian-plugin/flashcards.png&quot; alt=&quot;A card note ready for the Spaced Repetition plugin&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;what-the-community-directory-review-actually-checks&quot;&gt;What the community directory review actually checks&lt;/h3&gt;
&lt;p&gt;Submitting is no longer a PR against &lt;code&gt;obsidian-releases&lt;/code&gt;. You log in at &lt;a rel=&quot;nofollow&quot; href=&quot;https://community.obsidian.md&quot;&gt;community.obsidian.md&lt;/a&gt;, link GitHub, and an automated reviewer runs. Mine came back with a list. The useful items:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;No inline styles.&lt;/strong&gt; Anything you'd reach for as &lt;code&gt;el.style.foo = bar&lt;/code&gt; belongs in &lt;code&gt;styles.css&lt;/code&gt;. My auto-growing textarea became the CSS &lt;code&gt;field-sizing: content&lt;/code&gt; instead of a resize handler, which is a better implementation anyway.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Settings must use the declarative 1.13 API&lt;/strong&gt; (&lt;code&gt;getSettingDefinitions()&lt;/code&gt; with groups, &lt;code&gt;getControlValue&lt;/code&gt; / &lt;code&gt;setControlValue&lt;/code&gt;). That forced &lt;code&gt;minAppVersion&lt;/code&gt; up to &lt;code&gt;1.13.0&lt;/code&gt;, because the &lt;em&gt;no-unsupported-api&lt;/em&gt; rule rejects 1.13 APIs while you claim to support older versions. Pick a lane; you can't straddle.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Attest your build.&lt;/strong&gt; &lt;code&gt;actions/attest-build-provenance&lt;/code&gt; in the release workflow, with &lt;code&gt;id-token&lt;/code&gt; and &lt;code&gt;attestations&lt;/code&gt; write permissions. &lt;code&gt;gh attestation verify main.js --owner &amp;lt;you&amp;gt;&lt;/code&gt; exiting 0 with no output is what success looks like.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The tag must match the manifest version&lt;/strong&gt;, and &lt;code&gt;npm version&lt;/code&gt; writes tags with a &lt;code&gt;v&lt;/code&gt; prefix that Obsidian doesn't want. &lt;code&gt;git tag -d v0.2.2 &amp;amp;&amp;amp; git tag 0.2.2&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One false alarm worth pre-empting: right after I re-cut a release, the checker insisted &quot;No release matches your manifest version.&quot; It had scanned during the draft window and cached that. Re-scan, it passes. Don't go rewriting your workflow like I nearly did.&lt;/p&gt;
&lt;h3 id=&quot;three-obsidian-css-facts-that-cost-me-an-evening&quot;&gt;Three Obsidian CSS facts that cost me an evening&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;.view-content&lt;/code&gt; ships with &lt;code&gt;padding: 12px 12px 32px&lt;/code&gt;. If your custom view's layout looks mysteriously inset, that's why. Zero it with a same-specificity selector: &lt;code&gt;.workspace-leaf-content[data-type=&quot;…&quot;] .view-content.your-class&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;On desktop the status bar is &lt;code&gt;position: fixed&lt;/code&gt;, 27px tall, and floats &lt;strong&gt;over&lt;/strong&gt; the bottom of the right sidebar. Leave it room or your input box is permanently half-covered.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;button:not(.clickable-icon)&lt;/code&gt; picks up Obsidian's background and shadow. For a text link, use an &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;links-1&quot;&gt;Links&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Plugin: &lt;a rel=&quot;nofollow&quot; href=&quot;https://community.obsidian.md/plugins/summarize-video-to-text&quot;&gt;community.obsidian.md/plugins/summarize-video-to-text&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Source (MIT): &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/diaozxin007/obsidian-summarize-video-to-text&quot;&gt;github.com/diaozxin007/obsidian-summarize-video-to-text&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Setup walkthrough: &lt;a rel=&quot;nofollow&quot; href=&quot;https://summarizevideototext.com/obsidian-plugin&quot;&gt;summarizevideototext.com/obsidian-plugin&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you're building something that writes into other people's vaults, the markers-and-&lt;code&gt;ownedKeys&lt;/code&gt; pattern is the piece I'd steal. Everything else was replaceable; that one is what makes a destructive command safe to press twice.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/27564/turning-youtube-videos-into-obsidian-notes-and-what-shipping-the-plugin-taught-me</guid>
<pubDate>Fri, 18 Sep 2026 01:29:36 +0000</pubDate>
</item>
<item>
<title>I built an all-in-one AI workspace (Chat, Code, Live Web Research, Docs &amp; Images) — looking for tech</title>
<link>https://coderlegion.com/27521/i-built-an-all-in-one-ai-workspace-chat-code-live-web-research-docs-images-looking-for-tech</link>
<description>&lt;p&gt;Hi everyone,&lt;/p&gt;
&lt;p&gt;I wanted to share a project I've been working on to solve my own daily workflow frustration: &lt;strong&gt;SMS AI&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://sms-ai-delta.lovable.app&quot;&gt;SMS AI&lt;/a&gt;. &lt;/p&gt;
&lt;p&gt;The goal was to build a highly responsive, unified three-pane workspace where you can handle complex code generation, live web research, document parsing, and visual generation without constantly swapping tabs or losing your active chat state context.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What's Built-In Right Now:&lt;/strong&gt;&lt;br&gt;
*&lt;strong&gt;Asynchronous Token Streaming Chat:&lt;/strong&gt; Smooth, word-by-word token delivery with Markdown formatting and native syntax-highlighted code rendering blocks.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Intelligent Coding Assistant:&lt;/strong&gt; Formats, refactors, and explains code across multiple programming languages instantly without freezing the browser execution thread.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Live Web Research Integration:&lt;/strong&gt; Executes live web queries and dynamically infuses clickable, cited research sources straight into the conversational interface.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Document Parsing RAG Pipeline:&lt;/strong&gt; Drop in dense PDF, DOCX, or TXT documents to instantly run automated local passes to pull summaries and extract key text insights.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;In-Chat Multimedia Actions:&lt;/strong&gt; Native speech-to-text dictation mechanics alongside instant visual art generation using dedicated in-chat model toggles.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;-the-tech-stack-&quot;&gt;⚙️ The Tech Stack:&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Frontend UI Layout:&lt;/strong&gt; React + Vite + Tailwind CSS for a hyper-lightweight layout structure and fast initial load speeds.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Backend Cloud Infra:&lt;/strong&gt; Native Supabase integration to manage user session authentication states securely.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The workspace features a completely live &lt;strong&gt;100% free tier&lt;/strong&gt; (10 messages per day, no credit card required) so you can jump in and try it out. &lt;/p&gt;
&lt;p&gt;I’m looking to aggressively optimize the layout, performance, and API delivery speeds. Please throw some tough optimization tasks or complex data sheets at it. What features do you feel are missing? What feels slow? I'll be monitoring the comments to answer any technical questions about the architecture!&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/27521/i-built-an-all-in-one-ai-workspace-chat-code-live-web-research-docs-images-looking-for-tech</guid>
<pubDate>Thu, 17 Sep 2026 12:38:47 +0000</pubDate>
</item>
<item>
<title>I Love Fragrances, So I Built a 6-Game Arcade + Concierge About My Obsession</title>
<link>https://coderlegion.com/27057/i-love-fragrances-so-i-built-a-6-game-arcade-concierge-about-my-obsession</link>
<description>&lt;p&gt;Hi, my name's Ibrahim, I'm a university student, and I have a problem: I love fragrances way more than my bank account loves me for it.&lt;/p&gt;
&lt;p&gt;It started small, the way these things always do. A cheap Middle Eastern attar someone gave me as a gift, the kind that costs less than a coffee but somehow smells like it belongs in a much fancier bottle. Then another. Then I started actually reading about notes, pyramids, accords, sillage, the whole rabbit hole. Fast forward through a lot of saved-up allowance and skipped nights out, and I've now got about 20 bottles on my shelf. Mostly affordable Middle Eastern gems (some of them genuinely punch way above their price), with a small handful of designer pieces I saved up for and treat like trophies.&lt;/p&gt;
&lt;p&gt;If you're a fellow fragrance enthusiast, you already know the feeling: you don't just &quot;wear&quot; a scent, you &lt;em&gt;collect&lt;/em&gt; them, you &lt;em&gt;study&lt;/em&gt; them, you have opinions about whether a note is top, heart, or base and you will absolutely fight someone about it. That obsession is basically the entire reason this project exists.&lt;/p&gt;
&lt;p&gt;So when I saw the DEV Weekend Challenge's &quot;Passion&quot; prompt, there was only one thing I could possibly build.&lt;/p&gt;
&lt;h3 id=&quot;what-i-built-recommendmeafragrance&quot;&gt;What I built: recommendmeafragrance&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;recommendmeafragrance&lt;/strong&gt; is a browser arcade for fragrance nerds: six small daily games built around real perfume data (notes, brands, years, price tiers), plus an AI Concierge you can actually talk to about what you're in the mood for. Every game feeds into a personal &quot;shelf&quot; that tracks which fragrances you've discovered, plus streaks so you have a reason to come back tomorrow.&lt;/p&gt;
&lt;p&gt;Here's the tour.&lt;/p&gt;
&lt;h4 id=&quot;scentle-wordle-but-for-your-nose&quot;&gt;Scentle: Wordle, but for your nose&lt;/h4&gt;
&lt;p&gt;A new fragrance is picked every day (the same one for everyone, worldwide, no matter your timezone). You get 6 guesses, and after each one you get Wordle style feedback: was the brand exact or just the same house family, did the real answer come out earlier or later than your guess, is it pricier or cheaper, same gender, same concentration, how many notes do you actually share. You can even copy a little emoji share-grid at the end, because obviously.&lt;/p&gt;
&lt;p&gt;There's now an &lt;strong&gt;Easy/Hard toggle&lt;/strong&gt;: Easy leans on a famous, recognizable bottle for the day; Hard pulls from the deep catalog for people who want a real challenge.&lt;/p&gt;
&lt;h4 id=&quot;note-detective-reverse-engineer-a-scent-from-its-notes-only&quot;&gt;Note Detective: reverse engineer a scent from its notes only&lt;/h4&gt;
&lt;p&gt;This one's my favorite. You're given nothing but notes, revealed one at a time in rotating order (one top, one heart, one base, cycling through the pyramid), and you have to guess the perfume before your score hits zero. Every note you reveal costs points, but the cost is calculated as a percentage of that specific perfume's total note count, so a fragrance with 6 notes and one with 16 notes both cost the same fair share to fully reveal. As you burn through reveals, letters of the name itself slowly uncover hangman-style, so there's a nice tension between &quot;guess now&quot; and &quot;wait, I need one more hint.&quot;&lt;/p&gt;
&lt;p&gt;Also has an Easy/Hard mode, same famous-vs-deep-catalog split as Scentle.&lt;/p&gt;
&lt;h4 id=&quot;build-a-bottle-invent-your-own-scent-find-out-what-it-actually-is&quot;&gt;Build-a-Bottle — invent your own scent, find out what it actually is&lt;/h4&gt;
&lt;p&gt;Pick as many top, heart, and base notes as you want from a searchable list of hundreds of real notes, hit &quot;invent,&quot; and the app runs a weighted similarity match (base notes count extra, since they carry a scent's identity the most) against the whole catalog to tell you what you basically just invented, plus two runner-up matches. It also treats closely related notes as the same family (so picking &quot;vanilla&quot; still matches perfumes listed under &quot;Madagascar vanilla,&quot; etc.), because with hundreds of real notes in the system, exact-string-only matching would've been brutal.&lt;/p&gt;
&lt;h4 id=&quot;higher-or-lower&quot;&gt;Higher or Lower&lt;/h4&gt;
&lt;p&gt;Two perfumes, one question: which came out first, or which one's pricier? Keep guessing right to build a streak, one miss ends the run. Easy mode keeps the gap obvious and leans on famous bottles; Hard mode pulls from the deep catalog and can come down to a single year or price tier.&lt;/p&gt;
&lt;h4 id=&quot;scent-roulette&quot;&gt;Scent Roulette&lt;/h4&gt;
&lt;p&gt;Set some dials (season, occasion, how much chaos you want), pull the lever, get a fragrance recommendation that actually respects your filters instead of just being random.&lt;/p&gt;
&lt;h4 id=&quot;blind-date&quot;&gt;Blind Date&lt;/h4&gt;
&lt;p&gt;Swipe through fragrances blind, no brand or hype attached, just notes and vibe, and decide &quot;buy&quot; or &quot;skip.&quot; At the end it tells you your &quot;nose score,&quot; how many of your buys were actually the famous, universally-loved ones. Humbling, honestly.&lt;/p&gt;
&lt;h3 id=&quot;meet-the-concierge&quot;&gt;Meet the Concierge&lt;/h3&gt;
&lt;p&gt;Alongside the games there's a chat-style AI Concierge, built to feel like the app's little mascot come to life: warm, encouraging, genuinely curious about what you like, not just a recommendation-spitting bot. Tell it &quot;something warm and cozy for winter nights&quot; and it'll actually ask a follow-up question before throwing fragrances at you, and when it does recommend something, it comes with a real, buyable card attached, not just a name typed in a chat bubble.&lt;/p&gt;
&lt;h4 id=&quot;bringing-google-ai-into-it&quot;&gt;Bringing Google AI into it&lt;/h4&gt;
&lt;p&gt;This is where I'm submitting for the &lt;strong&gt;Best Use of Google AI&lt;/strong&gt; category. The Concierge runs on a shared daily budget by default so everyone gets to try it for free, but I added a &lt;strong&gt;&quot;bring your own Gemini API key&quot;&lt;/strong&gt; option: paste in a free key from Google AI Studio, and the Concierge switches over to running entirely on &lt;strong&gt;Gemini&lt;/strong&gt; for you, with zero daily limit. The key never touches our servers, it lives only in your browser's local storage and gets sent along with your own requests, so you're fully in control of it. It felt like the right way to let people who want unlimited access get it, without me needing to foot an unlimited AI bill as a student running this on personal savings.&lt;/p&gt;
&lt;h3 id=&quot;real-bottles-real-links&quot;&gt;Real bottles, real links&lt;/h3&gt;
&lt;p&gt;None of this matters if it's just a trivia game, so every single fragrance in the catalog (over a thousand of them) is linked to a live, working offer through FragranceShop.com. Win a game, discover something in the Concierge, or just click &quot;buy&quot; on a card, and it adds to your personal shelf and takes you straight to a real product you can actually purchase. It's a small, honest way to make an app about a hobby self-sustaining without shoving ads in your face.&lt;/p&gt;
&lt;h3 id=&quot;try-it-2&quot;&gt;Try it&lt;/h3&gt;
&lt;p&gt; &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.recommendmeafragrance.com/&quot;&gt;recommendmeafragrance.com&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;No pressure to sign up for anything just to poke around, though creating a free account (Google sign-in, ten seconds) saves your streaks and shelf across devices and unlocks the Concierge chat.&lt;/p&gt;
&lt;h3 id=&quot;closing-thoughts&quot;&gt;Closing thoughts&lt;/h3&gt;
&lt;p&gt;I've genuinely lost track of how many hours went into this between actual classwork, but every time I opened a new bottle from my (very modest, very Middle-Eastern-attar-heavy) collection for &quot;research,&quot; it felt worth it. If you're even a little bit of a fragrance nerd, or you just like small, weird, well-made web games, I'd love for you to come play and tell me what your nose score is.&lt;/p&gt;
&lt;h2 id=&quot;weekendchallenge-googleai-webdev-showdev&quot;&gt;weekendchallenge #googleai #webdev #showdev&lt;/h2&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/27057/i-love-fragrances-so-i-built-a-6-game-arcade-concierge-about-my-obsession</guid>
<pubDate>Sun, 13 Sep 2026 12:02:18 +0000</pubDate>
</item>
<item>
<title>I built an async wrapper for OpenAI/Anthropic SDKs because I didn't want a proxy in my request path</title>
<link>https://coderlegion.com/27038/built-async-wrapper-for-openai-anthropic-sdks-because-didnt-want-proxy-in-my-request-path</link>
<description>&lt;p&gt;I kept running into the same tradeoff building cost tooling for teams shipping LLM features. Every attribution tool in this space works the same way: you point &lt;code&gt;base_url&lt;/code&gt; at a proxy, and it sees every call before it happens. That's genuinely useful if you want to block or downgrade a call before it fires. It also means the proxy's uptime is now your uptime, and you've added a network hop to every single request.&lt;/p&gt;
&lt;p&gt;I wanted the attribution without touching the request path at all. So I wrote a wrapper instead.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Cognocient&lt;/code&gt; wraps the OpenAI and Anthropic Python clients directly. You still call &lt;code&gt;client.chat.completions.create()&lt;/code&gt; exactly the way you always did. The wrapper times the call, then fires a cost report on a background thread after your real response has already returned to your code.&lt;/p&gt;
&lt;p&gt;The part I actually spent the most time on wasn't the happy path, it was making sure a dead reporting endpoint can never touch your application. If Cognocient's ingestion API is slow, down, or just does not exist, that failure has to stay invisible to whatever you're building. No exception bubbling up, no retry pile-up blocking your real call. There is a test, &lt;code&gt;test_reporter_failure_isolation.py&lt;/code&gt;, that specifically points the reporter at an unreachable host and asserts the actual API call still returns clean. Writing that test is what convinced me the design was sound, not the other way around.&lt;/p&gt;
&lt;p&gt;What you get: per-call cost, and tags for feature/team/user if you want chargeback-style reporting later.&lt;/p&gt;
&lt;p&gt;What you do not get, on purpose: pre-call blocking. If a call is about to blow your budget, this wrapper finds out after it already happened, same as any billing dashboard does. If you need to stop a call before it fires, you want a proxy, and honestly LiteLLM or Portkey do that well. This is for people who've already decided visibility without a critical-path dependency is the right tradeoff for their setup.&lt;/p&gt;
&lt;p&gt;Also worth knowing before you reach for it: streaming responses (&lt;code&gt;stream=True&lt;/code&gt;) aren't reported yet. If most of your traffic streams, this won't give you complete numbers right now. Non-streaming is solid, I would call it production-safe there. Streaming is next.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;from cognocient import CognocientOpenAI as OpenAI

client = OpenAI(api_key=&quot;sk-...&quot;, cognocient_key=&quot;sk-cog-...&quot;)
# everything else about the client works exactly like before
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;MIT licensed, signed provenance on the PyPI release, genuinely early (v0.1.x). If you're already routing through a gateway and it is working for you, this probably isn't for you. If you have been putting off cost visibility specifically because you didn't want another hop in the path, I would like to hear if this is useful or if I have missed something obvious.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/mandarvshinde/cognocient-python-wrapper&quot;&gt;https://github.com/mandarvshinde/cognocient-python-wrapper&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;PyPI&lt;/strong&gt;: &lt;code&gt;pip install cognocient&lt;/code&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/27038/built-async-wrapper-for-openai-anthropic-sdks-because-didnt-want-proxy-in-my-request-path</guid>
<pubDate>Sun, 13 Sep 2026 06:46:31 +0000</pubDate>
</item>
<item>
<title>Marla to Gaj (Square Yard) Conversion: A Simple Tool I Built for My Local Marketplace</title>
<link>https://coderlegion.com/26758/marla-to-gaj-square-yard-conversion-a-simple-tool-i-built-for-my-local-marketplace</link>
<description>&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/4cmnihzdewqqoj1sxfu7.jpg&quot; alt=&quot;Image description&quot;&gt;If you've ever bought or sold property in Pakistan, you've run into this problem: land size gets quoted in &lt;strong&gt;marla&lt;/strong&gt;, &lt;strong&gt;kanal&lt;/strong&gt;, &lt;strong&gt;square feet&lt;/strong&gt;, or &lt;strong&gt;gaj (square yards)&lt;/strong&gt; — often interchangeably, depending on who you're talking to. A property dealer might tell you a plot is &quot;5 marla,&quot; while the registry documents list it in square yards, and a buyer coming from a different city might only think in gaj.&lt;/p&gt;
&lt;p&gt;That mismatch causes real confusion — and sometimes real money lost — during property deals.&lt;/p&gt;
&lt;p&gt;So when I was building &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://connect-bazaar.com/&quot;&gt;Connect Bazaar&lt;/a&gt;&lt;/strong&gt;, a local marketplace and services platform for Pirpiai and Nowshera, I added a simple &lt;strong&gt;Marla to Gaj calculator&lt;/strong&gt; as part of a broader Property &amp;amp; Land tools section.&lt;/p&gt;
&lt;p&gt;Here's the conversion logic, and why a dedicated tool beats just Googling the number.&lt;/p&gt;
&lt;h3 id=&quot;the-basic-conversion&quot;&gt;The Basic Conversion&lt;/h3&gt;
&lt;p&gt;In most of Pakistan, the standard conversion used is:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Unit &lt;/th&gt;&lt;th&gt;Equals&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;1 Marla &lt;/td&gt;&lt;td&gt;≈ 30.25 square yards (gaj)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;1 Kanal (20 Marla) &lt;/td&gt;&lt;td&gt;≈ 605 square yards (gaj)&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Marla → Gaj&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;Gaj = Marla × 30.25
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Gaj → Marla&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;Marla = Gaj ÷ 30.25
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Simple math — the friction isn't the formula, it's that nobody wants to redo it manually every time, especially when comparing several plots or double-checking what a dealer just quoted.&lt;/p&gt;
&lt;h3 id=&quot;why-a-dedicated-calculator-actually-helps&quot;&gt;Why a Dedicated Calculator Actually Helps&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Speeds up real decisions.&lt;/strong&gt; Comparing 3–4 plots quoted in different units? Converting everything to one unit makes the comparison honest and fast.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reduces reliance on the dealer's math.&lt;/strong&gt; Buyers can independently verify what they're being told — which matters a lot in informal or semi-formal property markets.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;One less spreadsheet.&lt;/strong&gt; A shared, always-available tool replaces everyone's personal napkin-math formula.&lt;h3 id=&quot;how-it-fits-into-connect-bazaar&quot;&gt;How It Fits Into Connect Bazaar&lt;/h3&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The calculator lives inside the &lt;strong&gt;Property &amp;amp; Land&lt;/strong&gt; tools category, alongside other land conversion tools (marla, kanal, square feet, plot area). It's built mobile-first, since most users check it standing on-site at a plot or mid-conversation with a dealer — not sitting at a desk.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt; Try it here: &lt;a rel=&quot;nofollow&quot; href=&quot;https://connect-bazaar.com/tools/property/&quot;&gt;Property &amp;amp; Land Tools on Connect Bazaar&lt;/a&gt;&lt;/strong&gt; &lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;If you're building something similar — a local utility site, a regional marketplace, or any tool involving unit conversions people rely on for real transactions — I'd love to compare notes in the comments. &lt;/p&gt;
&lt;p&gt;&lt;em&gt;What conversion or utility tools have you found genuinely useful (not just &quot;nice to have&quot;) on a niche or local website?&lt;/em&gt; &lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/26758/marla-to-gaj-square-yard-conversion-a-simple-tool-i-built-for-my-local-marketplace</guid>
<pubDate>Thu, 10 Sep 2026 18:45:17 +0000</pubDate>
</item>
<item>
<title>One I can't finish, one I can't measure</title>
<link>https://coderlegion.com/26266/one-i-cant-finish-one-i-cant-measure</link>
<description>&lt;p&gt;I shipped two things this week that could not be less alike.&lt;/p&gt;
&lt;p&gt;One is an integration against the SaaS that most process-serving firms run their dispatch on — a system of legal record, where a wrong write becomes a wrong document attached to a real court case. The other is a 24.92 KiB Chrome extension containing a dinosaur that walks across your page and refuses to leave.&lt;/p&gt;
&lt;p&gt;They have exactly one thing in common, and it took me until I'd written both up to notice it: &lt;strong&gt;each one has a hole in it that I put there on purpose.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;One I can't finish. One I can't measure.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-one-i-can-t-finish&quot;&gt;The one I can't finish&lt;/h3&gt;
&lt;p&gt;ServeManager speaks &lt;a rel=&quot;nofollow&quot; href=&quot;https://jsonapi.org/&quot;&gt;JSON:API&lt;/a&gt;, not flat REST. If you've never hit that shape before, the first thing it does is lie to you quietly:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// Returns undefined. Does not throw. Ships to production as a blank column.
const status = job.data.service_status;

// The scalar actually lives here
const status = job.data.attributes.service_status;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Relationships are worse — they're pointers, not objects, and the thing they point at lives in a separate top-level &lt;code&gt;included&lt;/code&gt; array you have to resolve yourself. Getting a court name off a job is three hops:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;const resolve = (included, ref) =&amp;gt;
  ref?.data
    ? included.find(i =&amp;gt; i.id === ref.data.id &amp;amp;&amp;amp; i.type === ref.data.type) ?? null
    : null;

const courtCase = resolve(included, job.data.relationships.court_case);
const court     = resolve(included, courtCase?.relationships?.court);
const courtName = court?.attributes?.name;   // null-guard every hop
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Every one of those failure modes is silent. Nothing throws. You get &lt;code&gt;undefined&lt;/code&gt;, it flows through your mapper, and it lands in a report as an empty cell — in a domain where an empty cell means &quot;we have no idea whether this person was legally served.&quot;&lt;/p&gt;
&lt;p&gt;So here's the hole. There is one write I have never confirmed: attaching a file to an &lt;strong&gt;already-existing&lt;/strong&gt; job. The documented flow covers uploading during job &lt;em&gt;creation&lt;/em&gt;. The attach-afterward body schema, I have never captured from a real request.&lt;/p&gt;
&lt;p&gt;I could infer it. The create flow is right there, the naming is consistent, and I'd probably be right.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&quot;Probably right&quot; is a fine standard for a side project and a terrible one for a system of legal record.&lt;/strong&gt; A malformed write here doesn't throw an exception — it produces a wrong document on someone's real case, and nobody finds out until the day it matters. So the feature is gated. It does not run against production. It sits there, unfinished, until I spend the hour with a network tab open and capture what the request actually looks like.&lt;/p&gt;
&lt;p&gt;The full writeup, with the status codes that look like your bug and aren't (406, 409, and a 403 that's a permissions level rather than a bad payload), is here: &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dhseadev.online/2026/08/06/servemanager-api-document-not-record/&quot;&gt;The ServeManager API is a document, not a record&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-one-i-can-t-measure&quot;&gt;The one I can't measure&lt;/h3&gt;
&lt;p&gt;Same week. Different universe.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dhseadev.online/projects/annoying-dino/&quot;&gt;Annoying Dino&lt;/a&gt; is a Manifest V3 extension that puts a tiny T-Rex in your browser. You can tap him, feed him, or ignore him. His mood shifts based on which you pick. There's an egg-and-hatch collection running alongside. That's the product. It is the least ambitious thing I have ever shipped.&lt;/p&gt;
&lt;p&gt;It is also, by a factor of nearly five, &lt;strong&gt;the most-installed thing in my portfolio.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;I didn't want to write that sentence without checking it, so I counted every listing:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Extension &lt;/th&gt;&lt;th&gt;Installs&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;strong&gt;Annoying Dino&lt;/strong&gt; &lt;/td&gt;&lt;td&gt;&lt;strong&gt;19&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Arcanum Idle &lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Dino Dots &lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Planet Express Lounge &lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Cobalt Star / Kawaii Time Garden / Spellsurge / Hot Dog Rush &lt;/td&gt;&lt;td&gt;2 each&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Devotion &lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Four listings wouldn't surface a count at all, so the honest version is &quot;leads everything I could measure.&quot; Nineteen users is not a business or a validation — it's nineteen browsers. But it beats the idle game with nine automated test suites, and that gap is the interesting part.&lt;/p&gt;
&lt;p&gt;Here's the hole: &lt;strong&gt;I have no idea whether any of those nineteen people have opened it since.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;There is no analytics in any of my extensions. No event pings, no install beacon, no heartbeat. I check that the way I check a bug — grep the whole codebase for network APIs before the privacy claims get written. Which means I cannot tell you a retention curve, a DAU number, or whether the feeding mechanic is the part people actually like. I can't even tell you if it's broken on someone's machine right now.&lt;/p&gt;
&lt;p&gt;Anyone with an analytics tag would have known all of that on day two.&lt;/p&gt;
&lt;p&gt;And I still won't add one — because of what this specific extension is. It runs on the pages you visit. Any usage telemetry worth having from it would be, functionally, a log of when you were browsing and what you were looking at when a cartoon reptile walked past. There is no version of &quot;just a little event tracking&quot; there that isn't a surveillance surface wearing a joke costume.&lt;/p&gt;
&lt;p&gt;Full numbers and reasoning: &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dhseadev.online/2026/08/06/annoying-dino-most-installed/&quot;&gt;The most-installed thing I have built is a dinosaur that bothers you&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-thing-they-actually-share&quot;&gt;The thing they actually share&lt;/h3&gt;
&lt;p&gt;I thought these were opposites. A legal-record API and a joke dinosaur; maximum consequence and zero consequence.&lt;/p&gt;
&lt;p&gt;They're the same decision made twice.&lt;/p&gt;
&lt;p&gt;In both cases there was a shortcut available that would have made the product &lt;em&gt;better on paper&lt;/em&gt;. Infer the field shape and ship the attach feature. Add three lines of telemetry and finally know if anyone plays the thing. Both shortcuts are small, normal, and what most people would do without a second thought.&lt;/p&gt;
&lt;p&gt;And in both cases the cost of taking the shortcut lands on &lt;strong&gt;someone who is not me&lt;/strong&gt;. A guessed field shape puts a wrong document on a stranger's court case. A telemetry ping puts a stranger's browsing pattern on a server. The benefit is all mine — a shipped feature, a retention chart — and the risk is all theirs.&lt;/p&gt;
&lt;p&gt;So the rule I've ended up with is not &quot;be careful.&quot; It's narrower than that:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;When the shortcut's upside accrues to you and its downside accrues to your user, the shortcut isn't a tradeoff. It's a transfer.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The follow-on is that you have to be willing to hold the gap. Ship the integration with the feature switched off and say so. Ship the extension and admit publicly you can't measure it. Both look like weakness in a portfolio. Both are the actual engineering.&lt;/p&gt;
&lt;p&gt;The unglamorous coda: neither hole is permanent. The attach schema needs one hour with DevTools open. The dino telemetry is never coming — but the install count is a real signal, and it already told me something I didn't want to hear about the gap between what I find interesting to build and what a stranger will actually click.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;If you want the longer versions, I keep the case studies at &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dhseadev.online/writing/&quot;&gt;dhseadev.online/writing&lt;/a&gt;&lt;/strong&gt; and the running devlog at &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://dhseadev.online/updates/&quot;&gt;dhseadev.online/updates&lt;/a&gt;&lt;/strong&gt;. Everything I ship is local-first, no accounts, no telemetry — which, as established, is occasionally very inconvenient for me.&lt;/p&gt;
&lt;p&gt;What's the shortcut you refuse to take, and what does it cost you?&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/26266/one-i-cant-finish-one-i-cant-measure</guid>
<pubDate>Sat, 05 Sep 2026 15:40:23 +0000</pubDate>
</item>
<item>
<title>How an Abandoned Client Project Became My Proudest Showcase</title>
<link>https://coderlegion.com/26005/how-an-abandoned-client-project-became-my-proudest-showcase</link>
<description>&lt;p&gt;In the &lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/23258/why-titles-and-price-tags-dont-guarantee-quality-the-work-speaks-for-itself&quot;&gt;first part of this series&lt;/a&gt;, I walked through the technical grit of rebuilding a musician's web platform from scratch—spending over 320 hours fixing legacy WordPress code, writing custom CLI tools with Node.js and FFmpeg, and crafting a lightweight Vanilla JS SPA router.&lt;/p&gt;
&lt;p&gt;If Part 1 was about the &lt;strong&gt;engineering side&lt;/strong&gt;, Part 2 is about the &lt;strong&gt;human side&lt;/strong&gt;: scope creep, irrational client expectations, and why finishing an &quot;abandoned&quot; project is sometimes the ultimate test of a developer’s character.&lt;/p&gt;
&lt;h3 id=&quot;-appetite-comes-with-eating-how-a-volunteer-portfolio-case-turned-into-scope-creep&quot;&gt;&quot;Appetite Comes With Eating&quot;: How a Volunteer Portfolio Case Turned Into Scope Creep&lt;/h3&gt;
&lt;p&gt;They say the road to hell is paved with good intentions.&lt;/p&gt;
&lt;p&gt;We stepped into this project on pure enthusiasm. The agreement was simple: we help an independent artist build a sleek web presence for free, and in return, we get a real-world production case for our engineering portfolios. Win-win, right?&lt;/p&gt;
&lt;p&gt;At the beginning, everything was smooth. The client was absolutely thrilled with the initial UI/UX prototypes. But as soon as the application was actually hosted and brought to life, the &quot;appetite&quot; started growing exponentially:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Phase 1 (Initial tweaks):&lt;/strong&gt; &quot;Can we change the album cover art?&quot; — &lt;em&gt;Sure thing. It's your music, your Bandcamp embed—done.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Phase 2 (The Breaking Point):&lt;/strong&gt; &quot;The fonts don't feel right... can we rewrite the copy?&quot; &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This was the final straw. Keep in mind: we had repeatedly confirmed typography and styling choices with the client earlier, and everything had been approved. &lt;/p&gt;
&lt;p&gt;When my teammate David politely informed the client that fundamental UI changes were outside the scope of our volunteer agreement, the client responded with: &lt;em&gt;&quot;Just show me where the files are, and I'll change the fonts myself.&quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;For anyone who works in web development, this was the ultimate ironic punchline. Changing fluid typography, responsive SCSS breakpoints, and layout variables isn't like picking a font in Microsoft Word. Knowing that the client had previously struggled to set up a basic Bandcamp profile, we wished him the best of luck—and officially closed the client chapter.&lt;/p&gt;
&lt;p&gt;And the kicker? The expectation was that all these endless, fundamental redesigns should continue under the same &quot;volunteer&quot; umbrella indefinitely.&lt;/p&gt;
&lt;h3 id=&quot;-you-live-and-you-learn-the-hardest-tech-stack-is-working-with-people&quot;&gt;&quot;You Live and You Learn&quot;: The Hardest Tech Stack is Working With People&lt;/h3&gt;
&lt;p&gt;Seniors and project managers reading this might smirk: &lt;em&gt;&quot;Well, that’s classic Scope Creep. That's why you need ironclad contracts, strict specs, and change-request fees!&quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;And you are 100% right. Coming from a non-tech background where I spent years working directly with clients, I already knew that &lt;strong&gt;PEOPLE&lt;/strong&gt; are always the most complex variable in any equation. But experiencing it firsthand as a developer hits differently.&lt;/p&gt;
&lt;p&gt;It wasn't just about a client changing his mind; it was about watching a project you deeply cared about get derailed by communication breakdowns. Eventually, the client realized he wanted something completely different, and our custom architecture was left orphaned.&lt;/p&gt;
&lt;h3 id=&quot;giving-an-orphaned-project-a-second-life&quot;&gt;Giving an Orphaned Project a Second Life&lt;/h3&gt;
&lt;p&gt;At that point, I had two choices:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Option A:&lt;/strong&gt; Throw 320+ hours of blood, sweat, and clean code into the dark abyss of a discarded folder and forget it ever happened.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Option B:&lt;/strong&gt; Refuse to leave it broken, roll up my sleeves, and transform this project into an independent, high-performance Open-Source Showcase.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I chose &lt;strong&gt;Option B&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;I spent another full week polishing the codebase, replacing client-specific PII with clean configuration placeholders, refining responsive layouts, writing comprehensive tests, and building a dedicated showcase identity under the &lt;strong&gt;D&amp;amp;K Custom Media Platform&lt;/strong&gt; brand.&lt;/p&gt;
&lt;p&gt;If you’re too lazy to dive directly into the GitHub repository, here is a quick 2-minute video walkthrough of what we built:&lt;/p&gt;
&lt;p&gt;&lt;iframe width=&quot;100%&quot; height=&quot;320px&quot; src=&quot;https://www.youtube.com/embed/R8rgODfEkXc&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allowfullscreen=&quot;allowfullscreen&quot;&gt;&lt;/iframe&gt;&lt;/p&gt;
&lt;h3 id=&quot;conclusion-even-failures-deserve-a-logical-conclusion&quot;&gt;Conclusion: Even Failures Deserve a Logical Conclusion&lt;/h3&gt;
&lt;p&gt;Some might say it’s foolish to pour even more time and emotion into a project that failed commercially.&lt;/p&gt;
&lt;p&gt;I disagree. You never get anywhere in life—or in software engineering—if you abandon things the moment they get messy. Completing this project gave me something far more valuable than a client's signature:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;A deep, practical understanding of full-stack media pipelines, SPA&lt;br&gt;
state management, and CI/CD automation.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ironclad resilience in handling unexpected project shifts.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A clean, open-source portfolio piece that I can proudly stand behind.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the end, every failure deserves a logical, high-standard conclusion. As the saying goes: You’ll never reach your destination if you stop and throw stones at every dog that barks. The code is live, the repository is public, and the work speaks for itself.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Links &amp;amp; Resources:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;🔗 GitHub Repository:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/kate8382/custom-media-platform-showcase.git&quot;&gt;https://github.com/kate8382/custom-media-platform-showcase.git&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;📺 YouTube Video Walkthrough:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.youtube.com/watch?v=R8rgODfEkXc&quot;&gt;Watch Here&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;👤 Frontend Developer:&lt;/strong&gt; Ecaterina Sevciuc (&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.linkedin.com/in/ecaterina-sevciuc-497017364&quot;&gt;LinkedIn&lt;/a&gt; | &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/kate8382&quot;&gt;GitHub&lt;/a&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;👤 Backend Architect:&lt;/strong&gt; David H. Watson (&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.linkedin.com/in/david-h-watson/&quot;&gt;LinkedIn&lt;/a&gt; | &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/dEhiN&quot;&gt;GitHub&lt;/a&gt;)&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/26005/how-an-abandoned-client-project-became-my-proudest-showcase</guid>
<pubDate>Tue, 01 Sep 2026 15:07:19 +0000</pubDate>
</item>
<item>
<title>Why I built 28 web tools that run 100% in your browser (and zero on my servers)</title>
<link>https://coderlegion.com/25563/why-i-built-28-web-tools-that-run-100-in-your-browser-and-zero-on-my-servers</link>
<description>&lt;p&gt;Have you ever needed to quickly compress an official invoice, sign a PDF contract, or format a photo online, but hesitated before clicking &quot;Upload&quot;? &lt;/p&gt;
&lt;p&gt;I kept running into this exact dilemma. Almost every popular online PDF or image converter forces you to send your private documents—passwords, ID cutouts, financial statements—to a remote cloud server. You have no idea where your files are stored, who has access to them, or how long they sit on someone else's database.&lt;/p&gt;
&lt;p&gt;I wanted a better way. So I set out to build &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://resizertools.com&quot;&gt;Resizer Tools&lt;/a&gt;&lt;/strong&gt; with a simple, strict rule: &lt;strong&gt;Zero server uploads. Everything must run 100% locally inside the browser.&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-how-it-works-under-the-hood&quot;&gt;🛠️ How it works under the hood&lt;/h3&gt;
&lt;p&gt;Instead of shipping user files to a backend Node or Python server, the entire processing engine runs locally inside your browser's client-side memory:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Browser-Native Graphics Engine&lt;/strong&gt;: Compression, resizing, format conversions (HEIC/PNG/JPEG/WebP), and passport photo grid layouts execute directly on HTML5 Canvas API and WebAssembly.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Client-Side PDF Streams&lt;/strong&gt;: PDF stitching, page extraction, password encryption, and digital signatures run locally using WebCrypto and &lt;code&gt;pdf-lib&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Works 100% Offline&lt;/strong&gt;: Try turning off your Wi-Fi or switching to airplane mode—the app continues editing and compiling documents without missing a beat.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-what-s-inside-the-tool-studio-&quot;&gt;🧰 What's inside the tool studio?&lt;/h3&gt;
&lt;p&gt;I've built 28 standalone browser tools so far, categorized into 4 core hubs:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;📄 PDF Manipulation&lt;/strong&gt;: Merge, split, compress, extract/delete pages, rotate, and add watermarks.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;🖼️ Image &amp;amp; Graphics&lt;/strong&gt;: Lossless compressor, resizer, batch image-to-PDF converter, and photo print sheet arranger.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;🔒 Security &amp;amp; Docs&lt;/strong&gt;: Password encrypter/decrypter, digital signature creator, corporate invoice compiler, and ATS resume builder.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;⚡ Studios &amp;amp; Utilities&lt;/strong&gt;: App Store screenshot designer, QR generator &amp;amp; scanner, and document camera scanner.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-i-d-love-your-feedback-&quot;&gt;💬 I'd love your feedback!&lt;/h3&gt;
&lt;p&gt;Building this was a fun engineering challenge around client-side performance, memory management, and privacy-first web design. &lt;/p&gt;
&lt;p&gt;Give it a spin at &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://resizertools.com&quot;&gt;resizertools.com&lt;/a&gt;&lt;/strong&gt; and let me know what you think! I’d love to hear your feedback, thoughts on the UI, or any new tools you’d like to see added.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/25563/why-i-built-28-web-tools-that-run-100-in-your-browser-and-zero-on-my-servers</guid>
<pubDate>Thu, 27 Aug 2026 23:50:03 +0000</pubDate>
</item>
<item>
<title>I mapped India's public financial data endpoints so you don't have to</title>
<link>https://coderlegion.com/25222/i-mapped-indias-public-financial-data-endpoints-so-you-dont-have-to</link>
<description>&lt;p&gt;Indian financial market data can cost companies lakhs of rupees every year.&lt;/p&gt;
&lt;p&gt;But a lot of the underlying data is already publicly available.&lt;/p&gt;
&lt;p&gt;The expensive part isn't always the data itself. It's figuring out where it lives and how to reliably consume it.&lt;/p&gt;
&lt;p&gt;If you've ever tried building something around Indian financial data, you've probably encountered the same problems: undocumented endpoints, APIs with strange headers, inconsistent file formats, identifier changes, historical gaps, and datasets spread across different public sources.&lt;/p&gt;
&lt;p&gt;I've spent a lot of time digging through those problems.&lt;/p&gt;
&lt;p&gt;So instead of keeping the findings in a pile of scripts and notes, I mapped the endpoints I could find and open-sourced them.&lt;/p&gt;
&lt;h3 id=&quot;what-s-in-the-repository-&quot;&gt;What's in the repository?&lt;/h3&gt;
&lt;p&gt;The repository currently documents public endpoints covering several areas of Indian financial data.&lt;/p&gt;
&lt;h4 id=&quot;mutual-fund-nav-data&quot;&gt;Mutual fund NAV data&lt;/h4&gt;
&lt;p&gt;Around &lt;strong&gt;14,000 mutual fund schemes&lt;/strong&gt; with more than &lt;strong&gt;18 years of NAV history&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;There are also parameterized APIs for retrieving historical NAV timelines.&lt;/p&gt;
&lt;h4 id=&quot;expense-ratios&quot;&gt;Expense ratios&lt;/h4&gt;
&lt;p&gt;Daily &lt;strong&gt;Total Expense Ratio (TER)&lt;/strong&gt; data, broken down across the available regulatory expense components.&lt;/p&gt;
&lt;h4 id=&quot;nse-index-data&quot;&gt;NSE index data&lt;/h4&gt;
&lt;p&gt;Historical valuation metrics including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;PE&lt;/li&gt;
&lt;li&gt;PB&lt;/li&gt;
&lt;li&gt;Dividend yield&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The dataset covers &lt;strong&gt;140+ NSE indices&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;There's also historical &lt;strong&gt;Total Return Index (TRI)&lt;/strong&gt; data for Nifty indices.&lt;/p&gt;
&lt;h4 id=&quot;industry-aum&quot;&gt;Industry AUM&lt;/h4&gt;
&lt;p&gt;Industry-wide mutual fund AUM statistics going back to &lt;strong&gt;2009&lt;/strong&gt;.&lt;/p&gt;
&lt;h4 id=&quot;corporate-shareholding&quot;&gt;Corporate shareholding&lt;/h4&gt;
&lt;p&gt;Corporate shareholding data sourced from public &lt;strong&gt;iXBRL disclosures&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The goal isn't to build another financial-data product.&lt;/p&gt;
&lt;p&gt;It's to make the underlying discovery work easier for developers building things on top of public data.&lt;/p&gt;
&lt;h3 id=&quot;the-interesting-part-the-data-has-traps&quot;&gt;The interesting part: the data has traps&lt;/h3&gt;
&lt;p&gt;Finding an endpoint is only the beginning.&lt;/p&gt;
&lt;p&gt;While mapping these sources, I ran into several things that could quietly cause problems in production.&lt;/p&gt;
&lt;h4 id=&quot;historical-discontinuities&quot;&gt;Historical discontinuities&lt;/h4&gt;
&lt;p&gt;Some datasets have historical discontinuities around &lt;strong&gt;April 2019&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;If you're building a long-term time series, you can't blindly assume that the data behaves consistently across the entire history.&lt;/p&gt;
&lt;h4 id=&quot;files-that-lie-about-their-format&quot;&gt;Files that lie about their format&lt;/h4&gt;
&lt;p&gt;Some files claim to be one format while actually containing data in another.&lt;/p&gt;
&lt;p&gt;That can be particularly annoying when you're writing automated ingestion pipelines that trust file extensions or content types.&lt;/p&gt;
&lt;h4 id=&quot;fund-identifiers-aren-t-always-stable&quot;&gt;Fund identifiers aren't always stable&lt;/h4&gt;
&lt;p&gt;Mergers, rebranding and other changes can make fund identifier mappings surprisingly messy.&lt;/p&gt;
&lt;p&gt;If you're maintaining historical data, you need to account for those relationships rather than treating every identifier as permanently immutable.&lt;/p&gt;
&lt;h4 id=&quot;the-one-header-problem&quot;&gt;The one-header problem&lt;/h4&gt;
&lt;p&gt;Here's one of my favorite examples.&lt;/p&gt;
&lt;p&gt;There's a public corporate shareholding endpoint that appears to reject automated requests.&lt;/p&gt;
&lt;p&gt;You might assume there's authentication, some elaborate anti-bot mechanism, or another access restriction involved.&lt;/p&gt;
&lt;p&gt;In one case, the problem was simply the &lt;code&gt;Origin&lt;/code&gt; header.&lt;/p&gt;
&lt;p&gt;Remove it and the request works.&lt;/p&gt;
&lt;p&gt;That's it.&lt;/p&gt;
&lt;p&gt;One HTTP header was the difference between &quot;this endpoint doesn't work&quot; and getting the data immediately.&lt;/p&gt;
&lt;h3 id=&quot;why-i-open-sourced-this&quot;&gt;Why I open-sourced this&lt;/h3&gt;
&lt;p&gt;I've had to spend hours figuring out things that probably shouldn't take hours to figure out.&lt;/p&gt;
&lt;p&gt;And I know I'm not the only developer doing it.&lt;/p&gt;
&lt;p&gt;Someone building a portfolio tracker shouldn't have to spend days reverse-engineering network requests just to discover where historical data comes from.&lt;/p&gt;
&lt;p&gt;Someone building a quantitative research tool shouldn't have to rediscover the same identifier mappings.&lt;/p&gt;
&lt;p&gt;Someone building a fintech prototype shouldn't have to start from zero because the documentation around a public endpoint is scattered or nonexistent.&lt;/p&gt;
&lt;p&gt;So I decided to put the findings in one place.&lt;/p&gt;
&lt;p&gt;The repository is &lt;strong&gt;MIT licensed&lt;/strong&gt;, so you can use it as a starting point for projects involving:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Indian fintech&lt;/li&gt;
&lt;li&gt;Wealth management&lt;/li&gt;
&lt;li&gt;Quantitative research&lt;/li&gt;
&lt;li&gt;Portfolio tracking&lt;/li&gt;
&lt;li&gt;Market analytics&lt;/li&gt;
&lt;li&gt;Financial data pipelines&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are probably still gaps.&lt;/p&gt;
&lt;p&gt;That's intentional.&lt;/p&gt;
&lt;p&gt;I mapped what I could find, documented the behavior I encountered, and would rather have other developers extend it than keep trying to maintain a private collection of scripts.&lt;/p&gt;
&lt;h3 id=&quot;one-less-thing-to-reverse-engineer&quot;&gt;One less thing to reverse-engineer&lt;/h3&gt;
&lt;p&gt;The data was public.&lt;/p&gt;
&lt;p&gt;The documentation wasn't.&lt;/p&gt;
&lt;p&gt;That's the problem I'm trying to solve with this repository.&lt;/p&gt;
&lt;p&gt;If you're building something around Indian financial data, hopefully this saves you a few days of digging through network requests and figuring out why an otherwise perfectly reasonable API request gets rejected because of one header.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Repository:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/satwikbasu&quot;&gt;https://github.com/satwikbasu&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;If you know of other undocumented Indian market-data endpoints, PRs and contributions are very welcome.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/25222/i-mapped-indias-public-financial-data-endpoints-so-you-dont-have-to</guid>
<pubDate>Mon, 24 Aug 2026 06:00:00 +0000</pubDate>
</item>
<item>
<title>The EU AI Act deadline nobody's ready for (and what developers actually have to do)</title>
<link>https://coderlegion.com/25213/the-eu-ai-act-deadline-nobodys-ready-for-and-what-developers-actually-have-to-do</link>
<description>&lt;p&gt;If you've been following AI regulation at all, you've probably heard about the EU AI Act.&lt;/p&gt;
&lt;p&gt;You may also have heard that parts of it were delayed.&lt;/p&gt;
&lt;p&gt;That's true, but it's causing a lot of confusion.&lt;/p&gt;
&lt;p&gt;The recent Digital Omnibus changes pushed several high-risk AI obligations into 2027 and 2028. Many teams interpreted that as &quot;the AI Act got delayed.&quot;&lt;/p&gt;
&lt;p&gt;It didn't.&lt;/p&gt;
&lt;p&gt;Some of the first transparency requirements are still arriving on schedule, and for many software teams they become relevant on &lt;strong&gt;August 2, 2026&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;If your product has an AI chatbot, AI-generated content, voice assistant, image generator, emotion detection feature, or anything similar that reaches users in the European Union, Article 50 is worth understanding now.&lt;/p&gt;
&lt;p&gt;This article explains what developers actually need to know, in plain English.&lt;/p&gt;
&lt;h3 id=&quot;the-short-version&quot;&gt;The short version&lt;/h3&gt;
&lt;p&gt;Article 50 focuses on transparency.&lt;/p&gt;
&lt;p&gt;The idea is simple:&lt;/p&gt;
&lt;p&gt;People should know when they're interacting with AI, when content was generated by AI, and when systems are performing sensitive analysis such as emotion recognition.&lt;/p&gt;
&lt;p&gt;The obligations are split across four sections:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Article 50(1): Disclosure for AI systems that interact with people&lt;/li&gt;
&lt;li&gt;Article 50(2): Machine-readable marking of AI-generated content&lt;/li&gt;
&lt;li&gt;Article 50(3): Disclosure for emotion recognition and biometric categorisation systems&lt;/li&gt;
&lt;li&gt;Article 50(4): Labeling requirements for deepfakes and certain AI-generated public-interest content&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Most of these obligations apply from &lt;strong&gt;August 2, 2026&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The machine-readable marking requirement in Article 50(2) applies from &lt;strong&gt;December 2, 2026&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;who-is-affected-&quot;&gt;Who is affected?&lt;/h3&gt;
&lt;p&gt;A common misconception is that only large AI companies need to care.&lt;/p&gt;
&lt;p&gt;In practice, many ordinary software products could be affected.&lt;/p&gt;
&lt;p&gt;Examples include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SaaS products with AI chat assistants&lt;/li&gt;
&lt;li&gt;Customer support chatbots&lt;/li&gt;
&lt;li&gt;AI writing tools&lt;/li&gt;
&lt;li&gt;AI image generation products&lt;/li&gt;
&lt;li&gt;Voice agents&lt;/li&gt;
&lt;li&gt;Meeting assistants&lt;/li&gt;
&lt;li&gt;Internal business tools used by EU employees&lt;/li&gt;
&lt;li&gt;Mobile apps with AI features&lt;/li&gt;
&lt;li&gt;Websites embedding third-party AI widgets&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You don't need to be based in Europe.&lt;/p&gt;
&lt;p&gt;If your product reaches users in the EU, these obligations can become relevant.&lt;/p&gt;
&lt;p&gt;For many startups, that's the surprising part.&lt;/p&gt;
&lt;h3 id=&quot;article-50-1-tell-people-they-re-interacting-with-ai&quot;&gt;Article 50(1): Tell people they're interacting with AI&lt;/h3&gt;
&lt;p&gt;This is the easiest requirement to understand.&lt;/p&gt;
&lt;p&gt;If a user is interacting directly with an AI system, they should be informed that they're interacting with AI.&lt;/p&gt;
&lt;p&gt;Think about:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Chatbots&lt;/li&gt;
&lt;li&gt;Voice assistants&lt;/li&gt;
&lt;li&gt;AI customer support agents&lt;/li&gt;
&lt;li&gt;AI companions&lt;/li&gt;
&lt;li&gt;AI-powered onboarding assistants&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In practical terms, this usually means providing clear disclosure before or during the interaction.&lt;/p&gt;
&lt;p&gt;The regulation is trying to prevent situations where users believe they're communicating with a human when they're actually communicating with software.&lt;/p&gt;
&lt;p&gt;For most teams, the work here is not technical.&lt;/p&gt;
&lt;p&gt;It's identifying every AI interaction surface and ensuring the disclosure is visible and understandable.&lt;/p&gt;
&lt;h3 id=&quot;article-50-2-machine-readable-marking-of-ai-generated-content&quot;&gt;Article 50(2): Machine-readable marking of AI-generated content&lt;/h3&gt;
&lt;p&gt;This is the requirement that seems to catch developers off guard.&lt;/p&gt;
&lt;p&gt;It's not only about visible labels.&lt;/p&gt;
&lt;p&gt;The regulation expects AI-generated content to include machine-readable information indicating that AI was involved.&lt;/p&gt;
&lt;p&gt;This becomes applicable on &lt;strong&gt;December 2, 2026&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The exact implementation details will continue evolving, but developers should already be thinking about metadata pipelines.&lt;/p&gt;
&lt;p&gt;Questions worth asking:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How are generated images stored?&lt;/li&gt;
&lt;li&gt;What metadata survives exports?&lt;/li&gt;
&lt;li&gt;What happens when content is transformed?&lt;/li&gt;
&lt;li&gt;Can downstream systems detect that content originated from AI?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If your product generates images, documents, audio, or video, this deserves engineering attention well before December.&lt;/p&gt;
&lt;h3 id=&quot;article-50-3-emotion-recognition-and-biometric-categorisation&quot;&gt;Article 50(3): Emotion recognition and biometric categorisation&lt;/h3&gt;
&lt;p&gt;This obligation applies to systems that perform emotion recognition or biometric categorisation.&lt;/p&gt;
&lt;p&gt;If a system is analyzing facial expressions, voice characteristics, behavioral patterns, or similar signals to classify emotional states or biometric traits, users generally need to be informed.&lt;/p&gt;
&lt;p&gt;Many teams assume they don't do this.&lt;/p&gt;
&lt;p&gt;Some are wrong.&lt;/p&gt;
&lt;p&gt;Features marketed as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Mood detection&lt;/li&gt;
&lt;li&gt;Sentiment analysis from video&lt;/li&gt;
&lt;li&gt;Interview analysis&lt;/li&gt;
&lt;li&gt;Engagement scoring&lt;/li&gt;
&lt;li&gt;Attention tracking&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;may deserve closer review depending on how they operate.&lt;/p&gt;
&lt;p&gt;The key lesson is simple:&lt;/p&gt;
&lt;p&gt;Don't rely on marketing names.&lt;/p&gt;
&lt;p&gt;Review what the software is actually doing.&lt;/p&gt;
&lt;h3 id=&quot;article-50-4-deepfake-and-ai-generated-content-labeling&quot;&gt;Article 50(4): Deepfake and AI-generated content labeling&lt;/h3&gt;
&lt;p&gt;This section addresses synthetic content that could be mistaken for authentic content.&lt;/p&gt;
&lt;p&gt;Deepfakes are the obvious example.&lt;/p&gt;
&lt;p&gt;The goal is transparency.&lt;/p&gt;
&lt;p&gt;People should have a reasonable ability to understand when media has been artificially generated or manipulated.&lt;/p&gt;
&lt;p&gt;The exact implementation can vary depending on the context and the type of content involved, but teams creating AI-generated media should not assume this requirement only affects large platforms.&lt;/p&gt;
&lt;p&gt;A surprisingly small product can generate surprisingly large amounts of synthetic content.&lt;/p&gt;
&lt;h3 id=&quot;what-happens-if-you-ignore-it-&quot;&gt;What happens if you ignore it?&lt;/h3&gt;
&lt;p&gt;Article 99 provides the penalty framework.&lt;/p&gt;
&lt;p&gt;For certain violations, fines can reach:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;€15 million or 3% of global annual turnover, whichever is higher.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Whether a specific situation reaches that level depends on the facts, enforcement decisions, and legal interpretation.&lt;/p&gt;
&lt;p&gt;But even without discussing fines, most companies would prefer to avoid discovering transparency gaps after a regulator, customer, journalist, or enterprise buyer finds them first.&lt;/p&gt;
&lt;h3 id=&quot;what-can-a-development-team-do-this-week-&quot;&gt;What can a development team do this week?&lt;/h3&gt;
&lt;p&gt;You do not need a six-month compliance project to make progress.&lt;/p&gt;
&lt;p&gt;Start with inventory.&lt;/p&gt;
&lt;h4 id=&quot;step-1-find-every-ai-surface&quot;&gt;Step 1: Find every AI surface&lt;/h4&gt;
&lt;p&gt;Create a list of:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Chatbots&lt;/li&gt;
&lt;li&gt;Agents&lt;/li&gt;
&lt;li&gt;AI search features&lt;/li&gt;
&lt;li&gt;Image generation features&lt;/li&gt;
&lt;li&gt;Voice features&lt;/li&gt;
&lt;li&gt;Third-party AI integrations&lt;/li&gt;
&lt;li&gt;Embedded widgets&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Most organizations have more AI entry points than they initially expect.&lt;/p&gt;
&lt;h4 id=&quot;step-2-map-each-feature-to-an-obligation&quot;&gt;Step 2: Map each feature to an obligation&lt;/h4&gt;
&lt;p&gt;Ask:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Does a user interact with AI?&lt;/li&gt;
&lt;li&gt;Is AI-generated content produced?&lt;/li&gt;
&lt;li&gt;Is emotion recognition involved?&lt;/li&gt;
&lt;li&gt;Is synthetic media produced?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A single feature can trigger multiple obligations.&lt;/p&gt;
&lt;h4 id=&quot;step-3-review-disclosures&quot;&gt;Step 3: Review disclosures&lt;/h4&gt;
&lt;p&gt;Check whether disclosures are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Visible&lt;/li&gt;
&lt;li&gt;Understandable&lt;/li&gt;
&lt;li&gt;Consistent&lt;/li&gt;
&lt;li&gt;Present across all user journeys&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Don't only check the happy path.&lt;/p&gt;
&lt;p&gt;Look at mobile flows, embedded widgets, and edge cases.&lt;/p&gt;
&lt;h4 id=&quot;step-4-review-generated-content-pipelines&quot;&gt;Step 4: Review generated content pipelines&lt;/h4&gt;
&lt;p&gt;If your product generates content, determine:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What metadata exists today?&lt;/li&gt;
&lt;li&gt;What metadata gets removed?&lt;/li&gt;
&lt;li&gt;What survives exports?&lt;/li&gt;
&lt;li&gt;What survives sharing?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The answers are often surprising.&lt;/p&gt;
&lt;h4 id=&quot;step-5-create-evidence&quot;&gt;Step 5: Create evidence&lt;/h4&gt;
&lt;p&gt;Document:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What you reviewed&lt;/li&gt;
&lt;li&gt;What was found&lt;/li&gt;
&lt;li&gt;What was fixed&lt;/li&gt;
&lt;li&gt;When reviews occurred&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;the-approach-i-m-taking&quot;&gt;The approach I'm taking&lt;/h3&gt;
&lt;p&gt;I built a scanner for this because manually hunting through repositories and live websites became repetitive.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npx article50 scan
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The point isn't to replace legal review.&lt;/p&gt;
&lt;p&gt;It's to help developers find AI surfaces, transparency gaps, and implementation work that might otherwise be missed.&lt;/p&gt;
&lt;h3 id=&quot;final-thoughts&quot;&gt;Final thoughts&lt;/h3&gt;
&lt;p&gt;The biggest misunderstanding around the EU AI Act right now is that teams think the important deadlines are years away.&lt;/p&gt;
&lt;p&gt;For Article 50 transparency obligations, they aren't.&lt;/p&gt;
&lt;p&gt;August 2, 2026 is close.&lt;/p&gt;
&lt;p&gt;For many products, the hardest part won't be implementing disclosures.&lt;/p&gt;
&lt;p&gt;It will be discovering every place AI is already present.&lt;/p&gt;
&lt;p&gt;Start with inventory.&lt;/p&gt;
&lt;p&gt;Then review disclosures.&lt;/p&gt;
&lt;p&gt;Then review content-marking workflows.&lt;/p&gt;
&lt;p&gt;The earlier you start, the easier the work becomes.&lt;/p&gt;
&lt;h3 id=&quot;resources&quot;&gt;Resources&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;EU AI Act text&lt;/li&gt;
&lt;li&gt;European Commission guidance materials&lt;/li&gt;
&lt;li&gt;article50 (MIT): &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/satwikbasu/article50&quot;&gt;https://github.com/satwikbasu/article50&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Disclaimer: This is technical guidance, not legal advice.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/25213/the-eu-ai-act-deadline-nobodys-ready-for-and-what-developers-actually-have-to-do</guid>
<pubDate>Mon, 24 Aug 2026 05:33:43 +0000</pubDate>
</item>
<item>
<title>Show DEV: I built an AI Agent inside VS Code</title>
<link>https://coderlegion.com/24832/show-dev-i-built-an-ai-agent-inside-vs-code</link>
<description>&lt;p&gt;Hey everyone,&lt;/p&gt;
&lt;p&gt;For the past few months, I’ve been building &lt;strong&gt;Kalai Agent&lt;/strong&gt;, a VS Code extension designed to bring fully agentic AI directly into your editor, bypassing the need to constantly copy-paste code snippets into a browser tab.&lt;/p&gt;
&lt;p&gt;It’s finally at a stable v2.1.5, and I’m looking for some brutal engineering feedback on the architecture, security flows, and UX.&lt;/p&gt;
&lt;h3 id=&quot;the-architecture-amp-tech-stack&quot;&gt;The Architecture &amp;amp; Tech Stack&lt;/h3&gt;
&lt;p&gt;Instead of building a wrapper around a basic chat interface, I wanted something that could safely interact with the local workspace.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Frontend:&lt;/strong&gt; VS Code Webview API with batched streaming to prevent UI thread stuttering.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LLM Engine:&lt;/strong&gt; Proxied Moonshot/Kimi API.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Backend:&lt;/strong&gt; A lightweight Node service managing auth, workspace routing, and pay-as-you-go billing.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Capabilities:&lt;/strong&gt; File-system operations, local terminal command execution, Python/Node script running, and web search capabilities.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h4 id=&quot;check-it-out&quot;&gt;Check it out&lt;/h4&gt;
&lt;p&gt;If you want to take it for a spin, install it directly from the marketplace:&lt;br&gt;
 &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://marketplace.visualstudio.com/items?itemName=kalculus.kalai-agent&quot;&gt;Kalai Agent - Visual Studio Marketplace&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you run into bugs, have notes on the UI, or just think a specific feature is clunky, drop a comment below or email us on culustech@gmail.com. I’m entirely open to critiques!&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/24832/show-dev-i-built-an-ai-agent-inside-vs-code</guid>
<pubDate>Tue, 18 Aug 2026 12:20:28 +0000</pubDate>
</item>
<item>
<title>How to Check If AI Systems Can Find and Cite Your Site (in 5 Minutes)</title>
<link>https://coderlegion.com/23698/how-to-check-if-ai-systems-can-find-and-cite-your-site-in-5-minutes</link>
<description>&lt;p&gt;&lt;em&gt;Co-authored by &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/RudrenduPaul&quot;&gt;Rudrendu Paul&lt;/a&gt; and &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Sourav-nandy-ai&quot;&gt;Sourav Nandy&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Repo:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/RudrenduPaul/LLMScout&quot;&gt;github.com/RudrenduPaul/LLMScout&lt;/a&gt;, a zero-dependency, cross-platform CLI that runs 21 GEO/AEO checks against any website. &lt;code&gt;npm install -g llmscout-cli&lt;/code&gt; or &lt;code&gt;pip install llmscout-cli&lt;/code&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Position-1 click-through rate on keywords that trigger an AI Overview fell from 7.3% in December 2023 to 1.6% in December 2025, per Ahrefs. If an AI system can't find or cite your site, that traffic doesn't come back on its own. Here's how to check where your own site actually stands, in under five minutes.&lt;/p&gt;
&lt;h3 id=&quot;what-llmscout-checks&quot;&gt;What LLMScout checks&lt;/h3&gt;
&lt;p&gt;LLMScout is a CLI that runs 21 checks against any site: 12 covering technical SEO fundamentals, 9 covering GEO signals specifically for AI systems. Every check reports PASS, WARN, or FAIL, and the whole run exits with a machine-readable code, so it drops straight into CI. It ships as two distributions: an npm package with two runtime dependencies, and a Python package with zero. Neither one needs a headless browser.&lt;/p&gt;
&lt;p&gt;One check worth knowing about before you run it is &lt;code&gt;ai-crawler-directives&lt;/code&gt;. It reports on all 7 major AI bots separately, because blocking GPTBot (which trains models) does nothing to stop OAI-SearchBot (which does live retrieval for ChatGPT) from fetching and citing your page. They're independently configurable in robots.txt, and most tools treat them as a single signal.&lt;/p&gt;
&lt;h3 id=&quot;install-1&quot;&gt;Install&lt;/h3&gt;
&lt;p&gt;There are two ways to install it. If you're not sure which one you want, try npm first:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install -g llmscout-cli
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This needs Node 18+, and it pulls in exactly two runtime dependencies: &lt;code&gt;cheerio&lt;/code&gt; and &lt;code&gt;commander&lt;/code&gt;. No Python, no browser download, no subprocess calls.&lt;/p&gt;
&lt;p&gt;If you'd rather use Python:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;pip install llmscout-cli
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This version has zero runtime dependencies; it's pure standard library. It runs the same 21 checks and gives the same PASS/WARN/FAIL verdicts as the npm version.&lt;/p&gt;
&lt;p&gt;Both install in under a minute on Windows, macOS, and Linux.&lt;/p&gt;
&lt;h3 id=&quot;run-your-first-check&quot;&gt;Run your first check&lt;/h3&gt;
&lt;p&gt;Once installed, point it at a site:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;llmscout init ./my-site --site-url https://yourdomain.com
llmscout check ./my-site
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;init&lt;/code&gt; writes an &lt;code&gt;llmscout.json&lt;/code&gt; config file into &lt;code&gt;./my-site&lt;/code&gt;. &lt;code&gt;check&lt;/code&gt; fetches the URL in that config and runs all 21 checks against it.&lt;/p&gt;
&lt;p&gt;Here's real output from running &lt;code&gt;check&lt;/code&gt; against example.com:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-plaintext&quot;&gt;LLMScout check -- https://example.com

[PASS] (technical) Title tag
  Title &quot;Example Domain&quot; is 14 characters, within the recommended 10-60 range.

[WARN] (technical) Meta description
  No meta description found.
  Fix: Add &amp;lt;meta name=&quot;description&quot; content=&quot;...&quot;&amp;gt; with 50-160 characters summarizing the page.

[WARN] (technical) Canonical tag
  No &amp;lt;link rel=&quot;canonical&quot;&amp;gt; tag found.
  Fix: Add a canonical link tag pointing at the preferred URL for this page.

[FAIL] (technical) robots.txt
  robots.txt was not reachable at https://example.com/robots.txt (HTTP 404).
  Fix: Add a robots.txt file at your site root, even a permissive one, so crawlers
  and agents have explicit directives.

[WARN] (technical) sitemap.xml
  No sitemap was reachable (tried: https://example.com/sitemap.xml).
  Fix: Add a sitemap.xml at your site root, or point to one with a Sitemap: directive
  in robots.txt, to help search engines discover pages.

[PASS] (technical) Heading structure
  Exactly one &amp;lt;h1&amp;gt; and no skipped heading levels detected.

[WARN] (geo) Structured data (JSON-LD)
  No JSON-LD structured data found.
  Fix: Add schema.org JSON-LD markup (e.g. Organization, WebSite, or Article) so
  generative engines can understand the page's entities.

[WARN] (geo) AI crawler directives
  robots.txt is unreachable, so AI-crawler directives could not be determined.
  Fix: Add a reachable robots.txt if you want to state an explicit policy for AI
  crawlers (GPTBot, OAI-SearchBot, ClaudeBot, Claude-SearchBot, PerplexityBot,
  Google-Extended, Applebot-Extended).

Summary: 6 PASS, 14 WARN, 1 FAIL (21 checks)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are three things worth understanding about that output.&lt;/p&gt;
&lt;p&gt;FAIL exits with code 1. At least one check failed outright, and that's the signal a CI pipeline should gate on. WARN is a missed optimization; it won't fail the run on its own. PASS means the check came back clean.&lt;/p&gt;
&lt;p&gt;Notice the dependency between the last two checks: because robots.txt is unreachable, &lt;code&gt;ai-crawler-directives&lt;/code&gt; can't determine any policy, so it reports WARN instead of FAIL. An unreachable robots.txt and one that explicitly blocks every AI bot are different problems with different fixes, and the tool keeps that distinction explicit instead of collapsing both into one warning.&lt;/p&gt;
&lt;p&gt;Every WARN and FAIL comes with a fix suggestion built in. You don't need to look up what a canonical tag is or how to add one; the fix line tells you.&lt;/p&gt;
&lt;h3 id=&quot;json-output-and-ci&quot;&gt;JSON output and CI&lt;/h3&gt;
&lt;p&gt;For CI, add &lt;code&gt;--json&lt;/code&gt; and the output becomes something a script can actually parse:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;llmscout --json check ./my-site
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Exit codes: &lt;code&gt;0&lt;/code&gt; for a clean run, &lt;code&gt;1&lt;/code&gt; if at least one check FAILed, &lt;code&gt;2&lt;/code&gt; for a usage or config error. That's enough to fail a build on a new FAIL without writing your own parser logic.&lt;/p&gt;
&lt;p&gt;The JSON includes per-check &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;status&lt;/code&gt;, &lt;code&gt;message&lt;/code&gt;, and &lt;code&gt;fix&lt;/code&gt; fields, plus a &lt;code&gt;summary&lt;/code&gt; object with the totals. Wire it into a pre-merge check and a PR that regresses on structured data or robots.txt breaks the build instead of shipping quietly. The full schema is in the README.&lt;/p&gt;
&lt;h3 id=&quot;fleet-mode-for-multiple-sites&quot;&gt;Fleet mode for multiple sites&lt;/h3&gt;
&lt;p&gt;If you manage more than one site, &lt;code&gt;llmscout fleet&lt;/code&gt; takes a JSON manifest of site paths and runs the full 21-check suite against every one of them in a single command. &lt;code&gt;--out-dir&lt;/code&gt; writes one auto-named report file per site instead of dumping everything to stdout at once.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;llmscout fleet ./fleet.json --out-dir ./reports
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The manifest:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;sites&quot;: [
    { &quot;name&quot;: &quot;client-a&quot;, &quot;path&quot;: &quot;./clients/client-a&quot; },
    { &quot;name&quot;: &quot;client-b&quot;, &quot;path&quot;: &quot;./clients/client-b&quot; }
  ]
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That produces &lt;code&gt;client-a.txt&lt;/code&gt; and &lt;code&gt;client-b.txt&lt;/code&gt; (or &lt;code&gt;.json&lt;/code&gt; with &lt;code&gt;--json&lt;/code&gt;), one report per site, named straight from the manifest's &lt;code&gt;name&lt;/code&gt; field. That gives an agency a separate, named report file per client that you can open, send, or archive, instead of scrolling back through one combined terminal dump.&lt;/p&gt;
&lt;h3 id=&quot;wrap-up&quot;&gt;Wrap up&lt;/h3&gt;
&lt;p&gt;The checks cover the signals that matter most for AI-system citability: structured data, crawler access policies, content extractability, entity markup. Contested signals, like &lt;code&gt;llms.txt&lt;/code&gt;, are flagged as informational since the evidence for their impact is still developing. Run it against your own site and see where you actually stand.&lt;/p&gt;
&lt;p&gt;GitHub: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/RudrenduPaul/LLMScout&quot;&gt;https://github.com/RudrenduPaul/LLMScout&lt;/a&gt;&lt;br&gt;
npm: &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.npmjs.com/package/llmscout-cli&quot;&gt;https://www.npmjs.com/package/llmscout-cli&lt;/a&gt;&lt;br&gt;
PyPI: &lt;a rel=&quot;nofollow&quot; href=&quot;https://pypi.org/project/llmscout-cli/&quot;&gt;https://pypi.org/project/llmscout-cli/&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Co-authored by &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/RudrenduPaul&quot;&gt;Rudrendu Paul&lt;/a&gt; and &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Sourav-nandy-ai&quot;&gt;Sourav Nandy&lt;/a&gt;. Rudrendu is an AI/ML builder and published researcher (ICML, Springer Nature, Elsevier, IEEE) with 15+ years of experience at Fortune 50 companies, building open-source tools for the AI development ecosystem. Find the code at &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/RudrenduPaul&quot;&gt;github.com/RudrenduPaul&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/23698/how-to-check-if-ai-systems-can-find-and-cite-your-site-in-5-minutes</guid>
<pubDate>Tue, 28 Jul 2026 18:15:17 +0000</pubDate>
</item>
<item>
<title>Launching my first startup on product hunt</title>
<link>https://coderlegion.com/23641/launching-my-first-startup-on-product-hunt</link>
<description>&lt;p&gt;Built Quorel: turn any public website into a structured, versioned dataset. Describe the fields you want in plain English, it extracts nightly, and every refresh is saved as a diffable, reversible version.&lt;br&gt;
Real example, not a mockup: 25 rows added, 78 removed, 312 modified between two versions of the same live dataset: quorel.vercel.app/dataset/web-diff/1&lt;br&gt;
MCP server ships on every plan, including Free, so Claude or any agent can query, clean, and fix data directly.&lt;br&gt;
Live on Product Hunt today if you want to check it out: &lt;a rel=&quot;nofollow&quot; href=&quot;https://producthunt.com/posts/quorel&quot;&gt;https://producthunt.com/posts/quorel&lt;/a&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/23641/launching-my-first-startup-on-product-hunt</guid>
<pubDate>Tue, 28 Jul 2026 07:22:05 +0000</pubDate>
</item>
<item>
<title>Firecrawl vs Apify vs Quorel</title>
<link>https://coderlegion.com/23443/firecrawl-vs-apify-vs-quorel</link>
<description>&lt;p&gt;Firecrawl and Apify both do the hard part well. They render JavaScript, dodge bot detection, and hand you back clean markdown or structured JSON. If you need a page turned into text, either one will do that job.&lt;/p&gt;
&lt;p&gt;But most of the time the point isn't a single page. It's a page you need to check again tomorrow, and the day after, and the day after that: a price, a listing, a status, a set of rows that might quietly change. And that's where the workflow, not the extraction, starts costing you.&lt;/p&gt;
&lt;p&gt;Firecrawl bills per credit, and the credit math is not what the pricing page implies. Scrape is 1 credit. Add JSON extraction and Enhanced Mode — both of which you probably want for structured data — and it's 9 credits per page. A $16/mo Hobby plan advertised at 3,000 credits works out closer to 300–500 pages once you're actually extracting fields, not just fetching markdown. Credits also don't roll over, so a quiet month doesn't bank anything for a busy one.&lt;/p&gt;
&lt;p&gt;Apify flips the complaint around. Pricing is compute-unit based (RAM × time) plus separate line items for proxy and storage. It's flexible and it scales, but you can't look at a number on the pricing page and know what a job will cost until you've run it. For teams that want a predictable bill, that unpredictability is the whole problem.&lt;/p&gt;
&lt;p&gt;Neither tool keeps history by default. You get the current state of the page. If you want yesterday's version to compare against today's, that's on you to build — a database, a diffing script, a place to store snapshots, a way to roll back if a refresh comes back wrong.&lt;/p&gt;
&lt;p&gt;Quorel starts from a different question: not &quot;how do I fetch this page,&quot; but &quot;how do I keep a page as structured, versioned data over time.&quot; You describe the fields you want in plain English. Every refresh (nightly by default, or on demand via a ping URL, webhook, or your own cron) is saved as its own immutable version. Nothing is overwritten. You can diff any two versions to see exactly what changed, and roll back in one click if a refresh comes back bad.&lt;/p&gt;
&lt;p&gt;Pricing is flat, not metered. Free tier is one dataset, 20 URLs, full version history included. Pro is $19/mo for 5 datasets and 100 URLs, with on-demand refresh and webhooks. No credit multipliers, no compute-unit math, no surprise bill.&lt;/p&gt;
&lt;p&gt;This isn't a claim that Quorel replaces Firecrawl or Apify at everything they do. If you need to fetch an arbitrary one-off page as clean markdown for an LLM right now, Firecrawl is built for exactly that. If you need thousands of site-specific scrapers off a marketplace, Apify's Actor ecosystem has years of head start. Quorel is built for the narrower, recurring job: turning a page you check regularly into a structured, versioned API you can trust and query later.&lt;/p&gt;
&lt;p&gt;Every dataset ships with an MCP server on every plan, free included, so an agent can query it directly — or pull the raw rows, fix nulls, and publish a new version itself.&lt;/p&gt;
&lt;p&gt;If you've been burned by a scraper that silently broke, or wished you'd kept last week's data to compare against, that's the specific gap Quorel is built to close.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;I'm building &lt;a rel=&quot;nofollow&quot; href=&quot;https://quorel.vercel.app&quot;&gt;Quorel&lt;/a&gt; — happy to answer questions on versioning, pricing, or how the MCP integration works.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/23443/firecrawl-vs-apify-vs-quorel</guid>
<pubDate>Sat, 25 Jul 2026 21:23:40 +0000</pubDate>
</item>
<item>
<title>I Built a Job Listing + E-commerce API Dataset in 4 Hours, No Code</title>
<link>https://coderlegion.com/23414/i-built-a-job-listing-e-commerce-api-dataset-in-4-hours-no-code</link>
<description>&lt;h3 id=&quot;the-problem-with-just-build-your-own-api-&quot;&gt;The problem with &quot;just build your own API&quot;&lt;/h3&gt;
&lt;p&gt;If you've ever wanted a personal dataset (job listings, product prices, whatever) you know the real cost isn't the idea, it's the pipeline. Scraping. Cleaning. Storage. Refreshing on a schedule. Serving it over an API. Handling schema drift when the source site redesigns. Every one of those is its own mini-project, and each is its own source of friction.&lt;/p&gt;
&lt;p&gt;I wanted a dataset combining &lt;strong&gt;Amazon products for developers&lt;/strong&gt; and &lt;strong&gt;job listings scraped from multiple sites&lt;/strong&gt;. Normally that's a multi-day side project. I built it in about &lt;strong&gt;4 hours&lt;/strong&gt;, without writing a single line of scraping or backend code.&lt;/p&gt;
&lt;p&gt;Here's how.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/pvy8oezm7hjrndfx7fq1.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;the-magic-part-describing-data-instead-of-scraping-it&quot;&gt;The magic part: describing data instead of scraping it&lt;/h3&gt;
&lt;p&gt;I used a tool called &lt;a rel=&quot;nofollow&quot; href=&quot;https://quorel.vercel.app&quot;&gt;Quorel&lt;/a&gt;. The workflow was almost embarrassingly simple:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;I pasted in my source URLs (Amazon product pages, a few job board listing pages).&lt;/li&gt;
&lt;li&gt;I described the schema and extraction intent in plain English: no selectors, no XPath, no code. Something like &quot;product name, price, rating, whether it's in stock&quot; for the Amazon side, and &quot;job title, company, location, salary range, posted date&quot; for the listings side.&lt;/li&gt;
&lt;li&gt;I hit run and left it alone for 30–60 minutes.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;When I came back, the data was fully structured and ready to query.&lt;/p&gt;
&lt;p&gt;From there I asked Claude to build me a dashboard on top of the API so I could actually see and explore the data. That part took the rest of the 4 hours. The result is live here: &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://diffboard.vercel.app&quot;&gt;diffboard.vercel.app&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/2xd7l5ql02xmihbdguvd.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/l4xp2bw7y1gykueq74j0.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
&lt;h3 id=&quot;the-part-that-actually-sold-me-it-s-versioned-like-git&quot;&gt;The part that actually sold me: it's versioned like git&lt;/h3&gt;
&lt;p&gt;Structuring the data was the easy win. What kept me around is that Quorel treats datasets like a git repo for live web data:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Nightly refresh&lt;/strong&gt; (or your own cron schedule): the data doesn't go stale.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Every refresh is a saved version&lt;/strong&gt;, nothing overwrites the last run.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Diffing between versions&lt;/strong&gt;: I can see exactly what changed, like prices that moved, listings that disappeared, new rows that showed up.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Time travel&lt;/strong&gt;: I can pull the dataset as it looked days or months ago.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Rollback&lt;/strong&gt;: a bad refresh doesn't mean starting over.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Forking&lt;/strong&gt;: you can clone a public dataset the way you'd fork a repo, and extend it for your own use case.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That combination, structure plus history, is the part a plain scraper never gives you. Most tools hand you &lt;em&gt;today's&lt;/em&gt; snapshot. This hands you the whole timeline.&lt;/p&gt;
&lt;h3 id=&quot;talking-to-your-data-instead-of-querying-it&quot;&gt;Talking to your data instead of querying it&lt;/h3&gt;
&lt;p&gt;The other piece that stood out: Quorel ships an MCP server with every dataset, on every plan, including free. That means I can point an AI agent at my dataset and just talk to it, like &quot;find listings with price drops this week&quot; or &quot;fix the null prices and publish a new version,&quot; and it does the pull, clean, and push cycle for me, still landing in the same versioned system. It's genuinely like chatting with your data pipeline instead of writing scripts for it.&lt;/p&gt;
&lt;h3 id=&quot;try-it-yourself&quot;&gt;Try it yourself&lt;/h3&gt;
&lt;p&gt;The datasets I built are public and free to use. You can query them directly, or clone one and extend it to fit your own project:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Quorel: &lt;a rel=&quot;nofollow&quot; href=&quot;https://quorel.vercel.app&quot;&gt;quorel.vercel.app&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Public datasets: &lt;a rel=&quot;nofollow&quot; href=&quot;https://quorel.vercel.app/datasets&quot;&gt;quorel.vercel.app/datasets&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;My dashboard: &lt;a rel=&quot;nofollow&quot; href=&quot;https://diffboard.vercel.app&quot;&gt;diffboard.vercel.app&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I've barely scratched what's possible here. Next I want to enrich the job listings with salary normalization and wire the Amazon dataset into a price-drop alert bot. If you build something on top of this, I'd love to see it.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/23414/i-built-a-job-listing-e-commerce-api-dataset-in-4-hours-no-code</guid>
<pubDate>Fri, 24 Jul 2026 21:37:07 +0000</pubDate>
</item>
<item>
<title>Why Titles and Price Tags Don't Guarantee Quality: The Work Speaks for Itself</title>
<link>https://coderlegion.com/23258/why-titles-and-price-tags-dont-guarantee-quality-the-work-speaks-for-itself</link>
<description>&lt;h3 id=&quot;how-we-rebuilt-a-website-for-a-musician-and-why-engineering-ownership-matters-more-than-commercial-labels-&quot;&gt;How We Rebuilt a Website for a Musician and Why Engineering Ownership Matters More Than Commercial Labels.&lt;/h3&gt;
&lt;p&gt;In the tech market, people tend to judge a book by its cover: if a &quot;design agency&quot; takes on a project, clients are willing to pay big checks on blind trust. If an entry-level developer works on it, they're met with skepticism (&lt;em&gt;&quot;they stand no chance of getting hired right now anyway&quot;&lt;/em&gt;).&lt;/p&gt;
&lt;p&gt;But do titles and price tags actually guarantee results? We recently completed a full-stack custom web platform for an independent Canadian musician. And this story is the best answer to anyone who measures quality by brand name alone.&lt;/p&gt;
&lt;h3 id=&quot;background-the-client-s-journey-through-professionals-&quot;&gt;Background: The Client's Journey Through &quot;Professionals&quot;&lt;/h3&gt;
&lt;p&gt;The story of this website is a classic example of a client going in circles searching for a decent solution:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The &quot;Professional&quot; Agency:&lt;/strong&gt; The artist originally hired a specialized agency. They quoted a fee he assumed was the total cost, but halfway through, the agency demanded more money. In the end, the client was left with a half-baked mess despite paying a commercial rate.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DIY Attempts:&lt;/strong&gt; The artist tried building the site himself using popular website builders, but ran straight into their rigid limitations.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Volunteer Format:&lt;/strong&gt; Eventually, the project shifted into a hands-on practice model. I stepped in alongside David (LinkedIn: &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.linkedin.com/in/david-h-watson/&quot;&gt;David H. Watson&lt;/a&gt;, GitHub: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/dEhiN&quot;&gt;@dEhiN&lt;/a&gt; — backend), taking full charge of the frontend, UI/UX, client logic, and site architecture.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For me, this wasn't just a &quot;learning pet project&quot;—it was a real production case where I poured in over 320 hours of work, striving for top-tier quality.&lt;/p&gt;
&lt;h3 id=&quot;the-before-phase-what-did-the-client-pay-the-agency-for-&quot;&gt;The &quot;BEFORE&quot; Phase: What Did the Client Pay the Agency For?&lt;/h3&gt;
&lt;p&gt;When I received the raw source files from the studio (a WordPress dump, a bloated theme, and chaotic HTML/CSS), my initial reaction was deep repulsion. This wasn't a site built by professionals; it was a product slapped together &quot;on the knee&quot; during coffee breaks.&lt;/p&gt;
&lt;h4 id=&quot;what-was-under-the-hood-and-in-the-ui-&quot;&gt;What was under the hood and in the UI:&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;An Infrastructure Monster:&lt;/strong&gt; A heavy system loaded with overlapping styles just to display a couple of audio tracks and photos.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Complete Disregard for Accessibility (a11y):&lt;/strong&gt; A musician's website should be comfortable for every single user. In the original codebase, accessibility logic was completely wrecked: broken element focus, lack of semantic structure, and a mess of CSS rules overriding native browser behavior.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Zero UX Consideration:&lt;/strong&gt; Clunky player interactions, broken spacing, and complete unusable layouts on mobile devices.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Developer's Reflection:&lt;/strong&gt;&lt;br&gt;
Everyone preaches Best Practices—semantics, accessibility (a11y), proper focus handling, clean code, and optimization. Then you look at code produced by a &quot;commercial agency&quot; and find a Frankenstein monster where all those standards were simply tossed aside.&lt;/p&gt;
&lt;p&gt;When it comes to music, a website should be accessible and alive. If developers don't care about that, it's not a technology problem—it's an attitude problem.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That's exactly why I decided against patching up this WordPress corpse and chose to build a clean, lightweight, and accessible frontend from scratch—one I could actually be proud of.&lt;/p&gt;
&lt;h3 id=&quot;the-after-phase-having-nothing-but-320-hours-and-engineering-passion&quot;&gt;The &quot;AFTER&quot; Phase: Having Nothing but 320 Hours and Engineering Passion&lt;/h3&gt;
&lt;p&gt;I can already hear the ironic smirks from seniors and team leads: &lt;br&gt;
&lt;em&gt;“320 hours on a single website?! Talk about inexperience, typical Junior. You could rewrite three SPAs in that time!”&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Alright, let’s break down letter by letter what actually went into those 320 hours.&lt;/p&gt;
&lt;h4 id=&quot;at-the-start-we-had-absolutely-nothing-&quot;&gt;At the start, we had ABSOLUTELY NOTHING:&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Design and Content?&lt;/strong&gt; Zero. No logo, no promotional copy, not even a single usable photo. I spent hours generating promotional assets, tailoring them to fit various grid formats, layouts, and resolutions. Everything visual on this site is the work of a frontend developer wearing a designer hat.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Understanding What the Client Wanted?&lt;/strong&gt; Nonexistent. The client expected a flawless outcome, but the vision kept shifting on the fly. I had to iterate through dozens of concepts and finalize a responsive layout.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Audio Content?&lt;/strong&gt; There was only an idea: link the site's playlist to Bandcamp so we wouldn't have to hardcode every track card manually.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h4 id=&quot;technical-breakdown-&quot;&gt;Technical Breakdown:&lt;/h4&gt;
&lt;h5 id=&quot;1-audio-pipeline-automation-build-tools-&quot;&gt;1. Audio Pipeline Automation (&lt;code&gt;build-tools&lt;/code&gt;)&lt;/h5&gt;
&lt;p&gt;It turned out the client didn't even have a set-up profile, and the audio files were in the wrong format. While David was uploading tracks to Bandcamp, I wrote a CLI script using Node.js and &lt;code&gt;fluent-ffmpeg&lt;/code&gt; to automatically convert MP3s to required WAV formats, generate copies, and verify duration via &lt;code&gt;ffprobe&lt;/code&gt; down to 60 ms tolerance, checking channel counts and sample rates along the way.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// MP3 to WAV conversion with auto-verification via ffprobe (excerpt)
async function convertAndVerify(inputPath, outGenerated, outDist, attempts = 1) {
  for (let attempt = 0; attempt &amp;lt;= attempts; attempt++) {
    try {
      await convertOne(inputPath, outGenerated, outDist);
      if (!VERIFY) return;

      const srcInfo = await ffprobeAsync(inputPath);
      const actualOut = SKIP_DIST ? outGenerated : outDist;
      const outInfo = await ffprobeAsync(actualOut);

      const durDiff = Math.abs((srcInfo.duration || 0) - (outInfo.duration || 0));
      const DURATION_THRESHOLD = 0.06; // 60ms толерантность

      const sampleMatch = srcInfo.sample_rate === outInfo.sample_rate;
      const channelsMatch = srcInfo.channels === outInfo.channels;

      if (durDiff &amp;gt; DURATION_THRESHOLD || !sampleMatch || !channelsMatch) {
        throw new Error(`Verification failed (durDiff: ${durDiff}s)`);
      }
      return;
    } catch (err) {
      if (attempt &amp;gt;= attempts) throw err;
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;2-dynamic-playlist-class-playlist-js-&quot;&gt;2. Dynamic Playlist Class (&lt;code&gt;playlist.js&lt;/code&gt;)&lt;/h4&gt;
&lt;p&gt;Dedicated client-side logic was written for the music section:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;- Backend Integration:&lt;/strong&gt; The &lt;code&gt;Playlist&lt;/code&gt; class executes requests with automatic retries (_&lt;code&gt;fetchWithRetry&lt;/code&gt;) targeting &lt;code&gt;/api/bandcamp&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;- Inline Markup Cleanup (&lt;code&gt;normalizeIframes&lt;/code&gt;):&lt;/strong&gt; The method forcibly strips hardcoded &lt;code&gt;width&lt;/code&gt; and &lt;code&gt;height&lt;/code&gt; attributes from Bandcamp iframes, delegating layout control entirely to SCSS and CSS custom properties (&lt;code&gt;--collapsed-height&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;- Smart Expand/Collapse &amp;amp; Accessibility:&lt;/strong&gt; Automatically calculates grid items per row (&lt;code&gt;_computeItemsPerRow&lt;/code&gt;), dynamically updates &lt;code&gt;aria-expanded&lt;/code&gt; and &lt;code&gt;aria-controls&lt;/code&gt; attributes for the &quot;See more / See less&quot; button, and handles viewport changes with a &lt;code&gt;debounce&lt;/code&gt; listener.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;// Frame normalization and collapsed playlist height calculation (excerpt)
normalizeIframes() {
  const iframes = Array.from(document.querySelectorAll('iframe.playlist__iframe, iframe'));
  iframes.forEach((frame) =&amp;gt; {
    try {
      frame.removeAttribute('width');
      frame.removeAttribute('height');
      if (frame.style) {
        frame.style.removeProperty('width');
        frame.style.removeProperty('height');
      }
    } catch (e) { /* ignore CORS/readonly */ }
  });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;3-architecture-multi-page-feel-without-heavy-frameworks-custom-spa-router-&quot;&gt;3. Architecture: Multi-Page Feel Without Heavy Frameworks (Custom SPA Router)&lt;/h4&gt;
&lt;p&gt;The client preferred a traditional multi-page website (MPA) feel—with explicit URLs, intuitive navigation, and page state. However, I didn't want to bloat the workspace, inflate bundle sizes, or deal with maintaining multi-page routing overhead.&lt;/p&gt;
&lt;p&gt;The solution? Build a custom vanillajs SPA router using hash routing that looks like a traditional MPA from the outside while running blazingly fast under the hood.&lt;/p&gt;
&lt;p&gt;The router intercepts internal link clicks, manages &lt;code&gt;history.pushState&lt;/code&gt; / &lt;code&gt;popstate&lt;/code&gt;, updates accessibility attributes (&lt;code&gt;aria-current=&quot;page&quot;&lt;/code&gt;) and seamlessly handles browser back/forward buttons.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;export default class Router {
  constructor(routes = {}, options = {}) {
    this.routes = routes;
    this.useHash = options.useHash !== undefined ? options.useHash : true;
    this._onLinkClick = this._onLinkClick.bind(this);
    this._onPopState = this._onPopState.bind(this);
  }

  init() {
    document.addEventListener('click', this._onLinkClick);
    window.addEventListener(this.useHash ? 'hashchange' : 'popstate', this._onPopState);
    const initialPath = this._getPath();
    this._routeTo(initialPath);
    try { history.replaceState({ path: initialPath }, '', window.location.href); } catch (e) {}
  }

  _onLinkClick(e) {
    const anchor = e.target.closest &amp;amp;&amp;amp; e.target.closest('a');
    if (!anchor) return;
    const href = anchor.getAttribute('href');
    if (!href || href.startsWith('http') || anchor.target === '_blank') return;

    e.preventDefault();
    let path = href.startsWith('#') ? href.slice(1) || '/' : href;
    this.navigate(path);
  }

  _updateActiveLink(path) {
    const links = document.querySelectorAll('.header__nav-link');
    links.forEach((link) =&amp;gt; {
      const linkPath = link.getAttribute('href').replace(/^#/, '');
      if (linkPath === path) link.setAttribute('aria-current', 'page');
      else link.removeAttribute('aria-current');
    });
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;4-what-about-infrastructure-and-ci-cd-&quot;&gt;4. What About Infrastructure and CI/CD?&lt;/h4&gt;
&lt;p&gt;Not to mention writing end-to-end (E2E) tests and configuring complete CI/CD deployment pipelines. At the start, we had no idea where or how we would host the project.&lt;/p&gt;
&lt;p&gt;Ultimately, we settled on Render.com's free tier. You could say spending time configuring production-grade CI/CD was a bit overkill for this stage, but as a result, the project got a solid setup that deploys in a single click.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note on Project Status &amp;amp; Demo:&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;As is common in software engineering, project handoffs and client hosting agreements can evolve over time. While the original client instance is currently offline, our custom-built frontend architecture, Vanilla JS router, and media pipelines remain a fully functional showcase of high-performance web engineering. A standalone demo repository/showcase stand will be published shortly.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&quot;conclusion-quality-isn-t-measured-by-price-tags&quot;&gt;Conclusion: Quality Isn't Measured by Price Tags&lt;/h3&gt;
&lt;p&gt;Is 320+ hours a lot? If you view a developer as a &quot;button pusher executing pre-made Jira tasks and Figma designs&quot;—yes, it is.&lt;/p&gt;
&lt;p&gt;But if your job is to take absolute chaos, zero media assets, and broken legacy code, and turn it into a production-ready, beautiful, accessible (a11y), and optimized web product—that is the real cost of deep engineering commitment.&lt;/p&gt;
&lt;p&gt;I’m proud of what we accomplished on this architecture. We built a fully functional, high-standard platform from scratch and gained immense experience along the way.&lt;/p&gt;
&lt;p&gt;So next time someone tells you entry-level developers can't build anything without third-party libraries or constant senior oversight—just show them this case. In the end, the code and the work speak louder than any title.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/23258/why-titles-and-price-tags-dont-guarantee-quality-the-work-speaks-for-itself</guid>
<pubDate>Wed, 22 Jul 2026 15:44:58 +0000</pubDate>
</item>
<item>
<title>We Measure Everything Except Code Quality — So I Built CodeMultiVitals</title>
<link>https://coderlegion.com/23137/we-measure-everything-except-code-quality-so-i-built-codemultivitals</link>
<description>&lt;p&gt;As software engineers, we've become incredibly good at monitoring our applications.&lt;/p&gt;
&lt;p&gt;We monitor CPU usage.&lt;/p&gt;
&lt;p&gt;We monitor memory.&lt;/p&gt;
&lt;p&gt;We monitor API latency.&lt;/p&gt;
&lt;p&gt;We monitor database performance.&lt;/p&gt;
&lt;p&gt;We monitor logs, traces, and infrastructure.&lt;/p&gt;
&lt;p&gt;Our CI/CD pipelines run tests, security scans, dependency audits, and linters before every deployment.&lt;/p&gt;
&lt;p&gt;Yet there's one thing we rarely monitor continuously:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The health of the codebase itself.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;That realization became the motivation behind building &lt;strong&gt;CodeMultiVitals&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-problem-2&quot;&gt;The Problem&lt;/h3&gt;
&lt;p&gt;Every project starts with clean, maintainable code.&lt;/p&gt;
&lt;p&gt;Then reality kicks in.&lt;/p&gt;
&lt;p&gt;New features are added.&lt;/p&gt;
&lt;p&gt;Developers join the team.&lt;/p&gt;
&lt;p&gt;Deadlines become tighter.&lt;/p&gt;
&lt;p&gt;Requirements change.&lt;/p&gt;
&lt;p&gt;Months later, the project still works—but the code becomes increasingly difficult to maintain.&lt;/p&gt;
&lt;p&gt;You begin to notice:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Large functions&lt;/li&gt;
&lt;li&gt;Deeply nested logic&lt;/li&gt;
&lt;li&gt;Growing cyclomatic complexity&lt;/li&gt;
&lt;li&gt;Duplicate code&lt;/li&gt;
&lt;li&gt;Files nobody wants to touch&lt;/li&gt;
&lt;li&gt;Pull requests that are harder to review&lt;/li&gt;
&lt;li&gt;Refactoring becoming increasingly risky&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These issues don't appear overnight.&lt;/p&gt;
&lt;p&gt;They accumulate gradually.&lt;/p&gt;
&lt;p&gt;Unfortunately, by the time we notice them, fixing them is significantly more expensive.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;existing-tools-solve-different-problems&quot;&gt;Existing Tools Solve Different Problems&lt;/h3&gt;
&lt;p&gt;Don't get me wrong—we already have fantastic tools.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ESLint catches code smells.&lt;/li&gt;
&lt;li&gt;Prettier keeps formatting consistent.&lt;/li&gt;
&lt;li&gt;TypeScript improves type safety.&lt;/li&gt;
&lt;li&gt;Jest validates behavior.&lt;/li&gt;
&lt;li&gt;GitHub Actions automates CI/CD.&lt;/li&gt;
&lt;li&gt;Security scanners identify vulnerabilities.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These tools answer an important question:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;&quot;Does the application work correctly?&quot;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;But they don't necessarily answer another question:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;&quot;Is the codebase becoming healthier over time?&quot;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Passing tests don't guarantee maintainable software.&lt;/p&gt;
&lt;p&gt;A successful deployment doesn't guarantee sustainable architecture.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-question-that-started-everything&quot;&gt;The Question That Started Everything&lt;/h3&gt;
&lt;p&gt;I kept asking myself:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why can't every commit tell us whether the project just became healthier—or worse?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Imagine opening a Pull Request and immediately seeing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Complexity increased by 12%&lt;/li&gt;
&lt;li&gt;Maintainability score dropped&lt;/li&gt;
&lt;li&gt;Three files exceeded recommended thresholds&lt;/li&gt;
&lt;li&gt;Technical debt is trending upward&lt;/li&gt;
&lt;li&gt;Overall project health decreased&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Instead of relying entirely on opinions during code reviews, we'd have measurable engineering data.&lt;/p&gt;
&lt;p&gt;That's exactly what I wanted to build.&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&quot;introducing-codemultivitals&quot;&gt;Introducing CodeMultiVitals&lt;/h2&gt;
&lt;p&gt;CodeMultiVitals is an open-source code quality metrics engine for JavaScript and TypeScript.&lt;/p&gt;
&lt;p&gt;Its goal isn't to replace existing tools.&lt;/p&gt;
&lt;p&gt;Instead, it complements them by continuously measuring code quality across an entire project.&lt;/p&gt;
&lt;p&gt;The current pilot version includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Complexity Analysis&lt;/li&gt;
&lt;li&gt;Maintainability Metrics&lt;/li&gt;
&lt;li&gt;❤️ Project Health Scores&lt;/li&gt;
&lt;li&gt;️ CLI&lt;/li&gt;
&lt;li&gt;Programmatic API&lt;/li&gt;
&lt;li&gt;HTML Dashboard&lt;/li&gt;
&lt;li&gt;⚙️ GitHub Actions Integration&lt;/li&gt;
&lt;li&gt;SARIF Output&lt;/li&gt;
&lt;li&gt;️ SVG Badges&lt;/li&gt;
&lt;li&gt;Historical Health Tracking&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The objective is simple:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Turn code quality into measurable engineering data.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id=&quot;why-this-matters&quot;&gt;Why This Matters&lt;/h3&gt;
&lt;p&gt;Poor code quality doesn't just affect developers.&lt;/p&gt;
&lt;p&gt;It affects the entire business.&lt;/p&gt;
&lt;p&gt;Healthy code leads to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Faster onboarding&lt;/li&gt;
&lt;li&gt;Better code reviews&lt;/li&gt;
&lt;li&gt;Easier refactoring&lt;/li&gt;
&lt;li&gt;Lower maintenance costs&lt;/li&gt;
&lt;li&gt;Higher developer productivity&lt;/li&gt;
&lt;li&gt;More predictable releases&lt;/li&gt;
&lt;li&gt;Reduced technical debt&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The earlier teams identify quality issues, the cheaper they are to fix.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-long-term-vision&quot;&gt;The Long-Term Vision&lt;/h3&gt;
&lt;p&gt;My vision extends beyond building another CLI tool.&lt;/p&gt;
&lt;p&gt;I'd like to see continuous code quality become a standard engineering practice.&lt;/p&gt;
&lt;p&gt;Just as every project runs automated tests before merging code, I believe every project should also answer questions like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Is maintainability improving?&lt;/li&gt;
&lt;li&gt;Is complexity increasing?&lt;/li&gt;
&lt;li&gt;Are we introducing technical debt?&lt;/li&gt;
&lt;li&gt;Which modules should be refactored first?&lt;/li&gt;
&lt;li&gt;Is the project healthier than it was yesterday?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Code quality shouldn't be something we think about only during refactoring sprints.&lt;/p&gt;
&lt;p&gt;It should be continuously visible.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;this-is-only-the-beginning&quot;&gt;This Is Only the Beginning&lt;/h3&gt;
&lt;p&gt;The current release is intentionally a &lt;strong&gt;Pilot Version&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;There are many ideas planned for future releases:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Framework-specific analysis&lt;/li&gt;
&lt;li&gt;Pull Request Quality Reports&lt;/li&gt;
&lt;li&gt;Team Dashboards&lt;/li&gt;
&lt;li&gt;Organization-wide Analytics&lt;/li&gt;
&lt;li&gt;AI-assisted insights&lt;/li&gt;
&lt;li&gt;CI/CD Quality Gates&lt;/li&gt;
&lt;li&gt;Custom metric plugins&lt;/li&gt;
&lt;li&gt;Trend-based quality forecasting&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The goal is to build an ecosystem that helps engineering teams make better architectural decisions using measurable data.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;i-d-love-your-feedback&quot;&gt;I'd Love Your Feedback&lt;/h3&gt;
&lt;p&gt;If you work with JavaScript or TypeScript, I'd really appreciate your thoughts.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What code quality metrics matter most to your team?&lt;/li&gt;
&lt;li&gt;Which quality problems do existing tools fail to solve?&lt;/li&gt;
&lt;li&gt;What would make a tool like this genuinely useful in your workflow?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The pilot version is available now, and I'm actively looking for feedback from the community.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;NPM:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.npmjs.com/package/code-multivitals&quot;&gt;https://www.npmjs.com/package/code-multivitals&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Open-source projects improve through honest discussions, and I'd love to hear your ideas, critiques, and feature requests.&lt;/p&gt;
&lt;p&gt;Let's make &lt;strong&gt;code quality measurable, affordable, and part of every engineering workflow&lt;/strong&gt;.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/23137/we-measure-everything-except-code-quality-so-i-built-codemultivitals</guid>
<pubDate>Tue, 21 Jul 2026 13:01:19 +0000</pubDate>
</item>
<item>
<title>I Built a Tool to Visualize DSA. Let’s Learn Together! (DSA View View 👀👀)</title>
<link>https://coderlegion.com/22758/i-built-a-tool-to-visualize-dsa-lets-learn-together-dsa-view-view</link>
<description>&lt;p&gt;Hoi hoi!&lt;/p&gt;
&lt;p&gt;I’m &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru&quot;&gt;@nyaomaru&lt;/a&gt;, a frontend engineer currently fighting the intense European heatwave by building DIY window screens here in the Netherlands. 🛠️&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://medium.com/data-science-collective/should-you-still-learn-to-code-in-2026-034685e17707&quot;&gt;https://medium.com/data-science-collective/should-you-still-learn-to-code-in-2026-034685e17707&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;As this article also discusses, I believe that &lt;strong&gt;improving our fundamental engineering skills is still essential in the age of AI&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Imagine that AI makes an F1 car fully autonomous. 🏎️&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Would you let someone without even a driver’s license enter a race as its driver?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Or imagine that AI completely automates maintenance during pit stops. &lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Would you put someone who knows absolutely nothing about cars, their parts, or their internal structure in charge of that AI?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;AI is certainly reducing the amount of code we need to write by hand. Typing quickly and memorizing every small piece of syntax or every method are becoming less important.&lt;/p&gt;
&lt;p&gt;But &lt;strong&gt;can we confidently release a product without understanding its fundamental concepts or implementation?&lt;/strong&gt; If something goes wrong in production, can we take responsibility and fix it?&lt;/p&gt;
&lt;p&gt;My answer was &lt;strong&gt;no&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Building things faster is obviously a good thing. But if we build without understanding, we may simply produce fragile software with no long-term sustainability at a faster rate.&lt;/p&gt;
&lt;p&gt;The real value comes from building meaningful things quickly.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Then, how can we develop the fundamental skills we still need in the AI era?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I believe one useful approach is learning &lt;strong&gt;DSA: Data Structures and Algorithms&lt;/strong&gt;. So I decided to start learning DSA again.&lt;/p&gt;
&lt;p&gt;However, while solving problems on &lt;code&gt;LeetCode&lt;/code&gt;, I repeatedly ran into the same problem.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;I want to visualize the data flow and understand what is happening, but I still can’t clearly see it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So I used the skills I have developed through frontend engineering and built a tool that visualizes the execution flow of DSA implementations! 🚀&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru/dsa-view-view&quot;&gt;https://github.com/nyaomaru/dsa-view-view&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru/dsa-view-view&quot;&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/7z2mrykqy49xd42oxyod.gif&quot; alt=&quot;DSA View View demo&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In this article, I’ll explain how &lt;strong&gt;DSA View View&lt;/strong&gt; works and how we can use it to study DSA.&lt;/p&gt;
&lt;p&gt;Let’s take a look together!&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Disclaimer&lt;/strong&gt;&lt;br&gt;
I may sound confident so far, but I’m definitely not a DSA expert. I’m still learning, just like many of you.&lt;/p&gt;
&lt;p&gt;In fact, I built this tool precisely because I was struggling. When I realized that I couldn’t immediately solve Binary Search, I started to panic a little. 😸&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-what-is-dsa-view-view-&quot;&gt;🖥️ What Is DSA View View?&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;DSA View View&lt;/strong&gt; is a tool that executes functions and classes written in &lt;code&gt;TypeScript&lt;/code&gt; and lets you inspect how variables and return values change on each line through a runtime timeline.&lt;/p&gt;
&lt;p&gt;The easiest way to understand is to try it yourself!&lt;/p&gt;
&lt;p&gt;Open the tool and click the &lt;strong&gt;Run Demo&lt;/strong&gt; button.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru/dsa-view-view&quot;&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/hd0vewv2n72vf15buviw.png&quot; alt=&quot;Run Demo Button&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You’ll quickly see how the workflow works:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Write your implementation&lt;/li&gt;
&lt;li&gt;Compile it&lt;/li&gt;
&lt;li&gt;Enter the input values&lt;/li&gt;
&lt;li&gt;Run it&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That’s really it.&lt;/p&gt;
&lt;p&gt;Although I created it primarily for DSA problems, you can also use it to inspect and test smaller &lt;code&gt;TypeScript&lt;/code&gt; implementations.&lt;/p&gt;
&lt;p&gt;And, of course, it’s completely free!&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-what-can-it-do-&quot;&gt;🤔 What Can It Do?&lt;/h3&gt;
&lt;p&gt;With DSA View View, you can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Write DSA implementations in &lt;code&gt;TypeScript&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Test implementations with different input values&lt;/li&gt;
&lt;li&gt;Move forward and backward through runtime steps&lt;/li&gt;
&lt;li&gt;Visualize supported data structures and algorithms&lt;/li&gt;
&lt;li&gt;Review LeetCode solutions and confirm your understanding&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can also use it as a lightweight environment for executing functions or formatting code blocks for technical articles such as Dev.to 😸&lt;/p&gt;
&lt;p&gt;Feel free to experiment with it and find your own use cases!&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-how-to-use-it&quot;&gt;🧑🏫 How to Use It&lt;/h3&gt;
&lt;p&gt;The main workflow consists of three modes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Editor&lt;/li&gt;
&lt;li&gt;Verification&lt;/li&gt;
&lt;li&gt;Runtime&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let’s look at each one.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;editor&quot;&gt;Editor&lt;/h3&gt;
&lt;p&gt;First, write your implementation!&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru/dsa-view-view&quot;&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/xfn7q92x88jgncmku5ia.png&quot; alt=&quot;Image description&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The Editor is designed to feel similar to a basic IDE.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;TypeScript&lt;/code&gt; diagnostics are available by default, so the editor can detect some errors while you write.&lt;/p&gt;
&lt;p&gt;You can also press &lt;code&gt;Cmd/Ctrl + S&lt;/code&gt; to format your code with Prettier, so feel free to write first and clean it up afterward.&lt;/p&gt;
&lt;p&gt;Common classes used in DSA problems, such as &lt;code&gt;TreeNode&lt;/code&gt; and &lt;code&gt;ListNode&lt;/code&gt;, are already available.&lt;/p&gt;
&lt;p&gt;You don’t need to declare or import them every time.&lt;/p&gt;
&lt;p&gt;You can also define your own classes or replace the provided definitions when necessary.&lt;/p&gt;
&lt;p&gt;Once your implementation is ready, click &lt;strong&gt;Compile Code&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;If compilation fails, the tool displays the relevant location and error message so that you can fix it.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru/dsa-view-view&quot;&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/1np9j681z3850xkqk2zv.png&quot; alt=&quot;Image description&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;There is also an &lt;strong&gt;Example&lt;/strong&gt; menu containing several common implementations.&lt;/p&gt;
&lt;p&gt;For example, if you want to quickly inspect a Heap Sort implementation and see how it behaves, you can load the example instead of writing everything manually.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;verification&quot;&gt;Verification&lt;/h3&gt;
&lt;p&gt;In Verification mode, you define the input values that should be passed to your function or class.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru/dsa-view-view&quot;&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/ya388vbn1ihnn98qadwv.png&quot; alt=&quot;Image description&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Enter a test value for each argument.&lt;/p&gt;
&lt;p&gt;For arrays, both of the following formats are supported:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;[1, 2, 3]
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1, 2, 3
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once your input values are ready, click &lt;strong&gt;Run&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;runtime&quot;&gt;Runtime&lt;/h3&gt;
&lt;p&gt;Finally, let’s inspect the runtime!&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru/dsa-view-view&quot;&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/4qbwieq7xxhytjfni4pe.png&quot; alt=&quot;Image description&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;When you click &lt;strong&gt;Run&lt;/strong&gt; in Verification mode, your implementation is executed.&lt;/p&gt;
&lt;p&gt;Runtime mode then lets you play through the execution steps or move backward and forward manually.&lt;/p&gt;
&lt;p&gt;When the tool recognizes supported logic, &lt;strong&gt;an additional visualization is displayed in a modal&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Not every possible implementation is supported yet, of course. If something you need is missing, I would be happy to receive an issue or pull request!&lt;/p&gt;
&lt;p&gt;You can move backward and forward through the executed lines to inspect:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Which line is currently running&lt;/li&gt;
&lt;li&gt;How variables change&lt;/li&gt;
&lt;li&gt;Where values are returned&lt;/li&gt;
&lt;li&gt;How the execution reaches its final result&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can return to the beginning, jump directly to the end, or move through the timeline one step at a time.&lt;/p&gt;
&lt;p&gt;The main execution process runs entirely in the browser.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;share-&quot;&gt;Share 🐈&lt;/h3&gt;
&lt;p&gt;DSA View View also includes a sharing feature.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru/dsa-view-view&quot;&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/0nw9ys2wj0mbh41yty50.png&quot; alt=&quot;DSA View View Share button&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can create a URL that preserves your implementation and share it with someone else.&lt;/p&gt;
&lt;p&gt;Use the &lt;strong&gt;Share&lt;/strong&gt; button at the top of the page to post it on social media or copy the link directly.&lt;/p&gt;
&lt;p&gt;Share your implementations, compare different solutions, and let’s learn together!&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; The shared URL contains your implementation. Do not paste private, confidential, or proprietary code into the tool before sharing it.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;what-is-that-thing-in-the-top-right-corner-&quot;&gt;What Is That Thing in the Top-Right Corner? 👀👀&lt;/h3&gt;
&lt;p&gt;Meet Mr. View.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru/dsa-view-view&quot;&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/adl9obeidbaf1fn0ufv6.gif&quot; alt=&quot;Image description&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Mr. View watches over your implementation.&lt;/p&gt;
&lt;p&gt;Nothing more.&lt;/p&gt;
&lt;p&gt;Nothing less.&lt;/p&gt;
&lt;p&gt;He is simply there to support you.&lt;/p&gt;
&lt;p&gt;When you need to concentrate, press the TV button to hide him. Press it again to bring him back.&lt;/p&gt;
&lt;p&gt;However, if Mr. View is enough to distract you, you may not be ready for an attack from ten simultaneous Slack notifications. 😸&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-how-should-we-study-dsa-&quot;&gt;✏️ How Should We Study DSA?&lt;/h3&gt;
&lt;p&gt;Now you know how to use DSA View View.&lt;/p&gt;
&lt;p&gt;The next question is probably&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Which problems should I solve?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;LeetCode is an obvious place to start, and the study plan from Tech Interview Handbook also provides a useful collection of practice problems. 👇&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.techinterviewhandbook.org/coding-interview-study-plan/&quot;&gt;https://www.techinterviewhandbook.org/coding-interview-study-plan/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Try solving these problems with &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru/dsa-view-view&quot;&gt;DSA View View&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Once your implementation works, use the runtime timeline to inspect the data flow and understand exactly how the algorithm reaches its answer.&lt;/p&gt;
&lt;p&gt;You can also use tools such as ChatGPT, Codex, Claude, Claude Code, Gemini, and Grok to explore your solution further.&lt;/p&gt;
&lt;p&gt;Don’t limit yourself to asking&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Is this implementation correct?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Try asking questions such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What would happen if I rewrote this DFS solution using BFS?&lt;/li&gt;
&lt;li&gt;Could Dynamic Programming solve this problem?&lt;/li&gt;
&lt;li&gt;Are there any other reasonable approaches?&lt;/li&gt;
&lt;li&gt;What are the time and space complexities?&lt;/li&gt;
&lt;li&gt;Why is this approach better than the alternatives?&lt;/li&gt;
&lt;li&gt;Under which conditions would another approach be better?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;AI becomes much more useful for learning when it helps us compare and understand different approaches instead of only generating a final answer. 👍&lt;/p&gt;
&lt;h4 id=&quot;why-typescript-instead-of-python-&quot;&gt;Why TypeScript Instead of Python? 🐍&lt;/h4&gt;
&lt;p&gt;The simple answer is that &lt;code&gt;TypeScript&lt;/code&gt; is my favorite language.&lt;/p&gt;
&lt;p&gt;I mainly work as a frontend engineer, so &lt;code&gt;TypeScript&lt;/code&gt; is also the language in which I feel most comfortable experimenting and learning.&lt;/p&gt;
&lt;p&gt;However, I designed the tool so that additional languages could be supported in the future.&lt;/p&gt;
&lt;p&gt;If there is enough demand for another language, please leave a comment or open an issue!&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-conclusion&quot;&gt;🎯 Conclusion&lt;/h3&gt;
&lt;p&gt;AI is helping us write code faster and faster.&lt;/p&gt;
&lt;p&gt;However, understanding what happens inside an implementation remains an important engineering skill.&lt;/p&gt;
&lt;p&gt;In fact, I believe that this ability may become even more valuable as AI-generated code becomes increasingly common.&lt;/p&gt;
&lt;p&gt;I built DSA View View because I wanted a tool that could help me strengthen those fundamental skills from the ground up.&lt;/p&gt;
&lt;p&gt;To be honest, I still have a lot to learn about DSA.&lt;/p&gt;
&lt;p&gt;That is exactly why I hope we can learn together.&lt;/p&gt;
&lt;p&gt;Please leave a comment or open an issue if:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You want support for a particular problem or data structure&lt;/li&gt;
&lt;li&gt;You have an idea for a new visualization&lt;/li&gt;
&lt;li&gt;You find a bug&lt;/li&gt;
&lt;li&gt;You want support for another programming language&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let’s train our DSA muscles together! 💪😸&lt;/p&gt;
&lt;p&gt;If you like it, please give a star ⭐&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru/dsa-view-vie&quot;&gt;https://github.com/nyaomaru/dsa-view-vie&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And I also launched on ProductHunt! Come say hello and let me know what you think 😸 👇&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.producthunt.com/products/dsa-view-view&quot;&gt;https://www.producthunt.com/products/dsa-view-view&lt;/a&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/22758/i-built-a-tool-to-visualize-dsa-lets-learn-together-dsa-view-view</guid>
<pubDate>Wed, 15 Jul 2026 13:00:13 +0000</pubDate>
</item>
<item>
<title>Malcolm X Is Not a Suffix</title>
<link>https://coderlegion.com/22370/malcolm-x-is-not-a-suffix</link>
<description>&lt;p&gt;Parse &lt;code&gt;Malcolm X&lt;/code&gt; with almost any name splitter and you get a suffix. &lt;code&gt;X&lt;/code&gt; looks like a roman numeral, roman numerals are suffixes (&lt;code&gt;John III&lt;/code&gt;, &lt;code&gt;Henry VIII&lt;/code&gt;), so &lt;code&gt;X&lt;/code&gt; becomes the suffix and the surname disappears. That's the kind of bug you only find once you already have a working parser, because it isn't a missing rule. It's two correct rules wanting the same token.&lt;/p&gt;
&lt;p&gt;Last month I wrote about the &lt;a rel=&quot;nofollow&quot; href=&quot;https://ilia.ws/blog/casing-aware-php-name-parser&quot;&gt;casing fix in nameparser&lt;/a&gt;: the idea that a parser reading &lt;code&gt;Jane Doe DDS&lt;/code&gt; should keep &lt;code&gt;Doe&lt;/code&gt; as the surname and &lt;code&gt;DDS&lt;/code&gt; as a credential, using letter casing as the signal that lowercasing throws away. Version 1.3.0 is the follow-up, and it is almost entirely about collisions like &lt;code&gt;Malcolm X&lt;/code&gt;: tokens that two disambiguation rules both claim. nameparser is a maintained fork of the dormant &lt;code&gt;theiconic/name-parser&lt;/code&gt;, by way of &lt;code&gt;codebyzach/name-parser&lt;/code&gt;'s PHP 8.3+ modernization; the contribution here is the casing and credential layer plus a confidence pass, not the parser core.&lt;/p&gt;
&lt;h3 id=&quot;the-credential-that-is-also-a-name&quot;&gt;The credential that is also a name&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;JO ANDERSON PhD&lt;/code&gt; used to parse to first name &lt;code&gt;J&lt;/code&gt;. &lt;code&gt;Jo&lt;/code&gt; is a real short given name, but in all caps it also reads as a credential-run token, and the credential rule won. 1.3.0 keeps all-caps short given names intact beside a mixed-case salutation or credential, so &lt;code&gt;Jo&lt;/code&gt; survives.&lt;/p&gt;
&lt;p&gt;The mirror image is the credential that gets read as a surname. &lt;code&gt;Jane DDS&lt;/code&gt; (a credential-only row with no comma) and &lt;code&gt;John A. MD&lt;/code&gt; (an initial plus a credential) used to rewrite the trailing credential as the last name, because a token in the surname slot with nothing after it looks like a surname. Both now keep the credential in the suffix.&lt;/p&gt;
&lt;p&gt;The collision gets worse with more than one credential. &lt;code&gt;John Smith MD FACS&lt;/code&gt; used to keep &lt;code&gt;MD&lt;/code&gt; and leak &lt;code&gt;FACS&lt;/code&gt; into the name, because &lt;code&gt;FACS&lt;/code&gt; is not in the dictionary and an unknown trailing token defaults to a name. Now a known credential anchors the tail and pulls the unknown one in with it, so both stay in the suffix. Parenthetical credentials were their own trap: &lt;code&gt;Jane Doe (MD)&lt;/code&gt; used to parse &lt;code&gt;(MD)&lt;/code&gt; as a nickname, since parentheses mark nicknames, and now reads it as a suffix. And a credential-only segment that lands before the given name, as in &lt;code&gt;Smith, MD, John&lt;/code&gt;, is pulled out to the suffix so the first name stays &lt;code&gt;John&lt;/code&gt; rather than becoming &lt;code&gt;MD&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Casing is the tiebreaker for all of these, which means it fails exactly where casing carries no signal. Uniform all-caps registry data, or all-lowercase input, has nothing to disambiguate on, so an ambiguous trailing token defaults to being read as a credential. That's a hard limitation, not a bug, and it's why the library ships a confidence pass: &lt;code&gt;getConfidence()&lt;/code&gt; flags a casing-undecidable row as ambiguous so it can go to a human instead of silently into your database wrong.&lt;/p&gt;
&lt;h3 id=&quot;word-order-that-hides-the-surname&quot;&gt;Word order that hides the surname&lt;/h3&gt;
&lt;p&gt;Surname-first input is its own minefield when a salutation and a credential sit on the same name. &lt;code&gt;Dr. Kim Jong Un&lt;/code&gt; used to lose the surname to the title; &lt;code&gt;Kim Jong Un, MD&lt;/code&gt; used to fall back to Western order. Both now keep surname &lt;code&gt;Kim&lt;/code&gt;, and &lt;code&gt;Dr. Kim Jong Un, MD&lt;/code&gt; correctly yields surname &lt;code&gt;Kim&lt;/code&gt;, salutation &lt;code&gt;Dr.&lt;/code&gt;, and credential &lt;code&gt;MD&lt;/code&gt;. The salutation and the credential were each pulling the parser back toward a different word order, and the fix is teaching it that a leading salutation and a trailing credential-only comma tail can coexist with a surname-first name.&lt;/p&gt;
&lt;h3 id=&quot;the-comma-inside-the-nickname&quot;&gt;The comma inside the nickname&lt;/h3&gt;
&lt;p&gt;A comma in a name usually means surname-first. So a comma inside a delimited nickname is a direct collision with the rule that reads commas as name-order markers. &lt;code&gt;John (Bob, Jr) Doe&lt;/code&gt; and &lt;code&gt;John 'Bob, Jr' Doe&lt;/code&gt; used to bisect on that inner comma and lose half the name. 1.3.0 keeps the nickname &lt;code&gt;Bob, Jr&lt;/code&gt; whole, for bracketed, quoted, and custom multi-character delimiter pairs alike, and the same holds on the given-name side: &lt;code&gt;Smith, John (Jack, Robert)&lt;/code&gt; keeps &lt;code&gt;Jack, Robert&lt;/code&gt; intact.&lt;/p&gt;
&lt;h3 id=&quot;scripts-with-no-casing-at-all&quot;&gt;Scripts with no casing at all&lt;/h3&gt;
&lt;p&gt;If casing is the signal, a script without upper and lower case has none. &lt;code&gt;Wang, 李明&lt;/code&gt; used to split &lt;code&gt;李明&lt;/code&gt; into two initials, because the initials rule fires on short tokens and nothing told it these were not initials. 1.3.0 leaves caseless-script names intact as the first name. In the other direction, an elided surname particle like the one in &lt;code&gt;'t Hooft&lt;/code&gt; used to read as an unterminated nickname or an initial; it now survives as the leading particle. Dutch honorifics (&lt;code&gt;Dhr.&lt;/code&gt;, &lt;code&gt;Mevr.&lt;/code&gt;, &lt;code&gt;Mw.&lt;/code&gt;) also join the default salutation set, so &lt;code&gt;Dhr. Jan de Vries&lt;/code&gt; reads the title as a salutation rather than a first name.&lt;/p&gt;
&lt;h3 id=&quot;and-the-input-that-is-trying-to-break-you&quot;&gt;And the input that is trying to break you&lt;/h3&gt;
&lt;p&gt;A name parser sits directly on user-supplied strings, so an adversarial input is a real exposure, not a theoretical one. Kilobytes of unmatched quotes, 100 KB tokens of any case shape, megabyte-long rows: 1.3.0 parses all of them in linear time and bounded memory. Invalid-UTF-8 delimiter keys and empty whitespace sets that used to emit a compile warning on every parse are now ignored quietly.&lt;/p&gt;
&lt;h3 id=&quot;turning-a-wrong-split-into-a-review-queue&quot;&gt;Turning a wrong split into a review queue&lt;/h3&gt;
&lt;p&gt;Every collision above resolves on casing, and casing runs out of signal on uniform-case data. So the honest design isn't to pretend the parser is always right; it's to say when it's guessing. The confidence pass reads the same input the parser does and flags a row whose split hinges on a signal that is not there.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-php&quot;&gt;use Iliaal\NameParser\Parser;

$name = (new Parser())-&amp;gt;parse('SMITH, JM');
$name-&amp;gt;getConfidence();   // ['ambiguous' =&amp;gt; true, 'notes' =&amp;gt; [...]]
$name-&amp;gt;getSource();       // 'SMITH, JM'  (the normalized input it judged)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;SMITH, JM&lt;/code&gt; is exactly the case with no answer: with mixed case, &lt;code&gt;Smith, JM&lt;/code&gt; splits &lt;code&gt;JM&lt;/code&gt; into the initials &lt;code&gt;J&lt;/code&gt; and &lt;code&gt;M&lt;/code&gt;, because the mixed-case surname proves those are initials; all-caps &lt;code&gt;SMITH, JM&lt;/code&gt; keeps &lt;code&gt;Jm&lt;/code&gt; as a first name, because the caps could equally be a short given name. The parser has to pick one, and the confidence flag records that it had to. &lt;code&gt;getSource()&lt;/code&gt;, new in 1.3.0, returns the normalized string the confidence was assessed against, so a batch importer can log precisely what it judged. The flag is advisory and opt-in; it never changes what &lt;code&gt;parse()&lt;/code&gt; returns. On a million-row import, that is the difference between silently corrupting an unknown fraction of records and routing a known set of them to a person.&lt;/p&gt;
&lt;p&gt;One caution the README states and this post repeats: &lt;code&gt;ambiguous =&amp;gt; false&lt;/code&gt; on an all-caps row is not a correctness guarantee. Clean credentials that are not also names (&lt;code&gt;RN&lt;/code&gt;, &lt;code&gt;PT&lt;/code&gt;, &lt;code&gt;OD&lt;/code&gt;) are left unflagged on purpose, to keep review volume manageable, so a false there means &quot;no name-colliding token,&quot; not &quot;definitely right.&quot;&lt;/p&gt;
&lt;h3 id=&quot;the-seams-where-cases-meet&quot;&gt;The seams where cases meet&lt;/h3&gt;
&lt;p&gt;None of these are new features. They're the boundaries between rules that were already there, the places where two disambiguation heuristics both reach for the same token. That's what this round of work looks like: not more cases, but the seams where cases meet. Casing decides most of them. Where it cannot, the confidence flag says so, and the row goes to review instead of into your data wrong.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;composer require iliaal/nameparser
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/iliaal/nameparser&quot;&gt;github.com/iliaal/nameparser&lt;/a&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/22370/malcolm-x-is-not-a-suffix</guid>
<pubDate>Sun, 12 Jul 2026 14:09:14 +0000</pubDate>
</item>
<item>
<title>Building Type Guards Like LEGO Blocks: Making Reusable Logic with is-kit</title>
<link>https://coderlegion.com/22119/building-type-guards-like-lego-blocks-making-reusable-logic-with-is-kit</link>
<description>&lt;p&gt;Hoi hoi!&lt;/p&gt;
&lt;p&gt;Still writing TypeScript guards by hand again and again? 😎&lt;br&gt;
I’m &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru&quot;&gt;@nyaomaru&lt;/a&gt;, a frontend engineer!&lt;/p&gt;
&lt;p&gt;When writing TypeScript, we often end up creating type guard functions (&lt;code&gt;value is Foo&lt;/code&gt;) over and over again.&lt;/p&gt;
&lt;p&gt;But you know the pain:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The same &lt;code&gt;isXXX&lt;/code&gt; functions appearing everywhere&lt;/li&gt;
&lt;li&gt;Copy → tweak → repeat = infinite boilerplate hell&lt;/li&gt;
&lt;li&gt;Runtime checks and type narrowing slowly drifting apart&lt;/li&gt;
&lt;li&gt;Complex guards becoming painful to reuse&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Sound familiar? 😅&lt;/p&gt;
&lt;p&gt;So today, I’ll show a pattern for turning type guards into reusable logic blocks.&lt;/p&gt;
&lt;p&gt;I’ll use my lightweight, zero-dependency library &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru/is-kit&quot;&gt;is-kit&lt;/a&gt; to illustrate the idea — but the mindset applies to vanilla TypeScript too.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=13034226594803526292&quot; alt=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru/is-kit&quot;&gt;https://github.com/nyaomaru/is-kit&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Alright, let’s dive in! 🧩&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;what-s-a-type-guard-anyway-&quot;&gt;What’s a Type Guard Anyway?&lt;/h3&gt;
&lt;p&gt;In short: &lt;strong&gt;a function that performs runtime validation while narrowing TypeScript types&lt;/strong&gt; at the same time.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;function isString(value: unknown): value is string {
  return typeof value === 'string';
}

declare const input: unknown;

if (isString(input)) {
  input.toUpperCase(); // input is string
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Simple, right?&lt;/p&gt;
&lt;p&gt;Here’s a slightly more realistic version:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;type SimpleUser = {
  id: number;
  name: string;
};

function isObject(value: unknown): value is Record&amp;lt;PropertyKey, unknown&amp;gt; {
  return typeof value === 'object' &amp;amp;&amp;amp; value !== null;
}

function isNumber(value: unknown): value is number {
  return typeof value === 'number' &amp;amp;&amp;amp; Number.isFinite(value);
}

function isUser(value: unknown): value is SimpleUser {
  return (
    isObject(value) &amp;amp;&amp;amp;
    isNumber(value.id) &amp;amp;&amp;amp;
    isString(value.name)
  );
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This works.&lt;/p&gt;
&lt;p&gt;But as your app grows, these guards start multiplying like crazy.&lt;/p&gt;
&lt;p&gt;You often need the same checks again and again:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;primitive checks&lt;/li&gt;
&lt;li&gt;object-shape checks&lt;/li&gt;
&lt;li&gt;literal checks&lt;/li&gt;
&lt;li&gt;optional/nullish checks&lt;/li&gt;
&lt;li&gt;extra domain rules&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Handy, but repetitive.&lt;br&gt;
That’s where composition comes in ♻️&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-build-guards-through-definition-and-composition&quot;&gt;♻️ Build Guards Through Definition and Composition&lt;/h3&gt;
&lt;p&gt;The core idea is simple:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Define small guards.&lt;/li&gt;
&lt;li&gt;Compose them.&lt;/li&gt;
&lt;li&gt;Reuse the result anywhere TypeScript narrowing matters.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;With &lt;code&gt;is-kit&lt;/code&gt;, the previous &lt;code&gt;isUser&lt;/code&gt; example becomes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import { isNumber, isString, struct } from 'is-kit';

const isUser = struct({
  id: isNumber,
  name: isString
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Clean, concise, and fully type-safe! ✨&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;declare const input: unknown;

if (isUser(input)) {
  input.id; // number
  input.name; // string
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;struct&lt;/code&gt; handles the object-shape check for you.&lt;/p&gt;
&lt;p&gt;It also only accepts plain objects — not arrays, dates, maps, or class instances.&lt;/p&gt;
&lt;p&gt;Want to reject extra own enumerable string keys too? Use exact mode:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const isExactUser = struct(
  {
    id: isNumber,
    name: isString
  },
  { exact: true }
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nice and strict 🛡️&lt;/p&gt;
&lt;p&gt;For existing TypeScript object types, use &lt;code&gt;typedStruct&amp;lt;T&amp;gt;()&lt;/code&gt; to keep your handwritten guard aligned with the type:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import { isNumber, isString, typedStruct } from 'is-kit';

type User = {
  id: number;
  name: string;
};

const isUser = typedStruct&amp;lt;User&amp;gt;()({
  id: isNumber,
  name: isString
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the &lt;code&gt;User&lt;/code&gt; type changes, TypeScript helps you notice when the guard no longer matches.&lt;/p&gt;
&lt;p&gt;That means fewer silent runtime/type mismatches.&lt;br&gt;
Very good. Very useful. 😎&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;define-custom-guards&quot;&gt;Define Custom Guards&lt;/h3&gt;
&lt;p&gt;Sometimes a primitive guard is not enough.&lt;/p&gt;
&lt;p&gt;For reusable custom checks, use &lt;code&gt;define&amp;lt;T&amp;gt;(...)&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import { define, isString } from 'is-kit';

const isSlug = define&amp;lt;string&amp;gt;(
  (value) =&amp;gt; isString(value) &amp;amp;&amp;amp; /^[a-z0-9-]+$/.test(value)
);

isSlug('release-110'); // true
isSlug('Release 110'); // false
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is great when you want to name a runtime rule and reuse it as a guard.&lt;/p&gt;
&lt;p&gt;No mystery regex scattered everywhere.&lt;br&gt;
Just one small reusable block ✨&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;compose-existing-guards&quot;&gt;Compose Existing Guards&lt;/h3&gt;
&lt;p&gt;Need “A or B”? Use &lt;code&gt;or&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import { isNumber, isString, or } from 'is-kit';

const isId = or(isString, isNumber);

isId('user-1'); // true
isId(123); // true
isId(false); // false
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Need “base guard plus extra rule”? Use &lt;code&gt;and&lt;/code&gt; with &lt;code&gt;predicateToRefine&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import { and, isNumber, predicateToRefine } from 'is-kit';

const isPositive = predicateToRefine&amp;lt;number&amp;gt;((value) =&amp;gt; value &amp;gt; 0);

const isPositiveNumber = and(isNumber, isPositive);

isPositiveNumber(1); // true
isPositiveNumber(-1); // false
isPositiveNumber('1'); // false
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The important part:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;isNumber&lt;/code&gt; runs first.&lt;/p&gt;
&lt;p&gt;So after that, the refinement can safely check &lt;code&gt;value &amp;gt; 0&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;No unsafe guessing.&lt;br&gt;
No “please TypeScript trust me” energy. 😅&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;example-role-based-user-guards&quot;&gt;Example: Role-Based User Guards&lt;/h3&gt;
&lt;p&gt;Let’s build something closer to real app code.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import {
  and,
  isNumber,
  isString,
  narrowKeyTo,
  oneOfValues,
  or,
  predicateToRefine,
  struct
} from 'is-kit';

type User = {
  id: string;
  name: string;
  age: number;
  role: 'admin' | 'member' | 'guest';
};

const isUser = struct({
  id: isString,
  name: isString,
  age: isNumber,
  role: oneOfValues('admin', 'member', 'guest')
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we can create role-specific guards from the base guard.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const byRole = narrowKeyTo(isUser, 'role');

const isAdmin = byRole('admin');
const isMember = byRole('member');
const isGuest = byRole('guest');
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Want multiple roles? Use &lt;code&gt;or&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const isStaff = or(isAdmin, isMember);

declare const input: unknown;

if (isStaff(input)) {
  input.role; // 'admin' | 'member'
  input.name; // string
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Want a role plus another condition? Use &lt;code&gt;and&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const isAdult = predicateToRefine&amp;lt;Readonly&amp;lt;User&amp;gt; &amp;amp; { role: 'admin' }&amp;gt;(
  (user) =&amp;gt; user.age &amp;gt;= 18
);

const isAdultAdmin = and(isAdmin, isAdult);

declare const candidate: unknown;

if (isAdultAdmin(candidate)) {
  candidate.role; // 'admin'
  candidate.age; // number
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That’s all.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;isUser&lt;/code&gt; validates the object shape&lt;/li&gt;
&lt;li&gt;&lt;code&gt;isAdmin&lt;/code&gt; narrows the role&lt;/li&gt;
&lt;li&gt;&lt;code&gt;isAdult&lt;/code&gt; adds the age rule&lt;/li&gt;
&lt;li&gt;&lt;code&gt;isAdultAdmin&lt;/code&gt; composes them&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;No giant nested &lt;code&gt;if&lt;/code&gt;.&lt;br&gt;
No repeated object checks.&lt;br&gt;
No copy-pasted guard logic.&lt;/p&gt;
&lt;p&gt;Just small reusable pieces snapping together like LEGO blocks 🧱✨&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;parse-unknown-values-safely&quot;&gt;Parse Unknown Values Safely&lt;/h3&gt;
&lt;p&gt;Guards become especially useful at boundaries:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;API responses&lt;/li&gt;
&lt;li&gt;localStorage&lt;/li&gt;
&lt;li&gt;form input&lt;/li&gt;
&lt;li&gt;JSON parsing&lt;/li&gt;
&lt;li&gt;message events&lt;/li&gt;
&lt;li&gt;feature flags&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Basically, anywhere &lt;code&gt;unknown&lt;/code&gt; enters your app.&lt;/p&gt;
&lt;p&gt;Use &lt;code&gt;safeParse&lt;/code&gt; when you want a small result object.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import { safeParse } from 'is-kit';

declare const payload: unknown;

const result = safeParse(isUser, payload);

if (result.valid) {
  result.value.id; // string
  result.value.role; // 'admin' | 'member' | 'guest'
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For JSON text, use &lt;code&gt;safeJsonParse&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import { safeJsonParse } from 'is-kit';

const result = safeJsonParse(
  '{&quot;id&quot;:&quot;u1&quot;,&quot;name&quot;:&quot;Ada&quot;,&quot;age&quot;:36,&quot;role&quot;:&quot;admin&quot;}',
  isUser
);

if (result.valid) {
  result.value.name.toUpperCase();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Invalid JSON? &lt;code&gt;{ valid: false }&lt;/code&gt;&lt;br&gt;
Guard mismatch? &lt;code&gt;{ valid: false }&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Simple and predictable. Very nice 😎&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;handle-nullish-values-explicitly&quot;&gt;Handle Nullish Values Explicitly&lt;/h3&gt;
&lt;p&gt;Nullish handling is another place where guard logic gets messy fast.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;is-kit&lt;/code&gt; gives you small helpers for that too.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import { isNil, isString, nullish, optional } from 'is-kit';

isNil(null); // true
isNil(undefined); // true
isNil(0); // false

const isNullishString = nullish(isString);
const isOptionalString = optional(isString);

isNullishString(null); // true
isNullishString('hello'); // true

isOptionalString(undefined); // true
isOptionalString('hello'); // true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Use:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;isNil(value)&lt;/code&gt; to check &lt;code&gt;null | undefined&lt;/code&gt; directly&lt;/li&gt;
&lt;li&gt;&lt;code&gt;nullable(guard)&lt;/code&gt; to allow &lt;code&gt;null&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;optional(guard)&lt;/code&gt; to allow &lt;code&gt;undefined&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;nullish(guard)&lt;/code&gt; to allow both&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For optional object keys inside &lt;code&gt;struct&lt;/code&gt;, use &lt;code&gt;optionalKey&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;import { isString, optionalKey, struct } from 'is-kit';

const isProfile = struct({
  id: isString,
  nickname: optionalKey(isString)
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This means &lt;code&gt;nickname&lt;/code&gt; may be missing.&lt;br&gt;
But if it exists, it must be a string.&lt;/p&gt;
&lt;p&gt;Small rule. Clear behavior. No confusion 👍&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-summary&quot;&gt;🎯 Summary&lt;/h3&gt;
&lt;p&gt;Type guards do not have to be one-off defensive functions.&lt;/p&gt;
&lt;p&gt;They can be &lt;strong&gt;reusable logic blocks&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;With &lt;code&gt;is-kit&lt;/code&gt;, you can build guards from small pieces:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;define&lt;/code&gt; for custom runtime checks&lt;/li&gt;
&lt;li&gt;&lt;code&gt;struct&lt;/code&gt; and &lt;code&gt;typedStruct&lt;/code&gt; for object shapes&lt;/li&gt;
&lt;li&gt;&lt;code&gt;and&lt;/code&gt;, &lt;code&gt;or&lt;/code&gt;, and &lt;code&gt;not&lt;/code&gt; for composition&lt;/li&gt;
&lt;li&gt;&lt;code&gt;predicateToRefine&lt;/code&gt; for extra conditions&lt;/li&gt;
&lt;li&gt;&lt;code&gt;narrowKeyTo&lt;/code&gt; for discriminant-style narrowing&lt;/li&gt;
&lt;li&gt;&lt;code&gt;safeParse&lt;/code&gt; and &lt;code&gt;safeJsonParse&lt;/code&gt; for boundary validation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Try converting just one existing &lt;code&gt;isXXX&lt;/code&gt; function into an &lt;code&gt;is-kit&lt;/code&gt; version.&lt;/p&gt;
&lt;p&gt;Once your guards are small and composable, they become much easier to share, test, and maintain.&lt;/p&gt;
&lt;p&gt;You’ll feel the difference pretty quickly 🚀&lt;/p&gt;
&lt;p&gt;If you like it, drop a ⭐ — it really helps! 😻&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/nyaomaru/is-kit&quot;&gt;https://github.com/nyaomaru/is-kit&lt;/a&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/22119/building-type-guards-like-lego-blocks-making-reusable-logic-with-is-kit</guid>
<pubDate>Wed, 08 Jul 2026 14:59:42 +0000</pubDate>
</item>
<item>
<title>JobApp AI</title>
<link>https://coderlegion.com/22066/jobapp-ai</link>
<description>&lt;p&gt; Job hunting shouldn't cost a fortune or your data.&lt;br&gt;
JobApp AI is a subscription platform helping job seekers streamline their applications, with pricing designed to stay accessible across Africa and other global regions. &lt;/p&gt;
&lt;p&gt;Milestones I'm proud of:  Shipped end-to-end encryption for stored user credentials  Built a self-service billing portal so users can manage their own subscriptions, no support ticket needed ️ Ran a full security audit of the codebase because trust isn't a feature; it's a foundation&lt;br&gt;
Building a SaaS product solo means wearing every hat: engineer, security reviewer, support desk, and product manager. This was a good reminder of why that audit work matters as much as the features people see.&lt;br&gt;
 Try it: &lt;a rel=&quot;nofollow&quot; href=&quot;https://jobapp.best&quot;&gt;https://jobapp.best&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;It's open source too. &lt;br&gt;
   &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/JulietChinenyeDuru/Job-App-AI&quot;&gt;https://github.com/JulietChinenyeDuru/Job-App-AI&lt;/a&gt;&lt;br&gt;
Feel free to fork it and build on it, and if you find it useful, a star ⭐ on the repo goes a long way in helping others discover it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/gkvfs0xycurukefc1h7s.png&quot; alt=&quot;Image description&quot;&gt;&lt;br&gt;
&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/01dnqi6peccjbnddzxso.png&quot; alt=&quot;Image description&quot;&gt;&lt;br&gt;
&lt;img src=&quot;https://dev-to-uploads.s3.us-east-2.amazonaws.com/uploads/articles/ekeicgg0ueshxuo4uj7q.png&quot; alt=&quot;Image description&quot;&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/22066/jobapp-ai</guid>
<pubDate>Tue, 07 Jul 2026 17:32:02 +0000</pubDate>
</item>
</channel>
</rss>