- AI
- Craftsmanship
- Clean Architecture
Why AI Needs Craftsmanship: The Case for Disciplined Code Generation
Raw AI code generation is a forge without a blacksmith. Here's how the craftsman approach: Iron Law, Quality Gates, Bias Detection: turns chaos into steel.
Alexandre Mallet3 min read
Ask Claude to generate a repository class five times in a row. You'll get five different implementations. Different naming conventions. Different error handling strategies. Sometimes an interface, sometimes not. Occasionally a docblock, occasionally nothing.
This isn't a flaw in the model. It's a flaw in the process. You're running a forge without a blacksmith.
The Consistency Problem
Every senior developer knows: the hardest part of software isn't writing code. It's writing code the same way, every time, across every file, every session, every sprint. Humans struggle with this. AI models struggle harder: because they have no memory of what "your way" looks like beyond the current context window.
Here's what unguarded AI generation produces in a typical Symfony project:
// Session 1: Claude generates this
class UserRepository
{
public function __construct(
private EntityManagerInterface $em
) {}
public function findByEmail(string $email): ?User
{
return $this->em->getRepository(User::class)
->findOneBy(['email' => $email]);
}
}
// Session 3: Claude generates this for the same pattern
final class OrderRepository implements OrderRepositoryInterface
{
public function __construct(
private readonly EntityManagerInterface $entityManager,
private readonly LoggerInterface $logger,
) {}
/**
* @throws OrderNotFoundException
*/
public function getByReference(string $reference): Order
{
$order = $this->entityManager
->createQueryBuilder()
->select('o')
->from(Order::class, 'o')
->where('o.reference = :ref')
->setParameter('ref', $reference)
->getQuery()
->getOneOrNullResult();
if ($order === null) {
throw new OrderNotFoundException($reference);
}
return $order;
}
}Same team. Same codebase. Two completely different approaches to the same pattern. One uses findOneBy, the other a full QueryBuilder. One has a docblock, the other doesn't. One is final, the other isn't. One injects a logger, the other doesn't.
Multiply this by fifty classes and you've got a codebase that looks like it was written by fifty different developers: because, in a sense, it was.
The Iron Law: One Pattern, One Truth
The Iron Law Pattern is the foundation of AI Craftsman Superpowers. Before generating any code, the plugin loads your canonical example of that pattern. Not a generic template. Not a blog post. Your team's actual, approved, reviewed implementation.
When you ask for a repository, the plugin finds your existing UserRepository: the one that passed code review, that follows your conventions, that your team agreed on, and feeds it as the reference. The result:
final class OrderRepository implements OrderRepositoryInterface
{
private function __construct(
private readonly EntityManagerInterface $entityManager,
) {}
public static function create(
EntityManagerInterface $entityManager,
): self {
return new self($entityManager);
}
public function findByReference(OrderReference $reference): ?Order
{
return $this->entityManager->getRepository(Order::class)
->findOneBy(['reference' => $reference->value()]);
}
}Same structure. Same conventions. Same patterns. Every time. Zero drift.
The Quality Gate: Progressive Validation
Writing consistent code is step one. Verifying it in real-time is step two.
Every file write enters a progressive pipeline. Each level is cheaper than the one after it, and what a regex can decide never reaches a model:
- Regex validation (under 50ms, always on): structural violations (missing
declare(strict_types=1), non-final classes, setters) - LSP semantics: live, and only when you already have the language server installed
- Static analysis: PHPStan and ESLint on the changed file. Opt-in per machine, because running a project's analysers runs its code
- Architecture check: dependency direction (Domain imports nothing, Infrastructure imports Domain)
Layer and strict_types violations are refused before the write lands. Everything else is handed back to Claude as a finding it has to answer for, and the same rule fails your pipeline if it reaches a pull request. You don't find out during code review. You don't find out in CI. You find out now, while the context is fresh and the fix is trivial.
Bias Detection: The Invisible Guardrail
Here's the part nobody talks about: your prompts to AI aren't objective. They're shaped by deadline pressure, sunk cost fallacy, scope creep, and a dozen other cognitive biases you can't see because you're inside them.
The Cognitive Bias Detector watches your prompts in real-time:
- "Just add it quickly" → Acceleration bias detected. Stop. Think about the architecture impact.
- "We've already spent two weeks on this approach" → Sunk cost. The time is spent regardless. What's the right path forward?
- "Let's also handle edge case X, Y, Z while we're here" → Scope creep. File it. Ship what's ready.
This isn't about slowing you down. It's about making sure speed doesn't cost you quality.
The Craftsman's Forge
Raw AI is molten metal: powerful, shapeless, dangerous without form. The craftsman's forge gives it structure:
- Iron Law ensures every piece matches the blueprint
- Quality Gates catch defects before they cool into the codebase
- Bias Detection keeps the blacksmith honest
The result isn't slower development. It's confident development. Code you can ship without wondering if this session's output matches last session's standards.
Your code deserves a craftsman at the forge. Not just a model with a prompt.