<?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: Muhammad Abdullah Shah</title>
    <description>The latest articles on DEV Community by Muhammad Abdullah Shah (@muhammad_abdullahshah_96).</description>
    <link>https://dev.to/muhammad_abdullahshah_96</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4160112%2F66926732-d2c3-4636-8441-2c3443cb61a0.webp</url>
      <title>DEV Community: Muhammad Abdullah Shah</title>
      <link>https://dev.to/muhammad_abdullahshah_96</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/muhammad_abdullahshah_96"/>
    <language>en</language>
    <item>
      <title>I built a debugger two people can watch at the same time</title>
      <dc:creator>Muhammad Abdullah Shah</dc:creator>
      <pubDate>Sun, 25 Oct 2026 07:51:43 +0000</pubDate>
      <link>https://dev.to/muhammad_abdullahshah_96/i-built-a-debugger-two-people-can-watch-at-the-same-time-pmp</link>
      <guid>https://dev.to/muhammad_abdullahshah_96/i-built-a-debugger-two-people-can-watch-at-the-same-time-pmp</guid>
      <description>&lt;h1&gt;
  
  
  I built a debugger two people can watch at the same time
&lt;/h1&gt;

&lt;p&gt;I have lost count of the debugging sessions that went like this. I share my screen, I run the code, and I narrate what it is doing line by line while the other person nods at a video feed. They cannot pause it. They cannot rewind it. They cannot point at a variable and ask what it held two steps ago.&lt;/p&gt;

&lt;p&gt;So I started building Trace. The short version is Figma, but for debugging. Two people join the same run, watch the same code execute live, and scrub back through it on a timeline when something looks wrong.&lt;/p&gt;

&lt;p&gt;I gave it four days before my exams forced a pause. Here is what actually shipped.&lt;/p&gt;

&lt;h2&gt;
  
  
  Day 1: the tracer
&lt;/h2&gt;

&lt;p&gt;A Python tracer built on sys.settrace, a Pydantic trace schema to keep every step honest, and a Next.js scaffold to show it in. Ten commits, 22 of 22 backend tests green. Nothing to look at yet, but the foundation was real.&lt;/p&gt;

&lt;h2&gt;
  
  
  Day 2: the runner
&lt;/h2&gt;

&lt;p&gt;A sandboxed runner behind POST /execute, traces retrievable at GET /traces/{id}, and a JavaScript tracer using acorn so it is not a Python-only toy. Five more commits, 60 of 60 tests green.&lt;/p&gt;

&lt;h2&gt;
  
  
  Day 3: the multiplayer part
&lt;/h2&gt;

&lt;p&gt;Rooms joined by code. A Yjs relay running inside FastAPI, not a Node sidecar, because one process and one deploy beats babysitting a second service. Presence with remote cursors, and a shared run button so either person can kick off the execution. Ten commits, 73 of 73 tests green.&lt;/p&gt;

&lt;h2&gt;
  
  
  Day 4: the part you actually look at
&lt;/h2&gt;

&lt;p&gt;An SVG execution graph with time on the x axis and line number on the y axis. A timeline scrubber with play and pause, 0.5x to 4x speed, and arrow-key stepping. A variables panel (text only, dunders filtered out), a stack panel, and a stdout view. I verified it with Playwright screenshots of a real 10-step loop trace, on desktop and mobile. Still 73 of 73 tests green.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is not built yet
&lt;/h2&gt;

&lt;p&gt;The AI co-pilot that explains each step as it runs. That is the plan, and I am saying so plainly instead of letting the pitch imply it exists. Exams pause the build until October 31, then Days 5 to 7 finish it.&lt;/p&gt;

&lt;p&gt;The part I keep coming back to is the scrubber. Debuggers show you the present. A timeline lets you argue about the past, and when two people are staring at the same bug, the past is usually where the answer lives.&lt;/p&gt;

</description>
      <category>python</category>
      <category>fastapi</category>
      <category>nextjs</category>
      <category>webdev</category>
    </item>
    <item>
      <title>I priced my own dead code and got a $775 bill</title>
      <dc:creator>Muhammad Abdullah Shah</dc:creator>
      <pubDate>Sun, 18 Oct 2026 07:51:40 +0000</pubDate>
      <link>https://dev.to/muhammad_abdullahshah_96/i-priced-my-own-dead-code-and-got-a-75-bill-162</link>
      <guid>https://dev.to/muhammad_abdullahshah_96/i-priced-my-own-dead-code-and-got-a-75-bill-162</guid>
      <description>&lt;h1&gt;
  
  
  I priced my own dead code and got a $775 bill
&lt;/h1&gt;

&lt;p&gt;Every developer has a graveyard. Mine was 35 files and 2,317 lines, and I couldn't have told you what half of it did. So I built GhostCode, a dead-code analyzer that reads your AST instead of guessing from text, and the first repo I ran it on was my own.&lt;/p&gt;

