Skip to main content
These examples show concrete before-and-after code for each of the four principles. Each one captures a pattern that appears repeatedly in real LLM-assisted coding sessions — use them to recognize when a model is about to go off the rails, and to calibrate what “correct” behavior looks like before you get there.

1. Think before coding

When a request is ambiguous, LLMs tend to pick an interpretation silently and implement it. The result looks complete but is built on assumptions the user never validated.

Hidden assumptions: exporting user data

Request: “Add a feature to export user data”
Wrong approach — silent assumptions
This code assumes: export ALL users (ignoring privacy and pagination), write to a local file, include specific fields, use hardcoded fieldnames — none of which were specified.
Correct approach — surface assumptions first

2. Simplicity first

LLMs frequently reach for abstractions and design patterns before any complexity is warranted. The result is code that follows best practices but is harder to read, harder to test, and solves a problem that doesn’t exist yet.

Over-abstraction: discount calculator

Request: “Add a function to calculate discount”
Wrong approach — overengineered for a single use case
Correct approach — minimum code that solves the problem
Add complexity only when you actually need multiple discount types. If that requirement comes later, refactor then.

3. Surgical changes

When fixing a bug, LLMs often “improve” unrelated code in the same function — reformatting, renaming, adding type hints, strengthening validation. Each individual change seems reasonable, but together they produce a diff that is hard to review and risky to ship.

Drive-by refactoring: fixing an email validation bug

Request: “Fix the bug where empty emails crash the validator”
Wrong approach — changes far beyond the bug fix
Problems: strengthened email validation beyond the reported bug, added username length and format rules nobody asked for, changed comments, added a docstring.
Correct approach — only the lines that fix empty email handling
Every changed line traces directly to fixing the empty email crash.

4. Goal-driven execution

Vague instructions produce vague plans. When a task has no explicit success criteria, the model improvises — and you have no way to verify the result is correct until something breaks in production.

Multi-step with verification: adding rate limiting

Request: “Add rate limiting to the API”
Wrong approach — all at once, no verification
Correct approach — incremental steps, each independently verifiable

Anti-patterns summary

Key insight

The “overcomplicated” examples are not obviously wrong — they follow design patterns and best practices. The problem is timing: they add complexity before it is needed, which makes code harder to understand, introduces more bugs, takes longer to implement, and is harder to test.
Good code is code that solves today’s problem simply, not tomorrow’s problem prematurely.
The simple versions are easier to understand, faster to implement, easier to test, and can be refactored when complexity is actually needed.