<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Laraib Hasan</title>
    <description>The latest articles on DEV Community by Laraib Hasan (@laraib_hasan).</description>
    <link>https://dev.to/laraib_hasan</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4110460%2F76ab4cb8-594b-4705-86e7-312eb4fae81f.png</url>
      <title>DEV Community: Laraib Hasan</title>
      <link>https://dev.to/laraib_hasan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/laraib_hasan"/>
    <language>en</language>
    <item>
      <title>13 repositories, 13 bugs: what open source taught me about my own tool</title>
      <dc:creator>Laraib Hasan</dc:creator>
      <pubDate>Sat, 05 Sep 2026 00:24:59 +0000</pubDate>
      <link>https://dev.to/laraib_hasan/13-repositories-13-bugs-what-open-source-taught-me-about-my-own-tool-939</link>
      <guid>https://dev.to/laraib_hasan/13-repositories-13-bugs-what-open-source-taught-me-about-my-own-tool-939</guid>
      <description>&lt;p&gt;I built a tool that draws architecture diagrams from a repository, where every edge cites the file, line and commit it came from. Then I ran it against thirteen repositories it had never seen, and every single one of them found something wrong with it.&lt;/p&gt;

&lt;p&gt;There were thirteen. These are the ones worth writing down. The list says nothing about those codebases. It says something about testing: a tool that reads other people's repositories has to be tested against other people's repositories, and there is no substitute.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The rule the tool works by&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Nothing is drawn that cannot be cited. Every edge in the output carries the file, the line and the commit that justifies it — click an arrow, see the import statement. If a reference cannot be resolved to something in the repository, it is not quietly dropped and it is not guessed at. It is reported as a gap.&lt;/p&gt;

&lt;p&gt;That second half is what made these bugs findable. A tool that silently drops what it cannot resolve looks perfect and is useless. A tool that reports gaps by name and count tells you, loudly, every time it is confused.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Java: a library sharing your package prefix is not you&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Guava declares &lt;code&gt;com.google.common&lt;/code&gt;. Truth is a separate library, and it lives in &lt;code&gt;com.google.common.truth&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;My resolver matched on package prefixes, so Truth looked like Guava's own code, and every reference to it became a gap against a package Guava does not contain. 834 false gaps — 28% of the repository.&lt;/p&gt;

&lt;p&gt;The fix is to require the next path segment to look like a type before peeling, because &lt;code&gt;com.google.common.truth.Truth&lt;/code&gt; peels to a package and &lt;code&gt;com.google.common.collect.ImmutableList&lt;/code&gt; peels to a class, and those are different shapes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Java: a file importing its own nested type&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Java requires the import for a nested enum constant even inside the same file. Treating that as a dependency has you drawing an arrow from a file to itself. It accounted for all 137 remaining gaps on Spring Boot and all 34 on Guava.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Java: static imports point one segment too deep&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;import static com.google.gson.Foo.BAR&lt;/code&gt; names a member, not a package. Reading it as a package produced 106 facts asserting that gson depends on a published copy of gson. Nested types left another 84 with the same shape.&lt;/p&gt;

&lt;p&gt;Never a dependency on a published copy of yourself. I would end up writing that sentence five times, once per language.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Go: the walker refused to look at directories named build&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I skipped directories called &lt;code&gt;build&lt;/code&gt; the way everyone skips &lt;code&gt;node_modules&lt;/code&gt;. Moby has four Go packages named &lt;code&gt;build&lt;/code&gt;. All 53 imports of them were reported as gaps against directories the walker had declined to open.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rust: a crate does not necessarily live in src/&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This was the big one. Deno's manifest says &lt;code&gt;[lib] path = "lib.rs"&lt;/code&gt;. I had hardcoded &lt;code&gt;src/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;3,896 gaps — 27% of every fact in the repository.&lt;/p&gt;

&lt;p&gt;What makes this one worth writing down: the totals looked fine. Deno is a large repository and thousands of unresolved references still left a plausible-looking number of resolved ones. I found it by opening the diagram and seeing a shape that was wrong, not by reading a count.&lt;/p&gt;

