Saltar al contenido

Introducción

En el mundo de la IA generativa aplicada al desarrollo de software, constantemente aparecen nuevos modelos que prometen revolucionar nuestra forma de escribir código. Hoy pongo a prueba dos de los modelos más potentes del momento: GLM 4.7 (plan Max) de Zhipu AI y Claude Sonnet 4.5 de Anthropic.

El objetivo: refactorizar un controlador Laravel de 252 líneas aplicando buenas prácticas. Mismo prompt, mismo código base, resultados muy diferentes.

Nota: Si quieres aprender a usar GLM 4.7 con Claude Code, he publicado un curso donde explico cómo configurarlo paso a paso: Usa Claude Code sin cuenta de Anthropic con GLM

El Escenario de Prueba

Código Original

Un OrderController típico de Laravel con las siguientes características:

  • 252 líneas de código

  • Lógica de negocio mezclada con validación

  • Estados de pedido como strings literales ("pendiente", "confirmado", etc.)

  • Código duplicado entre store() y update()

  • Sin separación de responsabilidades

  • Sin transacciones de base de datos

// Ejemplo del código original
public function store(Request $request)
{
    $validated = $request->validate([
        'customer_name' => 'required|string|max:255',
        // ... validación inline
    ]);

    try {
        $orderNumber = 'ORD-' . date('Ymd') . '-' . Str::random(6);
        $total = 0;
        // ... lógica de negocio mezclada
        $order = Order::create([...]);
        // ... más lógica
    } catch (\Exception $e) {
        return back()->with('error', '...');
    }
}

El Prompt

Ambos modelos recibieron exactamente el mismo prompt:

Refactoriza este controlador de pedidos aplicando buenas prácticas Laravel:

  • Código limpio y mantenible

  • Separación de responsabilidades

  • Patrones apropiados para la lógica de negocio

No cambies la funcionalidad, solo mejora la arquitectura y calidad del código.

Resultados: GLM 4.7 Max

Ejecución Sin Errores

GLM 4.7 completó la refactorización sin ningún error. El código funcionó correctamente desde el primer intento.

Estructura Creada

app/
├── OrderStatus.php              # Enum en raíz de app/
├── OrderQuery.php               # Query class simple
├── Services/
│   └── OrderService.php         # Service layer
└── Http/
    └── Requests/
        ├── StoreOrderRequest.php
        ├── UpdateOrderRequest.php
        └── UpdateOrderStatusRequest.php

Decisiones de Arquitectura

1. Enum de Estados

<?php
namespace App;

enum OrderStatus: string
{
    case Pendiente = 'pendiente';
    case Confirmado = 'confirmado';
    case Enviado = 'enviado';
    case Entregado = 'entregado';
    case Cancelado = 'cancelado';

    public function isNotEditable(): bool
    {
        return in_array($this, [self::Enviado, self::Entregado, self::Cancelado]);
    }

    public function isDeletable(): bool
    {
        return $this === self::Pendiente;
    }
}

2. Service Layer

Toda la lógica de negocio centralizada en OrderService:

public function createOrder(array $orderData, array $items): Order
{
    return DB::transaction(function () use ($orderData, $items) {
        $order = Order::create($orderData);
        foreach ($items as $item) {
            $order->items()->create($item);
        }
        return $order;
    });
}

3. Query Class Simple

class OrderQuery
{
    public function applyFilters(array $filters): Builder
    {
        $query = Order::query();
        // ... aplica filtros
        return $query;
    }
}

4. Controlador Resultante (142 líneas)

class OrderController extends Controller
{
    public function __construct(
        private OrderService $orderService,
        private OrderQuery $orderQuery
    ) {}

    public function store(StoreOrderRequest $request): RedirectResponse
    {
        $validated = $request->validated();
        ['items' => $items, 'total' => $total] = $this->orderService->calculateOrderTotal($validated['products']);
        
        $order = $this->orderService->createOrder($orderData, $items);
        
        return redirect()->route('orders.show', $order)
            ->with('success', "Pedido creado correctamente");
    }
}

Lo Que NO Tocó

GLM 4.7 no modificó el modelo Order.php. Mantuvo la propiedad $casts original sin actualizarla al método casts() de Laravel moderno.

Resultados: Claude Sonnet 4.5

Ejecución Con Errores

Sonnet 4.5 produjo código que falló con 4 errores antes de funcionar correctamente:

  1. Error en QueryBuilder: Declaration of OrderQueryBuilder::latest() incompatible con Illuminate\Database\Eloquent\Builder::latest($column = null)

  2. Error en Vista: TypeError en index.blade.php:77

  3. Error en UpdateOrderStatus: OrderStatus::from() recibiendo un OrderStatus en lugar de string

  4. Error en UpdateOrderRequest: Mismo problema de tipos con el enum

