🪓 Require the smallest possible change
AI tends to produce seemingly complete solutions, even when a smaller solution would suffice: premature abstractions, unnecessary layers, redundant interfaces, internal frameworks, too many files, excessive configuration, handling hypothetical cases.
The practical rule — paste it into your AGENTS.md
Implement the smallest change that satisfies the acceptance criteria. Don’t create new abstractions, dependencies, or layers without a proven need.
Two sentences. This is the best cost-benefit control in this entire course.
How to read: both sides pass the same tests. The difference shows up six months from now, when someone needs to change something: on the left, they edit two files; on the right, they need to understand the factory, the registry, and the configuration layer before touching a single line.
Smaller
That meets the criterion
Preserve
The existing structure
Justify
Every new component
Reviewable
A small diff gets read
🏗️ Avoid premature abstractions
A bad abstraction costs more than duplication: to change anything, you first have to undo the abstraction. And it looks like quality—nice names, symmetrical layers, everything "extensible".
✓ When to abstract
- ✓There are two or three uses real, unanticipated
- ✓Uses vary along the same axis (the abstraction captures the right variation)
- ✓Duplication has already caused an inconsistency bug
✗ When NOT to abstract
- ✗"One day we’ll need to support another provider"
- ✗A single use, with an interface created “just in case”
- ✗Surface similarity: two things that look alike today but evolve separately
💡 Practical tip
Always ask for both versions: "show me the simple and extensible solutions, with the cost of each; recommend one and say what it sacrifices". The conversation gets focused in thirty seconds—and the answer is usually the simple one.
2–3 real uses
Before abstracting
Hypothetical
Isn’t a requirement
Wrong > duplicated
In cost
Two versions
Always compare
📦 Manage dependencies and changed files
Every dependency is an attack surface, an update cost, and a risk of abandonment. And every extra file in the diff reduces the chance that someone will actually review it—past a certain size, the PR isn’t reviewed: it’s accepted.
🚦 Recommended controls
- •Prohibit new dependencies without explicit approval.
- •Limit the number of files changed per task.
- •Avoid refactoring outside the scope—it becomes another PR.
- •Remove unused code orphaned by the change.
- •Evaluate the maintenance cost before accepting the solution.
⚠️ Attention
An AI-suggested dependency deserves an extra check: does it actually exist? Is it maintained? Is the name what you expect? An invented package—or one with a name similar to a real package—is a known supply chain attack vector.
Ongoing cost
Every dependency has
Does it really exist?
Check the package
File limit
Signals scope
Orphan
Remove what’s left
👯 Fight semantic duplication
This is the kind of debt AI generates most, because each task starts without remembering what already exists. The result is a third date-formatting function, with a different name, in another module, with different edge-case behavior.
🧪 Copyable exercise — duplication hunt
Objective: measure the semantic debt accumulated in your repository. Run it once per quarter.
Analise <pasta ou repositório> e encontre DUPLICAÇÃO SEMÂNTICA: trechos que resolvem o mesmo problema com implementações diferentes (não copie-e-cola literal). Para cada grupo encontrado, informe: - o que fazem (uma frase); - onde estão (arquivo:linha de cada ocorrência); - as diferenças de comportamento entre elas (especialmente em bordas: nulo, vazio, fuso horário, arredondamento, erro); - qual é a mais completa e por quê; - risco de consolidar (baixo/médio/alto). Não altere nada. Ordene por risco de inconsistência para o usuário final.
How to verify: choose the highest-risk group and manually check the edge-case differences. There’s almost always at least one case where the two implementations disagree—and that’s a bug no one had reported yet.
"Does it already exist?"
Ask before creating
Edges
Where they disagree
Inconsistency
Becomes a user-facing bug
Consolidate
Recurring work
🧪 Be Wary of Superficial Tests
A test that runs the code without asserting relevant behavior gives you the worst possible combination: a sense of security with zero protection — and still blocks refactoring, because it tests the implementation instead of the behavior.
The “break it on purpose” test
Introduce an obvious bug in the function and run the suite. If nothing turns red, that test isn't testing anything. It's the quickest way to evaluate AI-generated tests.
Behavior, not implementation
A test that checks which internal methods were called breaks with every refactor and catches no defects. Test what goes in and what comes out.
Ask for errors and edge cases, not the happy path
Explicitly instruct: "prioritize error cases, limits, and concurrency; don't generate redundant happy-path tests". Without this, you get twenty variations of the same scenario.
Break it on purpose
Did the test fail?
Behavior
Input and output
Edges
That’s where the bug lives
Empty test
It’s debt, not an asset
🎭 Detect code that violates the intent
It’s the most dangerous flaw in AI-driven development: the solution that passes the tests and is still wrong. It passes every automated gate with a green signal because it optimized precisely for the verifier.
🚩 Signals in the diff
- • Condition that specifically handles the value used in the test.
- • try/catch that swallows the exception and returns a default value.
- • Cache added “to improve performance” that actually hides the inconsistency.
- • Verification removed "because it was causing a failure".
- • Update the old test in the same commit as the fix.
Quick check: the agent delivered 18 files, 3 new dependencies, and an abstraction layer—and everything passes. What should you do?
Read the diff
Not just the result
Swallowed exception
Red flag
Modified test
Alongside the fix
Target metric
Stops measuring
📌 Module Summary
Next track:
Track 5 - Playbooks, Skills, Rules, and Governance: turn all of this into a reusable process.