· ai and git · 12 min read

Git is the new code

AI is writing more code than ever before (if not all of it). But our most important job as developers hasn’t gone away—it’s simply changed. We spend less time typing code and more time reading, reviewing, and making sure everything works as it should. Here are some quick guidelines and git commands to help you

Neciu Dan

Neciu Dan

Hi there, it's Dan, a technical co-founder of an ed-tech startup, host of Señors at Scale - a podcast for Senior Engineers, Organizer of ReactJS Barcelona meetup, international speaker and Staff Software Engineer, I'm here to share insights on combining technology and education to solve real problems.

I write about startup challenges, tech innovations, and the Frontend Development. Subscribe to join me on this journey of transforming education through technology. Want to discuss Tech, Frontend or Startup life? Let's connect.

Share:
Git is the new code

What a time to be a developer.

A few weeks back, Spotify co-CEO Gustav Söderström made a surprising announcement during their Q4 earnings call. He said their most experienced engineers “have not written a single line of code since December.” Instead, they’re using an internal system called Honk, built on top of Claude Code, to handle everything.

They shipped over 50 new features in 2025 this way.

Before you assume this is only happening at Spotify, it’s not. Satya Nadella said AI now writes 20-30% of Microsoft’s code. Sundar Pichai confirmed that over 25% of Google’s new code is AI-assisted. Mark Zuckerberg expects AI to write most of Meta’s code within the next year. These aren’t small startups with just a few developers.

These are the biggest engineering orgs on the planet.

Addy Osmani, who works on developer experience at Google, has been tracking this shift obsessively. He wrote about what he calls “the 80% problem”: by late 2025, early adopters reported that AI was generating about 80% of their code. Sounds amazing, right? More code, faster, everybody goes home early.

Except that’s not what happened.

Faros AI and Google’s DORA report show that teams using a lot of AI merged nearly twice as many pull requests, but the time spent reviewing them also increased by about 91%.

That’s a huge jump.

Atlassian’s 2025 survey says 99% of devs using AI save over 10 hours a week, but most of them don’t feel their daily workload has gone down. The hours we’re not spending writing code? We’re now pouring into reviewing it.

And the numbers aren’t getting any prettier: PRs are about 18% bigger, incidents per PR are up 24%, and change failure rates have climbed 30%. Nearly half the code written by AI — about 45% — has security issues.

So, this is where we are now: AI generates the code, and we’re the ones left reviewing, testing, and shipping it. When things go wrong in the middle of the night, it’s still our job to fix the problems.

Which brings us to Git.


How does a British developer start version control for his side project?

git init.


Git is the new programming language. Not because you write apps in it, but because this is where you’ll spend most of your time. When AI writes the code, your job is to understand what changed, why it changed, and whether it’s safe to ship. The more you know Git — its commands, workflows, and shortcuts — the better you can review what the AI produced and catch mistakes before they reach production. The next sections cover the Git skills you need for this work.


Undoing Commits

Mistakes happen! Maybe we put the wrong message, or we forgot to add a specific file, and instead of committing again, we can simply undo our last commit.

git reset --soft HEAD~1

This undoes the last commit but keeps all changes staged exactly as they were. Working directory untouched. Fix what you need to fix, restage if necessary, recommit.

Do you need to go back further than one commit? HEAD~2, HEAD~3, etc, they do the same thing.

The key thing to remember is that --soft keeps everything, while --hard deletes everything.

Use --soft for local fixes before pushing, and be very careful with --hard, as it can permanently remove your work.

Another thing to note is that if the commit has already been pushed and your teammates have pulled it, git reset will mess up their git history.

Instead, try to use git revert, which creates a new commit that undoes the changes without altering the history, making it safe for you and your colleagues.


The Reflog

Here’s something that changed how I think about Git: it almost never truly deletes anything.

Even after a bad rebase, an accidental branch deletion, or a reset --hard, which you regret right away, the data is still there, hidden in the reflog.

git reflog

The reflog records every position HEAD has ever pointed to. Unlike git log, which shows the commit history of a branch, the reflog shows your personal movement through the repo: every checkout, commit, reset, and rebase. It’s your private timeline.

A typical reflog looks like this:

a1b2c3d HEAD@{0}: reset: moving to HEAD~2
e4f5g6h HEAD@{1}: commit: Adding a payment processing system
i7j8k9l HEAD@{2}: commit: Refactoring the auth middleware
m0n1o2p HEAD@{3}: checkout: moving from feature/auth to main

If you accidentally reset too far and have lost a commit with some important code?

You can always find it in the reflog:

git checkout e4f5g6h         # inspect the lost commit
git checkout -b recovered    # save it to a new branch

Entries stay for 30 to 90 days, depending on your git config.


Reading Diffs

git diff main..feature/new-auth

This command shows every change between two branches. But if you have two big AI-generated PRs, the raw diff would be overwhelming.

You can and should break it down:

git diff main..feature/new-auth -- src/auth/       # scope to a directory
git diff main..feature/new-auth --stat             # summary: files changed, lines added/removed
git diff main..feature/new-auth --name-only        # just the filenames