Estructura Creada

app/
├── Actions/
│   ├── CreateOrder.php
│   ├── UpdateOrder.php
│   └── UpdateOrderStatus.php
├── Enums/
│   └── OrderStatus.php          # Enum en carpeta dedicada
├── QueryBuilders/
│   └── OrderQueryBuilder.php    # Custom Query Builder
└── Http/
    └── Requests/
        ├── StoreOrderRequest.php
        ├── UpdateOrderRequest.php
        └── UpdateOrderStatusRequest.php

Decisiones de Arquitectura

1. Enum con Método label()

<?php
namespace App\Enums;

enum OrderStatus: string
{
    case Pendiente = 'pendiente';
    // ...

    public function label(): string
    {
        return match ($this) {
            self::Pendiente => 'Pendiente',
            // ...
        };
    }

    public function isEditable(): bool
    {
        return match ($this) {
            self::Pendiente, self::Confirmado => true,
            default => false,
        };
    }
}

2. Patrón Action

En lugar de un Service, usó Actions individuales:

class CreateOrder
{
    public function execute(array $data, int $userId = 1): Order
    {
        $orderNumber = $this->generateOrderNumber();
        $items = $this->prepareOrderItems($data['products']);
        $total = $this->calculateTotal($items);

        $order = Order::create([...]);
        $this->createOrderItems($order, $items);

        return $order->fresh('items.product');
    }
}

3. Custom Query Builder Integrado al Modelo

// En Order.php
public function newEloquentBuilder($query): OrderQueryBuilder
{
    return new OrderQueryBuilder($query);
}

// Permite llamadas como:
Order::query()->applyFilters($request)->latest()->paginate(15);

4. Modelo Actualizado a Laravel Moderno

class Order extends Model
{
    public function casts(): array  // Método en lugar de propiedad
    {
        return [
            'status' => OrderStatus::class,  // Cast automático al Enum
            'total' => 'decimal:2',
            'created_at' => 'datetime',
            'updated_at' => 'datetime',
        ];
    }

    public function isEditable(): bool
    {
        return $this->status->isEditable();
    }

    public function isDeletable(): bool
    {
        return $this->status->isDeletable();
    }
}

5. Controlador Resultante (129 líneas)

public function store(StoreOrderRequest $request, CreateOrder $createOrder): RedirectResponse
{
    try {
        $order = $createOrder->execute($request->validated());
        return redirect()->route('orders.show', $order)
            ->with('success', "Pedido creado correctamente: {$order->order_number}");
    } catch (\Exception $e) {
        return back()->with('error', '...')->withInput();
    }
}

Origen de los Errores

Los 4 errores de Sonnet 4.5 tienen una causa común: inconsistencia entre el cast automático del enum y el código que esperaba strings.

Al configurar 'status' => OrderStatus::class en el modelo, $order->status ya es un OrderStatus, no un string. Pero en varios lugares, el código intentaba:

// Error: $order->status ya es OrderStatus, no string
OrderStatus::from($order->status)

// Error: ucfirst espera string, recibe OrderStatus
ucfirst($order->status)

Este tipo de error revela una falta de coherencia interna en el refactor. El modelo se actualizó correctamente, pero el resto del código no se adaptó de forma consistente.

Comparativa Directa

Aspecto

GLM 4.7 Max

Claude Sonnet 4.5

Errores iniciales

0

4

Funcionó a la primera

No

Líneas controlador

142

129

Archivos nuevos

5

7

Patrón principal

Service Layer

Actions

Ubicación Enum

app/

app/Enums/

Query Builder

Clase standalone

Integrado al modelo

Modificó Model

No

Usa casts()

No (mantiene $casts)

Cast del status

Manual (string)

Automático (Enum)

Transacciones DB

No

Documentación

Básica

Exhaustiva

Análisis Técnico

Ventajas de GLM 4.7

  1. Fiabilidad: Código funcional sin iteraciones de corrección

  2. Transacciones: Uso correcto de DB::transaction() para operaciones atómicas

  3. Conservador: No tocó lo que no era necesario (el modelo funcionaba)

  4. Pragmático: Estructura simple y directa

Ventajas de Sonnet 4.5

  1. Modernización: Usó casts() en lugar de $casts (Laravel moderno)

  2. Cast automático: El enum se castea automáticamente en el modelo

  3. Organización: Estructura de carpetas más limpia (Enums/, Actions/, QueryBuilders/)

  4. Query Builder integrado: Patrón más elegante para queries

  5. Método label(): El enum puede mostrar su representación visual

  6. Helpers en modelo: $order->isEditable() es más expresivo que $this->orderService->canEditOrder($order)

  7. Documentación: REFACTOR.md mucho más detallado

