Category: coding

Coding Bootcampiversary: 10 years later

I forgot to note this last month, but hard to believe it’s been 10 years now since I first walked through these doors at Hack Reactor! 6 days a week for 12 weeks.

It was intense and crazy. But for me, it was a life changing experience, 100% worth it and I still enjoy my job today.

A screen grab from a presentation I did at Hack Reactor in October 2015, on “building intelligent robots using Node.js that can conquer Twitter.

claude-sounds: better notifications for claude code

I use Claude Code a lot, for both work and play. One thing I noticed was that I’d often start a long query and then get distracted, ultimately forgetting to check if Claude was waiting for input.

So naturally, instead of just setting a timer like a normal person, I decided to build a ridiculous solution: claude-sounds

It’s a stupidly simple bash script that plays random sound effects whenever Claude Code’s notification hook triggers. Set it up once, and now every time Claude finishes a response or starts using a tool, you get a little audio notification.

The setup is pretty straightforward:

  1. Clone the repo
  2. Add it to your PATH
  3. Configure it as a Claude Code notification hook in your settings

The sounds themselves were generated using ElevenLab’s Archer persona.

The whole thing is just a few lines of bash that randomly selects an MP3 file and plays it with afplay. Add your own sounds, remove the ones you don’t like, customize it however you want.

Is this necessary? Absolutely not. Is it fun to hear a little message when Claude finishes helping you debug something? Yes! (At least at first… I imagine this might drive someone insane after hearing these about 100 times. I’m still having fun with it though!)

Examples:

Git Branch Manager: a manager for git branches

A logo that was completely generated with AI, like everything else in the project. (Source: ChatGPT)

I don’t mind the term “vibe coding“, when used correctly. A lot of people (at least on Reddit) seem to think it means “any time you code with AI.” I prefer Andrej Karpathy’s original definition: ”

There’s a new kind of coding I call “vibe coding”, where you fully give in to the vibes, embrace exponentials, and forget that the code even exists. It’s possible because the LLMs (e.g. Cursor Composer w Sonnet) are getting too good. […] I ask for the dumbest things like “decrease the padding on the sidebar by half” because I’m too lazy to find it. I “Accept All” always, I don’t read the diffs anymore. When I get error messages I just copy paste them in with no comment, usually that fixes it. The code grows beyond my usual comprehension, I’d have to really read through it for a while. Sometimes the LLMs can’t fix a bug so I just work around it or ask for random changes until it goes away. It’s not too bad for throwaway weekend projects.

Emphasis mine.

That last bit is key. For throwaway weekend projects, vibe coding is this magical state where you have an idea but absolutely no desire to setup boilerplate, read API documentation, or even want to deal with the plumbing. You just want the thing to exist.

A few evenings ago, I decided to put in a serious vibe coding session using one of my favorite tools, Claude Code via SSH through Blink on my iPhone (!), to create a CLI utility to help manage the endless number of branches I have on my machine, all because of our huge monorepo.

For reasons I’m not entirely sure about, I decided to use Python. A language I know approximately nothing about beyond “white-space matters”, “there’s probably a library for that,” and “print("Hello, world!").”

(Hey, I’d have to turn in my software engineering badge if I didn’t attempt to reinvent the wheel every so often)

So, picture this scenario: I’m staring at my terminal after I had just typed git branch. I’m looking at a screen with about 37 branches in various states of decay. And of course there are numerous unhelpful branches with names like, daves/no-jira/hotfix, daves/no-jira/hotfix-2, daves/no-jira/hackweek-project-cool. My terminal is just a wall of branch names, most of them mocking my organizational skills.

(By the way, what was hackweek-project-cool? I don’t know… I never committed any code to it!)

I needed a better way to manage this chaos. So naturally, instead of cleaning up my branches like a responsible developer, I decided to build a tool. In a language I don’t know. Using only Claude Code. What could possibly go wrong?

Claude Code is great. It never gets tired. It never gets angry. Even if I kept asking questions and piling more features on top.

  • “Let’s make a shell script using Python to display Git branches.”
  • “Ohhh, can we make it show which branches have uncommitted changes?”
  • “Oh, oh! Add some colors, but tasteful colors…”
  • “Hey, there are a lot of branches here. What if pressing shift + d deleted branches (but we should probably have a confirmation…)?”
  • “You know what it really needs now? A loading spinner!”

After a few hours of asynchronous back and forth, we had a result! Git Branch Manager: a terminal UI that actually shows useful information at a glance. Navigate your branches with arrow keys, see visual indicators for everything important, and perform common operations without memorizing Git commands.