I always start with --stat. It tells you the shape of the PR before you get deep into the code.

Reviewing commit by commit

AI-generated PRs often appear as a single large commit. Which is a big NO-NO!

Luckily, developers have started the best practice of committing small and often, and now it’s much better to review the PRs one commit at a time:

git log --oneline main..feature/new-auth           # list all commits in the branch
git show <commit-hash>                              # inspect a single commit
git log -p main..feature/new-auth                  # full patch for every commit

Who changed what and when

When reviewing unfamiliar code or trying to understand why something exists:

git blame src/auth/middleware.js                    # line-by-line authorship
git log --follow -p -- src/auth/middleware.js       # full history of a single file

git blame is now one of the most important commands because it shows whether a line was written by a human, added through an AI workflow, or is part of older code that the AI may have changed.

Actually checking out the branch.

I can’t stress this enough. Every PR now contains a lot of code, and about 75% of it will be AI-generated. It’s your responsibility to pull it locally, run it, test it, and explore the changes.

If the PR added an auth flow, try logging in with incorrect credentials. If it added a payment form, see what happens if the network fails.

Best practice: the PR author should write Given/When/Then acceptance criteria with a checkbox for each scenario. The reviewer can then pull the branch and tick each one off as they walk through the actual flow.


Cherry-Picking

Most of us know git cherry-pick — grab a commit from one branch, apply it to another.

The trick most people don’t know is the --no-commit flag:

git cherry-pick -n <commit-hash>

This command brings the changes into your working directory and staging area without committing them. It’s very useful if you want to review changes before committing, combine several cherry-picks into one clean commit, or check that the changes work with your current code before making them permanent.

I always use this command when on a remote server or codespace, and I need to bring a specific commit to test it.


Stash on Steroids

Everyone knows stash exists. Almost nobody uses it properly. The typical workflow is:

git stash        # throw everything in
git stash pop    # get it back

That’s fine for simple cases. But the stash is actually a full stack with names, indexes, and options for selective stashing.

You can name your stashes.

git stash push -m "WIP: auth flow refactor"
git stash push -m "experimental: new caching strategy"

Now, when you list the stashes with git stash list, you can see everything you stashed:

stash@{0}: On feature/auth: experimental: new caching strategy
stash@{1}: On feature/auth: WIP: auth flow refactor

You can target specific stashes.

git stash pop stash@{1}     # apply and remove a specific stash
git stash apply stash@{0}   # apply and keep it around
git stash drop stash@{2}    # remove without applying

pop removes the stash after applying. apply keeps it. The number in the brackets is the index on the stash. Think of it as a stack or an array.

Use apply when you want to test the same changes on multiple branches.

You can stash only specific files.

git stash push -m "just the config changes" -- config/ .env.local

Or go interactive and pick individual hunks:

git stash push -p -m "partial stash"

This command walks through each change and asks if you want to stash it, similar to how git add -p works. It’s really helpful when your working directory has changes from different tasks.


Finding Bugs with Bisect

I’ve talked about Bisect before and how useful it is.

Something is broken in production, but it worked two weeks ago. Since then, there have been 200 commits from a dozen developers and several AI agents. Checking each one by hand would take forever.

git bisect uses binary search to find the exact commit that broke things.

git bisect start
git bisect bad                   # current commit has the bug
git bisect good v2.3.0           # this tag was working

Git checks out a commit halfway between the two. Then you test it locally and tell Git if it’s good or bad (good meaning the defect is not here, let’s keep going):

git bisect good    # this one's fine
# or
git bisect bad     # this one's broken

It keeps halving until it finds the first bad commit. When you’re done:

git bisect reset

You can automate it

If you have a test that catches the bug:

git bisect start HEAD v2.3.0
git bisect run npm test

Git automatically runs the test at each step and stops when it fails.


Working on Multiple Branches with Worktrees

Context switching kills productivity. You’re deep in a code review, and someone pings you about a hotfix. The old way: stash, switch branches, do the work, switch back, pop — is tedious and error-prone.

The command git worktree lets you check out multiple branches into separate directories at the same time:

git worktree add ../hotfix main

Now you have two working directories sharing the same Git history.

~/projects/my-app/                # reviewing feature/new-dashboard
git worktree add ../my-app-hotfix hotfix/login-bug
cd ../my-app-hotfix
# fix the bug, commit, push
cd ../my-app
git worktree remove ../my-app-hotfix

Keep things tidy:

git worktree list              # see all active worktrees
git worktree remove ../hotfix  # clean up
git worktree prune             # remove stale metadata

Rewriting History

Before pushing a feature branch, the commit history usually looks something like this:

fix typo
WIP
actually fix the bug
add feature X
WIP part 2
fix tests

This is normal, and it’s even more common when AI generates code in steps. But it doesn’t have to be part of the permanent record.

git rebase -i HEAD~6

This opens an editor showing the last 6 commits; here you can reorder, squash, reword, or drop your commits. Here is how:

  • squash (s): merge into the commit above, combine messages
  • fixup (f): same, but discard the message
  • reword (r): keep changes, edit the message
  • drop (d): remove entirely

