<?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: Owais Noor</title>
    <description>The latest articles on DEV Community by Owais Noor (@owaisnoor).</description>
    <link>https://dev.to/owaisnoor</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%2F2875711%2Fb9f263a8-7d2f-4fec-9892-904f7a78463b.png</url>
      <title>DEV Community: Owais Noor</title>
      <link>https://dev.to/owaisnoor</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/owaisnoor"/>
    <language>en</language>
    <item>
      <title>Interactive Algorithm Playground</title>
      <dc:creator>Owais Noor</dc:creator>
      <pubDate>Fri, 07 Aug 2026 10:29:16 +0000</pubDate>
      <link>https://dev.to/owaisnoor/interactive-algorithm-playground-5gaj</link>
      <guid>https://dev.to/owaisnoor/interactive-algorithm-playground-5gaj</guid>
      <description>&lt;p&gt;&lt;strong&gt;You're Learning Algorithms in the Wrong Order (and I Built a Playground to Fix It)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There's a moment in almost every technical interview that separates people who learned algorithms from people who revised them.&lt;/p&gt;

&lt;p&gt;The interviewer asks: "Quick sort is O(n log n) on average. Why does it degrade to O(n²)?"&lt;/p&gt;

&lt;p&gt;If you learned the table first, you're now doing archaeology in real time — trying to reconstruct a mechanism from a summary of that mechanism. If you learned the mechanism first, the answer is one sentence and you're already saying it.&lt;/p&gt;

&lt;p&gt;I built an interactive algorithm playground because I got tired of watching smart people fail that question. It's free, there's no sign-up, and there are 15 algorithms in it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://buildwithowais.com/algorithm" rel="noopener noreferrer"&gt;LINK FOR PLAYGROUND&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But the tool is the smaller half of this post. The bigger half is the order you should be learning in — because the tool only helps if you use it in that order.&lt;/p&gt;

&lt;p&gt;THE COMPLEXITY TABLE IS A SUMMARY, NOT AN EXPLANATION&lt;/p&gt;

&lt;p&gt;Here's how most algorithm learning goes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Read that bubble sort is O(n²).&lt;/li&gt;
&lt;li&gt;Read the code.&lt;/li&gt;
&lt;li&gt;Nod.&lt;/li&gt;
&lt;li&gt;Move on.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The problem is that step 1 comes first. O(n²) is a compressed conclusion. It's the last three characters of a long argument, and reading it first is like reading the final score before watching the match, you know the outcome, you understand nothing about how it happened.&lt;/p&gt;

&lt;p&gt;Worse, complexity notation is deliberately lossy. It throws away constants, it throws away the best case, and it flattens "this algorithm is fast on almost every real input" and "this algorithm is fast on inputs you'll never see" into the same three symbols. Quick sort and heap sort are both O(n log n) on average. Quick sort is usually faster in practice. Nothing in the table tells you that, and nothing in the table tells you why.&lt;/p&gt;

&lt;p&gt;So flip the order.&lt;/p&gt;

&lt;p&gt;STEP 1: WATCH IT RUN ON TEN ELEMENTS&lt;/p&gt;

&lt;p&gt;Ten, not a hundred. A hundred-element animation is a pretty light show that teaches you nothing, because your eye can't follow individual comparisons. Ten elements is small enough that you can point at the screen and say "okay, it's comparing 7 and 3, they're out of order, it swaps them, now it moves right."&lt;/p&gt;

&lt;p&gt;If you can't narrate it, you can't claim to know it.&lt;/p&gt;

&lt;p&gt;STEP 2: FIND THE INVARIANT&lt;/p&gt;

&lt;p&gt;The invariant is the one sentence that is true after every pass. It is the single highest-value thing to know about an algorithm, and it's usually the thing that gets left out entirely.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bubble sort: the last k elements are the k largest, in final order.&lt;/li&gt;
&lt;li&gt;Selection sort: the first k elements are the k smallest, in final order.&lt;/li&gt;
&lt;li&gt;Insertion sort: the first k elements are sorted relative to each other — but not necessarily in final position.&lt;/li&gt;
&lt;li&gt;Merge sort: every block of size 2^k is internally sorted.&lt;/li&gt;
&lt;li&gt;Quick sort: after a partition, the pivot is in its final position, and nothing will ever cross it.&lt;/li&gt;
&lt;li&gt;Heap sort: the heap property holds on the unsorted prefix; the suffix is sorted and final.&lt;/li&gt;
&lt;li&gt;Binary search: if the target exists, it lies inside [lo, hi].&lt;/li&gt;
&lt;li&gt;BFS: every node in the queue is at distance d or d+1 from the source, never more.&lt;/li&gt;
&lt;li&gt;Dijkstra: every settled node has its final shortest distance.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Read the insertion sort line again. That distinction — sorted relative to each other versus in final position — is the entire difference between insertion sort and selection sort, and it's why one is adaptive and the other isn't. Most people who can code insertion sort from memory have never articulated it.&lt;/p&gt;

