Defensive Design
Error prevention strategies that catch mistakes before they happen.
Error Prevention: Catch and prevent mistakes in real-time before they become problems, rather than requiring cleanup after the fact.
A writing assistant provides less value when feedback is late, vague, or detached from the text being reviewed:
These failure modes can cause:
Grammarly’s current support documentation says its products check text as the user writes and group suggestions into correctness, clarity, engagement, and delivery. It also documents colored underlines, suggestion cards, and controls to accept or dismiss a proposed change (Get started with Grammarly, Grammarly Editor user guide).
1. Real-Time Detection (Inline, As You Type)
2. Categorized visual coding
The exact color/category mapping can vary by Grammarly surface and plan, so this case study does not assign a universal meaning to every color. See the Editor user guide.
3. Contextual Explanations (Not Just Corrections)
These behaviors are described in How does Grammarly work? and the Editor user guide.
4. One-Click Application
See the Grammarly Proofreader user guide.
5. Goal-Based Customization
Grammarly documents these limits as well as the controls (What are Goals?).
6. Review without forced acceptance
The following are design inferences from the documented suggestion flow unless a primary source is cited; they are not Grammarly effectiveness findings.
The primary sources above establish product behavior and a historical audience figure. They do not establish the document-error, proofreading-time, confidence, or communication-quality comparisons previously shown here. Those figures have been removed.
An evaluation should define both the writing task and what counts as a valid suggestion:
| Measure | Method |
|---|---|
| Precision | Accepted valid suggestions divided by all suggestions reviewed by expert raters |
| Recall | Known issues detected, using a controlled error set |
| Correction cost | Time and actions needed to review, accept, dismiss, or reverse feedback |
| False-positive burden | Incorrect or irrelevant suggestions per 1,000 words |
| Learning transfer | Performance on an unaided follow-up task, not just edits made by the tool |
Defensive Design
Error prevention strategies that catch mistakes before they happen.
Feedback Mechanisms
Effective feedback patterns including timing, clarity, and actionability.
Learning & Memory
How immediate feedback strengthens learning and builds skills.
Forms Validation
Form validation patterns for real-time error detection.
When you use the Human Standards MCP server, these rules enforce error prevention:
forms-validate-on-blur
// Triggered when forms lack real-time validation{ severity: 'warning', rule: 'forms-validate-on-blur', message: 'Form fields should validate on blur for immediate feedback', recommendation: 'Add validation on blur to catch errors early, not on submit', reference: '/interaction-patterns/forms/'}defensive-specific-errors
// Checks if error messages are specific and actionable{ severity: 'error', rule: 'defensive-specific-errors', message: 'Generic error messages like "Invalid input" are not helpful', recommendation: 'Provide specific, actionable error messages with examples', reference: '/decision-making-errors/defensive-design/'}feedback-timing-immediate
// Validates that critical feedback is provided immediately{ severity: 'info', rule: 'feedback-timing-immediate', message: 'Error feedback should be immediate for best learning', recommendation: 'Validate on blur (individual fields) or keystroke (passwords)', reference: '/interaction-patterns/notifications-feedback/'}// Get relevant heuristics for error preventionconst errorPrevention = await mcp.callTool('get_heuristic', { id: 'H5' });// Returns: Error prevention - validation, confirmations, constraints
const errorRecovery = await mcp.callTool('get_heuristic', { id: 'H9' });// Returns: Help users recover from errors - specific messages, solutions
const systemStatus = await mcp.callTool('get_heuristic', { id: 'H1' });// Returns: Visibility of system status - immediate feedback
// Search for form validation patternsconst formDocs = await mcp.callTool('search_standards', { query: 'forms validation' });// Returns: Form design, validation timing, error messages
const defensiveDocs = await mcp.callTool('search_standards', { query: 'defensive design' });// Returns: Error prevention strategies, recovery patterns
// Example results inform implementation:// - H5 (Error Prevention): Validate on blur, not just on submit// - H9 (Error Recovery): "Password must be 8+ chars with 1 number" not "Invalid"// - H1 (System Status): Show validation state immediately (green check, red X)// - Forms docs: Position errors near fields, use aria-live for screen readersimport { useState, useEffect } from 'react';
interface ValidationRule { test: (value: string) => boolean; message: string; severity: 'error' | 'warning' | 'suggestion';}
interface FormFieldProps { label: string; name: string; type?: string; value: string; onChange: (value: string) => void; validationRules?: ValidationRule[]; helpText?: string;}
export function ValidatedFormField({ label, name, type = 'text', value, onChange, validationRules = [], helpText}: FormFieldProps) { const [isTouched, setIsTouched] = useState(false); const [validationErrors, setValidationErrors] = useState<ValidationRule[]>([]); const [validationWarnings, setValidationWarnings] = useState<ValidationRule[]>([]);
// Real-time validation (like Grammarly) useEffect(() => { if (!isTouched) return;
const errors: ValidationRule[] = []; const warnings: ValidationRule[] = [];
validationRules.forEach(rule => { if (!rule.test(value)) { if (rule.severity === 'error') { errors.push(rule); } else if (rule.severity === 'warning') { warnings.push(rule); } } });
setValidationErrors(errors); setValidationWarnings(warnings); }, [value, validationRules, isTouched]);
const handleBlur = () => { // Mark as touched on blur (after user leaves field) setIsTouched(true); };
const hasErrors = validationErrors.length > 0; const hasWarnings = validationWarnings.length > 0; const isValid = !hasErrors && isTouched && value.length > 0;
return ( <div className="form-field"> <label htmlFor={name} className="form-label"> {label} </label>
<div className="input-wrapper"> <input id={name} name={name} type={type} value={value} onChange={(e) => onChange(e.target.value)} onBlur={handleBlur} className={`form-input ${ hasErrors ? 'has-error' : hasWarnings ? 'has-warning' : isValid ? 'is-valid' : '' }`} aria-invalid={hasErrors} aria-describedby={ hasErrors ? `${name}-error` : hasWarnings ? `${name}-warning` : helpText ? `${name}-help` : undefined } />
{/* Visual indicators (like Grammarly's colored underlines) */} {isValid && ( <span className="validation-icon success" aria-label="Valid"> ✓ </span> )} {hasErrors && ( <span className="validation-icon error" aria-label="Error"> ✗ </span> )} {hasWarnings && ( <span className="validation-icon warning" aria-label="Warning"> ⚠ </span> )} </div>
{/* Error messages (like Grammarly's explanations) */} {hasErrors && ( <div id={`${name}-error`} className="validation-message error" role="alert" aria-live="polite" > {validationErrors.map((error, i) => ( <div key={i} className="validation-item"> <strong>Error:</strong> {error.message} </div> ))} </div> )}
{/* Warnings (like Grammarly's suggestions) */} {hasWarnings && !hasErrors && ( <div id={`${name}-warning`} className="validation-message warning" role="status" aria-live="polite" > {validationWarnings.map((warning, i) => ( <div key={i} className="validation-item"> <strong>Suggestion:</strong> {warning.message} </div> ))} </div> )}
{/* Help text */} {helpText && !hasErrors && !hasWarnings && ( <div id={`${name}-help`} className="help-text"> {helpText} </div> )} </div> );}
// Example usage: Password field with real-time validationexport function PasswordField() { const [password, setPassword] = useState('');
const passwordRules: ValidationRule[] = [ { test: (val) => val.length >= 8, message: 'Password must be at least 8 characters long', severity: 'error' }, { test: (val) => /[A-Z]/.test(val), message: 'Password should contain at least one uppercase letter', severity: 'error' }, { test: (val) => /[0-9]/.test(val), message: 'Password should contain at least one number', severity: 'error' }, { test: (val) => /[!@#$%^&*]/.test(val), message: 'Consider adding a special character (!@#$%^&*) for extra security', severity: 'warning' }, { test: (val) => val.length >= 12, message: 'Passwords longer than 12 characters are more secure', severity: 'warning' } ];
return ( <ValidatedFormField label="Password" name="password" type="password" value={password} onChange={setPassword} validationRules={passwordRules} helpText="Choose a strong password to protect your account" /> );}
// Example: Email field with pattern validationexport function EmailField() { const [email, setEmail] = useState('');
const emailRules: ValidationRule[] = [ { test: (val) => val.includes('@'), message: 'Email must contain an @ symbol', severity: 'error' }, { test: (val) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(val), message: 'Email format should be: name@example.com', severity: 'error' }, { test: (val) => !val.endsWith('.co') && !val.endsWith('.or'), message: 'Did you mean .com or .org? Double-check your email domain', severity: 'warning' } ];
return ( <ValidatedFormField label="Email Address" name="email" type="email" value={email} onChange={setEmail} validationRules={emailRules} helpText="We'll send a confirmation to this address" /> );}/* Form field container */.form-field { margin-bottom: 24px;}
.form-label { display: block; font-weight: 600; margin-bottom: 8px; color: #212121; font-size: 14px;}
/* Input wrapper for positioning validation icons */.input-wrapper { position: relative;}
/* Base input styling */.form-input { width: 100%; /* Ergonomics: 48px minimum touch target */ min-height: 48px; padding: 12px 48px 12px 16px; /* Right padding for icon */ font-size: 16px;
/* Default state: neutral */ border: 2px solid #BDBDBD; border-radius: 4px; transition: all 0.2s ease;}
.form-input:focus { outline: none; border-color: #2196F3; box-shadow: 0 0 0 3px rgba(33, 150, 243, 0.1);}
/* Error state (like Grammarly's red underline) */.form-input.has-error { border-color: #D32F2F; /* Red */ background-color: #FFEBEE; /* Light red background */}
.form-input.has-error:focus { box-shadow: 0 0 0 3px rgba(211, 47, 47, 0.1);}
/* Example warning state */.form-input.has-warning { border-color: #F57C00; /* Orange */ background-color: #FFF3E0; /* Light orange background */}
/* Success state (valid input) */.form-input.is-valid { border-color: #388E3C; /* Green */ background-color: #F1F8F4; /* Light green background */}
/* Validation icons (positioned in input) */.validation-icon { position: absolute; right: 16px; top: 50%; transform: translateY(-50%); font-size: 20px; pointer-events: none;}
.validation-icon.success { color: #388E3C; /* Green checkmark */}
.validation-icon.error { color: #D32F2F; /* Red X */}
.validation-icon.warning { color: #F57C00; /* Orange warning */}
/* Validation messages (like Grammarly's explanations) */.validation-message { margin-top: 8px; padding: 12px 16px; border-radius: 4px; font-size: 14px; line-height: 1.5;}
.validation-message.error { /* Accessibility: 7.2:1 contrast ratio */ background: #FFEBEE; color: #C62828; border-left: 4px solid #D32F2F;}
.validation-message.warning { background: #FFF3E0; color: #E65100; border-left: 4px solid #F57C00;}
.validation-item { margin-bottom: 4px;}
.validation-item:last-child { margin-bottom: 0;}
.validation-item strong { font-weight: 600;}
/* Help text */.help-text { margin-top: 8px; font-size: 13px; color: #757575; line-height: 1.5;}
/* Animation for validation messages appearing */@keyframes slideDown { from { opacity: 0; transform: translateY(-8px); } to { opacity: 1; transform: translateY(0); }}
.validation-message { animation: slideDown 0.2s ease-out;}When: After user leaves the field (blur event) Best for: Text inputs, email, phone numbers Why: Doesn’t interrupt typing, validates complete input
<input onBlur={validateField} />When: As user types (keystroke by keystroke) Best for: Passwords, usernames, character limits Why: Immediate feedback for length/format requirements
<input onChange={validateField} />When: When user submits the form Best for: Final check, server-side validation Why: Catches edge cases, enforces completeness
<form onSubmit={validateAllFields} />Why: Premature - user hasn’t entered anything yet Exception: Pre-fill detection (show format example)
| Severity | Color | When to Use | Example |
|---|---|---|---|
| Error | 🔴 Red | Blocks submission, critical issues | Invalid email format, missing required field |
| Warning | 🟡 Yellow | Doesn’t block, but worth reviewing | Weak password, potential typo (email ending in .co) |
| Suggestion | 🔵 Blue | Optional improvements | Password could be longer, consider adding 2FA |
| Pattern | Error Detection | User Experience | Learning |
|---|---|---|---|
| No Validation | ❌ Errors persist | ⚠️ Frustration on submit | ❌ No feedback |
| Validate on Submit | ⚠️ Late detection | ⚠️ Context switching | ⚠️ Vague errors |
| Validate on Blur | ✅ Early detection | ✅ No interruption | ✅ Immediate feedback |
| Real-Time (Grammarly) | ✅ Instant detection | ✅ Flow maintained | ✅ Contextual learning |
For Grammarly specifically, the cited documentation supports automatic checking, inline underlines, categorized suggestions, review cards, explanations, and accept/dismiss controls. Whether those behaviors reduce errors or proofreading time must be measured for a defined writing task; the historical audience figure is not an effectiveness metric.
Human Standards Integration: Analysis updated against cited primary documentation, August 2026