AI Bonus: Turn Claude Code Into a Senior Developer

Every Honest Code book comes with the tools to make Claude Code actually good. Here's what's in the bundle, and why it works.

"I spent two weeks building an app with Claude Code and ended up with something that looked impressive but was a nightmare to maintain. Then I applied the Honest Code methodology. The result was night and day: simplified code, faster performance, and a clean architecture I could actually understand and extend. I stored the methodology in Claude's memory, and now every conversation, every file, every decision starts from those principles. It went from fighting the tool to the tool fighting for me."

Vincent Guyaux, CEO, Buckler

The problem

Left to its defaults, Claude Code reaches for classes, inheritance, and caching layers by reflex. It creates three-level class hierarchies for problems that need a dict and a function. It adds React where server-rendered HTML is the right answer. Every conversation drifts further from what you actually wanted.

The file below fixes that. Permanently.

What comes with the book

Three things, delivered in your Gumroad download after purchase:

🧠
Honest Code Claude Skill
A plug-and-play skill for Claude Code. Drop it in your ~/.claude/skills/ directory and Claude flags class abuse, hidden state, mock-heavy tests, and other dishonest patterns automatically, every time you write or review code. Not a prompt template. Not a one-time instruction. A permanent upgrade to how Claude reasons about your codebase.
GET THE BUNDLE
📦
Honest FastAPI Starter Kit
A complete project scaffold that follows every principle in the book. Pure functions, dict-dispatch routes, I/O at the boundary, flat composition. Unzip it and start building without fighting framework defaults.
GET THE BUNDLE
📜
The Principles File
A single markdown file containing every principle from this book. The Claude skill is built on top of it. The starter kit ships it alongside the code. And it's also public, right here on this page, so you can read it, copy it, or feed it to whatever tools you already use.
Scroll down to copy. No purchase required for this one.

The skill and the starter kit are what make the bundle worth the price of admission. The principles file is the open-source core they are both built on. Have all three and you never write dishonest code again.

Setup (30 seconds)

  1. Copy the text below into a file called coding-principles.md
  2. Save it in your Claude Code project memory directory:
    ~/.claude/projects/YOUR_PROJECT/memory/coding-principles.md
  3. That's it. Now just ask Claude:
"Is this code honest?"

Claude will evaluate your code against every principle in the book and tell you exactly where the problems are. You can also ask it to rewrite existing code, audit a codebase, or build new features from scratch. The principles apply to everything it generates.

The principles file

coding-principles.md
# Honest Code: Coding Principles

## Dict-Lookup Polymorphism
Most imperative conditional structures (if/elif/else chains) that dispatch on type or category can be replaced by a dict mapping keys to functions: `HANDLERS = {"email": send_email, "sms": send_sms}` then `HANDLERS[channel](data)`. The dict is a declarative dispatch table. Adding a new case means adding a row, not modifying control flow.

## Typed Dicts Over Classes
A `class User` with fields, methods, getters, setters, and lifecycle hooks becomes `User = TypedDict("User", {"email": str, "name": str})`. The data is just data—no behavior attached. If you can't `json.dumps()` it, you have hidden state that will break serialization, caching, and testing.

## Pure Functions Over Methods
A method like `user.validate()` that mutates internal state becomes `validate_user(user: dict) -> dict`. Input in, output out. The function has no access to `self` because there is no `self`. No side effects, no surprises.

## I/O at the Boundary
Pure business logic functions in the middle; I/O (database, HTTP, file system) happens once, at the edges (route handlers, CLI entry points). The boundary calls the pure function and then does the I/O with the result. This is why mocks become unnecessary—the pure core has nothing to mock.

## Flat Composition Over Inheritance
Instead of `class B extends A extends Base`, use `pipe(validate, authenticate, rate_limit, create_order)`. Each step is an independent function. The pipeline is visible at the point of assembly. No `super()` calls, no hidden method resolution order.

## DOM as State (DATAOS)
The DOM *is* the state. Instead of Redux/MobX/Zustand synchronizing a shadow copy of server state, the server renders HTML and HTMX swaps it into the page. `hx-get` + `hx-target` replaces `useState` + `useEffect`. One copy of truth, not two.

## HTML Attributes Over Imperative DOM Manipulation
Instead of `addEventListener`, `querySelector`, `innerHTML` in JavaScript, use `hx-post="/endpoint"`, `hx-target="#result"`, `fx-format="currency"`. The attribute declares intent; the library handles mechanism. Seventy-three lines of JS become six attributes.

## Typed Exceptions at the Boundary
Don't catch inside business logic. Let functions raise. The route handler (or supervisor) catches, inspects the exception type (`ValidationError`, `GatewayTimeout`), and returns the appropriate status code. Retry logic belongs in the task queue infrastructure, not inline in the function.

## SQL Over Application Caches
Before adding a cache, profile the query. A single SQL join with proper indexes runs under 3ms. The cache adds invalidation bugs, stale data, and a second source of truth. Fix the query or the schema first. Only cache after measurement proves it necessary.

## Pure Function Assertions Over Mocks
`assert f(input) == expected_output`—that's the whole test. If you need 9 mocks to test a function, the function has 9 hidden dependencies. Extract the pure logic; test it directly. Test the wiring separately with integration tests that hit real services.

## Type Declarations Over Imperative Validation
Instead of writing `if not isinstance(x, str)`, `if len(x) > 255`, `if not re.match(...)`—declare a Pydantic schema, a TypedDict, a SQL column constraint, or an `<input type="email">`. The runtime, type checker, database, or browser enforces the constraint. The programmer declares it; the machinery enforces it.

## Context Managers Over Instance State
Instead of `self._connection = await connect()` stored on a class, use `async with create_connection(config) as conn:`. The connection opens and closes within the scope. No persistent state leaks into the caller. Crash recovery is trivial because there's nothing to clean up.

## Configuration as Parameters
Instead of `self._config` set in `__init__`, pass `config: dict` as an argument to each function that needs it. The dependency is visible in the signature. No hidden state, no initialization order bugs.

## Simple Gherkin Steps Signal Honest Architecture
If your Gherkin step definition is 30 lines of mock configuration, the code under test has hidden dependencies. When the function is pure, the step definition is: call the function, check the result. Simple step definitions are a signal of honest architecture.

## Declarative Equivalents Over Framework Lifecycle Hooks
Instead of `componentDidMount`, `useEffect` cleanup, `ngOnInit`—use HTMX attributes that declare when to load (`hx-trigger="load"`), or server-rendered HTML that arrives ready. No client-side initialization sequence.

## Strangler Pattern for Migration
Extract one pure function from one class method per sprint. The method now calls the function. The class still exists; the interface doesn't change. After six months the class is a thin shell that does nothing, and removing it is a trivial cleanup.
← Back to home