LLMs have a strong pull toward over-abstraction. Ask for a discount function and you might get a strategy pattern with abstract base classes, dataclasses, and a calculator object — all for a single multiplication. Simplicity First is the discipline of writing the minimum code that actually solves the stated problem, deferring every other complexity until the moment it’s genuinely needed.
The rules
- No features beyond what was asked. If the request didn’t mention it, don’t build it.
- No abstractions for single-use code. One call site does not need an interface.
- No “flexibility” or “configurability” that wasn’t requested. Optional parameters, pluggable backends, and configuration systems are features — treat them that way.
- No error handling for impossible scenarios. Handle the errors that can actually happen.
- If you write 200 lines and it could be 50, rewrite it. The first draft is not the final answer.
The test
Ask yourself: “Would a senior engineer say this is overcomplicated?”
If yes, simplify. Every time.
Over-abstraction example
Request: “Add a function to calculate discount”
What LLMs do wrong — strategy pattern for a single multiplication
The correct approach — one function until complexity is actually needed
Speculative features example
Request: “Save user preferences to database”
What LLMs do wrong — building cache, validation, and notifications nobody asked for
The correct approach — just what was asked
When to add complexity
Complexity earns its place only when you actually need it — not when you might need it someday.
Add caching when performance data shows it’s needed. Add validation when bad data actually appears. Add merging when the requirement is stated. Refactor to a class when you have multiple call sites with shared state. Until then, the simple version is the correct version.
The simple versions are faster to implement, easier to test, easier to understand, and easier to refactor when real complexity arrives. Good code solves today’s problem simply — not tomorrow’s problem prematurely.