Lesson 27: Hooks: Deterministic Automation
Day 27 of 50 · ~7 min read · Phase 4: Integrations
The Opening Question
You've spent the last few lessons learning how Claude Code can think through problems, make decisions, and act on your codebase. It's powerful stuff.
But here's a harder question: what if you don't want Claude to think?
Not because Claude is unreliable — but because some tasks shouldn't depend on judgment at all. They should just happen, every single time, the same way, no exceptions.
Think about linting. Every time you modify a file, you probably want it linted. But should Claude decide whether to lint? Should it reason about when linting is necessary? Or should it just... always lint, automatically, without asking?
That's the difference between automation and intelligence. Sometimes you want automation.
Discovery
Question 1: What if a task is 100% deterministic?
Let me ask you: when you finish editing a file, do you want Claude to choose whether to run your linter? Or do you want the linter to always run?
Pause and think. What's the problem with leaving it to Claude's judgment?
Here's the issue: if Claude decides whether to lint, you've introduced variability. Maybe today it lints, maybe tomorrow it forgets. Maybe it thinks "this file is already formatted well" and skips it. Now you have inconsistency.
But some things should never vary. Linting. Formatting. Running pre-commit hooks. Logging. Security checks. These aren't judgment calls — they're rules.
Question 2: So what's a hook?
In Claude Code, a hook is a deterministic script that runs automatically at specific points in your workflow — always, without Claude needing to decide.
Think of hooks like event listeners in JavaScript. They're tied to specific moments:
- PreToolUse: Before Claude uses a tool (edit, bash, etc.)
- PostToolUse: After Claude uses a tool
- SessionStart: When you start a Claude Code session
- SessionEnd: When you end a session
What should happen at each of these moments? It's up to you to define.
What's the advantage of having these run automatically, instead of Claude choosing?
Question 3: How do hooks differ from skills?
You learned about skills in earlier lessons — custom slash commands that Claude Code can invoke. If you want to run a linter, couldn't you just ask Claude to invoke a /lint skill?
You could. But there's a crucial difference:
Skills are AI-driven. Claude must decide to invoke them. "Should I run the linter now? Let me think..." — and sometimes it might forget, or decide it's not necessary.
Hooks are deterministic. They run automatically, 100% of the time, based on a trigger event. No decision. No variability. Pure automation.
One is reactive (Claude chooses). One is automatic (you choose, once, and it's locked in forever).
Question 4: What should hooks actually do?
Here are real examples:
- Post-edit hook: After Claude edits a file, automatically run the formatter
- Pre-commit hook: Before Claude creates a commit, run tests
- Session-start hook: When you start a session, log the timestamp and session ID
- Post-command hook: After any tool runs, validate that it succeeded
What do these have in common? They're not creative. They're not decisions. They're just "execute this script, no thinking required."
The Insight
Hooks are the part of Claude Code that should never think. They're deterministic automation — scripts that run at specific moments in your workflow, always the same way, no judgment. They're how you enforce consistency and safety without burdening Claude with decision-making.
The mental model: Hooks are like laws in a system. You don't re-decide the laws every day — they just apply, automatically. "Whenever Claude edits a file, the file must be linted afterward." Done. No negotiation.
Try It
Let's configure your first hook. You'll create a simple one that logs every session start.
-
Create the hook directory:
mkdir -p ~/.claude/hooks -
Create a hook script (
~/.claude/hooks/session-start.sh):#!/bin/bash echo "Session started at $(date)" >> ~/.claude/session-log.txt echo "Working directory: $(pwd)" >> ~/.claude/session-log.txt -
Make it executable:
chmod +x ~/.claude/hooks/session-start.sh -
Start a new Claude Code session and check the log:
tail ~/.claude/session-log.txt
You should see your timestamp. That's a hook working. No decision, no slack — just execution.
-
Create a more useful hook. Add a post-edit hook that automatically formats Python files:
Create
~/.claude/hooks/post-edit.sh:#!/bin/bash # Auto-format Python files after editing if [[ "$EDITED_FILE" == *.py ]]; then black "$EDITED_FILE" 2>/dev/null || true fi -
Test it. Have Claude edit a Python file. Your hook should automatically format it.
Key Concepts Introduced
| Concept | Definition |
|---|---|
| Hook | A deterministic script that runs automatically at specific workflow moments (pre/post-tool, session start/end) |
| Deterministic automation | Tasks that should run 100% consistently, without AI judgment, based on fixed rules |
| PreToolUse / PostToolUse | Hooks that trigger before and after Claude uses any tool (edit, bash, etc.) |
| SessionStart / SessionEnd | Hooks that trigger when a Claude Code session begins or ends |
| Hook chaining | Multiple hooks that work together to enforce a complete workflow |
Bridge to Lesson 28
Now you've seen how to enforce consistency without asking Claude to think. But hooks are just one kind of safety net.
Tomorrow's question: How do you actually know the code Claude writes is correct?
We'll tackle testing — the most powerful way to validate Claude's work before it ever reaches your users. And you'll see how to make testing automatic, too.