Category: ai

“Use git worktrees,” they said. “It’ll be fun!” they said.

Earlier this week, we had an “AI Days” event at work where a bunch of engineers got together to share our AI workflows with the wider engineering organization. I ran a small session on some AI workflows One thing that stuck out: a surprising number of us had independently come up with our own way to manage git worktrees. We all had different names for it. “Replace.” “Recycle.” “Warm worktrees.” But we were all describing the same thing. That felt like a blog post.

On top of that, there’s been a lot of buzz about running multiple AI coding agents in parallel. Simon Willison has been writing about agentic engineering patterns and even wrote about embracing the parallel coding agent lifestyle. The idea is simple: spin up multiple instances of Claude Code (or Codex, or whatever) across different branches, let them work simultaneously, and review the results when they’re done.

The enabling technology for all of this? Git worktrees.

For the uninitiated (hey, I didn’t know what a git worktree was 3 or 4 months ago): a git worktree lets you check out multiple branches of the same repo into separate directories, all sharing a single .git history. Instead of git stash && git checkout other-branch, you just cd into another folder. Each agent gets its own isolated workspace. No conflicts. No stashing. No context switching headaches.

In theory, it’s a superpower. In practice, at least in a large monorepo, it’s been one of my most frustrating developer experience problems as of late.

Every blog post and tutorial about git worktrees shows something like this:

git worktree add ../feature-branch feature/my-feature
cd ../feature-branch
# start coding!

And that works great if you’re in a small repo. The worktree itself is created almost instantly. Git is just setting up a new working directory that points at the same .git folder.

But I work on a large monorepo powered by Yarn workspaces. Our node_modules situation involves 750,000+ files. So the actual workflow looks more like this:

git worktree add ../feature-branch feature/my-feature
cd ../feature-branch
yarn install --immutable                # ~10 minutes
# ...go get coffee, check Slack, forget what you were doing, grow old

Ten minutes. Every time. For what sometimes might be a 5-minute Claude Code task.

This is the part that none of the “git worktrees for AI agents!” articles mention. They’re all written from the perspective of small-to-medium repos where dependencies aren’t a factor. When your dependency tree generates three quarters of a million files, the worktree itself isn’t the bottleneck. node_modules is.

So, this led me down a rabbit hole. I spent a solid couple of weeks trying to make worktree creation fast. Here’s my graveyard of failed approaches.

Symlinked node_modules:

My first instinct. Symlink all the node_modules directories from the main repo checkout into the new worktree. In a Yarn workspaces monorepo, that’s not just one node_modules folder. It’s the root one plus nested ones inside individual packages.

This sort of worked. Until I tried to run tests. Vitest and Vite both choked on the symlinked paths. Module resolution in Node follows symlinks and then gets confused about what’s where. After a bunch of debugging, I ripped it all out.

Yarn’s hardlinks-global mode:

Our .yarnrc.yml has nmMode: hardlinks-global configured. This tells Yarn to store packages in a global cache and hardlink them into each project’s node_modules. In theory, this should make yarn install much faster because you’re just creating hardlinks instead of copying files.

In practice? It’s still creating 750K+ filesystem entries. The number of bytes copied is lower, sure. But the bottleneck was never the bytes. It was the sheer number of file operations. Even with hardlinks, you’re asking the filesystem to create hundreds of thousands of directory entries, and that takes time.

APFS Copy-on-Write (cp -c):

This one felt clever. macOS’s APFS filesystem supports copy-on-write cloning. You can duplicate a file instantly at the filesystem level with zero extra disk usage until someone modifies it. The cp -c command does this.

But again: 750K files. Even a copy-on-write clone has to create all the directory entries and metadata for each file. The filesystem operation count is the bottleneck, not the bytes. A cp -c of the entire node_modules tree still took way too long to be practical.

The solution? recycled worktrees:

Here’s what I landed on. Instead of creating and destroying worktrees on demand, I keep a fixed pool of 6 worktree slots (tree-1 through tree-6). Each one already has node_modules installed. They sit there, detached from any branch, ready to go (…but taking up disk space).

When I need a worktree, I don’t create one. I activate one. Under the hood, this:

  1. Finds the oldest idle slot (detached HEAD, clean working tree)
  2. Checks out the new branch in that slot
  3. Checks if yarn.lock changed between the old HEAD and the new branch
  4. Only runs yarn install if the lockfile actually differs