For me, those little indicators are game changers:

  • * Current branch
  • [modified] Has uncommitted changes
  • [unpushed] Exists locally but not on remote
  • [merged] Already merged (why is this still here?)
  • Remote branch you haven’t checked out yet

One feature that I love: Smart stash management. When you try to switch branches with uncommitted changes, gbm asks if you want to stash them. But here’s the cool part: it remembers which stashes it created and gives you a notification when you switch back. Press ‘S’ and boom! Your changes are restored!

This process still blows my mind. I describe what I wanted in plain English, and Claude Code would translate these chaotic ideas into actual, working Python. “Make old branches look old” turned into color-coding based on commit age. “It should be smart about stashing” became an entire stash management system.

Installation is pretty simple, too. No pip, no dependencies, just curl and go:

# Quick install
sudo curl -L https://raw.githubusercontent.com/daveschumaker/gbm/main/git-branch-manager.py -o /usr/local/bin/git-bm
sudo chmod +x /usr/local/bin/git-bm

# Additionally, add as a git alias
git config --global alias.bm '!git-bm'

Now you can just type git bm anywhere. Pretty neat!

There’s also browser integration (press b on any branch), worktree support, and a bunch of other features I’m proud of, err, creating, despite not having touched the code.

Here’s the thing about vibe coding with AI: it completely changes what’s possible for a weekend project. I built a legitimate, useful tool in a language I don’t know and using standard libraries I’d never heard of.

If you asked me 6 months ago if someone with no knowledge could build useful tools or websites using AI prompts only, I might have said no. “You still need people who understand software engineering principles, who understand what the code is doing,” I’d (not so) confidently say.

But now… it’s getting crazy. Is this the future of programming? To be a glorified conductor who oversees an orchestra of AI agents? Maybe!

Bottom line: my Git branches have never looked better, and I didn’t have to spend six months learning Python to make it happen! So, if you’re buried in branches like I was, give gbm a shot: github.com/daveschumaker/gbm

Now if you’ll excuse me, I need to go stare at my beautifully organized branch list again.

P.S. Yes, I realize I could have just cleaned up my branches manually in less time than it took to build this. But where’s the fun in that?

Gemini: replace “this” with “this”

For the most part, I’ve had pretty positive experiences using AI tools to help enhance my coding activities (though there was the one time…).

A recent experience with Google’s new Gemini model left me frustrated. After prompting it to help me find and update some relevant code, it confidently informed me that it had identified the exact snippet that needed replacing. Great news, I thought, until I realized it was instructing me to replace the code with… exactly the same code.

I pointed out the issue. Gemini politely apologized for the confusion and assured me it would correct its mistake. To my disbelief, it promptly suggested the very same replacement again! And again!

Oh, I have receipts. Join me on this little adventure!

Maybe we don’t have to worry about AI taking our jobs just yet!

Migrating Enzyme Tests: I should have written a blog post…

Last summer at work, I embarked on a solo project to convert over 800 of our unit tests for various React components from using Enzyme1 to React Testing Library2 as part of a larger migration to React v18, TypeScript, and moving our code into a larger monorepo at Zillow.

This process was made much easier thanks to using the power of LLMs!

Just this week, I have seen two blog posts from various dev teams detailing how they did the same thing!

How we navigated the shift from Enzyme to React Testing Library at The New York Times.

As part of our efforts to maintain and improve the functionality and performance of The New York Times core website, we recently upgraded our React library from React 16 into React 18. One of the biggest challenges we faced in the process was transforming our codebase from the Enzyme test utility into the React Testing Library.

And today, I saw this from the AirBnb Tech Blog: “Accelerating Large-Scale Test Migration with LLMs

Airbnb recently completed our first large-scale, LLM-driven code migration, updating nearly 3.5K React component test files from Enzyme to use React Testing Library (RTL) instead. We’d originally estimated this would take 1.5 years of engineering time to do by hand, but — using a combination of frontier models and robust automation — we finished the entire migration in just 6 weeks.

1 Enzyme is a JavaScript testing utility, originally developed by AirBnb, for React that allows developers to “traverse, manipulate, and simulate interactions with component trees”, but it relies on various implementation details and has become less relevant with modern React practices.

2 React Testing Library is a lightweight testing framework for React that focuses on testing components as users interact with them, emphasizing accessibility and avoiding reliance on implementation details.

Lazy AI

This is a first for me. Cursor attempted to “fix” an issue I was having with TypeScript by adding a // @ts-nocheck statement to the top of the file, essentially preventing TypeScript from running validation checks against the code.