&lt;p&gt;Here is what I built, how it works, and what the numbers said.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dead code is a P&amp;amp;L line item, not a vibe
&lt;/h2&gt;

&lt;p&gt;Dead code doesn't just sit there. Someone reads it during onboarding, debugs around it, and keeps it passing CI. That's real time, so I wanted a number: how much does this code cost me per year?&lt;/p&gt;

&lt;p&gt;The crude version I started with: every dead line costs $0.50, scaled by complexity. Crude on purpose. A precise-but-wrong number is worse than a crude-but-directional one.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works: four layers
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Ingestion.&lt;/strong&gt; tree-sitter parses Python and JS/TS into real ASTs. No regex guessing, no string-matching for "import". If the parser can't see the symbol, it doesn't count.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Graph.&lt;/strong&gt; Everything becomes a NetworkX graph: files, functions, classes, imports. A "ghost node" is any node with zero incoming edges. Nothing calls it, nothing imports it, nothing references it. That's dead code by construction, not by opinion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Scorers.&lt;/strong&gt; Three of them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dead-code scorer: how confident are we this is actually dead (deletion confidence)?&lt;/li&gt;
&lt;li&gt;Debt scorer: how tangled is the code that IS alive? Circular imports, complexity hotspots.&lt;/li&gt;
&lt;li&gt;Cost scorer: dead LOC x $0.50, scaled by cyclomatic complexity. A simple dead utility is cheap. Dead code wrapped in nested conditionals that future-me has to read is expensive.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. Dashboard + auto-PR.&lt;/strong&gt; A Next.js dashboard over a FastAPI API (Docker, deployed on Render), and PyGithub opens the deletion PRs for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dogfooding: my own repo first
&lt;/h2&gt;

&lt;p&gt;35 files, 2,317 lines. The scan found 10 ghost files and 21 ghost symbols. Three circular import chains I didn't know about. Total: &lt;strong&gt;$775.49 of annual maintenance burn&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Some of it stung. A helper module I was "keeping in case I need it later" turned out to have zero incoming edges from day one. It was never needed. Deleting it took one PR and the tests stayed green, 91 out of 91.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I'd tell you before you build this yourself
&lt;/h2&gt;

&lt;p&gt;Start with tree-sitter, not regex. Regex-based "unused code" tools lie to you the moment a dynamic import shows up.&lt;/p&gt;

&lt;p&gt;Zero incoming edges is the right definition. It's falsifiable: show me one caller, or it's dead.&lt;/p&gt;

&lt;p&gt;Price it crudely early. The $0.50 number is wrong, but it turned "should I clean this up" into "this file costs $120 a year to keep." That's a decision, not a feeling.&lt;/p&gt;

&lt;p&gt;My repo now has 10 fewer files. The analyzer runs in CI now, so the graveyard doesn't grow back.&lt;/p&gt;

</description>
      <category>python</category>
      <category>ai</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>My first open-source PR got closed, and the maintainer was right</title>
      <dc:creator>Muhammad Abdullah Shah</dc:creator>
      <pubDate>Sun, 11 Oct 2026 07:51:57 +0000</pubDate>
      <link>https://dev.to/muhammad_abdullahshah_96/my-first-open-source-pr-got-closed-and-the-maintainer-was-right-fh3</link>
      <guid>https://dev.to/muhammad_abdullahshah_96/my-first-open-source-pr-got-closed-and-the-maintainer-was-right-fh3</guid>
      <description>&lt;p&gt;This week I did something I had been putting off for years: I fixed two real bugs in open-source projects and opened the PRs myself.&lt;/p&gt;

&lt;p&gt;The two were repowise-dev/repowise#3251 and sktime/sktime#11437.&lt;/p&gt;

&lt;p&gt;The repowise bug: &lt;code&gt;doc_anchors&lt;/code&gt; only registered the first anchor when a document had repeated headings. GitHub-style suffixed anchors (&lt;code&gt;slug-1&lt;/code&gt;, &lt;code&gt;slug-2&lt;/code&gt;) were just missing. The sktime bug: &lt;code&gt;LagLlamaForecaster._update&lt;/code&gt; was loading the entire series history on every update instead of keeping just &lt;code&gt;context_length&lt;/code&gt; rows. Unbounded memory growth hiding inside a method nobody looks at twice.&lt;/p&gt;

&lt;p&gt;Both fixes were small. Both took longer than I expected, because the real work was reading the codebase long enough to be sure I was not breaking something else. The repowise fix needed 2 regression tests to prove the anchors were right. The sktime fix needed 5 direct cases. Ruff clean, existing tests still green.&lt;/p&gt;

&lt;p&gt;Here is the part I did not expect. This morning the repowise maintainer closed my PR without merging it.&lt;/p&gt;