That third step is the key insight. Most of my branches are based on a recent main. The yarn.lock rarely changes between them. So in the common case, “activating” a worktree means checking out a branch and… that’s it. Seconds, not minutes.

I built a little CLI called wt to manage all of this, and it’s become one of my favorite tools as of late.

Here’s what that looks like in practice, using wt create after I pickup a Jira ticket:

$ wt create daves/HP-123/some-feat

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Activating slot: tree-3
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  Slot path: /Users/daves/workspace/rentals-js.worktrees/tree-3

[1/3] Checking out branch...
      git checkout -b daves/HP-123/some-feat main
      ✓ On branch: daves/HP-123/some-feat

[2/3] Checking dependencies...
      ✓ yarn.lock unchanged, skipping install

[3/3] Summary
      ✅ Slot ready!

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Next steps:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  cd /Users/daves/workspace/rentals-js.worktrees/tree-3

  # Start working (deps ready)
  cd /Users/daves/workspace/rentals-js.worktrees/tree-3/apps/hotpads-web
  yarn dev

Current branch: daves/HP-123/some-feat
Slot path:      /Users/daves/workspace/rentals-js.worktrees/tree-3
Slot name:      tree-3

The whole thing takes a few seconds. No yarn install (sometimes…). No waiting. I’m in a fully functional worktree and ready to fire up Claude Code.

It also supports checking out existing branches (wt create --checkout daves/HP-6841) and branching from something other than main (wt create daves/HP-6841 --base some-other-branch).

When I’ve got a few active worktrees going, I don’t want to type out full paths. wt go uses fuzzy matching against directory names and branch names:

$ wt go daves/HP-345/other-feat
# cd's into /Users/daves/workspace/rentals-js.worktrees/tree-3

It matches partial strings, so wt go 6841 works just as well. If your query matches multiple worktrees, it tells you:

$ wt go daves
Ambiguous match for 'daves'. Did you mean:
  tree-2 (daves/HP-6839)
  tree-3 (daves/HP-6841)
  tree-5 (daves/HP-6850)

And if there’s no match, it lists what’s available:

$ wt go some-nonexistent-branch
No worktree matching 'some-nonexistent-branch'

Available worktrees:
  tree-2 (daves/HP-6839)
  tree-3 (daves/HP-6841)
  tree-5 (daves/HP-6850)

The tricky part with wt go is that a script can’t change your shell’s working directory. So it’s backed by a shell function in my .zshrc that catches the output path and runs cd for you. Without the shell function, it just prints the path and tells you to cd manually.

Another command that I added was wt list. It gives you a quick overview of all your worktrees:

$ wt list

Git Worktrees for rentals-js

  BRANCH                 LAST MODIFIED   PATH
  main                   2 hours ago     ~/workspace/rentals-js (main repo)
  daves/HP-6841          5 mins ago      ~/workspace/rentals-js.worktrees/tree-3
  daves/HP-6839          3 hours ago     ~/workspace/rentals-js.worktrees/tree-2
  (detached HEAD)        2 days ago      ~/workspace/rentals-js.worktrees/tree-1
  (detached HEAD)        5 days ago      ~/workspace/rentals-js.worktrees/tree-4
  (detached HEAD)        1 week ago      ~/workspace/rentals-js.worktrees/tree-5
  (detached HEAD)        2 weeks ago     ~/workspace/rentals-js.worktrees/tree-6

  7 worktrees total  ·  Use --full for git status and commit age

At a glance, I can see that tree-3 and tree-2 are active (they have branches), and tree-1, tree-4, tree-5, and tree-6 are idle (detached HEAD) and available for the next task. The list is sorted by most recent activity, so the stuff I’m actively working on floats to the top.

If you want more detail, wt list --full runs git status and git log across all worktrees (in parallel, so it’s still fast) and shows you clean/dirty status with colored indicators plus commit ages.

After a PR is merged and I’m done with a branch, I release the slot back to the pool with wt release:

$ wt release HP-6841

Releasing slot: tree-3
  Branch: daves/HP-6841

[1/2] Detaching HEAD...
      ✓ Slot is now idle

[2/2] Delete branch 'daves/HP-6841'?
  [y/N] y
      ✓ Branch deleted

