Tag: LLMs

AI Brain Fry

A totally realistic image of my office that is totally not AI generated slop.

Anyone who knows me understands I’ve been all aboard the AI choo choo train since the early days. At work, we have something like 3,000 engineers and I’m number 4 in the entire company in terms of AI spend. It’s insane.

That said, a recent article in the Harvard Business Review highlighted something I’ve been feeling pretty deeply as of late: “AI Brain Fry” (archive link to article).

I find that I’m busier than I’ve ever been in my engineering career, juggling multiple tasks at once: build this feature in one worktree while fixing 2 separate bugs in additional worktrees, oh and we need to look at performance improvements in this other one.

My teammates are all onboard too, as we feel like we all have to maintain ridiculously high levels of output. Our jobs have changed. Coding was something I’ve always loved. I get to solve these interesting puzzles and challenges every day. Now it’s shifting to one of my personal least favorite, but most important, parts of the job: code review.

Because there is a lot of AI generated code to review.

We often joke about how many browser tabs someone has open. The joke is shifting to how many terminal tabs running multiple instances of Claude Code and Codex.

Meanwhile, we have senior leadership saying things like, “AI should improve your productivity. I’m not talking 10x. I’m talking 100x, 1,000x!” It feels like it’s all just becoming this impersonal numbers game that measures raw output above all else.

I think part of it is that our immediate team is working in scrappy startup mode, competing with a product offering from our parent company. That underdog feeling is fun and exciting. And we’re pushing boundaries and pioneering workflows that the rest of the company hasn’t embraced yet. The average engineer company-wide closes something like 2-3 Jira tickets per sprint. We’re average about 25!

But oh, man. There is so much to do and maybe 4 of us working on it. It’s crazy.

Needless to say, from the article:

Unsurprisingly, workers are finding themselves up against the limits of their cognitive abilities when working this way. In recent weeks, online AI users have described increased cognitive load, “saturated” attention, and mental fatigue in social media posts. Engineer Francesco Bonacci, founder of Cua AI, wrote a popular X post titled “Vibe Coding Paralysis: When Infinite Productivity Breaks Your Brain” in which he lamented: “I end each day exhausted—not from the work itself, but from the managing of the work. Six worktrees open, four half-written features, two ‘quick fixes’ that spawned rabbit holes, and a growing sense that I’m losing the plot entirely.”

I’ve definitely feel like I’ve been hitting a wall. What was once a blistering pace has been dialed back a bit as I try to better balance work, life, and the incessant demand for ever increasing output.

The HBR article goes on to recommend how leaders can better set expectations related to AI use:

When organizations celebrate “productivity gains” without clarifying workload implications, employees interpret this as work intensification. That ambiguity alone may increase stress. Leaders reduce strain when they clearly define AI’s purpose in the organization, articulating how it reshapes role scope, setting guidance around oversight, and clarifying how workload will evolve.

[…]

Incentivizing quantity of use will lead to waste, low quality work, and unnecessary mental strain. Start from a clear, strategic north star business objective, with measurable outcomes. Exercise caution in responding to efficiency innovation. Don’t rush to backfill work recently automated by an ingenious worker; doing so immediately will feel punitive and disincentivize further innovation.

[…]

Some of the most valuable human skills today, including discernment, decision making, and strategic planning, require focused attention. While burnout has become a point of concern in many workplaces, mental fatigue is more likely go undetected in existing workplace surveys. Organizations should evolve people analytics measures to monitor cognitive load overall, and safeguard against mental fatigue with AI use as a novel job-related risk. Cultures, teams, and leaders that prioritize cognitive thriving can expect to see better judgments, fewer errors, and higher retention rates for top talent.

Anyway, forgive me, for Claude beckons and the AI isn’t going to solve this bug completely by itself (not yet, at least…).

“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. “Replant.” “Recycle.” “Warm worktrees.” But we were all describing the same thing. Hey, this sounds 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, stashing and unstashing, or 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

10+ minutes, every single time you want to create a new worktree. And 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 I’ve read seem to 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…) and waiting times greatly reduced. 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!

“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

Old board games

One day, we’re going to take this for granted (maybe we already do?)

I remember a time in the late 80s or 90s, visiting one of my grandparents’ homes and playing… some board game. All I really remember is that it had:

– a bazaar
– some electronic component (with a sinister voice, maybe?)

That was it! I don’t really remember much else.

I search Google and get:

Clicking through, none of that looked or sounded familiar.

Let’s try ChatGPT.

Wouldn’t you know… it basically one-shot the search result!

That initial image looked like the ones Google returned, so no dice (hah) there. But! Dark Tower? What is this?

Uhhh, THIS WAS IT!

Apparently, it’s fondly remembered. And there is a modern sequel that uses your smartphone.

EDIT:

I realize that this might not be a totally fair comparison since I gave ChatGPT so much more context. It might be the nature of how we interact with these services (e.g., natural language chat vs “guttural” input in a search engine).

But… just to do some proper due diligence:

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:

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.