Common Anti-Patterns
When to Use
Learn from common mistakes to avoid technical debt, security vulnerabilities, poor user experience, and cost overruns. Every anti-pattern includes WHY it's problematic.
Anti-Pattern: Trusting AI Output Without Validation
Wrong:
$summary = $ai->generateSummary($node->body->value);
$node->set('field_summary', $summary);
$node->save();
Why it's wrong: - AI hallucinates facts and makes up plausible-sounding nonsense - Generated content may contain factual errors, broken logic, or biased perspectives - No validation means errors propagate to published content - Legal/compliance risk for regulated content (medical, legal, financial)
Right:
$summary = $ai->generateSummary($node->body->value);
// Validate and sanitize
$summary = $this->validateAIContent($summary, [
'max_length' => 500,
'check_facts' => TRUE,
'sanitize_html' => TRUE,
]);
// Flag for human review if confidence is low
if ($summary['confidence'] < 0.8) {
$node->set('field_needs_review', TRUE);
}
$node->set('field_summary', $summary['content']);
$node->save();
Anti-Pattern: Synchronous AI in Request Cycle
Wrong:
// Blocks form submission for 10-30 seconds
function mymodule_node_presave(NodeInterface $node) {
if ($node->isNew()) {
$tags = $ai->generateTags($node); // Blocks here
$node->set('field_tags', $tags);
}
}
Why it's wrong: - Users wait 10-30+ seconds for form to save - Timeouts cause form submission failures - Poor user experience drives users away - Concurrent requests pile up, degrading site performance
Right:
// Queue for background processing
function mymodule_node_presave(NodeInterface $node) {
if ($node->isNew()) {
$queue = \Drupal::queue('ai_tag_generation');
$queue->createItem(['node_id' => $node->id()]);
// Form saves immediately, AI runs in background
}
}
Anti-Pattern: Not Implementing Token Limits
Wrong:
// Can send 50,000 token prompts
$prompt = "Summarize: " . $node->body->value;
$summary = $ai->chat($prompt);
Why it's wrong: - Exceeds model token limits (crashes) - Costs scale with tokens (can be $5+ per request for GPT-4) - Slow responses (30+ seconds for long prompts) - Wasteful (most content only needs excerpt for context)
Right:
// Enforce token limits
$tokenizer = \Drupal::service('ai.tokenizer');
$body = $node->body->value;
// Truncate to 7000 tokens to leave room for response
if ($tokenizer->count($body, 'gpt-4') > 7000) {
$body = $tokenizer->truncate($body, 7000, 'gpt-4');
}
$prompt = "Summarize: " . $body;
$summary = $ai->chat($prompt);
Anti-Pattern: Hardcoding Model Names
Wrong:
$response = $ai_provider->chat($messages, 'gpt-4');
Why it's wrong: - Models deprecate (GPT-3.5-turbo, text-davinci-003 already deprecated) - No runtime model switching for cost/performance tuning - Can't A/B test different models - Forces code changes to update models
Right:
// Use configuration
$model = \Drupal::config('mymodule.settings')->get('default_chat_model') ?? 'gpt-4-turbo';
$response = $ai_provider->chat($messages, $model);
// Or use model selection in automator config (UI-driven)
Anti-Pattern: Ignoring Provider Capabilities
Wrong:
// Assumes all providers support all operations
$provider = $ai_provider_manager->createInstance($provider_id);
$image = $provider->generateImage($prompt); // May not be supported
Why it's wrong: - Not all providers support all operation types - Crashes when provider doesn't support requested operation - Anthropic doesn't support image generation - Some providers don't support function calling
Right:
// Check capabilities
$provider = $ai_provider_manager->createInstance($provider_id);
if ($provider->supportsOperation('image_generation')) {
$image = $provider->generateImage($prompt);
} else {
\Drupal::messenger()->addWarning('Selected provider does not support image generation');
// Fall back to alternative provider or show error
}
Anti-Pattern: Not Handling Provider Failures
Wrong:
// No error handling
$summary = $ai_provider->chat($messages, 'gpt-4');
$node->set('field_summary', $summary);
Why it's wrong: - API failures are common (rate limits, outages, network issues) - Unhandled exceptions crash the request - No fallback means lost work - Users see cryptic error messages
Right:
// Graceful error handling
try {
$summary = $ai_provider->chat($messages, 'gpt-4');
$node->set('field_summary', $summary);
} catch (RateLimitException $e) {
// Queue for retry
$queue->createItem(['node_id' => $node->id(), 'retry' => 3]);
\Drupal::messenger()->addWarning('AI service busy, summary will be generated shortly.');
} catch (AiException $e) {
// Log error, use fallback
\Drupal::logger('ai')->error('AI generation failed: @msg', ['@msg' => $e->getMessage()]);
$node->set('field_summary', ''); // Leave empty for manual entry
}
Anti-Pattern: Over-Automating Editorial Content
Wrong: Automating 100% of blog posts, thought leadership, personal stories with AI.
Why it's wrong: - AI cannot replicate human experience, unique insights, or brand voice - Readers detect and reject generic AI content - SEO penalizes low-quality, mass-generated content - Thought leadership requires human expertise (E-E-A-T) - Legal risk: AI-generated content may plagiarize or spread misinformation
Right: - Automate tactical content: Product descriptions, meta tags, summaries, alt text - AI-assist editorial content: Outlines, research, first drafts requiring heavy editing - Human-only content: Thought leadership, case studies, personal stories, expert analysis - Hybrid workflow: AI drafts + mandatory human review and enhancement
Anti-Pattern: Not Setting Cost Budgets
Wrong: Deploying AI features without cost monitoring or limits.
Why it's wrong: - AI costs are variable and can spike unexpectedly - Uncontrolled automation can cost thousands per month - No visibility into cost drivers - Budget overruns force emergency shutdowns
Right:
// Implement cost tracking
$cost_tracker = \Drupal::service('mymodule.cost_tracker');
$monthly_cost = $cost_tracker->getMonthlyTotal();
$budget = \Drupal::config('mymodule.settings')->get('monthly_budget') ?? 500;
if ($monthly_cost > $budget * 0.9) {
// Approaching budget - switch to cheaper models
$model = 'gpt-3.5-turbo'; // Fallback to cheaper
\Drupal::logger('ai')->warning('Approaching monthly budget, using cheaper models');
}
if ($monthly_cost > $budget) {
// Exceeded budget - disable non-critical AI
throw new BudgetExceededException('Monthly AI budget exceeded');
}
Anti-Pattern: Not Providing Manual Override
Wrong: AI automations with no way for editors to regenerate or manually edit.
Why it's wrong: - Editors cannot fix AI mistakes - No recovery from poor AI output - Frustrates content creators - Forces workarounds (disabling automations entirely)
Right: - Provide "Regenerate" button for each AI-generated field - Allow manual editing of AI output - Include "Skip AI generation" checkbox for edge cases - Store AI confidence scores and flag low-confidence content for manual review