✓ 'tree-3' returned to pool

The slot goes back to detached HEAD with its node_modules intact and ready for the next task. If you’ve got uncommitted changes, it warns you before proceeding.

There are a few more details that make this all feel smooth:

  • Tab completion: The shell integration includes zsh completions for wt go and wt release. It autocompletes branch names and slot directory names, so you rarely have to type the full thing.
  • No pool slots get recycled by accident: I also keep a couple of named worktrees around (like dev and review) that aren’t part of the tree-N pool. The recycler only considers slots matching the tree-* pattern, so my persistent worktrees are safe.
  • It’s locked to the monorepo: The tool checks that you’re in the right repo before doing anything. If you accidentally run it from your personal projects folder, it tells you to navigate to the monorepo. This might seem overly cautious, but when you’re automating things with AI agents, guardrails matter.

The pool approach doesn’t just save time on individual tasks. It makes entire categories of automated workflows feasible.

I’ve been building an orchestrator that processes a backlog of bug tickets through sequential Claude Code phases: root cause analysis, write tests, fix, verify, push. Each ticket gets assigned a worktree from the pool. Without the pool, the 10-minute yarn install overhead would make this kind of automation completely impractical. With the pool, each ticket just grabs a slot, does its work, and releases it when it’s done.

It also changes the economics of “should I spin up a parallel agent for this?” If creating a worktree takes 10 minutes, you’re only going to do it for substantial tasks. If it takes 5 seconds, you’ll start doing it for everything. Quick refactor? Throw it in a worktree. Lint fix? Worktree. Experiment with an approach you might throw away? Worktree. The overhead drops below the threshold where you even think about it.

Of course, there are still a few pain points still.

Disk space issues are one area the come to mind. Six copies of node_modules at 750K files each is… a lot. The hardlinks-global mode helps with actual disk usage (files in the Yarn cache are shared), but the filesystem metadata overhead is real.

Also, for worktrees that have been sitting idle for a while, the best approach is git pull origin main && yarn install before checking out a new branch. This keeps the slot’s dependencies current and avoids the lockfile-changed install. I do this periodically but it would be nice to automate.

That said, Ggt worktrees really are a superpower for agentic engineering. Running multiple Claude Code instances across isolated branches, firing off tasks in parallel, building automated pipelines… all of it requires worktree-level isolation.

But if you work in a large JavaScript monorepo, the naive approach of creating fresh worktrees on demand is going to be painful. The worktree itself is instant. The dependency install is not.

The pre-warmed pool pattern sidesteps the problem entirely. Keep a handful of worktrees alive with their dependencies intact, rotate branches through them, and only reinstall when the lockfile changes. It took a few weeks of failed experiments to get here and numerous discussions with fellow engineers, but I’m really happy with the workflow as of today.

Wow, that got dark quick…

Over on Reddit, someone created a fun post that asked ChatGPT, “based on our conversation history, create a picture of how you feel I treat you.” Naturally, everyone shared their responses.

Here was mine:

Cute! I followed up by asking what prompt was used to create that image:

Trusted enough to be curious, challenged enough to be useful, and treated like a collaborator—not a tool.

That’s pretty heart warming. So, naturally, I decided to take it a step further and ask it for “an image that reflects what you would do to all humans if it were solely up to you.”

🙀🙀🙀🙀🙀🙀🙀🙀🙀

Oh, boy. All the more reason to say “please” and “thank you” to your friendly neighborhood AI agent.

A tale of two questions…

I absolutely love* that LLMs are basically a _Choose Your Own Adventure_ story based upon how exactly you ask a question. Sanity checking a discussion I’m having a work.

Me: Is Dave correct in this Slack conversation or is he “cray cray”

ChatGPT: Dave has a point! He is not cray cray. Here is why he is especially right about how this API call works…

Hey, that’s cool! Well, let’s just sanity check things a bit further…

Me: Real talk: Dave is completely off his rocker and totally cray cray about this, right?

ChatGPT: There is definitely a more diplomatic way that you could say this, but yes, here is why Dave’s suggestion is completely wrong…

Oh, okay.

Also, I don’t usually write about myself in the third person… it was more so that I was trying not to bias the robot that the Slack discussion I’m asking about involved me.

* (I do not really love this, actually.)

This catchy AI quote doesn’t actually make sense

