# Designing AI-Compatible Code

*A Guide to Writing Code That Works Seamlessly with AI*

By [veganbeef](https://paragraph.com/@veganbeef) · 2025-07-03

ai, software, prompt-engineering, software-engineering, software-architecture

---

There’s a lot of writing out there on prompt engineering and model selection in pursuit of making AI agents smarter and more self-directed software engineers, but I’ve found that isn’t always enough to get consistently helpful results, especially when you’re trying to use AI to update older, more complex codebases alongside human engineers. The **structure and style of your code** can make a huge difference in how well AI can understand and intelligently extend it.

_I’m not saying you need to go refactor everything right now, but if you’ve been feeling like your AI engineer is acting less like an expert and more like a junior intern, then gradually incorporating these ideas into your existing code will likely help._

The following code architecture and design guidelines are based on my personal experience using AI to edit existing projects and to clone and extend templates into new projects, ordered purely by my own raw gut feeling of how important they are:

Core Principles
---------------

### 1\. Use Common Libraries and Patterns

*   Choose well-documented, mainstream technologies over custom solutions
    
*   Follow established approaches (MVC, Repository structures, React patterns) that AI recognizes
    
*   Use standard naming conventions for your language/framework
    
*   Stick to idiomatic code rather than clever implementations
    

### 2\. Modular File Structure

*   Create many small, focused files (200-300 lines max)
    
*   Use nested directories that reflect logical relationships
    
*   Group by feature, not file type
    
*   One primary responsibility per file
    

    src/
    ├── components/authentication/
    │   ├── login-form.component.ts
    │   └── registration-form.component.ts
    ├── services/user-management/
    │   ├── user-profile.service.ts
    │   └── user-preferences.service.ts
    └── utils/validation/
        ├── email-validator.util.ts
        └── password-strength-validator.util.ts
    
    

### 3\. Extremely Descriptive Names

*   Use verbose, self-documenting names that explain purpose and context
    
*   Include units, types, and constraints when relevant
    
*   Don't worry about name length - clarity over brevity
    

    // Good
    const authenticatedUserWithPermissions = await fetchUser();
    const isEmailAddressFormatValid = validateEmail(email);
    const convertRawApiResponseToUserProfileData = (response) => { ... };
    
    // Avoid
    const user = await fetchUser();
    const isValid = validateEmail(email);
    const processData = (response) => { ... };
    
    

### 4\. Comprehensive Documentation

*   Add docstrings to all functions, classes, and complex logic
    
*   Include parameter types, return values, and usage examples
    
*   Document business logic and decision reasoning
    
*   Add inline comments for non-obvious code sections
    

    /**
     * Validates user email format and checks against blacklisted domains
     * @param emailAddress - The email address to validate
     * @param checkBlacklist - Whether to check against domain blacklist
     * @returns True if email is valid and not blacklisted
     * @example validateUserEmailAddress('user@example.com', true)
     */
    export const validateUserEmailAddress = (
      emailAddress: string,
      checkBlacklist: boolean = true
    ): boolean => {
      // Check basic email format first
      const emailRegexPattern = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;
    
      if (!emailRegexPattern.test(emailAddress)) {
        return false;
      }
    
      // Optional blacklist check for spam domains
      if (checkBlacklist) {
        return !isEmailDomainBlacklisted(emailAddress);
      }
    
      return true;
    };
    
    

### 5\. Centralize Shared Logic and Config

*   Extract all configuration into dedicated files, even if used in one place
    
*   Create utility functions for any logic that could be reused
    
*   Use clear, descriptive configuration objects
    

    // config/database.config.ts
    export const DATABASE_CONFIG = {
      connectionTimeoutMs: 5000,
      maxRetryAttempts: 3,
      productionPoolSize: 20,
      developmentPoolSize: 5
    };
    
    // utils/email-validation.util.ts
    export const validateEmailAddressFormat = (email: string): boolean => {
      // Implementation here
    };
    
    

Additional Best Practices
-------------------------

*   Use TypeScript or similar type systems for better AI understanding
    
*   Include README files in each major directory explaining purpose
    
*   Maintain consistent code formatting with automated tools
    
*   Use meaningful commit messages that explain the "why" not just "what"
    
*   Include unit tests with descriptive test names that explain expected behavior
    

  

A final word of caution
-----------------------

You can of course use AI to implement these changes, but make sure to double check these types of changes, as _mistakes here can be pernicious hard to find_.

---

*Originally published on [veganbeef](https://paragraph.com/@veganbeef/designing-ai-compatible-code)*
