<?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: VLAD</title>
    <description>The latest articles on DEV Community by VLAD (@vladut02).</description>
    <link>https://dev.to/vladut02</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%2F4103356%2F7cf1ab18-c024-449c-8297-4c1143045611.jpg</url>
      <title>DEV Community: VLAD</title>
      <link>https://dev.to/vladut02</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vladut02"/>
    <language>en</language>
    <item>
      <title>Ariane 5, Flight 501: The Exception That Destroyed a Rocket</title>
      <dc:creator>VLAD</dc:creator>
      <pubDate>Sat, 10 Oct 2026 21:57:58 +0000</pubDate>
      <link>https://dev.to/vladut02/ariane-5-flight-501-the-exception-that-destroyed-a-rocket-48i6</link>
      <guid>https://dev.to/vladut02/ariane-5-flight-501-the-exception-that-destroyed-a-rocket-48i6</guid>
      <description>&lt;p&gt;On June 4th, 1996, Europe launched Ariane 5 for the first time. About forty seconds after the flight sequence began, the rocket veered off course, broke up and destroyed itself. The engines were fine. The weather was fine. What failed was a data conversion in code that had no reason to be running.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/KRkJZsA4o9o" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;This story gets retold a lot, usually as "an integer overflow blew up a rocket". The &lt;a href="https://www-users.cse.umn.edu/~arnold/disasters/ariane5rep.html" rel="noopener noreferrer"&gt;inquiry board's report&lt;/a&gt; (chaired by Jacques-Louis Lions, published July 19th, 1996) tells a more useful story. The check actually worked. What nobody had decided was what should happen when it fired.&lt;/p&gt;

&lt;h2&gt;
  
  
  The box that knows which way is up
&lt;/h2&gt;

&lt;p&gt;To fly straight, a rocket has to know which way it's pointing and how it's moving. On Ariane, that's the job of the inertial reference system, called SRI in the report after its French name. It has laser gyros, accelerometers and its own computer. It sends attitude and velocity data to the main on-board computer, and that computer steers by swiveling the engine nozzles.&lt;/p&gt;

&lt;p&gt;Ariane 5 carried two SRIs with identical hardware and identical software. One was active, the other was a hot standby. If the active one failed, the main computer would switch to the backup.&lt;/p&gt;

&lt;p&gt;The report also notes that the SRI design, and especially its software, was practically the same as on Ariane 4. It had flown for years. Nobody saw a reason to change software that worked.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code that had no job
&lt;/h2&gt;

&lt;p&gt;Before launch, the SRI aligns itself: it finds the direction of gravity and finds north from the rotation of the Earth. That only makes sense while the rocket stands still on the pad.&lt;/p&gt;

&lt;p&gt;On Ariane 4, the alignment function kept running for a while after the SRI went into flight mode. That was on purpose. If a countdown was held in its last seconds, a full realignment took 45 minutes or more, and the launch window could be lost. Keeping alignment alive a little longer let the team restart the countdown quickly. According to the report, the feature was used once, in 1989.&lt;/p&gt;

&lt;p&gt;Ariane 5 had a different preparation sequence and didn't need this. The requirement was kept anyway, to stay in common with Ariane 4. So on Ariane 5 the alignment code ran for about 40 seconds after lift-off, computing values that no longer meant anything.&lt;/p&gt;

&lt;h2&gt;
  
  
  The number that couldn't get too big
&lt;/h2&gt;

&lt;p&gt;Inside the alignment code there's a value the report calls BH, for horizontal bias. It's related to the horizontal velocity the platform senses. It was held as a 64-bit floating point number, and at one point the Ada code converted it to a 16-bit signed integer. A 16-bit signed integer tops out at 32,767.&lt;/p&gt;

&lt;p&gt;The developers knew conversions like this were a risk. The report says they analysed every operation that could raise an exception and found seven variables at risk. But there was a constraint: a maximum workload target of 80% for the SRI computer. So they protected four of the seven conversions. The other three, including BH, were left unprotected, because the reasoning was that those values were either physically limited or had a large safety margin.&lt;/p&gt;

&lt;p&gt;For Ariane 4 that reasoning was correct. But:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The justification wasn't in the source code. The report says the assumption, although agreed, was in practice hidden from any external review.&lt;/li&gt;
&lt;li&gt;The Ariane 5 trajectory data was not part of the SRI's requirements and specification.&lt;/li&gt;
&lt;li&gt;Ariane 5 builds up horizontal velocity about five times faster than Ariane 4 in the early part of the flight.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What happened in flight
&lt;/h2&gt;

&lt;p&gt;The rocket flew normally until about 36 seconds after the start of the main engine ignition sequence. Then BH exceeded what 16 bits can hold. The conversion raised an Operand Error, and nothing in the code handled it.&lt;/p&gt;

&lt;p&gt;For an unhandled exception, the SRI specification said: indicate the failure on the databus, store the failure context in memory, and shut the processor down. The report explains where that rule came from. The programme was designed around random hardware failures. If a part breaks at random, you switch the unit off and the backup takes over.&lt;/p&gt;

&lt;p&gt;This wasn't random. The backup ran the same software with the same inputs. It had already stopped, one 72-millisecond data cycle earlier, for the same reason. As the report puts it, two critical units that were still healthy got switched off.&lt;/p&gt;

&lt;p&gt;Then it got worse. The failed active SRI put a diagnostic bit pattern on the bus, and the main computer interpreted it as flight data. Based on that, it commanded full nozzle deflections on both solid boosters and then on the main engine. The rocket turned at an angle of attack of more than 20 degrees, the boosters started to separate from the core stage, and that correctly triggered the self-destruct system at about 4 km altitude.&lt;/p&gt;

&lt;p&gt;The debris fell on about 12 square kilometres of swamp and savanna near the launch site. Both SRIs were recovered, and the failure context was read out of their memory. The four Cluster science satellites on board were lost. The loss is widely reported at about $370 million; that figure is not from the inquiry report.&lt;/p&gt;

&lt;h2&gt;
  
  
  The same line in C
&lt;/h2&gt;

&lt;p&gt;Here's the part that matters for those of us who will never write flight software. In C#, the same conversion is one cast:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;bh&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;40000.0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;short&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;short&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;bh&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;            &lt;span class="c1"&gt;// compiles without a warning&lt;/span&gt;
&lt;span class="kt"&gt;short&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;checked&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="kt"&gt;short&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="n"&gt;bh&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// throws OverflowException&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default, C# evaluates non-constant expressions in an &lt;a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/statements/checked-and-unchecked" rel="noopener noreferrer"&gt;unchecked context&lt;/a&gt;. For a &lt;code&gt;double&lt;/code&gt; to integer conversion that's out of range, the &lt;a href="https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/numeric-conversions" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; says the result is an unspecified value of the destination type. No exception, no warning. Your program keeps going with a number that means nothing. In a &lt;code&gt;checked&lt;/code&gt; context, the same cast throws &lt;code&gt;OverflowException&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So which one is safer? It's tempting to say "checked, obviously". But Ariane's code was the checked one. The conversion raised the error, and the error is what shut both computers down. The value itself wasn't needed any more. The board points out that the SRIs could have kept providing their best estimates of the attitude data instead.&lt;/p&gt;

&lt;p&gt;That doesn't make silent garbage the right answer. It means a check is only half a decision. The other half is what the system does when the check fails: crash, retry, fall back to a safe value, or keep going in a degraded mode. On flight 501, that half had been decided years earlier, for a different kind of failure.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to take from this
&lt;/h2&gt;

&lt;p&gt;The board made 14 recommendations. Three ideas from the report apply to ordinary code:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Code that isn't needed shouldn't be running.&lt;/strong&gt; Recommendation R1 is exactly this: switch off the alignment function immediately after lift-off, and more generally, run no software function during flight unless it's needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reused code brings its old assumptions with it.&lt;/strong&gt; "This value can't get that big" was true for Ariane 4. It wasn't visible in the code, and nobody tested it against Ariane 5's trajectory. The report notes that a ground test with simulated Ariane 5 flight data would have exposed the failure. That test was never run on the SRI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A backup that runs the same code is no backup against a bug.&lt;/strong&gt; Redundancy protects you from random hardware faults. Two copies of the same software fail the same way at the same moment.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There's also one position in the report that still sounds bold. The project had treated software as correct until shown to be at fault. The board argued for the opposite view: assume software is faulty until accepted best-practice methods can demonstrate that it's correct.&lt;/p&gt;

&lt;p&gt;Ariane 5 recovered. According to &lt;a href="https://www.esa.int/Enabling_Support/Space_Transportation/Launch_vehicles/Ariane_5" rel="noopener noreferrer"&gt;ESA&lt;/a&gt;, it flew 117 times between 1996 and 2023, and it launched the James Webb Space Telescope. The Cluster satellites were rebuilt and &lt;a href="https://www.esa.int/Science_Exploration/Space_Science/Cluster_overview2" rel="noopener noreferrer"&gt;launched in 2000&lt;/a&gt; on Soyuz rockets.&lt;/p&gt;

&lt;p&gt;What's the oldest assumption you've found hiding in code you reused? Tell me in the comments.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I make Vlad's Stack, stories and explainers about how the tools you use every day actually work, for people who write code: &lt;a href="https://www.youtube.com/channel/UCUO8Uo5LsEy1b9eRkrH6JNg" rel="noopener noreferrer"&gt;https://www.youtube.com/channel/UCUO8Uo5LsEy1b9eRkrH6JNg&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>softwareengineering</category>
      <category>history</category>
    </item>
    <item>
      <title>The Court Order Behind C#: How Two Keywords Started a New Language</title>
      <dc:creator>VLAD</dc:creator>
      <pubDate>Tue, 06 Oct 2026 15:09:50 +0000</pubDate>
      <link>https://dev.to/vladut02/the-court-order-behind-c-how-two-keywords-started-a-new-language-29e8</link>
      <guid>https://dev.to/vladut02/the-court-order-behind-c-how-two-keywords-started-a-new-language-29e8</guid>
      <description>&lt;p&gt;On November 17th, 1998, a federal judge in San Jose gave Microsoft ninety days to change its Java tools. The court papers in that case talk about two words that C# developers still type every day: &lt;code&gt;delegate&lt;/code&gt; and &lt;code&gt;multicast&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/8Z8mTPPaI1w" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;This is the story of how a lawsuit over Java ended up producing C#. It's told from the court records, the documents the companies published at the time, and what C#'s lead designer, Anders Hejlsberg, has said in public.&lt;/p&gt;

&lt;h2&gt;
  
  
  The man from Borland
&lt;/h2&gt;

