{"slug": "we-asked-if-vibecoding-had-fried-people-s-brains", "title": "We asked if vibecoding had fried people's brains", "summary": "A quiz created by Codeset founder Nuno Saavedra to test whether 'vibecoding' — relying on AI to write code — had eroded programmers' fundamentals drew 5,783 completions and found most respondents scored in the middle range, with a mean score of 4.97 out of 10 and a median of 5. Only 12% scored in the 'Fried' range (0–2), while 31% landed in 'Mush' (3–4), 32% in 'Soft' (5–6), 19% in 'Toasted' (7–8), and 5% in 'Intact' (9–10), with 86 people achieving a perfect score. The quiz, hosted at codeset.ai/fried, revealed that respondents missed 54% of ordinary language questions and 42% of model-framed items, with common errors centering on Python's mutable default arguments and setdefault behavior.", "body_md": "# We asked if vibecoding had fried people’s brains\n\nNuno Saavedra7 min read\n\nWe wanted a meme. The bit was that AI had written enough of everyone’s code that the fundamentals were gone. Has vibecoding fried your brain?\n\nSo we made a quiz. We gave it funny characters so it looked like a personality test, and a roast at the end so each person walked away with something of their own to share. We put it at [codeset.ai/fried](https://codeset.ai/fried) and waited for the joke to land.\n\nIt took off. A LinkedIn post did the work: 120k+ views, and **5,783 people finished the quiz.**\n\nWhat started as a joke had a result in it, but not the result the title promised.\n\n## Most people were not fried\n\nThe mean score is **4.97** out of 10. The median is **5**. Eighty-six people got a perfect score.\n\nIf the meme were true, this chart would pile up on the left. It doesn’t. It peaks at 4 and 5, which is somewhat of a relief... at least for now.\n\n| Verdict | Score | People | Share | \n|---|---|---|---|\n| Fried | 0–2 | 718 | 12% | \n| Mush | 3–4 | 1,789 | 31% | \n| Soft | 5–6 | 1,871 | 32% | \n| Toasted | 7–8 | 1,097 | 19% | \n| Intact | 9–10 | 308 | 5% | \n\nFried and Intact are the characters you share as a bit, however most people got Mush or Soft.\n\nEach quiz mixed ordinary language questions with a few items framed as something a model wrote. People missed both: 54% of the ordinary items, 42% of the model ones.\n\n## Same quiz, different shapes\n\nPython was the default choice on the quiz, so most people took it: 3,767 finishes, against a few hundred each for JavaScript, Java, and C, and 160 for Rust.\n\nHowever, the interesting part is not the counts but where the scores sat.\n\nJavaScript, Java, and C look like the same quiz: a bump around 5, means within a tenth of a point of each other. Python, the crowd that never changed the picker, sits a little to the left and Rust sits clearly to the right.\n\nThat does not mean Python programmers are worse, or that Rust programmers are better, even though the Rust community will probably still say they are. Python is who showed up. Rust is who chose Rust. The comparison that survives is the middle three: different languages, same shape.\n\nWhat follows is mostly Python, because that is the sample large enough to look at individual questions. JavaScript shows up when the same idea appears there too.\n\n## The same few ideas, missed over and over\n\nThe title said fried brains. The questions said something more specific.\n\n### They still picture a fresh list\n\n``` python\ndef add(item, bag=[]):\n    bag.append(item)\n    return bag\n```\n\nTwo items in the quiz used this trap. One asked what `add(1)` then `add(2)` return. The other showed the same function with two prints, said the model claimed it prints `[1]` then `[2]`, and asked what it actually prints.\n\nThe answer to both is `[1]` then `[1, 2]`. The `[]` in `bag=[]` is not rebuilt on each call. Python creates that list once, when the function is defined, and every call appends to the same object.\n\nAbout six in ten missed the first question. About half missed the second. On the first, **583 of 676 wrong answers were `[1] then [2]`**: people assumed each call started with a fresh, empty list. They knew what `append` did; what they did not picture was the default argument as one actual list, created once, attached to the function, and quietly reused on every call.\n\nI (Nuno) would have been in that pile. I hit this exact bug in a university project: a list kept growing between calls I was sure were independent, and it took me far too long to realize the default argument was the culprit.\n\nA third item tested the same mental model from a different angle:\n\n```\nd = {}\na = d.setdefault(\"items\", [])\nb = d.setdefault(\"items\", [])\nb.append(\"x\")\nprint(a, b)\n```\n\n`setdefault` returns the value already stored under the key, so `a` and `b` point to the same list and the output is `['x'] ['x']`. About 56% missed it. The most common wrong answer was `[] ['x']`: the same instinct as before, treating each expression as if it produced a fresh list rather than another reference to an existing one.\n\n### They don't know what sort does\n\n```\nnums = [3, 1, 2]\nprint(nums.sort())\n```\n\nAbout 59% missed it. **495 people answered `[1, 2, 3]`**. The trap is that `sort()` does two separate things: it **changes `nums` in place**, and it **returns `None`**. So after the call, `nums` is `[1, 2, 3]`, but `print(nums.sort())` prints `None`.\n\nJavaScript has a related trap:\n\n```\nconsole.log([10, 2, 1].sort());\n```\n\nAbout 68% missed it. Most answered `[1, 2, 10]`. Here `sort()` does return the array, but JavaScript sorts elements **as strings by default**, not numerically. It compares `\"1\"`, `\"10\"`, and `\"2\"`, so the result is:\n\n```\n[1, 10, 2]\n```\n\nBoth questions expose the same habit: seeing a familiar method name and jumping straight to the result we expect, without checking exactly what the method returns or how it performs the operation.\n\n### Assignment makes a name local\n\nThe hardest identifiable Python item is not a meme.\n\n``` python\ndef f():\n    x = 1\n    def g():\n        print(x)\n        x = 2\n    g()\nf()\n```\n\n*What happens when `f()` runs?*\n\nAbout **83%** missed it. **292 of 395 misses said “Prints 1.”** That answer assumes `g()` reads the `x` from `f()` and only later creates its own.\n\nBecause `g()` assigns to `x` anywhere in its body, Python treats `x` as a **local variable throughout all of `g()`**. So `print(x)` tries to read that local variable before it has been assigned.\n\nThe result is an **`UnboundLocalError`**. The later `x = 2` changes what the earlier `print(x)` means.\n\n### A generator is not a list\n\n``` python\ndef split_positive(values):\n    it = (x for x in values if x > 0)\n    has_any = any(it)\n    return has_any, list(it)\n```\n\n*Which input makes this misbehave, if the returned list should contain every positive?*\n\nAbout **68%** missed it. The most popular wrong answer was `[0, 0]`. The input that actually breaks the function is **`[0, 4]`**: `any()` advances the iterator until it finds the truthy `4`, so by the time `list(it)` runs, the iterator is already exhausted.\n\nThat was the pattern across the iterator questions. With `all()` on a generator, about half assumed every value would still be checked even after a false one appeared. With `zip(it, it)` on an odd-length iterator, many expected the leftover element to remain untouched. In each case, the missing mental model was the same: **iterators have state**. If you consume them, they move; if you walk them to the end, they are gone.\n\n### `d.get` is not `d[key]`\n\n``` python\nclass Buckets(dict):\n    def __missing__(self, key):\n        value = []\n        self[key] = value\n        return value\n\nd = Buckets()\n\nd.get(\"items\", []).append(\"x\")\nprint(d)  # {}\n\nd[\"items\"].append(\"x\")\nprint(d)  # {'items': ['x']}\n```\n\nA dict subclass can define `__missing__` to create a value on demand, but that hook is only used by `d[key]`. `d.get(key, [])` skips it entirely — about **64%** thought it would still call the hook. If you want the bucket to be created before you mutate it, you need `d[key]`, not `get`.\n\nThe same protocol detail explains another trap: if `__missing__` does `return self[key]`, it just triggers itself again and again, ending in a **` RecursionError`**, not a `KeyError`.\n\nThe gap here is not really about defaults. Most people already understand that `settings.get(\"retries\", 1 / 0)` can still raise even when `\"retries\"` exists, because the default expression is evaluated before `get` is called.\n\nThe real gap is the **lookup protocol**. `get` looks like the safer form of dictionary access, but precisely because it is a different lookup path, it bypasses `__missing__`.\n\n## So did it fry anyone?\n\nNot in the way the joke wanted. The average person who clicked the meme got five out of ten. They catch a lying comment and an empty `except`, but they still picture a fresh default list, a sorted array coming back from `sort`, and a generator you can walk twice.\n\nThat is less “brains fried” and more “these objects do not do what the English suggests”.\n\nThe quiz is still up. [codeset.ai/fried](https://codeset.ai/fried)", "url": "https://wpnews.pro/news/we-asked-if-vibecoding-had-fried-people-s-brains", "canonical_source": "https://codeset.ai/blog/has-vibecoding-fried-your-brain", "published_at": "2026-09-09 11:22:35+00:00", "updated_at": "2026-09-09 11:41:03.166112+00:00", "lang": "en", "topics": ["artificial-intelligence", "developer-tools"], "entities": ["Codeset", "Nuno Saavedra"], "alternates": {"html": "https://wpnews.pro/news/we-asked-if-vibecoding-had-fried-people-s-brains", "markdown": "https://wpnews.pro/news/we-asked-if-vibecoding-had-fried-people-s-brains.md", "text": "https://wpnews.pro/news/we-asked-if-vibecoding-had-fried-people-s-brains.txt", "jsonld": "https://wpnews.pro/news/we-asked-if-vibecoding-had-fried-people-s-brains.jsonld"}}