&lt;p&gt;STEP 3: PRICE THE INVARIANT&lt;/p&gt;

&lt;p&gt;Now ask: what does it cost to maintain that invariant, and how does that cost grow?&lt;/p&gt;

&lt;p&gt;Selection sort's invariant says the first k are the k smallest. To extend it to k+1, you must find the minimum of everything remaining — and to be certain you've found the minimum, you have to look at every remaining element. No shortcuts exist. That's n-1 comparisons, then n-2, then n-3... which sums to n(n-1)/2. There's your O(n²), derived rather than memorised.&lt;/p&gt;

&lt;p&gt;And notice what fell out for free: selection sort cannot be adaptive. Not "isn't" — can't. The invariant forbids it.&lt;/p&gt;

&lt;p&gt;Merge sort's invariant says blocks of size 2^k are sorted. Each pass doubles the block size, so you need log₂ n passes, and each pass touches every element once to merge. n × log n. Same derivation, different answer — and now the table is a summary of something you already know rather than a fact you're storing.&lt;/p&gt;

&lt;p&gt;STEP 4: RUN IT ON THE INPUT DESIGNED TO HURT IT&lt;/p&gt;

&lt;p&gt;This is the step everyone skips, and it's the one that pays off in interviews. Every algorithm has an input that's out to get it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Insertion sort on a fully reversed array: every element slides all the way back → O(n²).&lt;/li&gt;
&lt;li&gt;Insertion sort on a nearly sorted array: almost no sliding → close to O(n).&lt;/li&gt;
&lt;li&gt;Quick sort with a last-element pivot on already sorted data: every partition is 1 vs (n−1) → O(n²).&lt;/li&gt;
&lt;li&gt;Binary search on unsorted input: silently returns the wrong answer, no crash.&lt;/li&gt;
&lt;li&gt;Dijkstra with a negative edge weight: settles a node too early, wrong result, no error.&lt;/li&gt;
&lt;li&gt;Counting sort on values from 1 to 1,000,000: O(n + k) where k dwarfs n → memory blowup.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Run insertion sort on a nearly-sorted array, then on a reversed one, back to back. The first finishes in what looks like a single sweep. The second grinds. You will understand best-case versus worst-case in roughly fifteen seconds — considerably faster than reading two paragraphs about it, including these ones.&lt;/p&gt;

&lt;p&gt;QUICK SORT'S O(n²), THE FULL ANSWER&lt;/p&gt;

&lt;p&gt;Since I opened with it: quick sort's invariant is that after partitioning, the pivot sits in its final position. The cost of establishing that invariant is one pass over the subarray — O(n). The recursion depth is what varies.&lt;/p&gt;

&lt;p&gt;If the pivot lands near the middle each time, each level halves the problem: log n levels × O(n) work per level = O(n log n).&lt;/p&gt;

&lt;p&gt;But the pivot's position isn't chosen — it's discovered. Pick the last element as pivot and hand quick sort an already-sorted array, and the pivot is always the maximum. The partition is 1 element versus n−1. Depth becomes n instead of log n, and n levels × O(n) work = O(n²).&lt;/p&gt;

&lt;p&gt;The punchline is what makes it a good interview answer: the worst case for naive quick sort is sorted input, which is exactly the input people assume is easiest. That's also why real implementations randomise the pivot or use median-of-three — they're not making quick sort faster, they're making the adversarial input impossible to construct in advance.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// The naive partition — fine until someone hands you sorted data
function partition(arr, lo, hi) {
  const pivot = arr[hi];        // &amp;lt;-- the vulnerability
  let i = lo - 1;
  for (let j = lo; j &amp;lt; hi; j++) {
    if (arr[j] &amp;lt; pivot) {
      i++;
      [arr[i], arr[j]] = [arr[j], arr[i]];
    }
  }
  [arr[i + 1], arr[hi]] = [arr[hi], arr[i + 1]];
  return i + 1;
}