&lt;p&gt;** Rust: integration tests are their own crates**&lt;/p&gt;

&lt;p&gt;Cargo compiles every direct child of &lt;code&gt;tests/&lt;/code&gt; as its own crate. So &lt;code&gt;crate::util&lt;/code&gt; inside an integration test does not mean the library's &lt;code&gt;util&lt;/code&gt; — it means the one sitting beside the test.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rust: use super::* in an inline test module&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The commonest shape in Rust, and I resolved it to the crate root. That is a &lt;em&gt;wrong answer&lt;/em&gt; rather than a gap, which is strictly worse: gaps get counted and reported, wrong answers just quietly inflate the fact count of every Rust repository that has tests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Kotlin: fun interface&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;fun interface Dns { }&lt;/code&gt; is ordinary Kotlin, and &lt;code&gt;fun&lt;/code&gt; was missing from my list of declaration modifiers. So &lt;code&gt;okhttp3.Dns&lt;/code&gt;, &lt;code&gt;okhttp3.Interceptor&lt;/code&gt; and LeakCanary's &lt;code&gt;EventListener&lt;/code&gt; were not in the type index at all.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Kotlin: a SCREAMING_SNAKE constant is not a type&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;USER_AGENT&lt;/code&gt;, &lt;code&gt;TYPE_A&lt;/code&gt;, &lt;code&gt;UTC&lt;/code&gt; — 31 more of OkHttp's gaps.&lt;/p&gt;

&lt;h2&gt;
  
  
  Kotlin and Java are one namespace
&lt;/h2&gt;

&lt;p&gt;A Kotlin file importing a Java type from the same repository is a real edge. An index built from one file extension cannot see the other, so 112 real edges in Spring Boot were reported as missing. The declaration index now spans both.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The four I would have missed&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here is the part I would rather not write.&lt;/p&gt;

&lt;p&gt;I had verified the Rust adapter against Next.js and the Kotlin adapter against Spring Boot. Both repositories contain Rust and Kotlin. Both are overwhelmingly JavaScript and Java. Neither exercised a repository actually &lt;em&gt;built&lt;/em&gt; in the language I was claiming to support.&lt;/p&gt;

&lt;p&gt;Somebody asked me whether I had verified against repositories where those languages are the point. I had not. Deno, Tokio, OkHttp and LeakCanary went through next, and four more defects came out — including the 3,896-gap one, the largest in this list.&lt;/p&gt;

&lt;p&gt;The lesson is not "test more". It is that a test corpus where your target language is a rounding error will pass and prove nothing.&lt;/p&gt;

&lt;p&gt;** What it does now**&lt;/p&gt;

&lt;p&gt;Reads JavaScript, TypeScript, Python, Go, Java, Rust and Kotlin. Resolves against what a repository declares — the module path in &lt;code&gt;go.mod&lt;/code&gt;, the package statements in &lt;code&gt;.java&lt;/code&gt; files, the paths in &lt;code&gt;Cargo.toml&lt;/code&gt; — rather than against directory conventions.&lt;/p&gt;

&lt;p&gt;Across the thirteen repositories: 278,982 facts from 85,930 files. Four came out with zero unresolved references.&lt;/p&gt;

&lt;p&gt;It does not read Ruby, C#, PHP or Swift. Point it at a Ruby repository today and it will draw nothing and tell you it read nothing — the honest answer, and not a useful one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx mirofy-cli map &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;MIT, zero runtime dependencies, output is one self-contained HTML file that opens from disk with no server.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Hasan-Laraib/Mirofy" rel="noopener noreferrer"&gt;https://github.com/Hasan-Laraib/Mirofy&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you run it on your own repository and the resolution is wrong, I want to know. That has been the most productive kind of message I get.&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>architecture</category>
      <category>opensource</category>
      <category>devtools</category>
    </item>
  </channel>
</rss>