&lt;p&gt;The reason was fair, and I will give it to him straight. Another contributor had claimed the issue in the thread first, and their version handles an edge case mine missed: a heading that literally reads &lt;code&gt;## Usage 1&lt;/code&gt; written out. With a plain counter, that collides with the auto-generated &lt;code&gt;usage-1&lt;/code&gt;. Their version gives the third &lt;code&gt;## Usage&lt;/code&gt; a proper &lt;code&gt;usage-2&lt;/code&gt;. Mine would have shipped a subtle duplicate-anchor bug. So, yes, closing mine was the right call.&lt;/p&gt;

&lt;p&gt;And then the maintainer did something I will remember: he personally pointed me at another issue, #3283, and said it was mine if I claimed it. Importing one helper from &lt;code&gt;ingestion.git_indexer&lt;/code&gt; drags in the whole package and all of GitPython. The fix is lazy imports. I have already written it in my fork, tests pass, and it is waiting on me to claim the issue and open the PR. The sktime PR is still open, awaiting review.&lt;/p&gt;

&lt;p&gt;Two lessons I keep turning over.&lt;/p&gt;

&lt;p&gt;First: writing the fix is the easy half. The other half is the process around it. Claim the issue in the thread before you start, read how maintainers respond, and treat every edge case in a review comment as something you should have thought of first.&lt;/p&gt;

&lt;p&gt;Second: a closed PR is not a failed PR. Mine found the bug, confirmed the fix shape, and got a maintainer to hand me a better issue. That is a net win by any measure that matters.&lt;/p&gt;

&lt;p&gt;If you are thinking about starting: pick a project you already use, look at its good-first-issues, and read more code than you write. The reading is the work.&lt;/p&gt;

</description>
      <category>python</category>
      <category>programming</category>
      <category>beginners</category>
      <category>webdev</category>
    </item>
    <item>
      <title>I built a merge-conflict predictor that ignores lines and reads the AST instead</title>
      <dc:creator>Muhammad Abdullah Shah</dc:creator>
      <pubDate>Sun, 04 Oct 2026 07:51:35 +0000</pubDate>
      <link>https://dev.to/muhammad_abdullahshah_96/i-built-a-merge-conflict-predictor-that-ignores-lines-and-reads-the-ast-instead-38fh</link>
      <guid>https://dev.to/muhammad_abdullahshah_96/i-built-a-merge-conflict-predictor-that-ignores-lines-and-reads-the-ast-instead-38fh</guid>
      <description>&lt;p&gt;Git's idea of a merge conflict is narrower than it should be. Two branches change the same lines, you get a conflict. Two branches touch the same function from different files and never collide, git merges cleanly and congratulates you. Then production breaks.&lt;/p&gt;

&lt;p&gt;That's the merge I've learned to fear most: the clean one. One person refactors a function while another adds a call to it, the merge goes through without a peep, and you find out a week later from a bug report.&lt;/p&gt;

&lt;p&gt;I'm building PRISM, an AI-powered git time machine, and this week I shipped a conflict predictor built around a simple idea: lines are the wrong unit. Symbols are better.&lt;/p&gt;

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

&lt;p&gt;Parse the merge-base and both branch heads into ASTs with tree-sitter, extract the symbols (functions, classes, methods), and flag overlap between the two sides. Both branches touching the same symbol is risk, even across different files. The score saturates so one hot file can't dominate the whole prediction. It's exposed as POST /repos/{id}/predict-conflict, and there's a branch-compare UI with a risk bar and the overlapping symbols listed, so the number is inspectable instead of magic.&lt;/p&gt;

&lt;h2&gt;
  
  
  The unglamorous parts took most of the time
&lt;/h2&gt;

&lt;p&gt;Tree-sitter grammar packages aren't always installed on the machine. The first version crashed when one was missing, which made it a demo, not a tool. Now the service degrades to line-level scoring when a grammar is missing. Boring engineering, but it's the difference between something you can actually run and something you can't.&lt;/p&gt;

&lt;p&gt;Method detection inside class bodies broke twice before my tree-sitter queries finally caught it. Queries for methods are fiddlier than function queries, and I learned that through two rounds of debugging. Skill issue on my part, honestly.&lt;/p&gt;

&lt;p&gt;9 new tests, 39 passing overall.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it's actually for
&lt;/h2&gt;

&lt;p&gt;The predictor doesn't replace tests or review. It answers one narrow question: did these two branches step on the same symbols? That's cheap to answer, and it catches exactly the class of breakage that git itself can't see. Next up I'm working on semantic code ownership with embeddings, which should catch the cases where even the symbol names diverge.&lt;/p&gt;

&lt;p&gt;If you merge a lot, you know the feeling of a clean merge that wasn't. What's the worst one you've shipped?&lt;/p&gt;

</description>
      <category>ai</category>
      <category>python</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