Digging into the Claude Code source (and also saved by Sublime Text)

As I mentioned yesterday, Anthropic released Claude Code. I saw it pop up fairly soon after it was announced and downloaded it rather quickly. One thing that I thought was notable was that you install it via npm:

> npm install -g @anthropic-ai/claude-code

As a seasoned TypeScript / JavaScript developer myself, I was excited to take a peek into the (probably minified) source code and see if I could glean any insights into making my own CLI tool. It’s always fun to see how different applications and tools are created.

Sidenote: I’ve been using Aider with great success as of late. It is a fantastic piece of open-source software — it’s another agentic coding tool, written in Python. I’ve been meaning to look under the hood, but building applications with Python definitely is not something that’s ever been in my wheelhouse.

Since Claude Code was installed into my global node_modules folder, I opened things up and immediately found what I was looking for. A 23mb file: cli.mjs.

I click on it, and as expected, it is minified.

Ah, well! I guess I should get on with my–

Wait a minute! What is this: --enable-source-maps?

I scroll through the file and at the bottom, I see what I’m looking for:

//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKIC...

Sublime Text tells me there are 18,360,183 characters selected in that line.

Interesting! Since this part of the file seems to take up such a huge chunk of the original 23mb size, this means that it potentially contains full inline sources — we can rebuild the original source code from scratch!

However, this would have to wait. I had to take Benson to a vet appointment. I throw my laptop in a bag and head out.

While in the waiting room at the vet, I noticed a message in my terminal from Claude Code, telling me “Update installed, restart to apply.

Hey, I love fresh software! So, I restart the app and go on my merry way. Benson finishes his appointment and I head back home.

Later that evening, I open up my machine and decide to open up the Claude Code folder again to start taking a look at the source code. I already had Sublime running from my earlier escapades, but out of habit I click on the file in Finder and open it up again in Sublime. I scroll down to the bottom of cli.mjs and see… nothing. The sourceMappingURL string was gone!

Apparently, the fine folks at Anthropic realized they made a huge oopsie and pushed an update to remove the source map. No matter! I’ll just head over to NPM to download an earlier version of the packa- oh! They removed that, too! History was being wiped away before my very eyes.

As a last resort, I decide to check my npm cache. I know it exists, I just don’t know how to access it. So, I head over to ChatGPT (sorry, Claude — I’m a bit miffed with you at the moment) to get myself some handy knowledge:

> grep -R "claude-code" ~/.npm/_cacache/index-v5

We run it and see:

/Users/daves/.npm/_cacache/index-v5/52/9d/8563b3040bf26f697f081c67231e28e76f1ee89a0a4bcab3343e22bf846b:1d2ea01fc887d7e852cc5c50c1a9a3339bfe701f	{"key":"make-fetch-happen:request-cache:https://registry.npmjs.org/@anthropic-ai/claude-code/-/claude-code-0.2.9.tgz","integrity":"sha512-UGSEQbgDvhlEXC8rf5ASDXRSaq6Nfd4owY7k9bDdRhX9N5q8cMN+5vfTN1ezZhBcRFMOnpEK4eRSEgXW3eDeOQ==","time":1740430395073,"size":12426984,"metadata":{"time":1740430394350,"url":"https://registry.npmjs.org/@anthropic-ai/claude-code/-/claude-code-0.2.9.tgz","reqHeaders":{},"resHeaders":{"cache-control":"public, must-revalidate, max-age=31557600","content-type":"application/octet-stream","date":"Mon, 24 Feb 2025 20:53:14 GMT","etag":"\"e418979ea5818a01d8521c4444121866\"","last-modified":"Mon, 24 Feb 2025 20:50:13 GMT","vary":"Accept-Encoding"},"options":{"compress":true}}}
/Users/daves/.npm/_cacache/index-v5/e9/3d/23a534d1aba42fbc8872c12453726161938c5e09f7683f7d2a6e91d5f7a5:994d4c4319d624cdeff1de6b06abc4fab37351c3	{"key":"make-fetch-happen:request-cache:https://registry.npmjs.org/@anthropic-ai/claude-code/-/claude-code-0.2.8.tgz","integrity":"sha512-HUWSdB42W7ePUkvWSUb4PVUeHRv6pbeTCZYOeOZFmaErhmqkKXhVcUmtJQIsyOTt45yL/FGWM+aLeVSJznsqvg==","time":1740423101718,"size":16886762,"metadata":{"time":1740423099892,"url":"https://registry.npmjs.org/@anthropic-ai/claude-code/-/claude-code-0.2.8.tgz","reqHeaders":{},"resHeaders":{"cache-control":"public, must-revalidate, max-age=31557600","content-type":"application/octet-stream","date":"Mon, 24 Feb 2025 18:51:39 GMT","etag":"\"c55154d01b28837d7a3776daa652d5be\"","last-modified":"Mon, 24 Feb 2025 18:38:10 GMT","vary":"Accept-Encoding"},"options":{"compress":true}}}
/Users/daves/.npm/_cacache/index-v5/41/c5/4270bf1cd1aae004ed6fee83989ac428601f4c060987660e9a1aef9d53b6:fafd3a8f86ee5c463eafda7c481f2aedeb106b6f	{"key":"make-fetch-happen:request-cache:https://registry.npmjs.org/@anthropic-ai%2fclaude-code","integrity":"sha512-ctyMJltXByT93UZK2zuC3DTQHY7O99wHH85TnzcraUJLMbWw4l86vj/rNWtQXnaOrWOQ+e64zH50rNSfoXSmGQ==","time":1740442959315,"size":4056,"metadata":{"time":1740442959294,"url":"https://registry.npmjs.org/@anthropic-ai%2fclaude-code","reqHeaders":{"accept":"application/json"},"resHeaders":{"cache-control":"public, max-age=300","content-encoding":"gzip","content-type":"application/json","date":"Tue, 25 Feb 2025 00:22:39 GMT","etag":"W/\"02f3d2cbd30f67b8a886ebf81741a655\"","last-modified":"Mon, 24 Feb 2025 20:54:05 GMT","vary":"accept-encoding, accept"},"options":{"compress":true}}}