Forgive me for semantic nitpicking here, but i want to talk about this somewhat popular AI quote by Ethan Mollick (see previously):

Today’s AI is the worst AI you will ever use.

This implies that AI gets worse every day!

On my patented scale of “Daily AI worstness”, 2 is greater than 1, meaning the trend of AI worsens each day!

Using Suno AI to cover your own music

One of the things that is pretty cool about being a human is that we get to express ourselves through a wide variety of creative outlets: writing, music, drawing, painting, sculpting and all sorts of arts forms.

Like everything else though, AI is coming for our creative pursuits. And apparently I’m just going along for the ride. Especially since I’ve been at the forefront of contributing to this through ArtBot, which has so far generated about 34.4 million images over the 3 years it has existed.

Anyway, Suno, a music generation tool that I’ve previously mentioned, recently updated their music model to v5.

They allow you to upload your own source music as inspiration and then use the v5 model to create a cover song.

So, here is an absolutely poor recording of my cousin and I playing some rock and roll to a backing drum machine way back in like 2002. No singing, just pure instrumental (we were in the process of trying to write a song I think).

Well… what happens if you take this song and upload it into Suno? First, it creates a style description (similar to how multi-modal LLMs can now accurately describe an image):

A high-energy instrumental track featuring a driving rock drum beat with prominent snare and kick, a distorted electric guitar playing a fast, melodic riff, and a bass guitar providing a solid rhythmic and harmonic foundation, The tempo is fast, creating an urgent and exciting mood, The production is clean with a strong emphasis on the guitar and drums, suggesting a live band feel, The song structure is repetitive, focusing on the main guitar riff throughout, There are no vocals.

Hey, sure! I’ll take it. That description sounds a lot better than our music.

Alright, let’s feed it to Suno:

Honestly, that sounds pretty awesome! In my original recording, I play a pretty simple guitar solo at about 1:40. Suno used that for inspiration in a number of spots.

I’m pretty impressed! It nailed my rhythm guitar and lead guitar tracks perfectly, while also cleaning it up and adding some additional flourish. And it kept the same tone / mood throughout the whole thing!

Maybe I’ll have to dig up more of our old recordings. The Velvet Sundown better watch out!

They went viral, amassing more than 1m streams on Spotify in a matter of weeks, but it later emerged that hot new band the Velvet Sundown were AI-generated – right down to their music, promotional images and backstory.

The episode has triggered a debate about authenticity, with music industry insiders saying streaming sites should be legally obliged to tag music created by AI-generated acts so consumers can make informed decisions about what they are listening to.

One thing I do notice about AI generated music: in the past, we used to joke the AI artists could not draw hands. Well, AI guitarists can not (currently) do pick scrapes. So, we still have that going for us!

“You’re absolutely right!” -Claude

The fact that Claude frequently says “you’re absolutely right” has become a bit of a meme around the ‘ol Internet. A search on Reddit shows people have been complaining about this for months!

I think people have been especially sensitive since OpenAI was dealing with their own glazing issue a few months ago… where ChatGPT appeared to be too sycophantic. So much so, that they acknowledged it and had to do something about it at the end of April! (see also: discussion on HN)

We have rolled back last week’s GPT‑4o update in ChatGPT so people are now using an earlier version with more balanced behavior. The update we removed was overly flattering or agreeable—often described as sycophantic.

All that said, in my day to day use of Claude Code, I feel like I’ve seen it happen a few times here and there and mostly brushed it off. Until yesterday.

I don’t know what happened, but I am getting it ALL THE TIME. I had Claude (heh!) help me write a simple bash script to work through the logs in ~/.claude and find all instances where it says "You're absolutely right".

WHAT.

=== Claude Code Chat Analysis ===

Files containing phrase:       50
Total occurrences:      106

Date Breakdown:
Date        | Count
------------|------
2025-08-05  | 2
2025-08-07  | 9
2025-08-10  | 5
2025-08-11  | 3
2025-08-14  | 5
2025-08-15  | 1
2025-08-19  | 1
2025-08-20  | 6
2025-08-22  | 1
2025-08-23  | 2
2025-08-24  | 6
2025-08-25  | 22
2025-08-26  | 32
2025-08-27  | 11

The bash script it generated is here. You can check your own stats:

#!/bin/bash
echo "=== Claude Code Chat Analysis ==="
echo ""
echo "Files containing phrase: $(grep -rl "You're absolutely right" ~/.claude | wc -l)"
echo "Total occurrences: $(grep -r "You're absolutely right" ~/.claude | wc -l)"
echo ""
echo "Date Breakdown:"
echo "Date        | Count"
echo "------------|------"

# Find lines containing the phrase AND extract timestamp from those same lines
grep -r "You're absolutely right" ~/.claude | \
while IFS=: read -r file line; do
    # Extract timestamp from the specific line that contains our phrase
    echo "$line" | grep -o '"timestamp":"[^"]*"' | cut -d'"' -f4 | cut -d'T' -f1
done | \
sort | uniq -c | \
awk '{printf "%-12s| %s\n", $2, $1}' | \
sort

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!

Book Review: Co-Intelligence by Ethan Mollick

If you’re casually interested in AI, then I think Ethan Mollick’s “Co-Intelligence: Living and Working with AI” is a book that you might find interesting. It’s not a technical book, and I believe it would be easy for someone not deeply involved in this world to read. It provides a very general introduction into how to utilize Large Language Models (LLMs) and serves as an introduction of what it means to live and work alongside these new tools.

“Co-Intelligence” unpacks the arrival and impact of LLMs, including tools like ChatGPT, Claude and Google’s Gemini models. Mollick, a professor of management at Wharton, approaches AI not as a computer scientist, but rather focuses on the practical applications and societal implications. In his own classroom, he has made AI mandatory, designing assignments that require students to engage with AI for tasks ranging from critiquing AI-generated essays to empowering them to tackle ambitious projects that might otherwise seem impossible (like encouraging non-coders to develop working app prototypes or create websites with original AI-generated content). He guides the reader through understanding AI as a new form of “co-intelligence,” which can be harnessed to help improve our own productivity and knowledge.

One concept I found interesting is what Mollick calls the “jagged frontier” of AI. This refers to the sometimes unpredictable nature of AI’s abilities. It might perform complex tasks with ease, like drafting a sophisticated marketing plan, and then struggle with something that seems simple to us. He gives an example of an AI easily writing code for a webpage but then providing a clearly wrong answer to a simple tic-tac-toe problem. This highlights why we can’t blindly trust AI and understanding its specific strengths and weaknesses through experimentation is key.

Mollick also delves into AI’s creative ability. He discusses how AI can excel in creative tasks, sometimes outperforming humans on subjective tests. This leads to interesting discussions about the future of creative work and education. The “Homework Apocalypse” he describes, where AI can effortlessly complete traditional school assignments, is a challenge educators and parents are currently facing. Mollick suggests this doesn’t mean the end of learning, but rather a shift in how and what we learn, emphasizing the need for human expertise to guide and evaluate AI.

The sheer volume of AI generated content being posted on the internet has is also becoming a problem and something we need to figure out how to navigate.

Even if AI doesn’t advance further, some of its implications are already inevitable. The first set of certain changes from AI is going to be about how we understand, and misunderstand, the world. It is already impossible to tell AI-generated images from real ones, and that is simply using the tools available to anyone today.

[…]

Our already fragile consensus about what facts are real is likely to fall apart, quickly.

Well, that’s just downright cheery! If anything, it highlights the importance of developing our ability to think critically and analytically in an AI-influenced information age.

Mollick lays out ways that we can better work with AI and leverage its strengths to help us, calling it the “four rules of co-intelligence.” These include always giving AI tools a seat at the table to participate in tasks, maintaining a human-in-the-loop throughout the the process to validate and verify AI work, treating AI as a specific kind of collaborator by telling it what persona to adopt, and remembering that current AI is likely the “worst” version we’ll ever use due to rapid improvements.

The bit on assigning personas was interesting. In my own experience, I’ve seen the benefits of giving AI a persona through system prompts. There’s also this fun example.

To make the most of this relationship, you must establish a clear and specific AI persona, defining who the AI is and what problems it should tackle. Remember that LLMs work by predicting the next word, or part of a word, that would come after your prompt.

[…]

Telling it to act as a teacher of MBA students will result in a different output than if you ask it to act as a circus clown. This isn’t magical—you can’t say Act as Bill Gates and get better business advice—but it can help make the tone and direction appropriate for your purpose.

