AI code generation: from prototype to production-ready code

Using AI to generate Laravel, Shopify and WordPress code, from scaffolding to boilerplate, and the checks that keep what it writes fit for production.

Updated 9 min read

AI code generation is best at the repetitive parts of a project: migrations, models, form fields, test scaffolding and boilerplate. It’s weakest where the code depends on the business, its security and its data. Give the model your project’s conventions, ask for one layer at a time, and review what it writes as you would a pull request.

Preparation does most of the work. A project documentation scaffold gives the model your architecture before it writes anything.

This guide covers code generation on Laravel, Shopify and WordPress projects, with examples and the checks that catch what the model gets wrong.

What to hand over, and what to keep

What AI does well

Models are good at patterns and repetition. They can write:

  • Structural code: migrations, models, controllers, routes and configuration
  • Boilerplate: CRUD operations, API endpoints and authentication scaffolding
  • Repeated components: form fields, table columns, list items and card layouts
  • Tests: unit, feature and integration tests for existing code
  • Documentation: inline comments, README files and API documentation
  • Conversions: moving code between languages, frameworks or patterns

What needs a person

Models struggle with:

  • Business logic: the rules and constraints particular to your business
  • Security: authentication, authorisation and encryption
  • Performance: indexing strategy and caching layers
  • Architecture: system design and technology choices
  • Code that depends on the rest of the application: anything that needs the whole system in mind

Let the model save you typing, and keep the decisions with a person.

Where it saves the most time

Laravel development

Database layer:

Generate a Laravel migration and model for a blog post system.

Requirements:
- Posts table with: title, slug, content, excerpt, status, published_at
- Categories table with: name, slug, description
- Many-to-many relationship between posts and categories
- Soft deletes on posts
- Indexes on slug and published_at fields
- Model should include relationships, scopes, and accessors

The model writes:

// Migration
public function up()
{
    Schema::create('posts', function (Blueprint $table) {
        $table->id();
        $table->string('title');
        $table->string('slug')->unique();
        $table->text('content');
        $table->text('excerpt')->nullable();
        $table->enum('status', ['draft', 'published', 'archived'])->default('draft');
        $table->timestamp('published_at')->nullable();
        $table->timestamps();
        $table->softDeletes();

        $table->index('slug');
        $table->index('published_at');
    });

    Schema::create('categories', function (Blueprint $table) {
        $table->id();
        $table->string('name');
        $table->string('slug')->unique();
        $table->text('description')->nullable();
        $table->timestamps();
    });

    Schema::create('category_post', function (Blueprint $table) {
        $table->foreignId('category_id')->constrained()->onDelete('cascade');
        $table->foreignId('post_id')->constrained()->onDelete('cascade');
        $table->primary(['category_id', 'post_id']);
    });
}

// Model
class Post extends Model
{
    use HasFactory, SoftDeletes;

    protected $fillable = [
        'title', 'slug', 'content', 'excerpt', 'status', 'published_at'
    ];

    protected $casts = [
        'published_at' => 'datetime',
    ];

    // Relationships
    public function categories()
    {
        return $this->belongsToMany(Category::class);
    }

    // Scopes
    public function scopePublished($query)
    {
        return $query->where('status', 'published')
                     ->whereNotNull('published_at')
                     ->where('published_at', '<=', now());
    }

    // Accessors
    public function getExcerptAttribute($value)
    {
        return $value ?? Str::limit(strip_tags($this->content), 150);
    }
}

Even this short example has a flaw. slug already has a unique index, so index('slug') adds a second, duplicate index that slows every insert. It looks right and runs fine, which is exactly why generated code needs review.

For the relationships themselves, see advanced Eloquent relationships, and for indexes, Laravel performance optimisation.

API controllers:

Generate a Laravel API controller for posts with:
- Resource responses
- Validation
- Policy authorisation
- Query optimisation
- Error handling

This gives you a first draft of the controller structure. Review it as you would a junior developer’s pull request before it goes anywhere near production.

Shopify theme development

Liquid sections:

