Pattern 8: Intelligent Defaults
Part II: Interaction Patterns - Intelligence Patterns
Opening Scenario: Filling Out the Same Form Twice
Dr. Sarah Chen was a family physician who'd been using the same electronic medical records system for five years. Every patient visit required documentation, and she saw 25-30 patients per day.
One afternoon, she was documenting a routine follow-up visit for a patient with hypertension. The form started blank:
VISIT DOCUMENTATION
Visit Type: [ ]
Chief Complaint: [ ]
Blood Pressure: [ / ]
Heart Rate: [ ] bpm
Weight: [ ] lbs
Temperature: [ ] °F
Medications Reviewed: ( ) Yes ( ) No
Vitals Normal: ( ) Yes ( ) No
Follow-up Needed: ( ) Yes ( ) No
Next Appointment: [ ] weeks
Every field was empty. Sarah had to: - Select "Follow-up Visit" from a dropdown (she does 15 of these daily) - Type "Hypertension follow-up" (she types this 3-4 times daily) - Manually enter vitals from the nurse's notes - Check "Yes" for medications reviewed (she always reviews medications) - Manually calculate next appointment date
She did this 25 times a day. 125 times a week. 6,000+ times a year.
The system knew: - 60% of Sarah's visits are follow-ups - She sees this patient every 3 months for hypertension - The patient's last BP was 142/88, weight was 187 lbs - The patient takes Lisinopril 10mg daily - Sarah always reviews medications - Her typical follow-up is 12 weeks for stable hypertension, 4 weeks if BP is elevated
But the form offered none of this intelligence.
One day, the hospital upgraded to a new system. The same visit documentation opened differently:
VISIT DOCUMENTATION - Follow-up Visit
Patient: Robert Martinez (Last visit: 3 months ago)
Visit Type: [Follow-up Visit ▼] ← Pre-filled based on 60% of your visits
Chief Complaint: [Hypertension follow-up] ← Based on patient history
VITALS (Nurse entered 5 minutes ago - click to review)
Blood Pressure: 138/86 ← Auto-imported from nurse station
Heart Rate: 72 bpm ← Auto-imported
Weight: 189 lbs ← Auto-imported (Trend: +2 lbs from last visit)
Temperature: 98.4°F ← Auto-imported
Previous visit vitals (3 months ago):
BP: 142/88, HR: 74, Weight: 187
ASSESSMENT
Medications Reviewed: (●) Yes ( ) No ← You always check this
Vitals Normal: ( ) Yes (●) No ← BP still elevated
→ BP above target (130/80 for this patient)
→ Consider medication adjustment or lifestyle counseling
Follow-up Needed: (●) Yes ( ) No ← Based on elevated BP
Next Appointment: [12] weeks ← Your typical follow-up for stable cases
⚠️ Suggestion: Consider 4 weeks given BP still above target
[Review & Sign]
The new system was intelligently pre-filled:
Context-aware: Knew this was likely a follow-up based on patient history and Sarah's typical visit mix
Historically informed: Showed previous vitals for comparison, pre-filled chief complaint based on patient's chronic conditions
Automatically integrated: Pulled vitals from the nurse's station tablet (entered 5 minutes ago)
Role-based: Knew Sarah always reviews medications (unlike residents who sometimes skip this)
Pattern-learning: Suggested 4-week follow-up instead of 12 weeks because BP was still elevated (learned from 5 years of Sarah's documentation patterns)
Medically intelligent: Calculated that BP 138/86 is above the 130/80 target for this patient (who has diabetes as comorbidity)
Sarah's documentation time dropped from 3-4 minutes per patient to 45 seconds. She reviewed the pre-filled information, made adjustments where needed, and signed. Over a year, this saved her 500+ hours - time she could spend with patients instead of with forms.
More importantly: The defaults encoded medical expertise. The suggestion to follow up in 4 weeks instead of 12 weeks for elevated BP wasn't arbitrary - it was based on clinical guidelines combined with Sarah's own practice patterns. The system learned from thousands of her previous decisions and offered that expertise back to her as intelligent defaults.
Context
Intelligent Defaults applies when:
Repetitive data entry: Users enter similar information frequently
Predictable patterns exist: Context, role, or history indicates likely values
Time is valuable: Data entry takes time that could be spent elsewhere
Expertise can be encoded: Default values represent domain knowledge
User override is possible: Defaults should help, not constrain
Context is available: System has access to relevant information (history, role, location, time)
Accuracy matters: Wrong defaults are worse than blank fields
Problem Statement
Most forms start blank, forcing users to enter information the system could reasonably predict:
Ignoring obvious context:
// User is creating their 47th invoice this month
// Previous 46 invoices all used:
// - Same tax rate (6%)
// - Same payment terms (Net 15)
// - Same currency (USD)
Tax Rate: [ ] ← Blank!
Payment Terms: [ ] ← Blank!
Currency: [ ] ← Blank!
Not learning from history:
// User has entered shipping address 15 times
// 14 times it was their home address
// 1 time it was their office
Shipping Address:
Street: [ ] ← Make user type it again
City: [ ]
State: [ ]
ZIP: [ ]
Missing role-based intelligence:
// Manager creating expense report
// Managers always select "Department Budget"
// Staff always select "Project Budget"
Budget Source: [ ] ← Should know based on role
Approver: [ ] ← Should default to their manager
Failing to integrate available data:
// Form asking for birth date
// User is logged in
// Birth date is in their profile
Date of Birth: [ / / ] ← System already knows this!
No temporal intelligence:
// Creating appointment on December 24th
// December 25th is a holiday
// System allows selection of 12/25
Appointment Date: [ / / ] ← Should skip holidays
We need forms that intelligently pre-fill based on context, learn from patterns, encode expertise, and save users time while improving accuracy.
Forces
Convenience vs Privacy
- Pre-filling saves time
- But reveals what system knows about user
- Some users uncomfortable with "the system knows too much"
- Balance helpfulness with privacy comfort
Prediction Accuracy vs User Autonomy
- Accurate defaults help users
- Inaccurate defaults frustrate and mislead
- Users must be able to easily override
- System shouldn't assume it knows better than user
Learning vs Staleness
- Systems should learn from user behavior
- But patterns change over time
- Old patterns may no longer apply
- Need to weight recent behavior more heavily
Explicit vs Implicit Defaults
- Explicit: "Based on your last 10 orders, we suggest..."
- Implicit: Field is just pre-filled
- Transparency builds trust
- But too much explanation is clunky
Performance vs Freshness
- Computing intelligent defaults takes time
- Fetching historical data adds latency
- Cached defaults are fast but may be stale
- Balance speed with accuracy
Solution
Pre-populate form fields with intelligent predictions based on context, history, role, and learned patterns, while making defaults obvious, easily overridable, and continuously improving.
The pattern has five types of intelligent defaults:
1. Contextual Defaults (Based on Current Situation)
Use available context to predict values:
class ContextualDefaults {
getDefaults(formType, context) {
const defaults = {};
// Geographic context
if (context.userLocation) {
defaults.timezone = this.getTimezoneFromLocation(context.userLocation);
defaults.country = context.userLocation.country;
defaults.state = context.userLocation.state;
defaults.language = this.getPreferredLanguage(context.userLocation.country);
}
// Temporal context
const now = new Date();
defaults.currentDate = now.toISOString().split('T')[0];
defaults.currentTime = now.toTimeString().slice(0,5);
// Next business day (skip weekends and holidays)
defaults.nextBusinessDay = this.getNextBusinessDay(now, context.holidays);
// Session context
if (context.userRole) {
defaults.department = this.getDepartmentForRole(context.userRole);
defaults.approver = this.getDefaultApprover(context.userRole);
}
// Related entity context
if (formType === 'invoice' && context.customerId) {
const customer = this.getCustomer(context.customerId);
defaults.billingAddress = customer.billingAddress;
defaults.paymentTerms = customer.defaultPaymentTerms;
defaults.currency = customer.preferredCurrency;
defaults.taxRate = this.getTaxRate(customer.location);
}
// Project context
if (context.projectId) {
const project = this.getProject(context.projectId);
defaults.budgetCode = project.defaultBudgetCode;
defaults.clientName = project.clientName;
defaults.projectManager = project.manager;
}
return defaults;
}
getNextBusinessDay(date, holidays = []) {
let nextDay = new Date(date);
nextDay.setDate(nextDay.getDate() + 1);
while (this.isWeekend(nextDay) || this.isHoliday(nextDay, holidays)) {
nextDay.setDate(nextDay.getDate() + 1);
}
return nextDay.toISOString().split('T')[0];
}
isWeekend(date) {
const day = date.getDay();
return day === 0 || day === 6; // Sunday or Saturday
}
isHoliday(date, holidays) {
const dateStr = date.toISOString().split('T')[0];
return holidays.includes(dateStr);
}
}
2. Historical Defaults (Based on Past Behavior)
Learn from user's previous entries:
class HistoricalDefaults {
async getDefaults(userId, formType) {
const history = await this.getUserHistory(userId, formType);
if (history.length === 0) return {};
const defaults = {};
// Most common values
defaults.mostCommonCategory = this.getMostFrequent(
history.map(h => h.category)
);
defaults.typicalAmount = this.getMedian(
history.map(h => h.amount)
);
// Last used values (recent behavior often predicts next action)
const lastEntry = history[0]; // Assuming sorted by recency
defaults.lastUsedVendor = lastEntry.vendor;
defaults.lastUsedPaymentMethod = lastEntry.paymentMethod;
// Time-based patterns
if (formType === 'timesheet') {
defaults.projectAssignment = this.predictCurrentProject(
history,
new Date()
);
}
// Frequency-based defaults
if (this.isRecurringPattern(history)) {
const recurring = this.identifyRecurringEntries(history);
defaults.suggestedRecurrence = recurring.frequency;
defaults.nextOccurrence = recurring.nextDate;
}
return defaults;
}
getMostFrequent(values) {
const frequency = {};
values.forEach(v => {
frequency[v] = (frequency[v] || 0) + 1;
});
return Object.keys(frequency).reduce((a, b) =>
frequency[a] > frequency[b] ? a : b
);
}
getMedian(numbers) {
const sorted = numbers.sort((a, b) => a - b);
const mid = Math.floor(sorted.length / 2);
if (sorted.length % 2 === 0) {
return (sorted[mid - 1] + sorted[mid]) / 2;
}
return sorted[mid];
}
predictCurrentProject(timesheetHistory, currentDate) {
// Look at what projects user worked on recently
const recentEntries = timesheetHistory
.filter(h => this.isRecentDate(h.date, currentDate, 7)) // Last 7 days
.map(h => h.project);
// Weight recent projects more heavily
const weights = {};
recentEntries.forEach((project, index) => {
const recency = recentEntries.length - index; // More recent = higher weight
weights[project] = (weights[project] || 0) + recency;
});
return Object.keys(weights).reduce((a, b) =>
weights[a] > weights[b] ? a : b
);
}
isRecurringPattern(history) {
if (history.length < 3) return false;
// Check if entries occur at regular intervals
const intervals = [];
for (let i = 1; i < history.length; i++) {
const days = this.daysBetween(history[i].date, history[i-1].date);
intervals.push(days);
}
// If intervals are consistent (within 2 days), it's recurring
const avgInterval = intervals.reduce((a, b) => a + b) / intervals.length;
const consistent = intervals.every(i => Math.abs(i - avgInterval) <= 2);
return consistent;
}
}
3. Role-Based Defaults (Based on User Role)
Different roles have different typical values:
class RoleBasedDefaults {
getDefaults(userRole, formType) {
const roleDefaults = {
'manager': {
expenseReport: {
budgetSource: 'Department Budget',
requiresApproval: false, // Managers self-approve
expenseLimit: 5000,
approver: null // No approver needed
},
timesheet: {
billable: false, // Managers typically non-billable
category: 'Management',
client: 'Internal'
},
purchaseRequest: {
approvalRequired: false,
spendingLimit: 10000
}
},
'staff': {
expenseReport: {
budgetSource: 'Project Budget',
requiresApproval: true,
expenseLimit: 1000,
approver: this.getManager(userId)
},
timesheet: {
billable: true, // Staff typically billable
category: 'Client Work',
client: this.getCurrentClient(userId)
},
purchaseRequest: {
approvalRequired: true,
spendingLimit: 500
}
},
'contractor': {
expenseReport: {
budgetSource: 'Project Budget',
requiresApproval: true,
expenseLimit: 500,
approver: this.getProjectManager(userId),
reimbursementType: 'Direct Deposit' // Not payroll
},
timesheet: {
billable: true,
category: 'Contractor Services',
client: this.getContractClient(userId),
overtimeEligible: false // Contractors typically not OT eligible
}
},
'executive': {
expenseReport: {
budgetSource: 'Executive Budget',
requiresApproval: false,
expenseLimit: 25000,
approver: null
},
purchaseRequest: {
approvalRequired: false,
spendingLimit: 50000,
expedited: true // Executive requests prioritized
}
}
};
return roleDefaults[userRole]?.[formType] || {};
}
// Industry-specific role defaults
getIndustryRoleDefaults(industry, role, formType) {
const industryDefaults = {
'healthcare': {
'physician': {
patientNote: {
template: 'SOAP Note',
reviewMedications: true,
orderLabs: true
}
},
'nurse': {
patientNote: {
template: 'Nursing Assessment',
reviewMedications: true,
orderLabs: false // Nurses don't typically order labs
}
}
},
'legal': {
'partner': {
caseIntake: {
conflictCheckRequired: true,
billingType: 'Hourly',
standardRate: 450,
approvalRequired: false
}
},
'associate': {
caseIntake: {
conflictCheckRequired: true,
billingType: 'Hourly',
standardRate: 275,
approvalRequired: true,
assigningPartner: this.getSupervisingPartner(userId)
}
}
}
};
return industryDefaults[industry]?.[role]?.[formType] || {};
}
}
4. Learned Defaults (Machine Learning from Patterns)
Discover patterns algorithmically:
class LearnedDefaults {
async getDefaults(userId, formType, context) {
const patterns = await this.learnPatterns(userId, formType);
const defaults = {};
// Time-based patterns
if (patterns.timeOfDay) {
const currentHour = new Date().getHours();
const typicalValueAtThisTime = patterns.timeOfDay[currentHour];
if (typicalValueAtThisTime) {
defaults.predictedCategory = typicalValueAtThisTime.category;
defaults.predictedAmount = typicalValueAtThisTime.avgAmount;
}
}
// Day-of-week patterns
if (patterns.dayOfWeek) {
const currentDay = new Date().getDay();
const typicalValueOnThisDay = patterns.dayOfWeek[currentDay];
if (typicalValueOnThisDay) {
defaults.predictedProject = typicalValueOnThisDay.project;
}
}
// Conditional patterns (if X then usually Y)
if (context.relatedEntity && patterns.conditionalDefaults) {
const conditional = patterns.conditionalDefaults[context.relatedEntity];
if (conditional) {
Object.assign(defaults, conditional);
}
}
return defaults;
}
async learnPatterns(userId, formType) {
const history = await this.getUserHistory(userId, formType, 180); // Last 6 months
const patterns = {
timeOfDay: {},
dayOfWeek: {},
conditionalDefaults: {}
};
// Learn time-of-day patterns
history.forEach(entry => {
const hour = new Date(entry.timestamp).getHours();
if (!patterns.timeOfDay[hour]) {
patterns.timeOfDay[hour] = {
category: [],
amounts: []
};
}
patterns.timeOfDay[hour].category.push(entry.category);
patterns.timeOfDay[hour].amounts.push(entry.amount);
});
// Aggregate to most common values
for (const hour in patterns.timeOfDay) {
patterns.timeOfDay[hour] = {
category: this.getMostFrequent(patterns.timeOfDay[hour].category),
avgAmount: this.getMedian(patterns.timeOfDay[hour].amounts)
};
}
// Learn conditional patterns (e.g., "when customer is X, usually category is Y")
const conditionalGroups = {};
history.forEach(entry => {
const key = entry.customer || entry.project || entry.vendor;
if (!conditionalGroups[key]) {
conditionalGroups[key] = [];
}
conditionalGroups[key].push(entry);
});
// For each group, find most common values
for (const key in conditionalGroups) {
const group = conditionalGroups[key];
if (group.length >= 3) { // Need at least 3 occurrences to establish pattern
patterns.conditionalDefaults[key] = {
category: this.getMostFrequent(group.map(g => g.category)),
paymentTerms: this.getMostFrequent(group.map(g => g.paymentTerms)),
typicalAmount: this.getMedian(group.map(g => g.amount)),
confidence: group.length / history.length // How confident are we?
};
}
}
return patterns;
}
}
5. Hierarchical Defaults (Cascade from General to Specific)
Apply defaults in priority order:
class HierarchicalDefaults {
async getDefaults(userId, formType, context) {
// Start with system-wide defaults
let defaults = this.getSystemDefaults(formType);
// Override with organization defaults
if (context.organizationId) {
const orgDefaults = await this.getOrganizationDefaults(
context.organizationId,
formType
);
defaults = { ...defaults, ...orgDefaults };
}
// Override with department defaults
if (context.departmentId) {
const deptDefaults = await this.getDepartmentDefaults(
context.departmentId,
formType
);
defaults = { ...defaults, ...deptDefaults };
}
// Override with role defaults
if (context.userRole) {
const roleDefaults = await this.getRoleDefaults(
context.userRole,
formType
);
defaults = { ...defaults, ...roleDefaults };
}
// Override with user's personal defaults
const userDefaults = await this.getUserDefaults(userId, formType);
defaults = { ...defaults, ...userDefaults };
// Finally, override with context-specific values (highest priority)
if (context.overrides) {
defaults = { ...defaults, ...context.overrides };
}
return defaults;
}
getSystemDefaults(formType) {
// Universal defaults that apply unless overridden
return {
currency: 'USD',
language: 'en-US',
dateFormat: 'MM/DD/YYYY',
timezone: 'America/New_York'
};
}
async getOrganizationDefaults(orgId, formType) {
// Organization-wide policies
const org = await this.getOrganization(orgId);
return {
currency: org.defaultCurrency,
fiscalYearStart: org.fiscalYearStart,
approvalWorkflow: org.defaultApprovalWorkflow,
expensePolicy: org.expensePolicy
};
}
async getDepartmentDefaults(deptId, formType) {
const dept = await this.getDepartment(deptId);
return {
budgetCode: dept.defaultBudgetCode,
costCenter: dept.costCenter,
approver: dept.manager
};
}
}
Implementation Details
Complete Intelligent Defaults Engine
class IntelligentDefaultsEngine {
constructor(config) {
this.contextual = new ContextualDefaults();
this.historical = new HistoricalDefaults();
this.roleBased = new RoleBasedDefaults();
this.learned = new LearnedDefaults();
this.hierarchical = new HierarchicalDefaults();
this.config = config;
}
async getDefaults(formType, userId, context = {}) {
const allDefaults = {};
// Strategy 1: Hierarchical defaults (system → org → dept → role → user)
const hierarchical = await this.hierarchical.getDefaults(
userId,
formType,
context
);
Object.assign(allDefaults, hierarchical);
// Strategy 2: Contextual defaults (current situation)
const contextual = this.contextual.getDefaults(formType, {
...context,
userId: userId,
userLocation: await this.getUserLocation(userId),
userRole: await this.getUserRole(userId)
});
Object.assign(allDefaults, contextual);
// Strategy 3: Historical defaults (past behavior)
const historical = await this.historical.getDefaults(userId, formType);
// Only apply if confidence is high
if (historical.confidence > 0.7) {
Object.assign(allDefaults, historical);
}
// Strategy 4: Role-based defaults
const roleBased = this.roleBased.getDefaults(
await this.getUserRole(userId),
formType
);
Object.assign(allDefaults, roleBased);
// Strategy 5: Learned patterns (ML-based)
const learned = await this.learned.getDefaults(userId, formType, context);
if (learned.confidence > 0.6) {
Object.assign(allDefaults, learned);
}
// Add metadata about why each default was chosen
const defaultsWithMetadata = this.addExplanations(allDefaults, {
hierarchical,
contextual,
historical,
roleBased,
learned
});
return defaultsWithMetadata;
}
addExplanations(defaults, sources) {
const explained = {};
for (const [field, value] of Object.entries(defaults)) {
explained[field] = {
value: value,
source: this.identifySource(field, sources),
confidence: this.calculateConfidence(field, sources),
editable: true
};
}
return explained;
}
identifySource(field, sources) {
// Track which strategy provided this default
if (sources.contextual[field] !== undefined) {
return {
type: 'contextual',
explanation: 'Based on current context'
};
}
if (sources.historical[field] !== undefined) {
return {
type: 'historical',
explanation: `Based on your last ${sources.historical.sampleSize} entries`
};
}
if (sources.roleBased[field] !== undefined) {
return {
type: 'role',
explanation: `Typical for ${sources.roleBased.role} role`
};
}
if (sources.learned[field] !== undefined) {
return {
type: 'learned',
explanation: 'Predicted from your patterns'
};
}
return {
type: 'hierarchical',
explanation: 'Organization default'
};
}
calculateConfidence(field, sources) {
// How confident are we in this default?
// Higher confidence = more data supporting it
let confidence = 0.5; // Base confidence
// Boost confidence if multiple strategies agree
const strategies = [
sources.contextual,
sources.historical,
sources.roleBased,
sources.learned
];
const agreementCount = strategies.filter(s =>
s[field] === sources.hierarchical[field]
).length;
confidence += agreementCount * 0.1;
// Boost if historical data is strong
if (sources.historical[field] && sources.historical.confidence > 0.8) {
confidence += 0.2;
}
return Math.min(confidence, 1.0);
}
}
// UI Integration
class SmartForm {
constructor(formDefinition, defaultsEngine) {
this.fields = formDefinition;
this.engine = defaultsEngine;
this.values = {};
this.defaults = {};
this.userModified = new Set(); // Track user overrides
}
async initialize(userId, context) {
// Get intelligent defaults
this.defaults = await this.engine.getDefaults(
this.fields.formType,
userId,
context
);
// Apply defaults to form
for (const [fieldName, defaultData] of Object.entries(this.defaults)) {
if (!this.userModified.has(fieldName)) {
this.values[fieldName] = defaultData.value;
}
}
// Render form with defaults
this.render();
}
render() {
// For each field, show default with visual indicator
for (const fieldName in this.fields) {
const field = this.fields[fieldName];
const defaultData = this.defaults[fieldName];
if (defaultData) {
this.renderFieldWithDefault(fieldName, field, defaultData);
} else {
this.renderEmptyField(fieldName, field);
}
}
}
renderFieldWithDefault(fieldName, field, defaultData) {
const container = document.getElementById(`field-${fieldName}`);
// Create input with default value
const input = document.createElement('input');
input.value = defaultData.value;
input.name = fieldName;
// Add visual indicator that this is a default
input.classList.add('has-default');
// Add explanation tooltip
const explanation = document.createElement('span');
explanation.classList.add('default-explanation');
explanation.textContent = `✓ ${defaultData.source.explanation}`;
explanation.title = `Confidence: ${(defaultData.confidence * 100).toFixed(0)}%`;
// Add "clear default" button
const clearBtn = document.createElement('button');
clearBtn.textContent = '×';
clearBtn.classList.add('clear-default');
clearBtn.onclick = () => this.clearDefault(fieldName);
container.appendChild(input);
container.appendChild(explanation);
container.appendChild(clearBtn);
// Track user modifications
input.addEventListener('change', () => {
this.userModified.add(fieldName);
input.classList.remove('has-default');
explanation.style.display = 'none';
clearBtn.style.display = 'none';
});
}
clearDefault(fieldName) {
this.values[fieldName] = '';
this.userModified.add(fieldName);
this.render();
}
// Track which defaults users accept vs override
async submitForm() {
const analytics = {
defaultsProvided: Object.keys(this.defaults).length,
defaultsAccepted: 0,
defaultsModified: this.userModified.size,
acceptanceRate: 0
};
// Calculate acceptance rate
for (const fieldName in this.defaults) {
if (!this.userModified.has(fieldName)) {
analytics.defaultsAccepted++;
}
}
analytics.acceptanceRate =
analytics.defaultsAccepted / analytics.defaultsProvided;
// Send analytics to improve defaults
await this.engine.recordDefaultPerformance(
this.fields.formType,
analytics
);
// Submit form data
return this.values;
}
}
Adaptive Learning
class AdaptiveDefaultLearning {
async recordFormSubmission(userId, formType, submittedValues, providedDefaults) {
// Track which defaults were accepted vs modified
const feedback = {
userId,
formType,
timestamp: new Date(),
defaults: {},
accepted: [],
modified: [],
cleared: []
};
for (const field in providedDefaults) {
const defaultValue = providedDefaults[field].value;
const finalValue = submittedValues[field];
feedback.defaults[field] = {
suggested: defaultValue,
actual: finalValue,
source: providedDefaults[field].source.type,
confidence: providedDefaults[field].confidence
};
if (finalValue === defaultValue) {
feedback.accepted.push(field);
} else if (finalValue === '' || finalValue === null) {
feedback.cleared.push(field);
} else {
feedback.modified.push({
field,
from: defaultValue,
to: finalValue
});
}
}
// Calculate acceptance rate for each default strategy
const strategyPerformance = this.calculateStrategyPerformance(feedback);
// Store feedback
await this.storeFeedback(feedback);
// Update strategy weights based on performance
await this.updateStrategyWeights(userId, formType, strategyPerformance);
return feedback;
}
calculateStrategyPerformance(feedback) {
const performance = {
contextual: { accepted: 0, total: 0 },
historical: { accepted: 0, total: 0 },
roleBased: { accepted: 0, total: 0 },
learned: { accepted: 0, total: 0 },
hierarchical: { accepted: 0, total: 0 }
};
for (const field in feedback.defaults) {
const source = feedback.defaults[field].source;
performance[source].total++;
if (feedback.accepted.includes(field)) {
performance[source].accepted++;
}
}
// Calculate acceptance rate for each strategy
for (const strategy in performance) {
const stats = performance[strategy];
stats.rate = stats.total > 0 ? stats.accepted / stats.total : 0;
}
return performance;
}
async updateStrategyWeights(userId, formType, performance) {
// Get current weights
const weights = await this.getStrategyWeights(userId, formType) || {
contextual: 1.0,
historical: 1.0,
roleBased: 1.0,
learned: 1.0,
hierarchical: 1.0
};
// Adjust weights based on performance
for (const strategy in performance) {
const rate = performance[strategy].rate;
if (performance[strategy].total >= 5) { // Need enough data
// Increase weight if acceptance rate is high
if (rate > 0.8) {
weights[strategy] = Math.min(weights[strategy] * 1.1, 2.0);
}
// Decrease weight if acceptance rate is low
else if (rate < 0.4) {
weights[strategy] = Math.max(weights[strategy] * 0.9, 0.5);
}
}
}
await this.saveStrategyWeights(userId, formType, weights);
}
}
Consequences
Benefits
Massive Time Savings: - Users don't re-enter information the system knows - Dr. Sarah saved 500+ hours per year - Reduced cognitive load on routine tasks
Improved Accuracy: - Pre-filled values are typically correct - Fewer typos than manual entry - Consistency across related forms
Encoded Expertise: - Defaults represent domain knowledge - New users benefit from organization's experience - Best practices preserved in defaults
Reduced Friction: - Forms feel intelligent and helpful - Users more likely to complete forms - Better user experience overall
Learning Organization: - System improves from user behavior - Patterns identified and codified - Continuous improvement cycle
Context Awareness: - Forms adapt to situation - Right defaults for right context - Professional, polished experience
Liabilities
Privacy Concerns: - Pre-filling reveals what system tracks - Some users uncomfortable with "system knows too much" - Transparency needed about data usage
Accuracy Dependence: - Wrong defaults worse than blank fields - Users may not notice incorrect defaults - Can reinforce incorrect patterns
Over-reliance Risk: - Users may stop thinking critically - Defaults accepted without review - Automation bias: trusting system over judgment
Maintenance Burden: - Patterns change over time - Defaults need continuous updating - Requires monitoring and tuning
Implementation Complexity: - Multiple strategies to coordinate - Historical data to maintain - Performance optimization needed
User Adaptation: - Power users want control - Novices need guidance - One size doesn't fit all
Domain Examples
Healthcare: Patient Visit Documentation
const visitDefaults = {
// Context: Follow-up visit for chronic condition patient
visitType: 'Follow-up', // 60% of this doctor's visits
// Historical: Last visit was 3 months ago for hypertension
chiefComplaint: 'Hypertension follow-up',
// Imported: Vitals entered by nurse 5 minutes ago
bloodPressure: '138/86', // From nurse station
heartRate: '72',
weight: '189',
// Calculated: Comparison to previous visit
bpChange: '+4/-2', // Compared to last visit
weightChange: '+2 lbs',
// Learned: This doctor always reviews medications
medicationsReviewed: true,
// Domain intelligence: BP above target for diabetic patient
treatmentPlan: 'Consider medication adjustment - BP above 130/80 target',
// Pattern: Typical follow-up interval for stable patient
nextVisit: '12 weeks',
// But: Suggestion based on elevated BP
suggestedNextVisit: '4 weeks',
suggestedReason: 'BP still elevated above target'
};
Legal: Client Intake
const intakeDefaults = {
// Role-based: Associate needs partner approval
assignedAttorney: 'Sarah Chen, Esq.',
supervisingPartner: 'Michael Roberts', // User's supervisor
// Historical: Last 8 intakes were family law
practiceArea: 'Family Law',
subSpecialty: 'Divorce',
// Learned: This attorney's typical billing
billingType: 'Hourly',
standardRate: 275, // Associate rate
// Contextual: Current date
intakeDate: '2024-12-25',
// Template: Standard conflict check questions
conflictCheckQuestions: [
'Opposing party name and DOB',
'Related parties',
'Previous legal representation',
'Known adverse parties'
],
// Organization: Standard intake process
initialConsultFee: 150,
retainerAmount: 3500, // Typical for family law
// Domain intelligence: Pennsylvania-specific
jurisdiction: 'Pennsylvania',
applicableLaw: 'PA Domestic Relations Code',
filingCourt: 'Court of Common Pleas'
};
E-commerce: Checkout
const checkoutDefaults = {
// Historical: Last 15 orders shipped here
shippingAddress: {
name: 'Richard Roberts',
street: '123 Main Street',
city: 'Pittsburgh',
state: 'PA',
zip: '15217'
},
// Learned: User always selects 2-day shipping
shippingMethod: 'UPS 2-Day',
shippingCost: 12.99,
// Saved: Payment method on file
paymentMethod: {
type: 'Credit Card',
last4: '4242',
expiry: '12/2025'
},
// Contextual: Pennsylvania sales tax
taxRate: 0.06,
taxAmount: calculateTax(subtotal, 0.06),
// Pattern: User typically adds gift message for December orders
giftMessage: '',
suggestGiftMessage: true, // It's December
// Smart suggestion: Combine with saved cart items?
savedCartItems: 3,
suggestCombine: 'You have 3 items in your saved cart. Add them to this order?'
};
Real Estate: Listing Creation
const listingDefaults = {
// Context: Agent creating listing
listingAgent: 'Maria Rodriguez',
listingOffice: 'Keller Williams Downtown',
// Historical: Agent's typical commission
commissionRate: 6.0, // This agent's standard
// Imported: From MLS pre-search
propertyAddress: '456 Oak Avenue, Pittsburgh, PA 15213',
parcelId: '123-45-678',
lotSize: '0.18 acres',
yearBuilt: 1925,
// Calculated: From comparable sales
suggestedPrice: 425000,
pricePerSqFt: 212,
comparables: [
{ address: '460 Oak Ave', price: 415000, sqft: 2100 },
{ address: '452 Oak Ave', price: 435000, sqft: 1950 }
],
// Template: Standard listing description structure
descriptionTemplate: 'Charming [style] home in [neighborhood]...',
// Learned: This agent always includes
includedAppliances: ['Refrigerator', 'Dishwasher', 'Washer/Dryer'],
// Domain: Pennsylvania seller disclosure requirements
requiredDisclosures: [
'Property Disclosure Statement',
'Lead Paint Disclosure (pre-1978)',
'Well/Septic Disclosure'
],
// Smart: Photos from previous listing (if relisting)
previousListing: {
id: 'MLS-12345',
photos: 24,
suggestReuse: 'Reuse photos from previous listing?'
}
};
Construction: Material Order
const materialOrderDefaults = {
// Project context: Active job
project: 'Riverside Apartments - Building C',
jobNumber: 'RA2024-003',
costCode: '03-Concrete',
// Historical: Last 5 orders from this vendor
vendor: 'Pittsburgh Concrete Supply',
deliveryTerms: 'Job Site',
paymentTerms: 'Net 30',
// Calculated: From project schedule
requestedDeliveryDate: '2025-01-15', // When concrete pour scheduled
earliestAcceptable: '2025-01-14',
latestAcceptable: '2025-01-15', // Concrete timing critical
// Learned: Superintendent's preferences
deliveryTimeWindow: '7:00 AM - 9:00 AM', // Before crew break
contactOnsite: 'John Smith (Foreman)',
contactPhone: '(412) 555-0123',
// Template: Standard concrete order
material: '3000 PSI Concrete',
quantity: calculateQuantity(area, thickness, wasteFactor),
unit: 'Cubic Yards',
// Domain intelligence: Weather-based suggestion
weatherForecast: 'Rain predicted Jan 15',
suggestion: 'Consider rescheduling pour - rain forecast',
alternativeDates: ['2025-01-14', '2025-01-16'],
// Safety: Required certifications
requiredCerts: [
'Material Safety Data Sheet',
'Concrete Mix Design',
'Slump Test Requirements'
]
};
Related Patterns
Prerequisites: - Volume 3, Pattern 6: Domain-Aware Validation (defaults must be valid) - Volume 3, Pattern 7: Calculated Dependencies (some defaults are calculated)
Synergies: - Volume 3, Pattern 1: Progressive Disclosure (defaults make sections feel complete) - Volume 3, Pattern 2: Contextual Scaffolding (defaults adapt to expertise level) - Volume 3, Pattern 9: Contextual Constraints (defaults respect constraints) - Volume 3, Pattern 23: Intelligent Pre-Population (specific implementation of this pattern)
Conflicts: - Audit requirements (some systems need blank forms for compliance) - Learning systems (need clean data, not influenced by defaults)
Alternatives: - Templates (user explicitly chooses a starting point) - Duplication (copy from previous entry) - Import (pull from external system)
Known Uses
Gmail: Smart Compose suggests sentence completions based on your writing patterns
QuickBooks: Pre-fills invoice fields based on customer history and previous invoices
Electronic Health Records (Epic, Cerner): Smart phrases, order sets, and documentation templates
TurboTax: Imports previous year's return, suggests deductions based on occupation
Salesforce: Pre-populates lead/contact fields based on email domain and company info
Google Calendar: Suggests meeting times based on availability patterns
Adobe Creative Cloud: Recent files, frequent colors, common settings all pre-selected
GitHub: Pre-fills PR descriptions with commit messages and issue references
Stripe: Remembers customer payment methods, pre-fills billing addresses
Further Reading
On Default Values and UX: - "The Power of Defaults." Nielsen Norman Group. https://www.nngroup.com/articles/the-power-of-defaults/ (How defaults influence behavior) - Thaler, Richard H., and Cass R. Sunstein. Nudge. Penguin, 2008. (Default effects on decision-making)
On Machine Learning for Predictions: - "Autocompletion and Suggestion APIs." Google Cloud. https://cloud.google.com/places/docs/predictions (Smart suggestions at scale) - Scikit-learn: https://scikit-learn.org/stable/modules/generated/sklearn.neighbors.KNeighborsClassifier.html (k-NN for similar cases)
On Form Design Best Practices: - Wroblewski, Luke. Web Form Design: Filling in the Blanks. Rosenfeld Media, 2008. (Chapter on smart defaults) - "Form Design: 13 Empirically-Backed Best Practices." Baymard Institute. https://baymard.com/blog/form-design-best-practices
On Privacy and Defaults: - "Privacy by Design." GDPR Guidelines. https://gdpr.eu/privacy-by-design/ (Default settings must respect privacy) - "Dark Patterns." Deceptive Patterns. https://www.deceptive.design/ (What NOT to do with defaults)
Implementation Examples: - Autofill Specification: https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofill (HTML autocomplete attribute) - React Hook Form Controller: https://react-hook-form.com/api/usecontroller/controller (Controlled defaults in React)
Related Trilogy Patterns
- Pattern 6: Domain-Aware Validation - Defaults must be valid
- Pattern 7: Calculated Dependencies - Can pre-populate based on calculations
- Pattern 9: Contextual Constraints - Defaults respect constraints
- Pattern 10: Semantic Suggestions - Intelligent suggestions complement smart defaults
- Volume 2, Pattern 11: Historical Pattern Matching - Finding similar cases for defaults
- Volume 2, Pattern 18: Opportunity Mining - Group-specific defaults
- Volume 1, Chapter 9: User Experience Design - Forms feed documents—good defaults improve document quality