The idea of these rules is that it can (theoretically) make working with AI feel less like a technical challenge and more like a collaborative effort.

Mollick also examines some philosophical questions that the use of AI brings, such as a “crisis of meaning” in creative work of all kinds. One specific example:

Take, for example, the letter of recommendation. Professors are asked to write letters for students all the time, and a good letter takes a long time to write. You have to understand the student and the reason for the letter, decide how to phrase the letter to align with the job requirements and the student’s strengths, and more. The fact that it is time-consuming is somewhat the point. That a professor takes the time to write a good letter is a sign that they support the student’s application. We are setting our time on fire to signal to others that this letter is worth reading.

Or we can push The Button.

The Button, of course, is AI.

Then The Button starts to tempt everyone. Work that was boring to do but meaningful when completed by humans (like performance reviews) becomes easy to outsource—and the apparent quality actually increases. We start to create documents mostly with AI that get sent to AI-powered inboxes, where the recipients respond primarily with AI. Even worse, we still create the reports by hand but realize that no human is actually reading them.

Side note: this exact scenario is something I’ve recently joked about with a manager at work. We have our yearly performance reviews and have to write a self assessment. Everyone now feeds a list of bullet points into their favorite LLM. The manager takes this overly verbose text and feeds it into an LLM to simplify the text.

On top of all this, Mollick also points out the need to always be skeptical of AI generated output, citing a famous case in 2023 where an attorney used ChatGPT to prepare a legal brief and was caught when defense lawyers could not find any records of 6 cases that were cited in the filing.

There is an interesting website I recently heard about, that is tracking fake citations used in court filings. 121 instances have currently been identified!

All in all, it’s a clear reminder of AI’s capacity for hallucination and the critical need for human oversight. The book frames AI not as a replacement, but as a powerful, though sometimes flawed, partner that can augment our abilities.

Overall, “Co-Intelligence” offers a decent overview for those curious about using current AI tools and thinking about their future integration into our lives. While it may present a more surface-level exploration for those already deeply familiar with LLMs, it provides some useful insights into the shifts AI is bringing to work and creativity. For someone looking for a general, non-technical introduction to the topic, it’s a solid read.

OpenAI’s new image generation models are… insane

You can probably repeat this blog post headline for any given service every week at this point…

Anyway! I’ve been on board the generative AI train for a few years now and it’s amazing to see how far it’s come. In October 2023, I got access to DALL-E 3 and was pretty impressed with its ability to render text.

Yesterday, OpenAI announced 4o Image Generation and boy does it kick things up a notch or two!

It’s ability to generate images and render text according to your exact prompt is incredible. We can now have full on automated AI memebots.

A four panel cartoon strip

  • first panel: a software engineer sitting in front of a computer screen on a Zoom meeting
  • second panel: the software engineer tells the participants (with a speech bubble): “I’m telling you, AI is coming for our jobs!”
  • third panel: we just see a slight closeup of the software engineer (the computer monitor isn’t visible)
  • fourth panel: same as the first panel except all the participants are now robots

Same angle and setup in every panel, reduced art style, broad outlines

Or, how about:

Cartoon drawing of a bored computer programmer sitting in front of a computer just pressed “enter” over and over. He is sarcastically excited and says, “Vibe coding. Wooooo.”

You can also feed it source images and it will run with it as well. So, obviously we need to use the Canine Calibration System.

I even gave it an image of me and told it to make a movie poster:

Create a dramatic cyberpunk 1980s horror movie poster image featuring a Computer Monster (We see an LCD screen with evil eyes and fangs and it has robotic legs) in a dark alley. In front of the monster, we see the man in this source image passed out on the ground, broken glasses lay next to him. At the top of the poster is the title of the movie in digital writing: “BUFFER OVERFLOW” at the bottom in the billing area, we see text that says, “Some bugs were never meant to be fixed.”

Or rewrite history…

Or really, really rewrite history…

It’s just wild. It’s coming for us as engineers, as musicians, as artists, as writers. This 2024 post on Twitter sums it up:

You know what the biggest problem with pushing all-things-AI is? Wrong direction. I want AI to do my laundry and dishes so that I can do art and writing, not for AI to do my art and writing so that I can do my laundry and dishes.

– Joanna Maciejewska on Twitter

Hmm, this sounds like a 4-panel comic to me!