&lt;p&gt;In the early eighties, Anders Hejlsberg wrote a small, very fast Pascal compiler. Borland sold it as Turbo Pascal. Later, at Borland, he led the team that designed Delphi. In Delphi you drew a window, dropped a button on it, and wrote the code that should run on click.&lt;/p&gt;

&lt;p&gt;Keep that button in mind. The whole story turns on one question: how do you connect a button to a piece of your code?&lt;/p&gt;

&lt;p&gt;In 1996, Hejlsberg left Borland and joined Microsoft (&lt;a href="https://www.artima.com/articles/the-c-design-process" rel="noopener noreferrer"&gt;Artima interview bio&lt;/a&gt;). He wasn't the only one to leave. In May 1997, Borland sued Microsoft, saying it had hired away at least 34 of its people in 30 months (&lt;a href="https://www.eetimes.com/borland-sues-microsoft-for-unfair-competition/" rel="noopener noreferrer"&gt;EE Times&lt;/a&gt;). The two companies &lt;a href="https://news.microsoft.com/source/1997/09/19/borland-and-microsoft-announce-settlement/" rel="noopener noreferrer"&gt;settled in September 1997&lt;/a&gt;, and the terms were never made public.&lt;/p&gt;

&lt;h2&gt;
  
  
  Microsoft's Java
&lt;/h2&gt;

&lt;p&gt;In the mid-nineties, Java promised "write once, run anywhere". In March 1996, Microsoft signed a license with Sun Microsystems, the company that owned Java, and built its own Java for Windows: its own virtual machine and a developer tool called &lt;strong&gt;Visual J++&lt;/strong&gt;. Hejlsberg became an architect on Visual J++ and on a Windows library for it, the Windows Foundation Classes (WFC).&lt;/p&gt;

&lt;p&gt;Microsoft's Java could call Windows directly. Code that did that only ran on Windows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sun sues
&lt;/h2&gt;