Generate a Shopify section for a featured products carousel.

Settings:
- Collection selector
- Number of products (1-10)
- Layout: grid or carousel
- Show prices: yes/no
- Button text (customisable)

Output as complete section with schema.

The model writes the Liquid template, the CSS and the JSON schema. Check the result against Shopify theme performance before it ships.

Shopify Functions:

Generate a Shopify discount Function for tiered quantity discounts:
- Buy 3+ items, get 10% off
- Buy 5+ items, get 15% off
- Buy 10+ items, get 20% off
Apply to products in one collection only

Shopify Scripts stopped running on 30 June 2026, so ask for a Function, not a Script. Models trained on older material still suggest Scripts.

For the API side, see Shopify API integration.

WordPress development

Custom post types:

Generate WordPress custom post type registration for "Case Studies" with:
- Custom taxonomies: Industry, Service
- Custom fields (ACF): Client name, project duration, results
- Archive template structure
- Single template structure

The model writes the registration code, the templates and the field definitions. The WordPress hook system post covers wiring them in.

Plugin scaffolding:

Generate WordPress plugin boilerplate for "Advanced Contact Form" with:
- Admin settings page
- Shortcode support
- Email notifications
- Database table for submissions
- AJAX form submission

Then check it against WordPress plugin performance.

Prompt templates

Structured request format

The same format every time gives more predictable output:

Task: [What you want generated]
Context: [Framework version, dependencies, constraints]
Requirements: [Specific features, patterns, standards]
Output Format: [Code only, with comments, with tests]
Constraints: [What to avoid, what to include]

Laravel controller example

Task: Generate Laravel resource controller for Product model
Context: Laravel 12, using API resources, JSON responses
Requirements:
- Index with pagination (15 per page)
- Store with validation
- Show with relationships (category, images)
- Update with authorisation
- Destroy with soft delete
Output Format: Complete controller with comments
Constraints:
- Use dependency injection
- Include type hints
- Follow PSR-12

A structured request gives more consistent code, which still needs review. Prompt engineering best practices covers the techniques behind it.

TypeScript component example

Task: Convert React component to TypeScript
Context: React 18, using hooks
Requirements:
- Preserve all functionality
- Add proper type definitions
- Include prop types and return types
- Add JSDoc comments
Output Format: Complete TypeScript component
Constraints:
- Use functional components
- Avoid 'any' type
- Enable strict mode compatibility

Code quality and safety

Automated review tooling

Run the same checks on generated code as on your own, before every commit:

PHP/Laravel:

# Static analysis
./vendor/bin/phpstan analyse --level=8

# Code style
./vendor/bin/php-cs-fixer fix --dry-run

# Known vulnerabilities in dependencies
composer audit

# Tests
php artisan test

JavaScript/TypeScript:

# Linting
npm run lint

# Type checking
npm run type-check

# Tests
npm test

# Build verification
npm run build

WordPress:

# WordPress Coding Standards
phpcs --standard=WordPress

# PHP compatibility
phpcs -p . --standard=PHPCompatibilityWP

# Known vulnerabilities in dependencies
npm audit

Security checklist

Before committing AI-generated code, verify:

  • No hardcoded credentials or API keys
  • Input validation on all user data
  • SQL injection prevention (parameterised queries)
  • XSS protection (escaped output)
  • CSRF protection on forms
  • Authorisation checks
  • Secure file upload handling
  • Rate limiting on public endpoints

Performance validation

Check AI-generated code for:

Database queries:

  • N+1 query problems
  • Missing indexes
  • Eager loading opportunities
  • Unnecessary SELECT *

Caching:

  • Cache frequently accessed data
  • Implement cache invalidation
  • Use appropriate cache drivers

Resource usage:

  • Memory-efficient operations
  • Proper pagination
  • Background job usage for heavy tasks

Laravel performance optimisation and WordPress caching strategies go further on both.

Workflow integration

IDE integration

VS Code with GitHub Copilot:

  • Inline suggestions and chat
  • Agent mode for multi-file changes