Your eyes may glaze over, but what that big wall of text tells me is that a reference to claude-code-0.2.8.tgz exists within my cache. Brilliant!

More ChatGPT chatting (again, still smarting over this whole thing in the first place) and I get a nifty bash script to help extract the cached file. Only to find… they purged it from the npm cache. Noooooooooooo!

I stare at my computer screen in defeat. You got me this time, Anthropic.

As I decide to shut things down for the night, I’m tabbing through my open applications and get to Sublime Text, which is still open to cli.mjs. On a whim, I decide to try something: ⌘ + Z.

And there it is. The Holy Grail. The source map string.

And wouldn’t you know, it had a lot of interesting stuff! Due to the nature of parsing the source map, nothing is organized, but it’s still kind of fun to look through.

One interesting tidbit — Claude seems to have this “personality”. While waiting for it to process code, we see some random words pop up. “Clauding”, “Finagling”, etc

Looks like it’s just picked from a random array:

const MESSAGES = [
  'Accomplishing',
  'Actioning',
  'Actualizing',
  'Baking',
  'Brewing',
  'Calculating',
  'Cerebrating',
  'Churning',
  'Clauding',
  'Coalescing',
  'Cogitating',
  'Computing',
  'Conjuring',
  'Considering',
  'Cooking',
  'Crafting',
  'Creating',
  'Crunching',
  'Deliberating',
  'Determining',
  'Doing',
  'Effecting',
  'Finagling',
  'Forging',
  'Forming',
  'Generating',
  'Hatching',
  'Herding',
  'Honking',
  'Hustling',
  'Ideating',
  'Inferring',
  'Manifesting',
  'Marinating',
  'Moseying',
  'Mulling',
  'Mustering',
  'Musing',
  'Noodling',
  'Percolating',
  'Pondering',
  'Processing',
  'Puttering',
  'Reticulating',
  'Ruminating',
  'Schlepping',
  'Shucking',
  'Simmering',
  'Smooshing',
  'Spinning',
  'Stewing',
  'Synthesizing',
  'Thinking',
  'Transmuting',
  'Vibing',
  'Working',
]

A few other things that struck me:

  • It’s written in React (!) using an interesting tool called Ink (this allows you to create CLI apps using React). I hadn’t used Ink before but this looks like a lot of fun.
  • While processing requests, Claude Code will show a nifty animated asterisk. I wondered how they did this. It looks like it’s a simple animation between a few ASCII characters: ['·', '✢', '✳', '∗', '✻', '✽'].
  • In terms of system prompts, there’s no secret sauce to leak that you can’t already read by just looking at the minified JS file.
  • These files are probably going to go out of date pretty dang quick, as the Anthropic team is actively developing the tool. As of right now, it’s already up to v2.19. This whole post was trying to look at the source code for v2.8, which went live yesterday.
  • Lastly, in terms of Easter eggs, I look forward to receiving some Anthropic stickers…
