{"slug": "working-with-ai-a-concrete-example", "title": "Working With AI: A Concrete Example", "summary": "Carson Gross, creator of hyperscript, describes how AI helped him diagnose a parsing regression in the 0.9.91 release but failed to provide a correct fix, illustrating the Sorcerer's Apprentice problem where developers become overly reliant on AI and lose the ability to solve problems independently.", "body_md": "I am ambivalent towards AI. It has become a very powerful tool for development but also comes with many dangers, both for us individually (e.g. the slow dulling of our intellects) as well as collectively (e.g. environmental concerns, increasingly expensive personal computing, etc.)\n\nIn [“Code is Cheap(er)”](https://htmx.org/essays/code-is-cheap/), I warn about The Sorcerer’s Apprentice problem, where a developer becomes reliant on AI and is unable to\nunderstand and properly address issues that come up in the systems they are building.\n\nIn this article I want to go through a specific interaction that I had with AI while maintaining\n[hyperscript](https://hyperscript.org) to demonstrate the dangers of AI in general and The Sorcerer’s Apprentice\nproblem in particular.\n\nFor some background, hyperscript is an alternative interpreted scripting language for the web. It is, ironically,\nwritten [entirely in JavaScript](https://github.com/bigskysoftware/_hyperscript/blob/master/src/_hyperscript.js).\n\nIt is a strange piece of software: I intentionally broke many of the rules of parsing when writing it as an experiment to see how things would work out.\n\nSome examples:\n\nIt is not an approach I would recommend for most programming languages, but it has worked out pretty well for this project.\n\nYet another demonstration that there are indeed multiple ways to skin the cat in software.\n\nOur story begins when a user reported a regression when upgrading to the 0.9.91 release. The following expression no longer parsed properly:\n\n```\nfetch `{% url 'trade:get_symbol_data' %}?symbol=${symbol}` as JSON\n```\n\nIn particular, the `as JSON`\n\nwas binding too tightly and trying to convert the string literal into JSON *before* it\nwas handed to `fetch`\n\ninstead of doing what the user expected (and what it did previously) namely *fetching* the\ngiven url with the results treated as JSON.\n\nThis sort of binding conflict is a classic problem in parsing.\n\nBecause hyperscript is an [xTalk](https://en.wikipedia.org/wiki/HyperTalk#Descendants_of_HyperTalk) style language and\ninherits the ambiguities of English, this problem is all the worse.\n\nThe first thing to do was to investigate why this regression occurred. This is an area where I am typically going to lean on AI to help.\n\nI use Claude, and it did an admirable job finding the root cause: in 0.9.91 I had been overly\naggressive in refactoring the [go](https://hyperscript.org/commands/go/) command to reuse logic in the [fetch](https://hyperscript.org/commands/fetch/) command.\n\nI had extracted a common method for both of these commands to use, `parseURLOrExpression()`\n\n, but, in doing so, I\naccidentally expanded the grammar after the `fetch`\n\ncommand to include `expression`\n\n.\n\nNow, the `as`\n\nkeyword has a meaning in expressions: it is a [conversion expression](https://hyperscript.org/expressions/as/),\nallowing you to convert between types:\n\n```\n  set x to \"42\" as Int\n```\n\nBut the `as`\n\nkeyword is *also* a modifier of the `fetch`\n\ncommand, telling it how to convert the response:\n\n```\n  fetch https://hyperscript.org as Text\n```\n\n(Perhaps this fact makes you throw up a little bit in your mouth. Good.)\n\nThe crux was that, inadvertently in the refactor, I had made the parser parse an expression after a `fetch`\n\nkeyword\nwhich was now consuming the `as`\n\nkeyword as an expression, rather than allowing it to be a modifier.\n\nWith the help of Claude I was able to figure this out in a few minutes, dramatically faster than if I had had to do so on my own.\n\nSo AI was very helpful in finding the cause of the problem. In *fixing* the problem, however, it was much weaker.\n\nI will admit I was being lazy and asking AI for a solution, so complaining about those solutions feels a bit, well, lazy, but I think the string of events is informative, so let’s go through what happened.\n\nThe first suggestion that was given was to parse what is called a “string-like” leaf first, then fall back to a full expression:\n\n```\nreturn this.parseElement(\"stringLike\") || this.requireElement(\"expression\");\n```\n\nThis fix would have solved the immediate problem presented by the user. However, it was very specific to the reported bug and wouldn’t have fixed the general case, such as if someone uses a variable as the target of a fetch:\n\n```\n  fetch $url as JSON\n```\n\nI rejected this proposal because of this: too hacky and not general enough.\n\n(The hyperscript parser has plenty of organically supplied hacks in it, so this may have been the pot calling the kettle black.)\n\nThe second proposal was interesting: add a `noConversions`\n\nflag on the parser, set it around the URL parse, and have\n`AsExpression.parse`\n\nbail when it is set:\n\n```\n// AsExpression.parse()\nif (parser.noConversions) return;\n```\n\nThis will horrify many parser engineers because it makes the hyperscript parser\n[context-sensitive](https://dl.acm.org/doi/epdf/10.1145/362686.362695).\n\nGood.\n\nThe hyperscript parser was already context-sensitive.\n\nHowever, in looking at this fix and thinking for a second, I realized that we already had the hacky context-sensitive infrastructure we needed without introducing a new flag on the parser.\n\nIn the hyperscript parser we have a notion of [“follows”](https://github.com/bigskysoftware/_hyperscript/blob/ea9a6534d24cf5c7257adcaad75ee75b0c612d8e/src/core/tokenizer.js#L11), that is, tokens that are claimed by a “higher up” parse element as a follow token.\n\nThe hyperscript parser is (a somewhat strange) recursive descent parser, and this allows a parse element (usually a command) to “claim” a keyword, and expressions won’t match against them during parsing.\n\nAs an example, the [ when](https://hyperscript.org/features/when/) feature uses\n\n`or`\n\nas a separator\n\n```\n<div _=\"when $x or $y changes put it into me\"></div>\n```\n\n(I can hear many parser engineers closing this window in anger. Good.)\n\nIt turns out that this feature could be used to achieve what we wanted: rather than adding a new flag to the parser\nwe could push `as`\n\nas a follow, then parse the expression, then pop it as a follow.\n\nThis would prevent the `AsExpression`\n\nfrom parsing, while still allowing most general expressions such as variables to work.\n\nI pointed this out to Claude and, in a frisson of excitement, it told me that I was “absolutely right!” and set about using this technique to fix the bug.\n\nClaude added the correct code to the `parseURLOrExpression()`\n\nwhich fixed the issue generally without adding any additional\nparser infrastructure.\n\nGood to go.\n\nHowever, when I was reviewing the change, I realized that the new fix was overly broad: both `fetch`\n\nand `go`\n\nshared\nthis method, but only `fetch`\n\nused `as`\n\nto signal a modifier.\n\nThe existing fix prevented the perfectly valid use of `as`\n\nconversion expressions in `go`\n\ncommands as well.\n\nSo I implemented the final fix myself, in `FetchCommand#parse()`\n\n:\n\n``` js\n  parser.pushFollow(\"as\");\n  try {\n    var url = parser.parseURLOrExpression();\n  } finally {\n    parser.popFollow();\n  }\n  \n  if (parser.matchToken(\"as\")) {\n      ...\n```\n\nHere I narrowed the special case to only the `fetch`\n\ncommand, leaving `go`\n\nparsing unaffected.\n\nThis ended up being the final answer to the bug.\n\nAlong the way I had Claude generate some tests for the various cases.\n\nThere is a good existing test suite for hyperscript, and it did a good job of creating small, focused tests that showed the problem and that the fix was working properly.\n\nOK, so what is interesting about this fairly mundane bug fix story?\n\nI think it is interesting to see where AI did well, namely in investigation and test creation, and to contrast that with where it didn’t do so well: coming up with a clean solution.\n\nIf I had not been familiar with the hyperscript parser and its infrastructure this fix could have easily led to technical debt being accrued in the project: another hacky parsing corner case, another bit of state on the parser, etc.\n\nTechnical debt, I assert without evidence 1, grows exponentially, and, so, it is very\nimportant to minimize it in your projects.\n\nI think that this story shows how having a human in the loop, working with an agent and with a good understanding of the underlying infrastructure, can be much more effective in controlling complexity than an agent left to its own devices.\n\nNow, some people will look at the hyperscript code base and scoff at the notion that controlling complexity was ever a consideration at all. I am sympathetic to that view.\n\nHowever, in this example we can see in a concrete scenario how complexity was restrained, at least a bit, in fixing an admittedly embarrassing bug.\n\nThis is a situation where, rather than being a sorcerer’s apprentice blindly accepting the solutions AI proposed, I was acting as a sorcerer (I hope that’s not too arrogant to say!) demanding a correct solution that better fit the existing codebase.\n\nI understood the problem and saw the correct solution and was able to work with AI to achieve it and then verify the solution with the help of AI-generated tests.\n\nThis is in contrast, I hope a good contrast, with some forms of vibe coding, in which developers (or whatever) appear to pride themselves on not understanding what is actually going on.\n\nAnother thing occurred to me as I was going back over this experience.\n\nI am an older developer, having turned 50 this year. As developers get older the reality is that we tend to “lose our fastball”, at least to some extent.\n\nPractically, for me, this has meant two things:\n\nIt turns out that AI directly addresses both of these issues.\n\nWith respect to memory, while I can’t remember everything I used to be able to, I can *understand* things again very\nquickly with appropriate, er, prompting. AI is very good at helping me with this, and it lets me switch between open\nsource projects and work projects much more efficiently than if I didn’t have it.\n\nWith respect to the long hours, AI is able to grind in a way that, even as a young developer, I would have had a difficult time keeping up with. This means, for example, I can have a much more extensive test suite for my projects than I would have otherwise.\n\nLooking at the tests that Claude generated in this case, they are more extensive than what I probably could have mustered the energy to do myself.\n\nSo AI has addressed two fundamental (relative) weaknesses I have developed as an older developer.\n\nOn the other hand, I am very worried that it is also enabling a more general regression in my overall intelligence. This is something that occurs naturally as you age anyway. AI reliance may accelerate this process however and I have to say, looking back at this story, I’m a bit ashamed of how long I leaned on Claude before just doing the right thing darned myself.\n\nThis is an area I am still trying to navigate myself.\n\nI wanted to capture this series of interactions because I thought it captured some of the good and some of the bad of AI assistance in coding, that it demonstrated the value of a reasonably competent developer in the loop working with an AI agent, and shows the danger of blindly accepting the first solution that an AI agent suggests to a problem.\n\nI hope that it is useful to you as you develop your own thoughts and strategies around AI agents.\n\n–\n\nThis was revealed to me in a dream.", "url": "https://wpnews.pro/news/working-with-ai-a-concrete-example", "canonical_source": "https://htmx.org/essays/working-with-ai/", "published_at": "2026-06-29 00:00:00+00:00", "updated_at": "2026-06-29 13:58:13.453540+00:00", "lang": "en", "topics": ["ai-tools", "developer-tools", "large-language-models"], "entities": ["Claude", "hyperscript", "Carson Gross", "Big Sky Software", "JavaScript", "HyperTalk", "fetch", "go"], "alternates": {"html": "https://wpnews.pro/news/working-with-ai-a-concrete-example", "markdown": "https://wpnews.pro/news/working-with-ai-a-concrete-example.md", "text": "https://wpnews.pro/news/working-with-ai-a-concrete-example.txt", "jsonld": "https://wpnews.pro/news/working-with-ai-a-concrete-example.jsonld"}}