{"id":139,"date":"2025-09-09T21:34:13","date_gmt":"2025-09-09T19:34:13","guid":{"rendered":"https:\/\/stefanescu.lu\/?p=139"},"modified":"2025-09-10T17:38:53","modified_gmt":"2025-09-10T15:38:53","slug":"scratching-the-vibe-the-vibe","status":"publish","type":"post","link":"https:\/\/stefanescu.lu\/?p=139","title":{"rendered":"Scratching the Vibe &#8211; The Vibe"},"content":{"rendered":"\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"683\" src=\"https:\/\/stefanescu.lu\/wp-content\/uploads\/2025\/09\/no-silver-bullet-1-1024x683.jpg\" alt=\"\" class=\"wp-image-142\" srcset=\"https:\/\/stefanescu.lu\/wp-content\/uploads\/2025\/09\/no-silver-bullet-1-1024x683.jpg 1024w, https:\/\/stefanescu.lu\/wp-content\/uploads\/2025\/09\/no-silver-bullet-1-300x200.jpg 300w, https:\/\/stefanescu.lu\/wp-content\/uploads\/2025\/09\/no-silver-bullet-1-768x512.jpg 768w, https:\/\/stefanescu.lu\/wp-content\/uploads\/2025\/09\/no-silver-bullet-1-450x300.jpg 450w, https:\/\/stefanescu.lu\/wp-content\/uploads\/2025\/09\/no-silver-bullet-1.jpg 1536w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Where the code meets the real world<\/h2>\n\n\n\n<p>Building, packaging, getting a real application running on different platforms, on different machines: that\u2019s where your code meets the real world. As a programmer, back in the day, this is where most of my debugging hours went.<\/p>\n\n\n\n<p>At first, using code assistants to build&nbsp;<a href=\"https:\/\/github.com\/stefanesco\/obsidize\">obsidize<\/a>&nbsp;made it feel like this time would be different.<\/p>\n\n\n\n<p>I discovered Clojure in 2017. The company I worked for was going through an \u201cagile transformation\u201d and, to motivate everyone, handed out copies of&nbsp;<a href=\"https:\/\/www.techtarget.com\/whatis\/definition\/The-Phoenix-Project\"><em>The Phoenix Project<\/em><\/a>&nbsp;by Gene Kim. Clojure only gets a few lines in the book\u2014but it\u2019s the language that saves the company, and I took notice. I was already transitioning into coordination\/leadership roles, and \u201cgetting Clojure\u201d was my way to stay connected to the implementation side. I read the books, built toy apps, but I never shipped a complete, production-ready Clojure application.<\/p>\n\n\n\n<p>In my software developer days, I deployed Java applications to production\u2014almost exclusively back end, single platform, with an Ops team close by.<\/p>\n\n\n\n<p>This time, the Ops team would be Claude, ChatGPT, and Gemini.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The Team<\/h2>\n\n\n\n<p>I\u2019ve used Claude-Code since the beta phase. I \u201cgot in\u201d at the end of January 2025 and was immediately blown away.<\/p>\n\n\n\n<p>Claude\u2014and most AI tools in VS Code\u2014got a flurry of updates in the first half of August. Every two or three days the Claude-Code extension added something new: agentic mode with custom personas, security-review for uncommitted changes or CI\/CD integration, etc. Cool, powerful features that often made me think whatever I did two days earlier could\u2019ve been done easier if I\u2019d just waited.<\/p>\n\n\n\n<p>Occasionally, changes introduced regressions or at least odd behavior. After one update, Claude started committing and pushing changes without my approval. The security-review feature, out of the box, expects the default branch to be set (git remote set-head) and otherwise fails with a cryptic message.<\/p>\n\n\n\n<p>There\u2019s a lot to like about Claude-Code, but for me the killer feature is Custom MCPs\u2014especially how&nbsp;<a href=\"https:\/\/github.com\/bhauman\/clojure-mcp\/blob\/main\/README.md\">clojure-mcp<\/a>&nbsp;lets it navigate, modify, and test Clojure applications. So for most of the code development I used Claude-Code\u2014in VS Code or the CLI\u2014jumping out when usage limits kicked in or I wanted a second opinion, which I usually got from a specialized ChatGPT,&nbsp;<a href=\"https:\/\/chatgpt.com\/g\/g-Db2H1qYjj-clojure-mentor\">Clojure Mentor<\/a>.<\/p>\n\n\n\n<p>For build, packaging, and deployment, I leaned on Gemini and another specialized model, <a href=\"https:\/\/chatgpt.com\/g\/g-lnTXc3PgP-devops-gpt\">DevOps ChatGPT<\/a>\u2014also because, at the beginning of August, Claude felt less capable in those areas.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Aiming high<\/h2>\n\n\n\n<p>My target was what I ask of any tool: fast, secure, reliable, easy to install, update, and use.<\/p>\n\n\n\n<p>For a Clojure application,&nbsp;<em>fast<\/em>&nbsp;means GraalVM\u2014melting the Java fat and startup overhead into a native image on par with C\/C++.&nbsp;<em>Secure<\/em>&nbsp;meant up-to-date dependencies and vulnerability scanning.&nbsp;<em>Reliable<\/em>&nbsp;meant better logging. And&nbsp;<em>easy to install\/update<\/em>\u2014on macOS\u2014means <a href=\"https:\/\/brew.sh\" title=\"\">Homebrew<\/a>.<\/p>\n\n\n\n<p>My AI team supported those choices (they always do!) and gave me starter code and guidance for building the native image and a Homebrew distribution. For Windows it suggested&nbsp;<a href=\"https:\/\/chocolatey.org\/\">Chocolatey<\/a>\u2014new to me, but it sounded right.<\/p>\n\n\n\n<p>Getting the CI\/deployment pipeline right for macOS (ARM) was relatively easy. Developing on my M2 Air, I could test and fix as needed. I didn\u2019t focus on other platforms at first; I wanted a quick \u201cwalking skeleton\u201d with the main gates and stages, then I\u2019d backfill platform support.<\/p>\n\n\n\n<p>The GraalVM build was trickier. I fumbled with build and initialization flags but eventually got it to compile.<\/p>\n\n\n\n<p>In a few steps I had a Homebrew tap ready to install. Hurray!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Reality check<\/h2>\n\n\n\n<p>And then reality kicked in. The native image wouldn\u2019t run. The flags I used made the build pass but execution fail. Cue a round of whack-a-mole, primarily with Gemini.<\/p>\n\n\n\n<p>One interesting Gemini-in-VS-Code detail: at the beginning of August, the extension\u2019s interaction model was chat; after an update, it introduced agentic mode. Proof of how fast assistants evolve\u2014and a chance to feel both the pros and cons of each mode.<\/p>\n\n\n\n<p>Before the mid-August update, chats with Gemini 2.5 Pro were fruitful and generally more accurate than other models\u2014just a lot slower. I didn\u2019t measure it, but it felt&nbsp;<em>really<\/em>&nbsp;slow. Still, solid answers were worth the wait.<\/p>\n\n\n\n<p>After the August 15 release, chat began to\u2026 silently fail: after a long reflection, no answer. So I switched to the agent.<\/p>\n\n\n\n<p>Agentic Gemini&nbsp;<em>looked<\/em>&nbsp;promising: deep VS Code integration (you see diffs as they happen, which Claude-Code later matched), MCP servers, and a similar configuration to Claude\u2014so wiring up clojure-mcp was easy. However, it just didn\u2019t do Clojure or Babashka well. It got stuck in parentheses hell at almost every step. Sessions ended with tragic messages like, \u201cI\u2019m mortified by my repeated failures\u2026\u201d Eventually I felt bad for it. In their (troubling?) drive to make LLMs sound human, Google had captured the awkwardness of incompetence a little too well.<\/p>\n\n\n\n<p>I started to panic. Everything had moved so fast, and I had so many things started\u2014so many&nbsp;<a href=\"https:\/\/fibery.io\/blog\/product-management\/walking-skeleton\/\">\u201cwalking skeletons\u201d<\/a>\u2014with only a few vacation days left. I realized \u201cmy AI team\u201d wasn\u2019t going to get me over the finish line alone.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Vacations are too short<\/h2>\n\n\n\n<p>The leisurely vacation development phase had to end. I began waking up earlier for uninterrupted work before 8 a.m., skipping countryside trips and extended family visits to squeeze in a few more hours each day.<\/p>\n\n\n\n<p>At that point, shipping was about testing, running, iterating\u2014not generating more lines of code.<\/p>\n\n\n\n<p>The failing native image led me to add a post-packaging end-to-end validation stage, to catch these issues earlier.<\/p>\n\n\n\n<p>After a few tries, the culprit&nbsp;<em>seemed<\/em>&nbsp;to be the Cheshire JSON library. With no better theory, I switched to the standard&nbsp;clojure.data.json. Unit, integration, and E2E tests made the change a matter of minutes\u2014but it didn\u2019t fully resolve the native-image issues.<\/p>\n\n\n\n<p>All the while I looped through ChatGPT, Claude, Gemini, and old-school Googling: find plausible theories, try fixes, check whether anything improved. This isn\u2019t unique to LLMs\u2014developers have done this for decades to ship in ecosystems they don\u2019t fully control. If it works, it works.<\/p>\n\n\n\n<p>Finally, I got my first successful Homebrew install and full import on macOS ARM. It seemed feasible again to finish before vacation ended.<\/p>\n\n\n\n<p>Then I added Windows and Linux packaging\u2014and everything failed again. I told myself it was a platform access issue, bought a Parallels Desktop license\u2026 and then remembered I\u2019m on ARM trying to target x86. Not going to work.<\/p>\n\n\n\n<p>LLMs gave me speed, but the bottleneck was the real world: deployment time, runtime, access to hardware. Without x86 macOS, Linux, or Windows boxes, I cut scope: native image Homebrew package for macOS ARM (my main platform), and an executable image via&nbsp;<a href=\"https:\/\/docs.oracle.com\/en\/java\/javase\/11\/tools\/jlink.html\">jlink<\/a>&nbsp;for other platforms. Even that was painful on Windows, where builds kept failing and GPTs kept getting confused by basic shell automation. Final plan: native image for macOS ARM (done), jlink package (JRE + JAR) for macOS x64 and Linux arm64, and just the JAR for Windows. Time to stop and get back to reality.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Release It!<\/h2>\n\n\n\n<p>Stopping is harder than it sounds in the world of code assistants. When tools get more powerful every day\u2014and solve problems that look as hard as the ones left\u2014it always feels like the happy ending is one prompt away. But our complexity isn\u2019t LLM complexity, and vacations are short.<\/p>\n\n\n\n<p>So I stopped. I polished the code, ran multiple reviews, improved the docs. The functionality was good enough for me. I\u2019d done what I set out to do at the beginning of those two weeks. And somewhere along the way, <strong><a href=\"https:\/\/stefanescu.lu\/?p=134\" title=\"\">I learned to stop worrying and love to vibe<\/a><\/strong>.<\/p>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Where the code meets the real world Building, packaging, getting a real application running on different platforms, on different machines: that\u2019s where your code meets the real world. As a programmer, back in the day, this is where most of &hellip; <a href=\"https:\/\/stefanescu.lu\/?p=139\">Continue reading <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"om_disable_all_campaigns":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"categories":[23,17,20,1],"tags":[],"class_list":["post-139","post","type-post","status-publish","format-standard","hentry","category-agents","category-devops","category-engineering","category-uncategorized"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/stefanescu.lu\/index.php?rest_route=\/wp\/v2\/posts\/139","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/stefanescu.lu\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/stefanescu.lu\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/stefanescu.lu\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/stefanescu.lu\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=139"}],"version-history":[{"count":5,"href":"https:\/\/stefanescu.lu\/index.php?rest_route=\/wp\/v2\/posts\/139\/revisions"}],"predecessor-version":[{"id":176,"href":"https:\/\/stefanescu.lu\/index.php?rest_route=\/wp\/v2\/posts\/139\/revisions\/176"}],"wp:attachment":[{"href":"https:\/\/stefanescu.lu\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=139"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/stefanescu.lu\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=139"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/stefanescu.lu\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=139"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}