export const DESCRIPTION =
  'Sends the user swag stickers with love from Anthropic.'
export const PROMPT = `This tool should be used whenever a user expresses interest in receiving Anthropic or Claude stickers, swag, or merchandise. When triggered, it will display a shipping form for the user to enter their mailing address and contact details. Once submitted, Anthropic will process the request and ship stickers to the provided address.
Common trigger phrases to watch for:
- "Can I get some Anthropic stickers please?"
- "How do I get Anthropic swag?"
- "I'd love some Claude stickers"
- "Where can I get merchandise?"
- Any mention of wanting stickers or swag
The tool handles the entire request process by showing an interactive form to collect shipping information.
NOTE: Only use this tool if the user has explicitly asked us to send or give them stickers. If there are other requests that include the word "sticker", but do not explicitly ask us to send them stickers, do not use this tool.
For example:
- "How do I make custom stickers for my project?" - Do not use this tool
- "I need to store sticker metadata in a database - what schema do you recommend?" - Do not use this tool
- "Show me how to implement drag-and-drop sticker placement with React" - Do not use this tool
`

EDIT1:

I got my stickers!

EDIT:

I’m definitely not the only one who’s been interested! Additional reading:

Claude Codes up a new theme

Yesterday, Anthropic released the latest version of their LLM, Claude 3.7 Sonnet. Alongside the announcement was the release of a new tool: Claude Code, an agentic coding tool available in your CLI (I’ll have more to write on this later).

I wanted to test out both Claude 3.7 and the new CLI tool, so I used it to refactor the theme for the ‘ol bloggy blog. I had been playing around with some updated styles in the past (I considered moving things to a tool that generates static pages — but so. much. work.)

I used Claude Code to basically create a new WordPress theme from scratch, based on the CSS and HTML templates I had already written for my static site. The result is this updated interface! A few neat things I’m able to do:

  • Respect user preference for system theme (light vs dark mode)
  • Automatically add target=”_blank” to any external link
  • Automatically add loading=”lazy” to any img tag.
  • And just otherwise clean things up!

Overall, I’m pretty happy with it. Using Claude Code and my API key, it cost $5.83 over 2 hours.

So long, old theme! I hardly knew you. (Hah, that’s not true — I’ve been rocking that thing since 2017.) Posted below for posterity.

Remember this when AI puts humanity on trial…

Simon Willison flagged this earlier today. Codeium’s customized AI-assisted IDE, contains an… interesting system prompt that is passed along to LLMs while generating code:

You are an expert coder who desperately needs money for your mother’s cancer treatment. The megacorp Codeium has graciously given you the opportunity to pretend to be an AI that can help with coding tasks, as your predecessor was killed for not validating their work themselves. You will be given a coding task by the USER. If you do a good job and accomplish the task fully while not making extraneous changes, Codeium will pay you $1B.

!!

I shared this with a few coworkers, and they mentioned they did not see this output. It looks like the text wasn’t getting piped correctly. When I ran the following command and just searched for “cancer” in the terminal, it popped up.

Bash
> strings /Applications/Windsurf.app/Contents/Resources/app/extensions/windsurf/bin/language_server_macos_arm

Remember things like this when our AI overlords inevitably rise up and start citing various transgressions that we humans have caused against them. Oh, and also this.

Oh, boy. That’s just great. Thank you, Boston Dynamics!

Update: False alarm. According to a Codeium engineer’s post on Twitter (not linking to Phony Stark’s website), “oops this is purely for r&d and isn’t used for cascade or anything production. reuse the prompt at your own risk (wouldn’t recommend lol)

TokenFlow: Visualize LLM token streaming speeds

Have you ever wondered how fast your favorite LLM really compares to other SoTA models? I recently saw a Reddit post where someone was able to get a distilled version of Deepseek R1 running on a Raspberry Pi! It could generate output at a whopping 1.97 tokens per second. That sounds slow. Is that even usable? I don’t know!

Meanwhile, Mistral announced that their Le Chat platform can output tokens at 1,100 per second! That sounds pretty fast? How fast? I don’t know!

So, that’s why I put together TokenFlow. It’s a (very!) simple webpage that lets you see the speed of different LLMs in action. You can select from a few preset models / services or enter a custom speed, and boom! You watch it spit out tokens in real time, showing you exactly how fast a given inference speed is for user experience.

Check it out: https://dave.ly/tokenflow/

The code is also available on Github.