La Paradoja de Sonnet 4.5

Sonnet 4.5 tomó decisiones arquitectónicamente más correctas:

  • El cast automático del enum es la forma idiomática de Laravel 11+

  • casts() es el estándar actual

  • El Query Builder integrado al modelo es más elegante

Pero falló en la coherencia interna. Al hacer el cambio al cast automático, no adaptó consistentemente todo el código que dependía del tipo de $order->status.

Esto sugiere que Sonnet 4.5 tiene un conocimiento más profundo de las mejores prácticas modernas de Laravel, pero carece de la atención al detalle necesaria para implementarlas sin errores.

La Solidez de GLM 4.7

GLM 4.7 optó por un enfoque más conservador:

  • No modificó el modelo

  • El enum no se castea automáticamente

  • Se usa OrderStatus::from($order->status) explícitamente donde hace falta

Este enfoque es menos elegante pero 100% funcional. No hay posibilidad de error por inconsistencia de tipos porque el tipo siempre es predecible.

Las Transacciones: Un Punto Crítico

Un detalle que no debe pasar desapercibido: GLM 4.7 implementó transacciones de base de datos y Sonnet 4.5 no.

// GLM 4.7 - OrderService.php
public function createOrder(array $orderData, array $items): Order
{
    return DB::transaction(function () use ($orderData, $items) {
        $order = Order::create($orderData);
        foreach ($items as $item) {
            $order->items()->create($item);
        }
        return $order;
    });
}
// Sonnet 4.5 - CreateOrder.php
public function execute(array $data, int $userId = 1): Order
{
    $order = Order::create([...]);
    $this->createOrderItems($order, $items);  // Sin transacción
    return $order->fresh('items.product');
}

Si la creación de items falla, con GLM 4.7 se hace rollback completo. Con Sonnet 4.5, quedaría un pedido huérfano sin items. En producción, esto es un bug crítico.

Conclusión

El Veredicto

GLM 4.7 Max gana esta comparativa, no por elegancia técnica, sino por lo que más importa en el mundo real: código que funciona a la primera.

En un entorno de desarrollo real:

  • GLM 4.7 habría permitido continuar trabajando inmediatamente

  • Sonnet 4.5 habría requerido debugging y 4 correcciones manuales

¿Cuándo Elegir Cada Uno?

Elige GLM 4.7 si:

  • Necesitas código funcional rápidamente

  • Valoras la fiabilidad sobre la elegancia

  • Trabajas en producción donde los errores tienen costo

  • Prefieres un enfoque conservador

Elige Sonnet 4.5 si:

  • Tienes tiempo para revisar y corregir

  • Quieres aprender mejores prácticas modernas

  • Valoras la documentación exhaustiva

  • Planeas usar el código como base educativa

Reflexión Final

Esta comparativa ilustra una verdad incómoda sobre los modelos de IA: conocimiento no es lo mismo que ejecución. Sonnet 4.5 demostró conocer patrones más modernos y mejores prácticas, pero falló en la implementación coherente.

GLM 4.7, con un enfoque más conservador, entregó código funcional sin sorpresas. En el desarrollo de software, a veces "boring" es exactamente lo que necesitas.

El código de ambos modelos está disponible en las ramas glm47 y sonnet45 del repositorio para que puedas analizarlo tú mismo.

Próximamente: ¿Claude Opus 4.5?

En una futura comparativa, pondremos a prueba a Claude Opus 4.5, el modelo más potente de Anthropic. Será interesante ver si el hermano mayor de Sonnet logra combinar el conocimiento técnico avanzado con la fiabilidad que demostró GLM 4.7.


Prueba realizada el 26 de diciembre de 2025 usando GLM 4.7 (plan Max) y Claude Sonnet 4.5 sobre un proyecto Laravel con un sistema de gestión de pedidos.


¿Quieres usar GLM 4.7 con Claude Code? Aprende cómo configurarlo en mi curso: Usa Claude Code sin cuenta de Anthropic con GLM

Artículos relacionados

school Curso completo

Curso Laravel 12
Completo 2026

El único curso 100% actualizado que incluye Laravel 12, Livewire 3, Vue 3, React 19 e Inertia 2. Aprende con proyectos reales y las últimas funcionalidades.

access_time 8 horas de contenido
layers 4 tecnologías en 1
update 100% actualizado
code Proyectos prácticos
Ver Curso Laravel 12 arrow_forward

star Incluido en cualquier suscripción

Rutas de aprendizaje