// The fix is one line, and it isn't about speed — it's about denying
// the adversary a predictable pivot.
function partitionRandom(arr, lo, hi) {
  const r = lo + Math.floor(Math.random() * (hi - lo + 1));
  [arr[r], arr[hi]] = [arr[hi], arr[r]];
  return partition(arr, lo, hi);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;THE SAME LENS ON GRAPHS&lt;/p&gt;

&lt;p&gt;The invariant framing pays off even harder on graph algorithms, where the code looks nearly identical between algorithms that do very different things.&lt;/p&gt;

&lt;p&gt;BFS's invariant: every node in the queue is at distance d or d+1 from the source — never further. That single fact is why BFS finds shortest paths on unweighted graphs. Because the queue never holds anything further than one level out, the first time you reach a node is guaranteed to be via a shortest path. Swap the queue for a stack and you get DFS, and that guarantee evaporates instantly — DFS will happily reach a neighbour by a fifteen-hop detour.&lt;/p&gt;

&lt;p&gt;Dijkstra's invariant: every settled node has its final shortest distance. Dijkstra is BFS where the queue is replaced by a priority queue, because with weighted edges "fewest hops" and "cheapest path" stop being the same thing.&lt;/p&gt;

&lt;p&gt;And now the classic question answers itself. Why does Dijkstra break on negative weights? Because the invariant assumes that once you settle the cheapest unvisited node, nothing can improve it — which is only true if every remaining edge adds cost. Introduce a negative edge and a later, cheaper route can appear after you've already locked the node in. Dijkstra doesn't crash. It returns a wrong answer with total confidence, which is worse. That's where Bellman-Ford earns its slower running time.&lt;/p&gt;

&lt;p&gt;You don't memorise that. It falls out of the invariant.&lt;/p&gt;

&lt;p&gt;AND THE ALGORITHMS THAT CHEAT&lt;/p&gt;

&lt;p&gt;Comparison sorts have a proven lower bound of Ω(n log n). There's no clever comparison sort waiting to be discovered that beats it.&lt;/p&gt;

&lt;p&gt;Counting sort runs in O(n + k). Radix sort runs in O(d·(n + k)). Neither breaks the bound — they sidestep it, by not comparing elements at all. Counting sort tallies occurrences and writes the output straight from the tallies; it never asks "is a &amp;lt; b?" even once.&lt;/p&gt;

&lt;p&gt;The catch lives in that k. Counting sort's cost scales with the range of values, not just how many there are. Sorting a million integers between 0 and 100 is spectacular. Sorting a hundred integers between 0 and a billion allocates a billion-slot array and falls over. Watch it run once with a wide range and you'll never mis-apply it.&lt;/p&gt;

&lt;p&gt;SO: THE PLAYGROUND&lt;/p&gt;

&lt;p&gt;That whole method needs a tool that lets you go slowly, pick your own input, and re-run the same algorithm on its best and worst case back to back. That's what I built. It's free, no sign-up:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://buildwithowais.com/algorithm" rel="noopener noreferrer"&gt;LINK FOR PLAYGROUND&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;15 algorithms, currently:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sorting (8): bubble, selection, insertion, merge, quick, heap, counting, radix&lt;/li&gt;
&lt;li&gt;Searching (2): linear, binary&lt;/li&gt;
&lt;li&gt;Graphs (5): BFS, DFS, Dijkstra, Prim, Kruskal&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every algorithm gets its own page with an interactive visualiser you can slow down and step through, selectable inputs (random, nearly-sorted, reversed) so step 4 takes one click, pseudo code that stays in sync with what's on screen, implementations in five languages, and a written explanation of the mechanism rather than just the table.&lt;/p&gt;

&lt;p&gt;The colour language is consistent across every visualiser, so you only learn it once:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Amber: a comparison in progress&lt;/li&gt;
&lt;li&gt;Coral: a swap or a write&lt;/li&gt;
&lt;li&gt;Mint: this element has reached its final position&lt;/li&gt;
&lt;li&gt;White: the pivot, or the current minimum&lt;/li&gt;
&lt;li&gt;Violet: still in play&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Graphs reuse it — amber for the node being processed, mint for visited, coral for the final path.&lt;/p&gt;

&lt;p&gt;Once you've internalised it, you can almost hear the difference between algorithms by watching the colour ratios. Selection sort is a sea of amber with one lonely coral flash per pass: lots of looking, one swap. Bubble sort is coral everywhere: constant swapping. That contrast is the whole comparison-versus-swap trade-off, visible in about two seconds, and it's genuinely hard to convey in prose.&lt;/p&gt;

&lt;p&gt;A TWO-WEEK PATH THROUGH IT&lt;/p&gt;

&lt;p&gt;If you want a concrete plan rather than "click around":&lt;/p&gt;

&lt;p&gt;Week 1 — build the intuition&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Day 1: Bubble and selection sort. Write down both invariants before you read them.&lt;/li&gt;
&lt;li&gt;Day 2: Insertion sort. Run reversed, then nearly-sorted. Explain the difference out loud.&lt;/li&gt;
&lt;li&gt;Day 3: Merge sort. Count the passes on 16 elements and confirm it's 4.&lt;/li&gt;
&lt;li&gt;Day 4: Quick sort. Run it on sorted input and watch it die.&lt;/li&gt;
&lt;li&gt;Day 5: Heap sort. Ask why it's O(n log n) but usually slower than quick sort in practice.&lt;/li&gt;
&lt;li&gt;Weekend: Implement three of them from scratch, no reference.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Week 2 — searching and graphs&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Day 1: Linear vs binary search. Write binary search and get the lo &amp;lt;= hi boundary right first try. (You won't. That's the point.)&lt;/li&gt;
&lt;li&gt;Day 2: BFS. State the queue invariant.&lt;/li&gt;
&lt;li&gt;Day 3: DFS. Explain why it doesn't give shortest paths.&lt;/li&gt;
&lt;li&gt;Day 4: Dijkstra. Explain the negative-weight failure without looking it up.&lt;/li&gt;
&lt;li&gt;Day 5: Prim and Kruskal — two routes to the same minimum spanning tree.&lt;/li&gt;
&lt;li&gt;Weekend: Explain any one of them to somebody who doesn't code.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last one is the real exam. Anything you can't explain without notation, you don't know yet.&lt;/p&gt;

&lt;p&gt;WHAT'S NEXT&lt;/p&gt;

&lt;p&gt;Tree algorithms — BSTs, traversals, AVL rotations, heaps — and dynamic programming — knapsack, LCS, edit distance — are both in progress. DP especially benefits from this treatment, because the table-first habit does the most damage there: people memorise recurrences without ever seeing the subproblem grid fill in.&lt;/p&gt;

&lt;p&gt;The playground is free, has no sign-up, and isn't going behind a paywall:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://buildwithowais.com/algorithm" rel="noopener noreferrer"&gt;LINK TO PLAYGROUND&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you use it, I'd like to know which visualiser made something click that hadn't clicked before, and which algorithm you want next. Drop it in the comments.&lt;/p&gt;

</description>
      <category>learn</category>
      <category>algorithms</category>
    </item>
    <item>
      <title>RoadMap For Full Stack JavaScript Developer</title>
      <dc:creator>Owais Noor</dc:creator>
      <pubDate>Mon, 17 Feb 2025 15:37:16 +0000</pubDate>
      <link>https://dev.to/owaisnoor/roadmap-for-full-stack-javascript-developer-1n9e</link>
      <guid>https://dev.to/owaisnoor/roadmap-for-full-stack-javascript-developer-1n9e</guid>
      <description>&lt;p&gt;Becoming a full-stack JavaScript developer is an exciting and rewarding journey, especially considering the growing demand for developers who are skilled in both front-end and back-end technologies. JavaScript’s versatility allows you to work across the entire stack, from creating interactive user interfaces to handling server-side logic and databases. The JavaScript ecosystem constantly evolves, offering an ever-expanding set of tools, libraries, frameworks, and resources that can help guide you.&lt;/p&gt;

&lt;p&gt;This expanded roadmap will give you a comprehensive overview of the essential skills, tools, and technologies you should master to become a proficient full-stack JavaScript developer:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpeiqafu5zcmrn77vvi8y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpeiqafu5zcmrn77vvi8y.png" alt="Image description" width="800" height="1132"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I believe this roadmap is a solid foundation for anyone looking to dive into full-stack JavaScript development. However, it’s important to remember that the JavaScript ecosystem is vast, and this roadmap only scratches the surface. There are countless libraries, frameworks, and tools to explore, and the choice of what to focus on can vary depending on your specific project needs or career goals. Don't hesitate to share your thoughts or suggestions in the comments—whether you think some skills are unnecessary or if there’s something critical missing from the list. The world of JavaScript is expansive and ever-changing, so continuous learning is key.&lt;/p&gt;

&lt;p&gt;For Beginners: If you're just starting out as a developer, don’t worry if you're not sure whether full-stack JavaScript will be your end goal. Even if you later decide to specialize in either front-end or back-end development, mastering these foundational skills will make you a more versatile and productive developer. The ability to work with JavaScript both on the client-side and server-side will give you a unique advantage, as you’ll be equipped to handle a variety of tasks and collaborate with other developers more effectively.&lt;/p&gt;

&lt;p&gt;As you embark on your full-stack JavaScript journey, remember that every step of the way offers new learning opportunities. From understanding core programming concepts to experimenting with cutting-edge frameworks, this roadmap provides a structured approach to developing your skills. But don't be afraid to dive deeper into areas that interest you most, and always keep an eye on emerging trends within the JavaScript ecosystem.&lt;/p&gt;

&lt;p&gt;Whether you're a beginner or an experienced developer, mastering full-stack JavaScript is a worthwhile pursuit that will allow you to build complete applications from end to end. It’s an exciting time to be involved in the world of JavaScript, and the skills you acquire will serve you well as the landscape continues to evolve.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://owaisnoor.info/" rel="noopener noreferrer"&gt;Visit my website&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
