Proto Artisan is an intelligent code analyzer that transforms AI-generated prototypes into production-ready applications by automatically detecting patterns, extracting reusable components, and reorganizing code according to LEGO Builder principles.
This case study explores how we built a tool that bridges the gap between rapid AI prototyping and maintainable software architecture.
The Problem: AI-Generated Technical Debt
AI coding assistants like Claude, ChatGPT, and GitHub Copilot have revolutionized prototyping. You can describe a feature and get working code in minutes. But there's a catch:
- Monolithic Functions: AI tends to generate everything in a single function (200+ lines)
- Inline Duplication: Similar logic repeated across different parts of the codebase
- No Architectural Patterns: Code that works but doesn't scale or maintain well
- Testing Difficulty: Tightly coupled code that's nearly impossible to test
Developers faced a choice: spend hours manually refactoring AI code, or accept technical debt from day one. Proto Artisan offers a third option: automated refactoring guided by LEGO Builder principles.
The Solution: Intelligent Pattern Detection
Architecture Overview
Proto Artisan consists of three main systems:
// System Architecture
src/
├── analyzers/ // Detect patterns and duplications
├── extractors/ // Pull out reusable blocks
├── generators/ // Create modular file structure
├── validators/ // Ensure LEGO compliance
└── orchestrators/ // Coordinate the refactoring process
Pattern Detection Engine
The core of Proto Artisan is its pattern detection system:
// analyzers/pattern-detector.js (29 lines)
export function detectPatterns(codeString) {
const ast = parseToAST(codeString);
const patterns = {
utilities: findPureFunctions(ast),
builders: findObjectConstructors(ast),
guards: findValidators(ast),
processors: findBusinessLogic(ast),
duplicates: findDuplicateLogic(ast)
};
return patterns;
}
// analyzers/duplicate-finder.js (27 lines)
export function findDuplicateLogic(ast) {
const functionBodies = extractFunctionBodies(ast);
const signatures = functionBodies.map(computeSignature);
const duplicates = [];
for (let i = 0; i < signatures.length; i++) {
for (let j = i + 1; j < signatures.length; j++) {
if (similarityScore(signatures[i], signatures[j]) > 0.8) {
duplicates.push({
first: functionBodies[i],
second: functionBodies[j],
similarity: similarityScore(signatures[i], signatures[j])
});
}
}
}
return duplicates;
}
Automatic Block Extraction
Once patterns are detected, Proto Artisan extracts them into proper LEGO blocks:
// extractors/utility-extractor.js (28 lines)
export function extractUtilities(patterns) {
const utilities = [];
patterns.utilities.forEach(func => {
const utility = {
name: generateDescriptiveName(func),
code: func.code,
inputs: func.parameters,
output: func.returnType,
category: categorizeUtility(func)
};
// Ensure LEGO compliance
if (utility.code.split('\n').length > 30) {
utility.needsRefactoring = true;
utility.suggestions = suggestBreakdown(utility.code);
}
utilities.push(utility);
});
return utilities;
}
Real-World Example: Refactoring a Monolith
Before: AI-Generated Monolith (127 lines)
// AI generated this as a single function
async function handleUserRegistration(req, res) {
// Validation (15 lines)
const email = req.body.email;
if (!email || !email.includes('@')) {
return res.status(400).json({ error: 'Invalid email' });
}
// ... more validation
// Database operations (25 lines)
const userExists = await db.query('SELECT * FROM users WHERE email = $1', [email]);
if (userExists.rows.length > 0) {
return res.status(409).json({ error: 'User exists' });
}
// ... more database code
// Password hashing (12 lines)
const salt = await bcrypt.genSalt(10);
const hashedPassword = await bcrypt.hash(req.body.password, salt);
// ... more hashing logic
// Email sending (20 lines)
const transporter = nodemailer.createTransport({ /* config */ });
const mailOptions = { /* options */ };
await transporter.sendMail(mailOptions);
// ... more email code
// Token generation (15 lines)
const token = jwt.sign({ userId: user.id }, process.env.JWT_SECRET);
// ... more token logic
// Response handling (10 lines)
res.json({ success: true, token, user });
}
After: Proto Artisan Refactoring
// utilities/email-validator.js (8 lines)
export function isValidEmail(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
}
// utilities/password-hasher.js (12 lines)
export async function hashPassword(password) {
const salt = await bcrypt.genSalt(10);
return bcrypt.hash(password, salt);
}
// guards/user-existence-guard.js (15 lines)
export async function requireNewUser(email) {
const existing = await getUserByEmail(email);
if (existing) {
throw new Error('User already exists');
}
}
// builders/user-builder.js (18 lines)
export function buildUser(userData, hashedPassword) {
return {
email: userData.email,
password: hashedPassword,
name: userData.name,
createdAt: new Date()
};
}
// bridges/email-bridge.js (22 lines)
export async function sendWelcomeEmail(email, name) {
const transporter = createTransporter();
const mailOptions = buildWelcomeEmail(email, name);
await transporter.sendMail(mailOptions);
}
// utilities/token-generator.js (14 lines)
export function generateAuthToken(userId) {
return jwt.sign(
{ userId },
process.env.JWT_SECRET,
{ expiresIn: '7d' }
);
}
// controllers/registration-controller.js (26 lines)
export async function handleUserRegistration(req, res) {
try {
const { email, password, name } = req.body;
if (!isValidEmail(email)) {
return res.status(400).json({ error: 'Invalid email' });
}
await requireNewUser(email);
const hashedPassword = await hashPassword(password);
const user = buildUser({ email, name }, hashedPassword);
await saveUser(user);
await sendWelcomeEmail(email, name);
const token = generateAuthToken(user.id);
res.json({ success: true, token, user });
} catch (error) {
res.status(500).json({ error: error.message });
}
}
The Technical Stack
Core Technologies
- Parser: Acorn.js for JavaScript AST generation
- Analysis: Custom pattern matching algorithms
- Similarity Detection: Levenshtein distance and structural hashing
- Code Generation: Template-based file generation
- UI: React with real-time diff visualization
Key Features
1. Smart Naming
Proto Artisan generates meaningful names based on function behavior:
// analyzers/name-generator.js (24 lines)
export function generateDescriptiveName(funcNode) {
const verbs = extractVerbs(funcNode);
const nouns = extractNouns(funcNode);
if (funcNode.returnsPure) return `${verbs[0]}${capitalize(nouns[0])}`;
if (funcNode.buildsObject) return `build${capitalize(nouns[0])}`;
if (funcNode.validates) return `validate${capitalize(nouns[0])}`;
if (funcNode.checks) return `is${capitalize(nouns[0])}Valid`;
return `${verbs[0]}${capitalize(nouns[0])}`;
}
2. LEGO Compliance Validation
Every extracted block is validated against LEGO Builder rules:
- Maximum 30 lines of code
- Single responsibility principle
- Clear input/output contracts
- No hidden dependencies
- Maximum 3 functions per file
3. Visual Diff Comparison
Developers can see before/after comparisons with:
- Line count reduction metrics
- Duplication elimination stats
- Test coverage improvements
- File organization visualization
Results and Impact
Measurable Improvements
- Code Reduction: Average 35% reduction in total lines (duplicate elimination)
- Maintainability: Cyclomatic complexity reduced by 60%
- Test Coverage: Testable blocks increased from 20% to 85%
- Refactoring Time: 3-hour manual refactoring → 10-minute automated process
Developer Productivity
Teams using Proto Artisan report:
- Faster onboarding (new developers understand modular code more quickly)
- Confident AI usage (no fear of accumulating technical debt)
- Faster debugging (small blocks are easier to trace)
- Reusable components (extracted utilities used across projects)
Lessons Learned
1. AI Code Has Patterns
Despite different prompts, AI assistants generate similar patterns. These patterns are predictable and can be detected algorithmically.
2. Naming is Critical
Automatically generated names must be descriptive. Poor names defeat the purpose of modular code.
3. Developers Need Control
Proto Artisan suggests refactorings but lets developers approve or modify them. Full automation would miss context that humans understand.
4. LEGO Principles Scale
The same principles that make hand-written code maintainable also make AI-refactored code maintainable. The 30-line rule works whether a human or machine wrote the original code.
The Future of AI-Assisted Development
Proto Artisan represents a new paradigm: using AI to prototype rapidly, then using intelligent tooling to transform prototypes into production-quality architecture.
This workflow combines the best of both worlds:
- Speed: AI generates working code in minutes
- Quality: Automated refactoring ensures maintainability
- Control: Developers review and approve all changes
- Learning: Developers see how monoliths decompose into blocks
As AI coding assistants become more powerful, tools like Proto Artisan will become essential for maintaining code quality at the speed of AI generation.
← Back to Blog