<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Blog |</title><link>https://mikelayuso.com/blog/</link><atom:link href="https://mikelayuso.com/blog/index.xml" rel="self" type="application/rss+xml"/><description>Blog</description><generator>HugoBlox Kit (https://hugoblox.com)</generator><language>en-us</language><lastBuildDate>Sun, 17 May 2026 00:00:00 +0000</lastBuildDate><image><url>https://mikelayuso.com/media/icon_hu_b0970716f810afd6.png</url><title>Blog</title><link>https://mikelayuso.com/blog/</link></image><item><title>Microdependency Hell: The Cost of Package Culture</title><link>https://mikelayuso.com/blog/microdependency-tax/</link><pubDate>Sun, 17 May 2026 00:00:00 +0000</pubDate><guid>https://mikelayuso.com/blog/microdependency-tax/</guid><description>&lt;p&gt;One thing I highlighted in my
almost as an afterthought: the Rust binary ships with &lt;em&gt;no runtime dependencies&lt;/em&gt;. Download it, run it, nothing to install. The more time I spend across C/C++, Rust, and Godot, the more that property looks less like a convenience and more like a design goal worth chasing on purpose, because the alternative has a cost.&lt;/p&gt;
&lt;h2 id="the-incident"&gt;The incident&lt;/h2&gt;
&lt;p&gt;A perfect example in the JavaScript ecosystem is what happened on March 22, 2016. Azer Koçulu published a small npm package called &lt;code&gt;left-pad&lt;/code&gt;, 17 lines that padded strings on the left. When Kik Messenger wanted the package name &lt;code&gt;kik&lt;/code&gt; for their own use, npm sided with the company and transferred ownership away from Koçulu. In protest, he unpublished all 273 of his packages.
When &lt;code&gt;left-pad&lt;/code&gt; vanished, Babel and React Native broke. Thousands of projects at Facebook, PayPal, Netflix, Spotify and many others stopped building. Not because of a sophisticated attack. Because a justifiably angry maintainer deleted code he had written for free.&lt;/p&gt;
&lt;h2 id="how-the-ecosystem-got-here"&gt;How the ecosystem got here&lt;/h2&gt;
&lt;p&gt;npm hosts over 4 million packages, not a measure of creativity, but of incentive. Each package is its own GitHub repo, its own resume line, and its own download counter. The ecosystem rewards fragmentation, because each successful package is a credential with a counter attached, and the ecosystem has never learned to care what the counter is counting. The result is packages like &lt;code&gt;is-odd&lt;/code&gt;, which checks whether a number is odd, depending on &lt;code&gt;is-number&lt;/code&gt;, depending on &lt;code&gt;kind-of&lt;/code&gt;. Three packages, three maintainers, three supply chain entry points for a question you can answer in one line.
A typical React or Next.js app pulls between 1,200 and 1,800 transitive dependencies. No human reads them. When I&amp;rsquo;m building something in Godot, I don&amp;rsquo;t &lt;code&gt;npm install physics&lt;/code&gt;, the engine ships with it, one curated trust boundary maintained by a team. That contrast is hard to unsee.&lt;/p&gt;
&lt;h2 id="the-real-risk"&gt;The real risk&lt;/h2&gt;
&lt;p&gt;Tools like Snyk scan for &lt;em&gt;known&lt;/em&gt; vulnerabilities, by definition, ones that have already burned someone else. What they don&amp;rsquo;t catch: a package three levels deep maintained by someone who hasn&amp;rsquo;t logged in years, whose npm account gets taken over by an untrusted maintainer, who publishes a new version with a helpful postinstall script that reads your environment variables.
This isn&amp;rsquo;t hypothetical. In 2018, &lt;code&gt;event-stream&lt;/code&gt; was handed off to a stranger who volunteered to help, then used that access to exfiltrate Bitcoin private keys, downloaded 2 million times a week for two months before anyone noticed. In 2022, the maintainer of &lt;code&gt;colors&lt;/code&gt; and &lt;code&gt;faker&lt;/code&gt; pushed a self-sabotaging update as a protest. No hack, no zero-day. A maintainer in a bad mood, 20 million weekly downloads, production builds broken overnight.
These aren&amp;rsquo;t bugs in the trust model. They &lt;em&gt;are&lt;/em&gt; the trust model.&lt;/p&gt;
&lt;h2 id="the-correction"&gt;The correction&lt;/h2&gt;
&lt;p&gt;Sindre Sorhus, the person more responsible than anyone for the small-modules culture, now publicly argues for fewer dependencies and bigger standard libraries. Node already added native fetch, a test runner, and SQLite without a single install in the last few years. The platform is starting to absorb what fragmentation spread.
The instinct to keep surface area small, to understand what runs in your process, isn&amp;rsquo;t NIH (Not Invented Here) syndrome. It&amp;rsquo;s engineering. The JavaScript ecosystem spent 15 years treating it as optional. The supply chain incidents are the cost.&lt;/p&gt;</description></item><item><title>From a Forgotten Python Script to a 2500x Faster Rust Tool: The Evolution of PNGToSVG</title><link>https://mikelayuso.com/blog/png-to-svg-performance/</link><pubDate>Sun, 19 Apr 2026 00:00:00 +0000</pubDate><guid>https://mikelayuso.com/blog/png-to-svg-performance/</guid><description>&lt;p&gt;I just wanted to share the story behind a tool I’ve been working on called
. As you can probably guess, it converts PNG images into SVG vectors.&lt;/p&gt;
&lt;p&gt;The project started back in 2019 as a small Python script I wrote for a frontend job. Sometimes we needed to work with SVGs, and I just wanted a quick way to convert images locally without uploading them to some random remote service. When I switched jobs, I took that code, tossed it onto GitHub, and completely forgot about it.&lt;/p&gt;
&lt;p&gt;Fast forward five years. Out of nowhere, a contributor (&amp;ldquo;Kartik Nayak&amp;rdquo; on GitHub) made the first Rust implementation of the tool. A few months later, someone else (&amp;ldquo;Salman Sali&amp;rdquo;) jumped in and improved it by adding parallelization.&lt;/p&gt;
&lt;p&gt;This was late 2024. Seeing the community breathe new life into my old script blew my mind and gave me the perfect excuse to dive in and learn Rust myself. Since then, I’ve been rewriting, polishing, and tweaking the tool, focusing heavily on ease of use and performance.&lt;/p&gt;
&lt;p&gt;What used to be a sluggish script that took a couple of seconds to process a tiny 64x64 icon can now chew through 8000x8000 images in a fraction of that time. To put this performance leap into perspective, I ran a benchmark using a relatively complex ~900KB image.&lt;/p&gt;
&lt;p&gt;Here is how the evolution of the project looks:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Version&lt;/th&gt;
&lt;th&gt;Time (Seconds)&lt;/th&gt;
&lt;th&gt;Relative Performance&lt;/th&gt;
&lt;th&gt;Speedup Strategy&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Python (Legacy)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1,124.14s&lt;/td&gt;
&lt;td&gt;100% (Baseline)&lt;/td&gt;
&lt;td&gt;Original implementation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;v0.4.0 / v0.4.1&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~24.50s&lt;/td&gt;
&lt;td&gt;~2.18%&lt;/td&gt;
&lt;td&gt;Initial Rust Port (O(n) lookups)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;v0.5.0&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1.15s&lt;/td&gt;
&lt;td&gt;0.10%&lt;/td&gt;
&lt;td&gt;O(1) Hash-Set Optimization&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;v0.6.0&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;0.43s&lt;/td&gt;
&lt;td&gt;0.04%&lt;/td&gt;
&lt;td&gt;Memory Reuse &amp;amp; Direct IO&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Going from over 18 minutes in the original Python implementation to under half a second in Rust represents a massive &lt;strong&gt;~2580x speedup&lt;/strong&gt;. But the numbers only tell half the story. Translating this project into Rust became an amazing learning exercise, and the technical journey went roughly like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Phase 1: The Rust Port (v0.4.x):&lt;/strong&gt; Moving to Rust gave us a huge baseline boost (about 45x faster than Python), but the code still relied on linear searches for neighbor lookups, which struggled under the heavy weight of complex paths.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Phase 2: The Breakthrough (v0.5.0):&lt;/strong&gt; As I learned more, I realized we could replace vector-based neighbor checks with O(1) Hash-Set lookups. This single change killed the quadratic scaling bottleneck and dropped processing time from 24 seconds to just 1.1 seconds—a 20x jump just within the Rust codebase.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Phase 3: Total Efficiency (v0.6.0):&lt;/strong&gt; Finally, I focused on memory efficiency. Instead of constantly allocating new structures for every shape, we now reuse memory buffers, read pixel data directly via raw buffers, and use sorted edge lookups to trace shapes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Recently, I published the latest release (v0.6.1), which polishes a few rough edges and includes a shiny new (and very &amp;ldquo;programmer art&amp;rdquo;) icon, hahaha. Since then, I&amp;rsquo;ve received plenty of feedback from the community. Based on that, I&amp;rsquo;m already planning the improvements for v0.6.2, as well as a v0.7.0 release (which will include some breaking API changes, hence the version bump).&lt;/p&gt;
&lt;p&gt;Honestly, the best part of this whole experience hasn&amp;rsquo;t been the crazy performance gains. It’s the fact that randomly deciding to share a quick script allowed me to receive very useful community contributions, learn a completely new language, and actually build a fast, easy-to-use tool that actually helps me without interrupting my workflow with wait times in my own projects.&lt;/p&gt;</description></item></channel></rss>