Cursor IDE:

  • Built-in AI chat
  • Context-aware suggestions
  • Multi-file editing

JetBrains AI Assistant:

  • Code generation
  • Refactoring suggestions
  • Documentation generation

CLI tools

A custom Artisan command:

class GenerateAIScaffolding extends Command
{
    protected $signature = 'ai:scaffold {model} {--type=crud}';

    public function handle(AIService $ai)
    {
        $model = $this->argument('model');
        $type = $this->option('type');

        $prompt = PromptTemplate::scaffold($model, $type);
        $code = $ai->generate($prompt);

        // Validate and save
        if ($this->validateCode($code)) {
            $this->saveFiles($code);
            $this->info("Scaffolding generated for {$model}");
        }
    }
}

Custom Artisan commands covers building commands like this one.

Git hooks

Pre-commit checks:

#!/bin/bash
# .git/hooks/pre-commit

# Check for AI-generated code markers
if git diff --cached | grep -q "GENERATED_BY_AI"; then
    echo "AI-generated code detected. Running validation..."

    # Run tests
    php artisan test

    # Run static analysis
    ./vendor/bin/phpstan analyse

    if [ $? -ne 0 ]; then
        echo "Validation failed. Please review AI-generated code."
        exit 1
    fi
fi

What this looks like in practice

The pattern in the opening section is the one I use. Teaching an AI to understand a codebase covers what happens when the conventions are missing. Ralph loop gotchas covers what goes wrong when an agent runs unattended.

Three more patterns

Tests first

Generate tests first, then code:

Generate PHPUnit tests for a Laravel service that processes customer orders.

Requirements:
- Test successful order processing
- Test validation failures
- Test payment gateway errors
- Test inventory checks
- Test email notifications

Then generate the service class that passes these tests.

Writing the tests first means:

  • The code is checked against the requirements
  • Edge cases are named before the code exists
  • Later refactoring is checked by the same tests
  • The tests document what the code does

Incremental generation

For complex features, generate in layers:

Layer 1: data structure

Generate database schema for e-commerce order system.
Include: orders, order_items, customers, addresses, payments

Layer 2: models

Generate Laravel models with relationships for the above schema.

Layer 3: business logic

Generate service classes for:
- Order creation
- Payment processing
- Inventory management

Layer 4: the API

Generate API controllers and resources for the order system.

Each layer is small enough to review, and the next prompt builds on code you’ve already checked.

Context-aware generation

Include project-specific patterns in the prompt, or let the tool read them from the repository:

Generate a new Laravel controller following our project patterns.

Project context:
${projectGuidelines}
${existingControllerExamples}

Generate controller for: ${modelName}

Keeping context small

Send less context:

// Less efficient
"Here's my entire 5000-line codebase... now generate a new controller"

// More efficient
"Generate controller following this pattern: [paste single example controller]"

Batch related files: Generate multiple related files in one request:

Generate these Laravel files in one response:
1. Model: Product
2. Migration for products table
3. Factory for testing
4. Resource for API
5. FormRequest for validation

Ensure all files work together correctly.

Do and don’t

Do

Use AI for repetitive boilerplate and scaffolding
Review all generated code before committing
Run automated tests and linters
Keep a person in charge of security and business logic
Use structured prompts for consistency
Keep a note of where generated code needed fixing
Improve the prompt when the output misses

Don’t

Commit generated code without review
Use AI for security-critical code
Trust AI with complex business logic
Skip testing generated code
Ignore static analysis warnings
Use AI in place of understanding the code
Generate code without clear requirements

Key takeaways

  • Code generation is best at patterns and repetition
  • Quality and security still need a person
  • Structured prompts give more consistent output
  • Run tests, linters and security scans on every change
  • It saves the most time on boilerplate, CRUD and scaffolding

On Laravel applications, Shopify themes and WordPress plugins alike, code generation saves time when the model has the context it needs and a person reviews every line.

More in this series

For platform-specific deep dives:

Published , updated .

Part 2 of Artificial Intelligence.

Related posts