&lt;p&gt;On October 7th, 1997, Sun sued Microsoft in federal court in San Jose. The charges included breach of contract, trademark infringement, false advertising and unfair competition (&lt;a href="https://www.mactech.com/1997/10/07/md1-sun-sues-microsoft-over-java/" rel="noopener noreferrer"&gt;Sun's announcement, via MacTech&lt;/a&gt;). Sun said Internet Explorer 4.0 and Microsoft's Java SDK failed Sun's compatibility tests while still carrying the "Java Compatible" logo. It also said pieces were missing: &lt;strong&gt;JNI&lt;/strong&gt;, the standard way for Java to call native code, and &lt;strong&gt;RMI&lt;/strong&gt;, for calling objects on other machines.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two new words
&lt;/h2&gt;

&lt;p&gt;While the case moved through the court, Microsoft shipped Visual J++ 6.0. Its compiler understood two words that weren't in Java: &lt;code&gt;delegate&lt;/code&gt; and &lt;code&gt;multicast&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here's the problem they solve. A button is written long before your code exists. When someone clicks it, it has to call one of &lt;em&gt;your&lt;/em&gt; methods. So you need a way to hand the button a method and say: call this, later.&lt;/p&gt;

&lt;p&gt;Java's answer, then, was an object. You wrote a small class that implements a listener interface and passed an instance to the button:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addActionListener&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ActionListener&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;actionPerformed&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ActionEvent&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello, world!"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Microsoft's answer was the &lt;strong&gt;delegate&lt;/strong&gt;: a reference that points straight at a method. You hand over the method itself, like a value. A &lt;strong&gt;multicast&lt;/strong&gt; delegate holds a list of methods, so one click runs all of them.&lt;/p&gt;

&lt;p&gt;If you write C#, you use this every day, even if you don't think about it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Click&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;OnClick&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;      &lt;span class="c1"&gt;// add a method to the delegate's list&lt;/span&gt;
&lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Click&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;LogClick&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;     &lt;span class="c1"&gt;// one click, both methods run&lt;/span&gt;

&lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnClick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EventArgs&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello, world!"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;LogClick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;object&lt;/span&gt; &lt;span class="n"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;EventArgs&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteLine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"clicked"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The court described the J++ version precisely. When Microsoft's compiler met &lt;code&gt;delegate&lt;/code&gt; and &lt;code&gt;multicast&lt;/code&gt;, it generated classes that extend &lt;code&gt;com.ms.lang.Delegate&lt;/code&gt; and &lt;code&gt;com.ms.lang.MulticastDelegate&lt;/code&gt;, classes that only Microsoft's virtual machine had. The bytecode was valid, but it looked for classes no other JVM shipped (&lt;a href="https://www.courtlistener.com/opinion/2577830/sun-microsystems-inc-v-microsoft-corp/" rel="noopener noreferrer"&gt;Sun v. Microsoft, 21 F. Supp. 2d 1109&lt;/a&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  Sun says no, for the second time
&lt;/h2&gt;

&lt;p&gt;Sun's Java team answered with a public white paper, &lt;a href="https://web.archive.org/web/19990210053040/http://www.java.sun.com/docs/white/delegates.html" rel="noopener noreferrer"&gt;"About Microsoft's 'Delegates'"&lt;/a&gt;. It argued that bound method references were unnecessary and bad for the language, and that inner classes could do the same job.&lt;/p&gt;

&lt;p&gt;Then it said something surprising. Sun had considered this exact idea in 1996. It had built working prototypes and thrown them away. And it made that decision in consultation with Borland, which already had bound method references in Delphi.&lt;/p&gt;

&lt;p&gt;Delphi, the language Hejlsberg had led at Borland. The idea Sun turned down in 1996 was back, inside Microsoft's Java.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ninety days
&lt;/h2&gt;

&lt;p&gt;On November 17th, 1998, Judge Ronald Whyte granted Sun a preliminary injunction. Microsoft had ninety days to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ship a Java runtime that supports JNI and passes Sun's tests;&lt;/li&gt;
&lt;li&gt;make its compiler &lt;strong&gt;disable&lt;/strong&gt; the Microsoft language extensions by default;&lt;/li&gt;
&lt;li&gt;show a &lt;strong&gt;warning&lt;/strong&gt; when a developer turned them on, saying the code might not run on other Java virtual machines;&lt;/li&gt;
&lt;li&gt;and stop &lt;strong&gt;adding any new keywords or compiler directives&lt;/strong&gt; to its Java tools.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Hejlsberg himself filed a declaration in the case. A later order cites it to explain that WFC, his Windows library, used the &lt;code&gt;delegate&lt;/code&gt; and &lt;code&gt;multicast&lt;/code&gt; keywords (&lt;a href="https://www.courtlistener.com/opinion/2307572/sun-microsystems-inc-v-microsoft-corp/" rel="noopener noreferrer"&gt;87 F. Supp. 2d 992, Jan 2000&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;While that order stood, Microsoft's Java couldn't get a single new language feature. The case went to the Ninth Circuit and back, and a new order came in January 2000. But Visual J++ 6.0 was the last version.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Cool
&lt;/h2&gt;

&lt;p&gt;Around the start of 1999, Hejlsberg put together a small team to design a new language. Its working name was Cool, for "C-like Object Oriented Language". He has said the core design team was about six people, meeting three times a week for two hours (&lt;a href="https://newsletter.pragmaticengineer.com/p/typescript-c-and-turbo-pascal-with" rel="noopener noreferrer"&gt;The Pragmatic Engineer&lt;/a&gt;). In 2001, he described the goal as the power and expressiveness of C++ with the ease of Visual Basic (&lt;a href="https://www.hpcwire.com/2001/04/27/tuning-up-for-c/" rel="noopener noreferrer"&gt;CNET interview, via HPCwire&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;This time, nobody else owned the language. Microsoft could add any keyword it wanted. So delegates went in, not as an extension but as a core part of the language. Multicast went in too, built into every delegate. And on top of it came a new keyword, &lt;code&gt;event&lt;/code&gt;. Properties, events and a button wired straight to a method were built into the language from day one.&lt;/p&gt;

&lt;h2&gt;
  
  
  C# ships
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;June 26, 2000:&lt;/strong&gt; Microsoft &lt;a href="https://news.microsoft.com/source/2000/06/26/microsoft-introduces-highly-productive-net-programming-language-c/" rel="noopener noreferrer"&gt;announced C#&lt;/a&gt; and submitted it to the standards body ECMA. An early preview followed at the Professional Developers Conference in Orlando, on July 11.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;January 23, 2001:&lt;/strong&gt; the Java case &lt;a href="https://news.microsoft.com/source/2001/01/23/microsoft-reaches-agreement-to-settle-contract-dispute-with-sun-microsystems/" rel="noopener noreferrer"&gt;settled&lt;/a&gt;. Microsoft paid Sun $20 million, the 1996 license was terminated, and Microsoft could keep shipping its existing Java products for seven years.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;December 2001:&lt;/strong&gt; ECMA approved &lt;a href="https://ecma-international.org/wp-content/uploads/ECMA-334_1st_edition_december_2001.pdf" rel="noopener noreferrer"&gt;ECMA-334&lt;/a&gt;, the C# standard, based on a submission from HP, Intel and Microsoft.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;February 13, 2002:&lt;/strong&gt; Bill Gates &lt;a href="https://news.microsoft.com/source/2002/02/13/microsoft-launches-xml-web-services-revolution-with-visual-studio-net-and-net-framework/" rel="noopener noreferrer"&gt;launched .NET and Visual Studio .NET&lt;/a&gt;. C# shipped with them.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The twist
&lt;/h2&gt;

&lt;p&gt;In April 2004, the two companies made peace. Microsoft paid Sun $700 million to settle antitrust issues and $900 million for patents (&lt;a href="https://news.microsoft.com/source/2004/04/02/microsoft-and-sun-microsystems-enter-broad-cooperation-agreement-settle-outstanding-litigation/" rel="noopener noreferrer"&gt;Microsoft announcement&lt;/a&gt;). Microsoft's Java virtual machine was retired for good at the end of 2007.&lt;/p&gt;

&lt;p&gt;And Java? In 2014, Java 8 added lambdas and method references, so you could finally hand a button a method directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;button&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;addActionListener&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It isn't the same thing as a C# delegate under the hood (Java still compiles it to an object that implements a functional interface). But it solves the problem Sun's white paper had called unnecessary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why C# exists
&lt;/h2&gt;

&lt;p&gt;Microsoft wasn't allowed to change a language it didn't own, so it built one it could change. The next time you write &lt;code&gt;+=&lt;/code&gt; on an event, remember that the feature behind it was once at the center of a federal court case.&lt;/p&gt;

&lt;p&gt;If Sun had said yes to delegates in 1998, would C# exist at all? Tell me what you think in the comments.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I make Vlad's Stack, stories and explainers about how the tools you use every day actually work, for people who write code: &lt;a href="https://www.youtube.com/channel/UCUO8Uo5LsEy1b9eRkrH6JNg" rel="noopener noreferrer"&gt;https://www.youtube.com/channel/UCUO8Uo5LsEy1b9eRkrH6JNg&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>csharp</category>
      <category>dotnet</category>
      <category>java</category>
      <category>history</category>
    </item>
    <item>
      <title>Knight Capital: How One Forgotten Server Lost $440 Million in 45 Minutes</title>
      <dc:creator>VLAD</dc:creator>
      <pubDate>Mon, 28 Sep 2026 21:51:50 +0000</pubDate>
      <link>https://dev.to/vladut02/knight-capital-how-one-forgotten-server-lost-440-million-in-45-minutes-3jd1</link>
      <guid>https://dev.to/vladut02/knight-capital-how-one-forgotten-server-lost-440-million-in-45-minutes-3jd1</guid>
      <description>&lt;p&gt;On August 1st, 2012, Knight Capital lost more than $400 million in about 45 minutes. It wasn't a hack or a bad bet. It was old code that nobody remembered was still running on one of eight servers.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/mIPLX_be15E" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Everything below comes from the official record: the &lt;a href="https://www.sec.gov/litigation/admin/2013/34-70694.pdf" rel="noopener noreferrer"&gt;SEC order against Knight Capital&lt;/a&gt; (Release No. 34-70694, October 2013). It reads like a postmortem, and most of its lessons apply far outside finance.&lt;/p&gt;

&lt;h2&gt;
  
  
  The machine: parent orders, child orders, and a counter
&lt;/h2&gt;

&lt;p&gt;Knight handled about one in ten trades in U.S. listed stocks in 2011–2012. One of its core systems was &lt;strong&gt;SMARS&lt;/strong&gt;, an automated router. It took a big "parent" order, split it into smaller "child" orders, and sent those to exchanges.&lt;/p&gt;

&lt;p&gt;The key part was a counter. SMARS tracked how many shares of the parent order had already been filled, and stopped sending children when the order was complete. Remember that counter.&lt;/p&gt;

&lt;h2&gt;
  
  
  The ghost in the code
&lt;/h2&gt;

&lt;p&gt;Inside SMARS lived an old feature called &lt;strong&gt;Power Peg&lt;/strong&gt;. Knight stopped using it in 2003, but the code was never deleted. It stayed on the servers, still callable.&lt;/p&gt;

&lt;p&gt;In 2005, the counter that told Power Peg when to stop was moved to a different place in the code. Power Peg was never retested after that. Nobody checked whether it still knew when to stop. It didn't.&lt;/p&gt;

&lt;p&gt;In 2012, the NYSE launched a new retail program starting August 1st, and Knight wrote new code for it. The new code needed a switch, a flag on each order that meant "use the new feature". Instead of adding a new flag, &lt;strong&gt;it reused the old flag that used to activate Power Peg&lt;/strong&gt;. The plan was to replace the old code everywhere, so the flag would simply mean something new.&lt;/p&gt;

&lt;h2&gt;
  
  
  The deploy: seven out of eight
&lt;/h2&gt;

&lt;p&gt;SMARS ran on eight servers. Starting July 27th, a technician copied the new code to them by hand over several days. Seven servers got it. The eighth didn't, so it still had Power Peg.&lt;/p&gt;

&lt;p&gt;There was no second person reviewing the deployment, and no written procedure requiring one. Other teams at Knight had written procedures; the SMARS team didn't.&lt;/p&gt;

&lt;h2&gt;
  
  
  97 warnings nobody read
&lt;/h2&gt;

&lt;p&gt;At 8:01 a.m., before the market opened, a Knight system started emailing a group of employees about SMARS orders with the error "Power Peg disabled". By 9:30 it had sent 97 of them. They weren't designed as alerts, and nobody generally read them. They named the exact problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  9:30: 212 orders become millions
&lt;/h2&gt;

&lt;p&gt;When the market opened, orders arrived carrying the reused flag. Seven servers ran the new code. The eighth ran Power Peg, which kept sending child orders because its counter wasn't where it expected. Another part of Knight's system knew the orders were filled, but that information never reached SMARS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;212 customer orders turned into millions of child orders&lt;/strong&gt;: about 4 million executions in 154 stocks, over 397 million shares.&lt;/p&gt;

&lt;p&gt;People inside saw positions piling up in one account with a $2 million limit. But the risk tool only displayed numbers to humans. It wasn't connected to the order system, it didn't raise automated alerts, and nothing cut the machine off.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix that made it worse
&lt;/h2&gt;

&lt;p&gt;The engineers reasoned that the trouble started with the new code, so they removed it from the seven servers where it was working. The orders still carried the reused flag. &lt;strong&gt;Now all eight servers woke up Power Peg.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The bill
&lt;/h2&gt;

&lt;p&gt;When it stopped, Knight held about $3.5 billion of stock it never wanted to buy and had sold about $3.15 billion it didn't own. Knight first estimated the loss at about $440 million; the SEC later put it at over $460 million. In 37 stocks, prices moved more than 10% with Knight doing most of the trading.&lt;/p&gt;

&lt;p&gt;Five days later, investors put in &lt;a href="https://www.sec.gov/Archives/edgar/data/0001060749/000119312512338098/d392396dex991.htm" rel="noopener noreferrer"&gt;$400 million to keep the firm alive&lt;/a&gt;. Less than a year later it merged with a rival. The SEC fined it $12 million for not having the controls the market access rule requires.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a developer should take from this
&lt;/h2&gt;

&lt;p&gt;None of the individual decisions looked dangerous on the day they were made. That's the point.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Delete dead code.&lt;/strong&gt; Code that's switched off is still code that can be switched on.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One flag, one meaning.&lt;/strong&gt; Give every new feature its own flag, and plan the day you'll remove it. Reusing a flag makes old code reachable in ways nobody is thinking about.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verify every deploy.&lt;/strong&gt; Automate it, and make every instance report which version it runs. The eighth server can't hide from a script.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build the kill switch before you need it.&lt;/strong&gt; If your code can spend money or send messages at machine speed, stopping it must not depend on first understanding the bug.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An alert nobody reads is not an alert.&lt;/strong&gt; If a message can say "Power Peg disabled" 97 times without anyone noticing, it's noise, and noise hides the one message that matters.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What's the oldest dead code you've ever found in production?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;I make Vlad's Stack — how the tools you use every day actually work, for people who write code: &lt;a href="https://www.youtube.com/@VladsStack" rel="noopener noreferrer"&gt;https://www.youtube.com/@VladsStack&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>deployment</category>
      <category>postmortem</category>
      <category>programming</category>
    </item>
    <item>
      <title>Embeddings explained for people who write code</title>
      <dc:creator>VLAD</dc:creator>
      <pubDate>Fri, 18 Sep 2026 22:08:01 +0000</pubDate>
      <link>https://dev.to/vladut02/embeddings-explained-for-people-who-write-code-5aja</link>
      <guid>https://dev.to/vladut02/embeddings-explained-for-people-who-write-code-5aja</guid>
      <description>&lt;p&gt;Type &lt;em&gt;"how do I reset my password"&lt;/em&gt; into a good search box, and it finds a page titled &lt;em&gt;"account recovery."&lt;/em&gt; Zero words in common. Nothing matched on text. So how did it know they mean the same thing?&lt;/p&gt;

&lt;p&gt;Your app turned &lt;strong&gt;meaning into numbers&lt;/strong&gt; — and once meaning is numbers, a computer can measure it. That trick is called an &lt;strong&gt;embedding&lt;/strong&gt;. Get this one idea and half the AI buzzwords — "vector search," "cosine similarity," "vector database" — stop being scary.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Prefer to watch? Full walkthrough with the meaning-space animation:&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/MfBKyEhz-x8?start=3" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  An embedding is a list of numbers
&lt;/h2&gt;

&lt;p&gt;Start with the machine itself. You hand it a piece of text — a word, a sentence, a whole paragraph. It hands back a &lt;strong&gt;list of numbers&lt;/strong&gt;. That list &lt;em&gt;is&lt;/em&gt; the embedding. Same text in, same numbers out, every time.&lt;/p&gt;

&lt;p&gt;How long is the list? For a common model — OpenAI's &lt;a href="https://platform.openai.com/docs/guides/embeddings" rel="noopener noreferrer"&gt;&lt;code&gt;text-embedding-3-small&lt;/code&gt;&lt;/a&gt; — it's &lt;strong&gt;1,536&lt;/strong&gt; numbers. That sounds like a lot, until you think of each number as a &lt;strong&gt;coordinate&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Two numbers place a point on a map. Three place it in a room. 1,536 place it in a space you can't picture — but the math works exactly the same as the map.&lt;/p&gt;

&lt;h2&gt;
  
  
  Meaning becomes position
&lt;/h2&gt;

&lt;p&gt;Here's the whole point of that space: the model places text so that &lt;strong&gt;similar meaning lands in a similar spot&lt;/strong&gt;. "cat" and "dog" end up as neighbors. "car" ends up far away.&lt;/p&gt;

&lt;p&gt;Nobody wrote that rule. The model &lt;strong&gt;learned&lt;/strong&gt; it — it read a mountain of text and noticed which words keep the same company. Words used the same way get pushed together; words used differently get pushed apart. This is an old idea from linguistics called the &lt;a href="https://en.wikipedia.org/wiki/Distributional_semantics" rel="noopener noreferrer"&gt;distributional hypothesis&lt;/a&gt;: a word's meaning is shaped by the words it usually appears next to. Meaning, in this space, is just &lt;strong&gt;where you land&lt;/strong&gt; relative to everything else.&lt;/p&gt;

&lt;h2&gt;
  
  
  "Cosine similarity" is just an angle
&lt;/h2&gt;

&lt;p&gt;Now the real question in search: are these two things &lt;em&gt;close&lt;/em&gt;? You embed the question, you embed every document, and you grab the points &lt;strong&gt;nearest&lt;/strong&gt; the question. But "nearest" means one specific thing — and it's simpler than it sounds.&lt;/p&gt;

&lt;p&gt;Draw an arrow from the center of the space out to each point.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Two texts with &lt;strong&gt;similar&lt;/strong&gt; meaning? Their arrows point almost the same way — a &lt;strong&gt;small angle&lt;/strong&gt; between them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unrelated&lt;/strong&gt; texts? The arrows point off in different directions — a wide angle.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That angle &lt;em&gt;is&lt;/em&gt; cosine similarity. A value near &lt;strong&gt;1&lt;/strong&gt; means the arrows point the same way (very similar). A value near &lt;strong&gt;0&lt;/strong&gt; means they're unrelated. That's the whole comparison — "cosine similarity" is just measuring the angle between two arrows.&lt;/p&gt;

&lt;h2&gt;
  
  
  In code, it's three lines
&lt;/h2&gt;

&lt;p&gt;Every "AI search" feature you've used is basically this, under a nicer name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;embed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;account recovery steps&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;      &lt;span class="c1"&gt;// → [0.02, -0.91, …] · 1536 numbers&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;embed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;how do I reset my password?&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// cosine: 1 = same direction, 0 = unrelated&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;score&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;cosine&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                   &lt;span class="c1"&gt;// ≈ 0.86 — close&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Embed your text, embed your query, compare them, keep the closest. (Those scores are illustrative — the real point is &lt;em&gt;high = same direction&lt;/em&gt;.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Trap 1: the numbers only make sense inside one model
&lt;/h2&gt;

&lt;p&gt;These coordinates are only meaningful &lt;strong&gt;within a single model&lt;/strong&gt;. A vector from model A and a vector from model B are not comparable — it's gibberish. Same coordinates, different maps.&lt;/p&gt;

&lt;p&gt;So embed your query and your documents with the &lt;strong&gt;exact same model&lt;/strong&gt;, always. Change the model, and you have to re-embed everything you're searching over.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trap 2: embeddings capture meaning, not exact strings
&lt;/h2&gt;

&lt;p&gt;Embeddings are great at meaning — which makes them bad at things that &lt;em&gt;have&lt;/em&gt; no meaning. An error code. A product ID. &lt;code&gt;SKU-4417&lt;/code&gt;. There's nothing to place on the meaning-map; it's just an exact string, and pure vector search fumbles it, because nothing is "close in meaning" to a serial number.&lt;/p&gt;

&lt;p&gt;That's why real systems run &lt;strong&gt;both&lt;/strong&gt;: keyword search to catch exact strings, vector search to catch meaning, and the results merged. If you've built RAG and watched it miss an obvious error code, this is usually why.&lt;/p&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;An embedding, start to finish:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Text becomes a list of numbers.&lt;/li&gt;
&lt;li&gt;The numbers are coordinates in a meaning-space.&lt;/li&gt;
&lt;li&gt;Close in meaning → close in space.&lt;/li&gt;
&lt;li&gt;"Similarity" is just the angle between two arrows.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Once you see it as numbers on a map, the buzzwords fall away — and you can actually &lt;strong&gt;debug&lt;/strong&gt; your search instead of trusting it. When a result looks wrong, you're not staring at magic; you're asking a concrete question about distance on a map.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's the weirdest match your vector search ever returned — the one that made no sense at all?&lt;/strong&gt; Drop it in the comments — I read them.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I make &lt;a href="https://www.youtube.com/channel/UCUO8Uo5LsEy1b9eRkrH6JNg" rel="noopener noreferrer"&gt;Vlad's Stack&lt;/a&gt; — how the tools you use every day actually work, for people who write code. Full video walkthrough is above.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>embeddings</category>
      <category>ai</category>
      <category>machinelearning</category>
      <category>vectordatabase</category>
    </item>
    <item>
      <title>SQL vs. NoSQL — stop asking the wrong question</title>
      <dc:creator>VLAD</dc:creator>
      <pubDate>Mon, 07 Sep 2026 00:37:44 +0000</pubDate>
      <link>https://dev.to/vladut02/sql-vs-nosql-stop-asking-the-wrong-question-3gm</link>
      <guid>https://dev.to/vladut02/sql-vs-nosql-stop-asking-the-wrong-question-3gm</guid>
      <description>&lt;p&gt;Two teams build the same app. One picks SQL. One picks NoSQL. Six months later, one team is rewriting their whole data layer — and it's not because they picked the &lt;em&gt;wrong&lt;/em&gt; database. It's because they asked the wrong &lt;em&gt;question&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Everyone fights about which one is faster, or which one scales. That's the wrong fight. By the end of this you'll know the one question that actually decides it, and why three things you've heard about SQL and NoSQL are just wrong.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Prefer to watch? Full walkthrough with the JOIN-vs-document animation:&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/wQXh4Vi1cFg" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The wrong question
&lt;/h2&gt;

&lt;p&gt;"Which one is faster?" "Which one is modern?" "Which one scales?"&lt;/p&gt;

&lt;p&gt;None of these pick your database. Speed and scale depend on &lt;em&gt;how you use&lt;/em&gt; a database, not on which logo you chose. You can make either one fast, and you can make either one fall over.&lt;/p&gt;

&lt;h2&gt;
  
  
  The right question
&lt;/h2&gt;

&lt;p&gt;What &lt;strong&gt;shape&lt;/strong&gt; is your data — and how will you &lt;strong&gt;read it back&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;Answer that, and the database almost picks itself. Here's what that means.&lt;/p&gt;

&lt;h2&gt;
  
  
  SQL: a relational database
&lt;/h2&gt;

&lt;p&gt;In SQL, your data lives in &lt;strong&gt;tables&lt;/strong&gt; — rows and columns, like a spreadsheet with rules. You define the shape up front. That's the schema.&lt;/p&gt;

&lt;p&gt;The superpower is the &lt;strong&gt;JOIN&lt;/strong&gt;. You keep users in one table and orders in another, then stitch them together on the fly to answer almost any question:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;
&lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"Every user, and how many times they ordered." You never &lt;em&gt;designed&lt;/em&gt; for that query — SQL answers it anyway. That flexibility is the whole point of a relational database.&lt;/p&gt;

&lt;p&gt;But it has a price. Your data is &lt;strong&gt;split&lt;/strong&gt; across tables (that's normalization), so to read one thing back, the database has to stitch those tables together on every read. Fine at normal size. So what does NoSQL do differently?&lt;/p&gt;

&lt;h2&gt;
  
  
  NoSQL isn't one thing
&lt;/h2&gt;

&lt;p&gt;First, an important correction: &lt;strong&gt;NoSQL isn't a single kind of database.&lt;/strong&gt; It's a family. The four categories you'll usually see are &lt;a href="https://www.mongodb.com/resources/basics/databases/nosql-explained" rel="noopener noreferrer"&gt;document, key-value, wide-column, and graph&lt;/a&gt; databases.&lt;/p&gt;

&lt;p&gt;They share one idea: you model your data around &lt;strong&gt;how you read it&lt;/strong&gt;. Store together what you read together.&lt;/p&gt;

&lt;p&gt;So instead of separate &lt;code&gt;users&lt;/code&gt; and &lt;code&gt;orders&lt;/code&gt; tables, you store one &lt;strong&gt;document&lt;/strong&gt; — the user, with their orders inside it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Vlad"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"orders"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"total"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;40&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"total"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One read. Nothing to stitch. It's already assembled.&lt;/p&gt;

&lt;h2&gt;
  
  
  Same question, two shapes
&lt;/h2&gt;

&lt;p&gt;That's the core difference. Ask "give me this user and their orders":&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SQL&lt;/strong&gt; joins two tables at read time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NoSQL&lt;/strong&gt; hands you a document that was already built that way.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Same answer, different shape. And that's the whole trade. The document is &lt;strong&gt;faster — for the read you planned&lt;/strong&gt;. But now ask a &lt;em&gt;new&lt;/em&gt; question: "every order over $40, across all users." In SQL that's one line. In your documents, that data is buried in a thousand separate places, because you didn't store it that way.&lt;/p&gt;

&lt;p&gt;That's the trap that kills projects. You pick the shape for &lt;em&gt;today's&lt;/em&gt; question. Then the product grows, the questions change — and the shape can't. That's the team rewriting everything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three myths, killed
&lt;/h2&gt;

&lt;p&gt;Three things you've probably heard push people toward the wrong choice.&lt;/p&gt;

&lt;h3&gt;
  
  
  Myth 1 — "NoSQL has no schema"
&lt;/h3&gt;

&lt;p&gt;False. The schema didn't disappear — it &lt;strong&gt;moved&lt;/strong&gt; into your application code. Every read still assumes a shape; you just gave up the database checking it for you.&lt;/p&gt;

&lt;p&gt;This even has a name: &lt;a href="https://martinfowler.com/articles/schemaless/" rel="noopener noreferrer"&gt;schema-on-read&lt;/a&gt;. As Martin Fowler puts it, a "schemaless" store still has an &lt;em&gt;implicit&lt;/em&gt; schema — the structure your code relies on when it reads the data — instead of an &lt;em&gt;explicit&lt;/em&gt; one enforced by the database. "Schemaless" doesn't mean no schema. It means you own it now.&lt;/p&gt;

&lt;h3&gt;
  
  
  Myth 2 — "SQL can't scale"
&lt;/h3&gt;

&lt;p&gt;Also false. Postgres runs &lt;a href="https://www.postgresql.org/about/" rel="noopener noreferrer"&gt;massive&lt;/a&gt; production workloads, and you'll hit product problems long before it's your bottleneck. And when you genuinely outgrow a single node, &lt;strong&gt;distributed SQL&lt;/strong&gt; exists now — &lt;a href="https://www.cockroachlabs.com/" rel="noopener noreferrer"&gt;CockroachDB&lt;/a&gt;, &lt;a href="https://cloud.google.com/spanner" rel="noopener noreferrer"&gt;Google Cloud Spanner&lt;/a&gt;, and &lt;a href="https://vitess.io/" rel="noopener noreferrer"&gt;Vitess&lt;/a&gt; all give you horizontal scale without giving up SQL.&lt;/p&gt;

&lt;h3&gt;
  
  
  Myth 3 — "NoSQL is faster"
&lt;/h3&gt;

&lt;p&gt;Only for the pattern it was built for. Ask it something off-pattern and it's slower — or it just can't answer without loading everything and filtering in your app. Fast isn't a property of the database. It's a property of the &lt;strong&gt;match&lt;/strong&gt; between your data's shape and your query.&lt;/p&gt;

&lt;h2&gt;
  
  
  So when do you use each?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Relationships that matter, and questions that will change&lt;/strong&gt; → go relational (SQL).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One known access pattern, at huge scale&lt;/strong&gt; → that's where NoSQL wins.&lt;/li&gt;
&lt;li&gt;And you're allowed to use &lt;strong&gt;both&lt;/strong&gt; — plenty of systems put a relational database next to a key-value cache or a document store, each for what it's good at.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For most apps starting out, the boring answer is the right one: &lt;strong&gt;start with Postgres&lt;/strong&gt;, and reach for NoSQL when you have a real, specific reason — not a vibe.&lt;/p&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;Stop asking which one is better. Ask what shape your data is, and how you'll read it back — and the database picks itself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What made you reach for NoSQL last time — the scale, or the schema?&lt;/strong&gt; Drop it in the comments — I read them.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I make &lt;a href="https://www.youtube.com/channel/UCUO8Uo5LsEy1b9eRkrH6JNg" rel="noopener noreferrer"&gt;Vlad's Stack&lt;/a&gt; — how the tools you use every day actually work, for people who write code. Full video walkthrough is above.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>sql</category>
      <category>nosql</category>
      <category>database</category>
      <category>mongodb</category>
    </item>
    <item>
      <title>What actually happens in a database index (and why half of them do nothing)</title>
      <dc:creator>VLAD</dc:creator>
      <pubDate>Sat, 05 Sep 2026 21:25:42 +0000</pubDate>
      <link>https://dev.to/vladut02/what-actually-happens-in-a-database-index-and-why-half-of-them-do-nothing-3mh6</link>
      <guid>https://dev.to/vladut02/what-actually-happens-in-a-database-index-and-why-half-of-them-do-nothing-3mh6</guid>
      <description>&lt;p&gt;Same query. Same table. Same million rows. One day it takes &lt;strong&gt;4 seconds&lt;/strong&gt;. The next day, &lt;strong&gt;4 milliseconds&lt;/strong&gt;. Nothing changed in the data. The only thing that changed was one line — you added an &lt;strong&gt;index&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Four seconds to four milliseconds is a thousand times faster, from one line of SQL. But here's the part nobody tells you: &lt;strong&gt;half&lt;/strong&gt; the indexes people add do nothing. The query stays slow, the writes get slower, and they can't figure out why.&lt;/p&gt;

&lt;p&gt;By the end of this you'll know what an index actually &lt;em&gt;is&lt;/em&gt; — and the one rule that decides whether yours even gets used.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Prefer to watch? Full walkthrough with the B-tree lookup animation:&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/aNzoh7S3Ca0" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  With no index: a full table scan
&lt;/h2&gt;

&lt;p&gt;You ask the database for one user by email. With no index, what does it do?&lt;/p&gt;

&lt;p&gt;It reads the first row. Not a match. The second row. Not a match. It keeps going — every single row — until it finds yours or runs out. A million rows, a million checks.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'vlad@stack.dev'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With no index, that &lt;code&gt;WHERE&lt;/code&gt; line has only one way to run: look at all of them. The work grows with the table — ten times the rows, ten times the wait. That's a &lt;strong&gt;full table scan&lt;/strong&gt;, and that's your four seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  What an index actually is
&lt;/h2&gt;

&lt;p&gt;Most people picture an index as a &lt;em&gt;copy&lt;/em&gt; of the table, or some kind of cache. It's neither.&lt;/p&gt;

&lt;p&gt;An index is a &lt;strong&gt;sorted map&lt;/strong&gt; — just the column you search on, kept in order, with a pointer back to the full row. And the shape it's sorted into has a name: a &lt;strong&gt;B-tree&lt;/strong&gt; (the default index in both Postgres and MySQL — technically a B+ tree).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;At the top, one node — the &lt;strong&gt;root&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;It splits into a few &lt;strong&gt;branches&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Each branch splits again, down to the &lt;strong&gt;leaves&lt;/strong&gt;, where the pointers to the rows actually live.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every node is sorted. The root doesn't hold your data — it holds &lt;strong&gt;signposts&lt;/strong&gt;. Emails before "M"? Go left. "N" and after? Go right. Each step throws away half the tree, or more. You're never reading rows. You're following signs.&lt;/p&gt;

&lt;h2&gt;
  
  
  The walk: three hops, not a million rows
&lt;/h2&gt;

&lt;p&gt;Watch what the lookup actually does:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The root — one hop.&lt;/li&gt;
&lt;li&gt;A branch — two.&lt;/li&gt;
&lt;li&gt;A leaf — &lt;strong&gt;found&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Three hops. Not a million rows. Three.&lt;/p&gt;

&lt;p&gt;That's the whole trick. A scan checks &lt;em&gt;every&lt;/em&gt; row — that's the four seconds. A B-tree walk checks a &lt;em&gt;handful&lt;/em&gt; — that's the four milliseconds. Grow the table to ten million rows and the scan gets ten times worse, while the tree adds &lt;strong&gt;one&lt;/strong&gt; more hop. That's &lt;code&gt;O(n)&lt;/code&gt; versus &lt;code&gt;O(log n)&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  So why not index everything?
&lt;/h2&gt;

&lt;p&gt;Because an index is &lt;strong&gt;not free&lt;/strong&gt;. It's a second structure the database has to keep in sync. Every time you insert a row, change that column, or delete one, the tree has to be updated too.&lt;/p&gt;

&lt;p&gt;Index the whole table, and your reads fly while your writes crawl.&lt;/p&gt;

&lt;p&gt;So an index is a &lt;strong&gt;trade&lt;/strong&gt;: faster reads, slower writes, more disk. You pay it on purpose — on the columns you actually search — not on all of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  The trap: an index your query can't use
&lt;/h2&gt;

&lt;p&gt;Here's the part that trips everyone up. You add the index. The query is &lt;strong&gt;still slow&lt;/strong&gt;. How?&lt;/p&gt;

&lt;p&gt;Because an index only helps if your query can actually &lt;em&gt;use&lt;/em&gt; it — and it's shockingly easy to write one that can't.&lt;/p&gt;

&lt;h3&gt;
  
  
  The phone-book rule
&lt;/h3&gt;

&lt;p&gt;Say you index two columns together — last name, then first name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;users&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;last_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;first_name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Search by &lt;strong&gt;last name&lt;/strong&gt;? Fast.&lt;/li&gt;
&lt;li&gt;Search by &lt;strong&gt;last and first&lt;/strong&gt;? Fast.&lt;/li&gt;
&lt;li&gt;Search by &lt;strong&gt;first name alone&lt;/strong&gt;? &lt;strong&gt;Scan.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The tree is sorted by last name &lt;em&gt;first&lt;/em&gt;, so first name is useless without it. It's a phone book: perfect for finding "Smith," useless for finding every "John." This isn't a quirk of one database — B-tree indexes everywhere work &lt;strong&gt;left to right&lt;/strong&gt;, or not at all.&lt;/p&gt;

&lt;h3&gt;
  
  
  Three more ways to quietly kill an index
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Leading wildcard:&lt;/strong&gt; &lt;code&gt;email LIKE '%dev'&lt;/code&gt; starts with a wildcard, so the tree can't seek — it scans.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Function on the column:&lt;/strong&gt; wrap it in &lt;code&gt;lower(email)&lt;/code&gt; and your index on plain &lt;code&gt;email&lt;/code&gt; doesn't match.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Low selectivity:&lt;/strong&gt; index a column that's half true, half false, and the database ignores it — a signpost that splits nothing saves nothing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;An index is a sorted &lt;strong&gt;B-tree&lt;/strong&gt;. It turns a full scan into a three-hop walk — but only if your query matches the way it's sorted. The &lt;strong&gt;order&lt;/strong&gt; of the columns isn't a detail. It &lt;em&gt;is&lt;/em&gt; the index. Get that order wrong, and you built a tree the query will never climb.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's the slowest query you've ever fixed with a single index?&lt;/strong&gt; Drop it in the comments — I read them.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I make &lt;a href="https://www.youtube.com/channel/UCUO8Uo5LsEy1b9eRkrH6JNg" rel="noopener noreferrer"&gt;Vlad's Stack&lt;/a&gt; — how the tools you use every day actually work, for people who write code. Full video walkthrough is above.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>database</category>
      <category>sql</category>
      <category>postgres</category>
      <category>performance</category>
    </item>
    <item>
      <title>Why more context makes your AI answers worse</title>
      <dc:creator>VLAD</dc:creator>
      <pubDate>Mon, 31 Aug 2026 22:16:08 +0000</pubDate>
      <link>https://dev.to/vladut02/why-more-context-makes-your-ai-answers-worse-555o</link>
      <guid>https://dev.to/vladut02/why-more-context-makes-your-ai-answers-worse-555o</guid>
      <description>&lt;p&gt;Your model says it has a one-million-token context window. Its real working memory is a lot smaller than that.&lt;/p&gt;

&lt;p&gt;On long-context benchmarks, models start failing well below the number printed on the box. And here's the part nobody warns you about: past a certain point, adding more context makes your answers &lt;strong&gt;worse&lt;/strong&gt;. Not better. Worse.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Prefer to watch? Full walkthrough with the attention-cost animation:&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/hH7Ko-x1Bn8?start=3" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  A context window is not memory
&lt;/h2&gt;

&lt;p&gt;Let's kill one idea first. A context window is &lt;strong&gt;not&lt;/strong&gt; memory.&lt;/p&gt;

&lt;p&gt;The model doesn't remember your last message — it's stateless. It keeps nothing between calls. Every single call, you send the whole conversation again. All of it.&lt;/p&gt;

&lt;p&gt;Look at what you actually ship over the wire:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Turn one:&lt;/strong&gt; a system prompt and one question. Small.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Turn ten:&lt;/strong&gt; the same system prompt, plus nine questions, plus nine answers, plus the new one.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The window is just how much text fits in that box before the API says no. And everything in the box gets processed. Every time.&lt;/p&gt;

&lt;h2&gt;
  
  
  What attention actually does
&lt;/h2&gt;

&lt;p&gt;Inside the model, attention has a job that's simple to say: every token looks at every other token and decides how much it cares about each one.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;10 tokens → 100 pairs&lt;/li&gt;
&lt;li&gt;1,000 tokens → &lt;strong&gt;1,000,000&lt;/strong&gt; pairs&lt;/li&gt;
&lt;li&gt;100,000 tokens → &lt;strong&gt;10,000,000,000&lt;/strong&gt; pairs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ten times the context isn't ten times the work. It's a hundred times the work. (That's just arithmetic: cost grows with the square of the input.)&lt;/p&gt;

&lt;p&gt;That's also why the first reply in a long chat feels slow and the rest feel fast — the model caches the keys and values it already computed. But the cache saves you &lt;em&gt;compute&lt;/em&gt;. It doesn't save your &lt;em&gt;answer quality&lt;/em&gt;. That's a separate problem, and it's the one you actually feel.&lt;/p&gt;

&lt;p&gt;Here are the three reasons big context hurts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reason 1 — attention is a budget
&lt;/h2&gt;

&lt;p&gt;For every token, the attention weights add up to one. Always one — that's what softmax does.&lt;/p&gt;

&lt;p&gt;So when you paste in fifty more documents, you don't hand the model more focus. You split the &lt;strong&gt;same&lt;/strong&gt; focus into more pieces.&lt;/p&gt;

&lt;p&gt;Think about search on your laptop. One folder, ten files — you find it in a second. Same search, ten thousand files, same query, and the right file is now somewhere on page four. The model has that exact problem. Except it never shows you page four. It just answers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reason 2 — lost in the middle
&lt;/h2&gt;

&lt;p&gt;Researchers took one correct answer and moved it around inside a long prompt. Same prompt, same question, different position. Then they measured accuracy.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Near the beginning: good.&lt;/li&gt;
&lt;li&gt;Near the end: good.&lt;/li&gt;
&lt;li&gt;In the middle: &lt;strong&gt;much worse&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(Liu et al., &lt;em&gt;&lt;a href="https://arxiv.org/abs/2307.03172" rel="noopener noreferrer"&gt;Lost in the Middle&lt;/a&gt;&lt;/em&gt;.) So your most important paragraph, sitting at 60% of a long prompt, is in the worst possible spot in the whole file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reason 3 — distractors in your own data
&lt;/h2&gt;

&lt;p&gt;This is the one I hit most in real code. The famous needle-in-a-haystack test is easy, because the needle looks nothing like the hay. Your production data is not like that.&lt;/p&gt;

&lt;p&gt;You've got version two of the doc. Version three. An old changelog. And a chat thread that contradicts all three. Four chunks that all look right. One of them is right — and the model can't tell which.&lt;/p&gt;

&lt;p&gt;It gets worse as the input grows: accuracy drops steadily with input length, even on tasks the same model nails at short length. So the number on the box tells you what &lt;em&gt;fits&lt;/em&gt;. It doesn't tell you what &lt;em&gt;works&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four things you can actually change
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Send less, but better.&lt;/strong&gt; If you're doing retrieval, stop pushing the top fifty chunks into the prompt. Search wide, re-sort the results, then send the top three to five. Fewer clean chunks beat more chunks, every time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Use the edges.&lt;/strong&gt; Instruction at the top. Data in the middle. Then repeat the actual question at the &lt;strong&gt;bottom&lt;/strong&gt;, right before the model answers. That last line costs you nothing, and it works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Stop reusing one giant thread.&lt;/strong&gt; Split the job into steps. Each step gets a fresh, small context with only what it needs. Between steps, pass a short summary — not the full transcript. That's most of what agent frameworks are doing for you under the hood.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Measure it on your own data.&lt;/strong&gt; Take twenty real questions from your app. Run them with 2,000 tokens of context, then run the same twenty with 20,000. Compare the answers side by side. You'll find your own limit — and it'll be lower than the spec sheet.&lt;/p&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;The context window is a hard limit on what fits. It was never a promise about what the model can use. Treat it like bandwidth you're paying for, and send the smallest thing that answers the question.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's the biggest prompt you've ever shipped to production? And when you cut it down, did it get better or worse?&lt;/strong&gt; Tell me below.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I make &lt;a href="https://www.youtube.com/channel/UCUO8Uo5LsEy1b9eRkrH6JNg" rel="noopener noreferrer"&gt;Vlad's Stack&lt;/a&gt; — how the AI tools you use every day actually work, for people who write code. Full video walkthrough is above.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>promptengineering</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>Why your RAG returns garbage (and it's not the model)</title>
      <dc:creator>VLAD</dc:creator>
      <pubDate>Mon, 31 Aug 2026 22:12:03 +0000</pubDate>
      <link>https://dev.to/vladut02/why-your-rag-returns-garbage-and-its-not-the-model-4d7d</link>
      <guid>https://dev.to/vladut02/why-your-rag-returns-garbage-and-its-not-the-model-4d7d</guid>
      <description>&lt;p&gt;Your RAG bot just gave a confident, detailed answer. And it's completely &lt;strong&gt;wrong&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Here's the part that'll annoy you: the model did &lt;em&gt;nothing&lt;/em&gt; wrong. It answered perfectly — using the text &lt;strong&gt;you&lt;/strong&gt; handed it. The bug isn't in the AI. It's in the five steps before the AI.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Prefer to watch? Full 6-minute walkthrough with the "lost in the middle" animation:&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/L2GJbg5IukM" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The pipeline, in one line of code
&lt;/h2&gt;

&lt;p&gt;RAG is simple on paper. You search your own documents, grab the best matches, paste them into the prompt, and the model answers from those. So when the answer's garbage, everyone blames the model.&lt;/p&gt;

&lt;p&gt;Wrong suspect. There are &lt;strong&gt;five&lt;/strong&gt; places this pipeline breaks — and four of them happen before the model even runs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;chunks&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;docs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                   &lt;span class="c1"&gt;// 1 · chunking&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;vectors&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;embed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;                 &lt;span class="c1"&gt;// 2 · embedding&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hits&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;embed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;vectors&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// 3 · retrieval&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;best&lt;/span&gt;    &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;rerank&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;hits&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;           &lt;span class="c1"&gt;// 4 · ranking&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;answer&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;best&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;      &lt;span class="c1"&gt;// 5 · generation&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Five lines. Every bug I'm about to show you lives on one of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Chunking — a fixed cut splits your answer in half
&lt;/h2&gt;

&lt;p&gt;Your documents are long, so you cut them into pieces before you store them. Most people cut every few hundred characters and move on. That's where it starts to rot.&lt;/p&gt;

&lt;p&gt;A fixed-length cut doesn't care about &lt;strong&gt;meaning&lt;/strong&gt;. It'll slice a sentence in half. It'll split a question from its answer. Now the one chunk that held your answer is two halves — and each half looks irrelevant on its own. So retrieval never even finds it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; cut on &lt;strong&gt;structure&lt;/strong&gt;, not length. Split on paragraphs and headers — where the meaning actually breaks. And let chunks overlap a little, so nothing gets stranded at the edge.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Embedding — your question doesn't look like its answer
&lt;/h2&gt;

&lt;p&gt;Every chunk goes through a model that turns text into a &lt;strong&gt;vector&lt;/strong&gt;: a long list of numbers that captures its meaning. Similar meaning, similar numbers. That's the whole trick that makes search work.&lt;/p&gt;

&lt;p&gt;But here's the trap. Your &lt;em&gt;question&lt;/em&gt; gets embedded by the same model — and a question rarely looks like its answer.&lt;/p&gt;

&lt;p&gt;You ask &lt;em&gt;"how do I reset my password?"&lt;/em&gt; The doc says &lt;em&gt;"account recovery procedure."&lt;/em&gt; Same thing to a human. Different words — and a weak embedder puts them far apart.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; pick an embedding model built for &lt;strong&gt;retrieval&lt;/strong&gt;, and test it on &lt;em&gt;your&lt;/em&gt; data. A model that's great on legal text can be useless on code.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Retrieval — vector search fumbles exact strings
&lt;/h2&gt;

&lt;p&gt;You take the question's vector and grab the closest chunks. This is the "vector search" everyone talks about. On its own, it's got a blind spot.&lt;/p&gt;

&lt;p&gt;Vector search matches &lt;strong&gt;meaning&lt;/strong&gt;, not exact words. Which is great — until someone searches for an error code. A product name. &lt;code&gt;SKU-4417&lt;/code&gt;. Those barely &lt;em&gt;have&lt;/em&gt; a meaning to embed. They're just exact strings, and nothing is "close in meaning" to a serial number.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; &lt;strong&gt;hybrid search&lt;/strong&gt;. Run keyword search &lt;em&gt;and&lt;/em&gt; vector search, then merge the results. Old-school keyword matching catches the exact strings embeddings miss.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Ranking — the best chunk was #8, and you dropped it
&lt;/h2&gt;

&lt;p&gt;Now you've got a pile of candidates — say the top twenty. But you can't paste twenty chunks into the prompt, so you keep the top five and drop the rest.&lt;/p&gt;

&lt;p&gt;Here's the question nobody asks: what if the &lt;strong&gt;best&lt;/strong&gt; chunk was ranked number eight? Then you just threw it away. Your answer was in the pile, and you cut it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; a &lt;strong&gt;re-ranker&lt;/strong&gt; — a second, smarter model that reads the question and each chunk &lt;em&gt;together&lt;/em&gt; and re-scores them. Cheap vector search casts a wide net; the re-ranker picks the real winners out of it. Now your top five are actually the top five.&lt;/p&gt;

&lt;h3&gt;
  
  
  And order matters too — "lost in the middle"
&lt;/h3&gt;

&lt;p&gt;Even the &lt;em&gt;order&lt;/em&gt; you paste chunks in changes the answer. Models pay the most attention to the &lt;strong&gt;start&lt;/strong&gt; and the &lt;strong&gt;end&lt;/strong&gt; of the context. Put your most important chunk in the middle, and the model can skim right past it.&lt;/p&gt;

&lt;p&gt;This isn't a hunch — it's measured. The same model gets &lt;em&gt;more&lt;/em&gt; accurate just by moving the right chunk to the edges of the context (Liu et al., &lt;em&gt;&lt;a href="https://arxiv.org/abs/2307.03172" rel="noopener noreferrer"&gt;Lost in the Middle&lt;/a&gt;&lt;/em&gt;, 2023). So put your best chunk first, or last. Never buried in the middle.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. The prompt — where people ruin it two ways
&lt;/h2&gt;

&lt;p&gt;Right chunks, right order. This is your last chance to ruin it, and people do it two ways:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;They dump in everything.&lt;/strong&gt; Ten thousand tokens of maybe-relevant text, hoping the model sorts it out. More context isn't more accuracy — it's more noise to get lost in.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;They never tell the model what to do when the answer isn't there.&lt;/strong&gt; So it does the one thing you don't want: it makes something up.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Fix&lt;/strong&gt; is basically one line in your prompt:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Answer only from the context below. If it's not there, say you don't know."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That single instruction turns a confident liar into a system you can trust. A RAG bot that admits &lt;em&gt;"I don't know"&lt;/em&gt; beats ten that guess.&lt;/p&gt;

&lt;h2&gt;
  
  
  Put the blame where it belongs
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Bad chunks&lt;/li&gt;
&lt;li&gt;Wrong embedder&lt;/li&gt;
&lt;li&gt;Vector-only search&lt;/li&gt;
&lt;li&gt;No re-ranker&lt;/li&gt;
&lt;li&gt;A sloppy prompt&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Five failure points — and notice the model was the &lt;em&gt;last&lt;/em&gt; one. Usually the innocent one.&lt;/p&gt;

&lt;p&gt;So next time your RAG bot lies to you, don't touch the model first. Walk the five steps backward — prompt, ranking, retrieval, embedding, chunks. The bug is almost always &lt;strong&gt;upstream&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's the worst answer your RAG bot has ever given you?&lt;/strong&gt; Drop it in the comments — I actually want to read those.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I make Vlad's Stack &lt;a href="https://www.youtube.com/channel/UCUO8Uo5LsEy1b9eRkrH6JNg" rel="noopener noreferrer"&gt;https://www.youtube.com/channel/UCUO8Uo5LsEy1b9eRkrH6JNg&lt;/a&gt; — how the AI tools you use every day actually work, for people who write code. Full video walkthrough of this one is above.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>rag</category>
      <category>ai</category>
      <category>llm</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>What a Linux container really is (it's not a VM)</title>
      <dc:creator>VLAD</dc:creator>
      <pubDate>Mon, 31 Aug 2026 22:10:38 +0000</pubDate>
      <link>https://dev.to/vladut02/what-a-linux-container-really-is-its-not-a-vm-53an</link>
      <guid>https://dev.to/vladut02/what-a-linux-container-really-is-its-not-a-vm-53an</guid>
      <description>&lt;p&gt;Right now, on your machine, a process is being &lt;strong&gt;lied to&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It thinks it's alone on the computer. It thinks it owns the whole filesystem. It thinks its process ID is &lt;code&gt;1&lt;/code&gt;. None of that is true. It's running right next to everything else, on the same kernel — and the kernel is lying to its face.&lt;/p&gt;

&lt;p&gt;That lie has a name. We call it a &lt;strong&gt;container&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Prefer to watch? Full 6-minute walkthrough with the animation:&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/Bm_K4SchaKY" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  There is no "container" in the kernel
&lt;/h2&gt;

&lt;p&gt;Here's the first thing nobody tells you. Open the Linux kernel source, search for a thing called a "container." You'll find &lt;strong&gt;zero&lt;/strong&gt;. There is no container object. The kernel has no idea what the word means.&lt;/p&gt;

&lt;p&gt;So what are you actually running? A normal process. The same &lt;em&gt;kind&lt;/em&gt; as your text editor. Just with a few settings flipped. Docker doesn't build a magic box around your app — it starts a plain process and flips those settings for you.&lt;/p&gt;

&lt;p&gt;There are two settings that do all the work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The first controls what the process can &lt;strong&gt;see&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;The second controls what it can &lt;strong&gt;use&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's the whole magic. See, and use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trick 1 — namespaces (what a process can see)
&lt;/h2&gt;

&lt;p&gt;Normally every process on the machine shares one view of the world: the same list of process IDs, the same filesystem, the same network. A &lt;strong&gt;namespace&lt;/strong&gt; hands one process its own private copy of that view.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Put it in a &lt;strong&gt;PID namespace&lt;/strong&gt;, and it sees itself as process &lt;code&gt;1&lt;/code&gt; — like the machine just booted for it.&lt;/li&gt;
&lt;li&gt;Put it in a &lt;strong&gt;mount namespace&lt;/strong&gt;, it gets its own filesystem root.&lt;/li&gt;
&lt;li&gt;Give it a &lt;strong&gt;network namespace&lt;/strong&gt;, and it has its own network interface and its own IP.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So on the real host, your process might be number &lt;code&gt;842&lt;/code&gt;, sitting in a list of hundreds. Inside its namespace, it looks down and sees... &lt;code&gt;1&lt;/code&gt;. Same machine, same kernel. The process just can't see past the wall the kernel drew around it.&lt;/p&gt;

&lt;h3&gt;
  
  
  root inside, nobody outside
&lt;/h3&gt;

&lt;p&gt;Here's the detail that trips everyone up: there's a &lt;strong&gt;user namespace&lt;/strong&gt; too.&lt;/p&gt;

&lt;p&gt;Inside the container, your process can be &lt;code&gt;root&lt;/code&gt; — user zero, full power. Outside, on the host, that same process is a boring, unprivileged user.&lt;/p&gt;

&lt;p&gt;Root on the inside. Nobody on the outside. Same process. That's namespaces doing their job — and it's the foundation of rootless containers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trick 2 — cgroups (what a process can use)
&lt;/h2&gt;

&lt;p&gt;Namespaces decide what you &lt;strong&gt;see&lt;/strong&gt;. The second trick decides what you &lt;strong&gt;get&lt;/strong&gt;. It's called a &lt;strong&gt;cgroup&lt;/strong&gt; — a control group.&lt;/p&gt;

&lt;p&gt;A cgroup sets limits. This process gets two CPU cores, not all of them. Half a gig of memory, and not a byte more. Cross the memory limit? The kernel kills it on the spot (that's your OOM kill).&lt;/p&gt;

&lt;p&gt;That's how one container can't starve every other one on the box. No limits, and one noisy app eats the whole machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  The whole idea in one line
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Namespaces are what you see. Cgroups are what you get.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A container is just a normal process wrapped in those two things. Nothing else is in the box — because there is no box.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why it's not a virtual machine
&lt;/h2&gt;

&lt;p&gt;This is the part people get wrong the most.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;virtual machine&lt;/strong&gt; boots its &lt;em&gt;own&lt;/em&gt; kernel, on fake hardware that a hypervisor pretends is real. A whole operating system, from scratch, every time.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;container&lt;/strong&gt; has no kernel of its own. It borrows the host's kernel — the one that's already running.&lt;/p&gt;

&lt;p&gt;That one difference explains everything:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Speed.&lt;/strong&gt; No kernel to boot means a container starts in milliseconds, where a VM takes seconds.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Isolation.&lt;/strong&gt; Sharing a kernel cuts both ways. One kernel bug can let a process break &lt;em&gt;out&lt;/em&gt; of its container and onto the host. A VM doesn't share that wall.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So you're trading isolation for speed. That's the deal — and it's a fair one, as long as you know you're making it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build one by hand — no Docker
&lt;/h2&gt;

&lt;p&gt;Don't believe there's no magic? Build a "container" yourself with two commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# a "container" with no Docker at all&lt;/span&gt;
unshare &lt;span class="nt"&gt;--pid&lt;/span&gt; &lt;span class="nt"&gt;--mount&lt;/span&gt; &lt;span class="nt"&gt;--net&lt;/span&gt; &lt;span class="nt"&gt;--uts&lt;/span&gt; &lt;span class="nt"&gt;--fork&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nb"&gt;chroot&lt;/span&gt; ./rootfs &lt;span class="se"&gt;\&lt;/span&gt;
  /bin/sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;unshare&lt;/code&gt; flips on the namespaces.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;chroot&lt;/code&gt; swaps the root filesystem.&lt;/li&gt;
&lt;li&gt;And you're dropped into a shell that thinks it's alone on a fresh machine.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No daemon, no image — just kernel features.&lt;/p&gt;

&lt;h2&gt;
  
  
  So what does &lt;code&gt;docker run&lt;/code&gt; actually do?
&lt;/h2&gt;

&lt;p&gt;Four steps, in pseudo-code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;unpackImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;        &lt;span class="c1"&gt;// files become the new root&lt;/span&gt;
&lt;span class="nf"&gt;applyNamespaces&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// its own PID, mounts, network&lt;/span&gt;
&lt;span class="nf"&gt;applyCgroups&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="c1"&gt;// capped CPU and memory&lt;/span&gt;
&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;             &lt;span class="c1"&gt;// now it's just... a process&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unpack the files. Wrap it in namespaces. Cap it with cgroups. Run it. That's the whole thing Docker does that felt like magic.&lt;/p&gt;

&lt;p&gt;And the image everyone ships around? It's just a stack of files in a tarball, plus a little metadata saying which program to start. No operating system inside. No kernel inside. Just files.&lt;/p&gt;

&lt;h2&gt;
  
  
  One number to leave you with
&lt;/h2&gt;

&lt;p&gt;The Linux manual page &lt;code&gt;namespaces(7)&lt;/code&gt; lists &lt;strong&gt;eight&lt;/strong&gt; separate kinds of namespace — eight different views the kernel can hand a single process: mount, PID, network, IPC, UTS, user, cgroup, and time. That's the whole toolbox a container is built from.&lt;/p&gt;

&lt;p&gt;So next time someone draws a container as a little box floating above the machine — remember there's no box. There's your kernel, showing one process a smaller version of the truth. Namespaces for what it sees, cgroups for what it gets, one shared kernel underneath.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What did you think a container was, before this?&lt;/strong&gt; Tell me in the comments.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I make Vlad's Stack &lt;a href="https://www.youtube.com/channel/UCUO8Uo5LsEy1b9eRkrH6JNg" rel="noopener noreferrer"&gt;https://www.youtube.com/channel/UCUO8Uo5LsEy1b9eRkrH6JNg&lt;/a&gt; — how the tools you use every day actually work, for people who write code. Full video walkthrough of this one is above.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>linux</category>
      <category>docker</category>
      <category>containers</category>
      <category>devops</category>
    </item>
    <item>
      <title>Why more context makes your AI answers worse</title>
      <dc:creator>VLAD</dc:creator>
      <pubDate>Mon, 31 Aug 2026 22:09:07 +0000</pubDate>
      <link>https://dev.to/vladut02/why-more-context-makes-your-ai-answers-worse-2mnd</link>
      <guid>https://dev.to/vladut02/why-more-context-makes-your-ai-answers-worse-2mnd</guid>
      <description>&lt;p&gt;Your model says it has a one-million-token context window. Its real working memory is a lot smaller than that.&lt;/p&gt;

&lt;p&gt;On long-context benchmarks, models start failing well below the number printed on the box. And here's the part nobody warns you about: past a certain point, adding more context makes your answers &lt;strong&gt;worse&lt;/strong&gt;. Not better. Worse.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Prefer to watch? Full walkthrough with the attention-cost animation:&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/JtZIjmPYwsU" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  A context window is not memory
&lt;/h2&gt;

&lt;p&gt;Let's kill one idea first. A context window is &lt;strong&gt;not&lt;/strong&gt; memory.&lt;/p&gt;

&lt;p&gt;The model doesn't remember your last message — it's stateless. It keeps nothing between calls. Every single call, you send the whole conversation again. All of it.&lt;/p&gt;

&lt;p&gt;Look at what you actually ship over the wire:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Turn one:&lt;/strong&gt; a system prompt and one question. Small.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Turn ten:&lt;/strong&gt; the same system prompt, plus nine questions, plus nine answers, plus the new one.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The window is just how much text fits in that box before the API says no. And everything in the box gets processed. Every time.&lt;/p&gt;

&lt;h2&gt;
  
  
  What attention actually does
&lt;/h2&gt;

&lt;p&gt;Inside the model, attention has a job that's simple to say: every token looks at every other token and decides how much it cares about each one.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;10 tokens → 100 pairs&lt;/li&gt;
&lt;li&gt;1,000 tokens → &lt;strong&gt;1,000,000&lt;/strong&gt; pairs&lt;/li&gt;
&lt;li&gt;100,000 tokens → &lt;strong&gt;10,000,000,000&lt;/strong&gt; pairs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ten times the context isn't ten times the work. It's a hundred times the work. (That's just arithmetic: cost grows with the square of the input.)&lt;/p&gt;

&lt;p&gt;That's also why the first reply in a long chat feels slow and the rest feel fast — the model caches the keys and values it already computed. But the cache saves you &lt;em&gt;compute&lt;/em&gt;. It doesn't save your &lt;em&gt;answer quality&lt;/em&gt;. That's a separate problem, and it's the one you actually feel.&lt;/p&gt;

&lt;p&gt;Here are the three reasons big context hurts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reason 1 — attention is a budget
&lt;/h2&gt;

&lt;p&gt;For every token, the attention weights add up to one. Always one — that's what softmax does.&lt;/p&gt;

&lt;p&gt;So when you paste in fifty more documents, you don't hand the model more focus. You split the &lt;strong&gt;same&lt;/strong&gt; focus into more pieces.&lt;/p&gt;

&lt;p&gt;Think about search on your laptop. One folder, ten files — you find it in a second. Same search, ten thousand files, same query, and the right file is now somewhere on page four. The model has that exact problem. Except it never shows you page four. It just answers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reason 2 — lost in the middle
&lt;/h2&gt;

&lt;p&gt;Researchers took one correct answer and moved it around inside a long prompt. Same prompt, same question, different position. Then they measured accuracy.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Near the beginning: good.&lt;/li&gt;
&lt;li&gt;Near the end: good.&lt;/li&gt;
&lt;li&gt;In the middle: &lt;strong&gt;much worse&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(Liu et al., &lt;em&gt;&lt;a href="https://arxiv.org/abs/2307.03172" rel="noopener noreferrer"&gt;Lost in the Middle&lt;/a&gt;&lt;/em&gt;.) So your most important paragraph, sitting at 60% of a long prompt, is in the worst possible spot in the whole file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reason 3 — distractors in your own data
&lt;/h2&gt;

&lt;p&gt;This is the one I hit most in real code. The famous needle-in-a-haystack test is easy, because the needle looks nothing like the hay. Your production data is not like that.&lt;/p&gt;

&lt;p&gt;You've got version two of the doc. Version three. An old changelog. And a chat thread that contradicts all three. Four chunks that all look right. One of them is right — and the model can't tell which.&lt;/p&gt;

&lt;p&gt;It gets worse as the input grows: accuracy drops steadily with input length, even on tasks the same model nails at short length. So the number on the box tells you what &lt;em&gt;fits&lt;/em&gt;. It doesn't tell you what &lt;em&gt;works&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four things you can actually change
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Send less, but better.&lt;/strong&gt; If you're doing retrieval, stop pushing the top fifty chunks into the prompt. Search wide, re-sort the results, then send the top three to five. Fewer clean chunks beat more chunks, every time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Use the edges.&lt;/strong&gt; Instruction at the top. Data in the middle. Then repeat the actual question at the &lt;strong&gt;bottom&lt;/strong&gt;, right before the model answers. That last line costs you nothing, and it works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Stop reusing one giant thread.&lt;/strong&gt; Split the job into steps. Each step gets a fresh, small context with only what it needs. Between steps, pass a short summary — not the full transcript. That's most of what agent frameworks are doing for you under the hood.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Measure it on your own data.&lt;/strong&gt; Take twenty real questions from your app. Run them with 2,000 tokens of context, then run the same twenty with 20,000. Compare the answers side by side. You'll find your own limit — and it'll be lower than the spec sheet.&lt;/p&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;The context window is a hard limit on what fits. It was never a promise about what the model can use. Treat it like bandwidth you're paying for, and send the smallest thing that answers the question.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's the biggest prompt you've ever shipped to production? And when you cut it down, did it get better or worse?&lt;/strong&gt; Tell me below.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I make Vlad's Stack &lt;a href="https://www.youtube.com/channel/UCUO8Uo5LsEy1b9eRkrH6JNg" rel="noopener noreferrer"&gt;https://www.youtube.com/channel/UCUO8Uo5LsEy1b9eRkrH6JNg&lt;/a&gt; — how the AI tools you use every day actually work, for people who write code. Full video walkthrough is above.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>promptengineering</category>
      <category>machinelearning</category>
    </item>
    <item>
      <title>Why more context makes your AI answers worse</title>
      <dc:creator>VLAD</dc:creator>
      <pubDate>Mon, 31 Aug 2026 21:42:30 +0000</pubDate>
      <link>https://dev.to/vladut02/why-more-context-makes-your-ai-answers-worse-jb1</link>
      <guid>https://dev.to/vladut02/why-more-context-makes-your-ai-answers-worse-jb1</guid>
      <description>&lt;p&gt;Your model says it has a one-million-token context window. Its real working memory is a lot smaller than that.&lt;/p&gt;

&lt;p&gt;On long-context benchmarks, models start failing well below the number printed on the box. And here's the part nobody warns you about: past a certain point, adding more context makes your answers &lt;strong&gt;worse&lt;/strong&gt;. Not better. Worse.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Prefer to watch? Full walkthrough with the attention-cost animation:&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/hH7Ko-x1Bn8" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  A context window is not memory
&lt;/h2&gt;

&lt;p&gt;Let's kill one idea first. A context window is &lt;strong&gt;not&lt;/strong&gt; memory.&lt;/p&gt;

&lt;p&gt;The model doesn't remember your last message — it's stateless. It keeps nothing between calls. Every single call, you send the whole conversation again. All of it.&lt;/p&gt;

&lt;p&gt;Look at what you actually ship over the wire:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Turn one:&lt;/strong&gt; a system prompt and one question. Small.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Turn ten:&lt;/strong&gt; the same system prompt, plus nine questions, plus nine answers, plus the new one.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The window is just how much text fits in that box before the API says no. And everything in the box gets processed. Every time.&lt;/p&gt;

&lt;h2&gt;
  
  
  What attention actually does
&lt;/h2&gt;

&lt;p&gt;Inside the model, attention has a job that's simple to say: every token looks at every other token and decides how much it cares about each one.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;10 tokens → 100 pairs&lt;/li&gt;
&lt;li&gt;1,000 tokens → &lt;strong&gt;1,000,000&lt;/strong&gt; pairs&lt;/li&gt;
&lt;li&gt;100,000 tokens → &lt;strong&gt;10,000,000,000&lt;/strong&gt; pairs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ten times the context isn't ten times the work. It's a hundred times the work. (That's just arithmetic: cost grows with the square of the input.)&lt;/p&gt;

&lt;p&gt;That's also why the first reply in a long chat feels slow and the rest feel fast — the model caches the keys and values it already computed. But the cache saves you &lt;em&gt;compute&lt;/em&gt;. It doesn't save your &lt;em&gt;answer quality&lt;/em&gt;. That's a separate problem, and it's the one you actually feel.&lt;/p&gt;

&lt;p&gt;Here are the three reasons big context hurts.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reason 1 — attention is a budget
&lt;/h2&gt;

&lt;p&gt;For every token, the attention weights add up to one. Always one — that's what softmax does.&lt;/p&gt;

&lt;p&gt;So when you paste in fifty more documents, you don't hand the model more focus. You split the &lt;strong&gt;same&lt;/strong&gt; focus into more pieces.&lt;/p&gt;

&lt;p&gt;Think about search on your laptop. One folder, ten files — you find it in a second. Same search, ten thousand files, same query, and the right file is now somewhere on page four. The model has that exact problem. Except it never shows you page four. It just answers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reason 2 — lost in the middle
&lt;/h2&gt;

&lt;p&gt;Researchers took one correct answer and moved it around inside a long prompt. Same prompt, same question, different position. Then they measured accuracy.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Near the beginning: good.&lt;/li&gt;
&lt;li&gt;Near the end: good.&lt;/li&gt;
&lt;li&gt;In the middle: &lt;strong&gt;much worse&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;(Liu et al., &lt;em&gt;&lt;a href="https://arxiv.org/abs/2307.03172" rel="noopener noreferrer"&gt;Lost in the Middle&lt;/a&gt;&lt;/em&gt;.) So your most important paragraph, sitting at 60% of a long prompt, is in the worst possible spot in the whole file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reason 3 — distractors in your own data
&lt;/h2&gt;

&lt;p&gt;This is the one I hit most in real code. The famous needle-in-a-haystack test is easy, because the needle looks nothing like the hay. Your production data is not like that.&lt;/p&gt;

&lt;p&gt;You've got version two of the doc. Version three. An old changelog. And a chat thread that contradicts all three. Four chunks that all look right. One of them is right — and the model can't tell which.&lt;/p&gt;

&lt;p&gt;It gets worse as the input grows: accuracy drops steadily with input length, even on tasks the same model nails at short length. So the number on the box tells you what &lt;em&gt;fits&lt;/em&gt;. It doesn't tell you what &lt;em&gt;works&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Four things you can actually change
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Send less, but better.&lt;/strong&gt; If you're doing retrieval, stop pushing the top fifty chunks into the prompt. Search wide, re-sort the results, then send the top three to five. Fewer clean chunks beat more chunks, every time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Use the edges.&lt;/strong&gt; Instruction at the top. Data in the middle. Then repeat the actual question at the &lt;strong&gt;bottom&lt;/strong&gt;, right before the model answers. That last line costs you nothing, and it works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Stop reusing one giant thread.&lt;/strong&gt; Split the job into steps. Each step gets a fresh, small context with only what it needs. Between steps, pass a short summary — not the full transcript. That's most of what agent frameworks are doing for you under the hood.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Measure it on your own data.&lt;/strong&gt; Take twenty real questions from your app. Run them with 2,000 tokens of context, then run the same twenty with 20,000. Compare the answers side by side. You'll find your own limit — and it'll be lower than the spec sheet.&lt;/p&gt;

&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;The context window is a hard limit on what fits. It was never a promise about what the model can use. Treat it like bandwidth you're paying for, and send the smallest thing that answers the question.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What's the biggest prompt you've ever shipped to production? And when you cut it down, did it get better or worse?&lt;/strong&gt; Tell me below.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>promptengineering</category>
      <category>machinelearning</category>
    </item>
  </channel>
</rss>