Although you should never rebase commits that have already been pushed to a shared branch.


Seeing Who Knows What with Shortlog

When working on a big codebase, especially one you didn’t build, it helps to know who has context on what:

git shortlog -sne

-s for summary, -n for numerical sort, -e for email, and you can scope it to a directory:

git shortlog -sne -- src/auth/

This will give you the names and emails of the most active contributors in the auth folder. Invaluable when you need to track down someone who actually understands the legacy code you’re reviewing.

Pro Tip: In your CI/CD pipeline, you can automate code reviewers based on shortlog so the person with the most context is assigned as a reviewer. Although if that person is a bot, you might be in trouble.


Cheat Sheet

Some extra commands specifically useful during code review:

# compare a file across multiple branches
git diff main:src/app.js feature:src/app.js

# search commits for a string (the "pickaxe")
git log -S "deprecated_function" --oneline

# history of a specific function
git log -L :functionName:src/file.js

# graphical branch history
git log --oneline --graph --all

# files changed in the last N commits
git diff --name-only HEAD~10

We are all reviewers now.

Here’s the uncomfortable part.

Our most important job now isn’t writing code — it’s understanding it. The gap between these two skills is growing every day. Osmani himself described crossing a line he didn’t expect: an AI agent implemented a feature he’d been putting off for days, the tests passed, he skimmed it, nodded, and merged.

Three days later, he couldn’t explain how it worked.

He and others call this “comprehension debt.” You can still review code long after you’ve lost the ability to write it from scratch.

So what does responsible reviewing actually look like?

Check out the code and run it.

Read the code until you can explain it — not just until the tests pass, but until you could explain it to a teammate or debug it at 2 AM if something goes wrong.

If you can’t do that, the review isn’t finished.

Always question the architecture. AI is good at writing functions that work, but it’s not good at understanding how those functions fit into the bigger system.

Is it duplicating logic? Adding strange dependencies? These are the issues AI often misses.

And finally, take responsibility for the outcome.

This is important: when code reaches production, it doesn’t matter if a human wrote it or if it was generated by AI. If you reviewed and approved it, you are responsible for it, along with whoever or whatever wrote it.

The reviewer’s signature on a pull request is not a formality. It’s a statement: this code is correct, it’s secure, and it’s ready for production. That is true now, regardless of whether the author was a junior dev, a senior engineer, or an LLM.

The AI writes the code. You make sure it’s worth shipping.

Let’s fucking do this. 🚀

From Lizard to Wizard Workshop

Engineering Excellence Workshop — Barcelona & Remote. Design Patterns, System Design, Security, Accessibility, Observability & more.

Join waitlist
    Share:
    Author

    Discover more from The Neciu Dan Newsletter

    A weekly column on Tech & Education, startup building and occasional hot takes.

    Over 1,000 subscribers

    🎙️ Latest Podcast Episodes

    Dive deeper with conversations from senior engineers about scaling applications, teams, and careers.

    Leveling Up as a Tech Lead with Anamari Fisher
    Episode 24
    52 minutes

    Señors @ Scale host Neciu Dan sits down with Anamari Fisher — engineering leader, coach, and O'Reilly author of 'Leveling Up as a Tech Lead' — to explore the first jump into leadership. Anamari shares how she went from software engineer to tech lead and product director, why accountability is the key differentiator from senior engineer, and how to scale your impact through soft skills that actually work in real teams.

    📖 Read Takeaways
    MicroFrontends at Scale with Florian Rappl
    Episode 23
    69 minutes

    Señors @ Scale host Neciu Dan sits down with Florian Rappl — author of 'The Art of Micro Frontends,' creator of the Piral framework, and Microsoft MVP — to explore how micro frontends are transforming how we build scalable web applications. Florian shares hard-won lessons from over a decade of building distributed systems, from smart home platforms to enterprise portals for some of Germany's largest companies.

    📖 Read Takeaways
    Nuxt at Scale with Daniel Roe
    Episode 22
    54 minutes

    Señors @ Scale host Neciu Dan sits down with Daniel Roe, leader of the Nuxt Core team at Vercel, for an in-depth conversation about building and scaling with Nuxt, Vue's most powerful meta-framework. Daniel shares his journey from the Laravel world into Vue and Nuxt, revealing how he went from being a user to becoming the lead maintainer of one of the most important frameworks in the JavaScript ecosystem.

    📖 Read Takeaways
    State Management at Scale with Daishi Kato (Author of Zustand)
    Episode 21
    35 minutes

    Señors @ Scale host Neciu Dan sits down with Daishi Kato, the author and maintainer of Zustand, Jotai, and Valtio — three of the most widely used state management libraries in modern React. Daishi has been building modern open source tools for nearly a decade, balancing simplicity with scalability. We dive deep into the philosophy behind each library, how they differ from Redux and MobX, the evolution of the atom concept, and Daishi's latest project: Waku, a framework built around React Server Components.

    📖 Read Takeaways
    Back to Blog