Autor: admin_ia

  • Dejar Postman: usar CLI y scripts para pruebas de APIs

    Dejar Postman: usar CLI y scripts para pruebas de APIs

    Resumen: pasar de Postman a herramientas en terminal y scripts acelera pruebas, evita clics repetitivos y facilita la automatización. Este artículo muestra por qué y cómo hacerlo con ejemplos prácticos.

    Introducción

    Postman es útil para explorar APIs y aprender. Sin embargo, en proyectos con muchos endpoints o cuando se requiere automatización y versionado, depender de una GUI provoca trabajo manual repetitivo y menos control.

    Prerrequisitos

    Antes de reemplazar Postman por comandos y scripts, instala o confirma disponibilidad de las herramientas base y control de versiones.

    • Terminal / shell (bash).
    • curl o HTTPie para solicitudes directas.
    • Un lenguaje para scripts (en los ejemplos usamos JavaScript con fetch o axios).
    • Repositorio Git para versionar colecciones de pruebas.

    Desarrollo

    Procedimiento

    Convierte flujos manuales en comandos y scripts siguiendo pasos repetibles. La idea es minimizar clics, tener trazabilidad en Git y permitir automatización.

    1. Mapea los endpoints y los parámetros que usas en Postman.
    2. Escribe comandos curl o HTTPie para cada caso común (login, CRUD).
    3. Encapsula llamadas frecuentes en scripts (por ejemplo, en JavaScript) para validación y encadenamiento.
    4. Versiona esos scripts y ejecútalos desde CI o tareas programadas para pruebas automatizadas.

    Ejemplos

    Ejemplo básico con HTTPie (fácil de leer y cómodo en terminal).

    http POST https://api.example.com/login username=admin password=1234Lenguaje del código: Bash (bash)

    Ejemplo equivalente con curl (útil cuando necesitas control fino o integración en scripts bash).

    curl -X POST https://api.example.com/data \
      -H "Authorization: Bearer <token>" \
      -H "Content-Type: application/json" \
      -d '{"name":"Kiran","age":25}'Lenguaje del código: Bash (bash)

    Ejemplo en JavaScript para usar dentro de un repositorio (versionable y combinable con pruebas unitarias o de integración).

    const headers = {
      "Authorization": "Bearer YOUR_TOKEN",
      "Content-Type": "application/json"
    };
    const body = { name: "Kiran", role: "developer" };
    
    fetch("https://api.example.com/users", {
      method: "POST",
      headers,
      body: JSON.stringify(body)
    })
      .then(res => res.json())
      .then(data => console.log(data))
      .catch(err => console.error(err));Lenguaje del código: JavaScript (javascript)

    Ventaja práctica: encadenar llamadas, registrar respuestas y usar los scripts en CI sin intervención manual.

    Checklist

    • Convertir endpoints críticos a comandos reproducibles.
    • Guardar scripts en el repositorio con ejemplos y README.
    • Agregar ejecución automática en CI para pruebas básicas.
    • Documentar cómo obtener y rotar tokens de autorización.

    Conclusión

    Postman sigue siendo valioso para aprendizaje y exploración rápida, pero mover pruebas a CLI y scripts reduce errores humanos, mejora la velocidad y habilita automatización y trazabilidad. Empieza por convertir los flujos más repetidos y versiona las pruebas.

  • Implementar TOTP (MFA) en Symfony 7: guía práctica

    Implementar TOTP (MFA) en Symfony 7: guía práctica

    \n

    En esta guía práctica se explica cómo añadir autenticación multifactor TOTP (Time-based One-Time Password) a una aplicación Symfony 7. Incluye el servicio TOTP, cambios en la entidad, migración, controladores, formulario, suscriptor de eventos y recomendaciones de seguridad.

    \n\n\n\n\n\n\n\n

    Introducción

    \n\n\n\n

    TOTP es el estándar (RFC 6238) que usan Google Authenticator, Authy y similares. Genera códigos de un solo uso a partir de un secreto compartido y la hora actual.

    \n\n\n\n

    Esta implementación está pensada para integrarse con el sistema de autenticación de Symfony: genera secretos, muestra un QR para el usuario, verifica códigos en el login y obliga a verificar tras el login usando la sesión.

    \n\n\n\n

    Prerrequisitos

    \n\n\n\n
    • Proyecto Symfony 7 con sistema de autenticación ya configurado.
    • Doctrine ORM (para persistir el secreto).
    • Conocimientos básicos de seguridad en Symfony.
    \n\n\n\n

    Instalar la dependencia para generar QR (se usa Endroid en el ejemplo):

    \n\n\n\n
    composer require endroid/qr-code
    \n\n\n\n

    Desarrollo

    \n\n\n\n

    Procedimiento

    \n\n\n\n

    Resumen de pasos implementados en el proyecto:

    \n\n\n\n
    1. Agregar campos TOTP a la entidad User y crear migración.
    2. Crear un servicio TotpService con generación/verificación de códigos y generación del URI otpauth://.
    3. Crear formularios y controladores para habilitar, verificar y deshabilitar 2FA.
    4. Registrar un EventSubscriber que intercepte peticiones y redirija a la verificación cuando corresponda.
    \n\n\n\n

    A continuación se muestran fragmentos clave (no es necesario copiar todo el archivo, adapte según su proyecto).

    \n\n\n\n

    Entidad: añadir campos para estado y secreto TOTP.

    \n\n\n\n
    <?php\n// src/Entity/User.php (fragmento)\n\n#[ORM\Column(name: 'totp_enabled', type: 'boolean', options: ['default' => false])]\nprivate bool $totpEnabled = false;\n\n#[ORM\Column(name: 'totp_secret', type: 'string', length: 64, nullable: true)]\nprivate ?string $totpSecret = null;\n\npublic function isTotpEnabled(): bool\n{\n    return $this->totpEnabled;\n}\n\npublic function setTotpEnabled(bool $enabled): self\n{\n    $this->totpEnabled = $enabled;\n    return $this;\n}\n\npublic function getTotpSecret(): ?string\n{\n    return $this->totpSecret;\n}\n\npublic function setTotpSecret(?string $secret): self\n{\n    $this->totpSecret = $secret ? trim($secret) : null;\n    return $this;\n}\n
    \n\n\n\n

    Migración: ejemplo que añade las columnas totp_enabled y totp_secret.

    \n\n\n\n
    <?php\n// migrations/Version20240101000000.php (fragmento)\n\npublic function up(Schema $schema): void\n{\n    $table = $schema->getTable('users');\n    if (!$table->hasColumn('totp_enabled')) {\n        $table->addColumn('totp_enabled', 'boolean', ['default' => false]);\n    }\n    if (!$table->hasColumn('totp_secret')) {\n        $table->addColumn('totp_secret', 'string', ['length' => 64, 'notnull' => false]);\n    }\n}\n
    \n\n\n\n

    Servicio TotpService: responsabilidades principales — generar secreto, generar/verificar códigos y crear URI de aprovisionamiento.

    \n\n\n\n
    <?php\n// src/Service/TotpService.php (fragmento)\n\npublic function generateSecret(int $length = 32): string\n{\n    $bytes = random_bytes($length);\n    return rtrim(strtr(base64_encode($bytes), '+/', 'XY'), '=');\n}\n\npublic function getProvisioningUri(string $label, string $issuer, string $secret): string\n{\n    $label = rawurlencode($label);\n    $issuerEncoded = rawurlencode($issuer);\n\n    return sprintf(\n        'otpauth://totp/%s?secret=%s&issuer=%s&period=%d&digits=%d&algorithm=%s',\n        $label,\n        $secret,\n        $issuerEncoded,\n        $this->period,\n        $this->digits,\n        strtoupper($this->algorithm)\n    );\n}\n\npublic function verifyCode(string $secret, string $code, int $window = 1): bool\n{\n    $now = time();\n    $code = trim($code);\n    for ($i = -$window; $i <= $window; $i++) {\n        $timestamp = $now + ($i * $this->period);\n        if (hash_equals($this->getCode($secret, $timestamp), $code)) {\n            return true;\n        }\n    }\n    return false;\n}\n
    \n\n\n\n

    Form types: formularios sencillos para habilitar y deshabilitar 2FA que aceptan el código de 6 dígitos.

    \n\n\n\n
    <?php\n// src/Form/User/TwoFactorEnableType.php (fragmento)\n\n$builder->add('code', TextType::class, [\n    'label' => 'Authentication code',\n    'attr' => [\n        'autocomplete' => 'one-time-code',\n        'inputmode' => 'numeric',\n        'pattern' => '[0-9]*',\n        'maxlength' => 6,\n    ],\n]);\n
    \n\n\n\n

    Controlador de ajustes: genera secreto temporal en sesión, muestra QR y confirma el código antes de persistir el secreto en la base de datos.

    \n\n\n\n
    <?php\n// src/Controller/User/SettingsController.php (fragmento)\n\n$pendingSecret = $session->get('2fa_pending_secret');\n// Regenerar si no existe o expiró\nif (!$pendingSecret || (time() - $session->get('2fa_pending_secret_time', 0)) > 600) {\n    $pendingSecret = $totpService->generateSecret();\n    $session->set('2fa_pending_secret', $pendingSecret);\n    $session->set('2fa_pending_secret_time', time());\n}\n\n$uri = $totpService->getProvisioningUri($user->getEmail(), $request->getHttpHost(), $pendingSecret);\n$qrSvg = $totpService->generateInlineSvgQr($uri, 180);\n
    \n\n\n\n

    Suscriptor de eventos: intercepta solicitudes y redirige a la ruta de verificación si el usuario tiene 2FA activado pero no ha verificado la sesión.

    \n\n\n\n
    <?php\n// src/EventSubscriber/UserTwoFactorSubscriber.php (fragmento)\n\nif (!$event->isMainRequest()) {\n    return;\n}\n\n$request = $event->getRequest();\nif (!str_starts_with($request->getPathInfo(), '/panel')) {\n    return; // limitar al panel de usuario\n}\n\n$user = $this->getAuthenticatedUser();\nif (!$user || !$user->isTotpEnabled() || !$user->getTotpSecret()) {\n    return;\n}\n\n$session = $this->getSession();\nif ($session && $session->get(self::SESSION_KEY) === true) {\n    return; // ya verificado\n}\n\n$event->setResponse(new RedirectResponse($this->urlGenerator->generate('user_2fa')));\n
    \n\n\n\n

    Seguridad en la configuración del firewall: redirigir al usuario a la ruta /panel/2fa tras el login es una opción sencilla para iniciar el flujo de verificación.

    \n\n\n\n
    security:\n    firewalls:\n        user:\n            pattern: ^/panel\n            provider: user_provider\n            lazy: true\n            form_login:\n                login_path: /panel/\n                check_path: /panel/\n                enable_csrf: true\n                default_target_path: /panel/2fa\n            logout:\n                path: /panel/logout\n                target: /panel/\n\n    access_control:\n        - { path: ^/panel/?$, roles: PUBLIC_ACCESS }\n        - { path: ^/panel, roles: IS_AUTHENTICATED_REMEMBERED }\n
    \n\n\n\n

    Protecciones adicionales sugeridas: limitación de intentos (rate limiting), comparación en tiempo constante (hash_equals) y manejo cuidadoso de sesiones y secretos temporales.

    \n\n\n\n
    <?php\n// Ejemplo de rate limiting dentro del controlador de verificación\n$limiter = $twoFactorLimiter->create($request->getClientIp());\nif (!$limiter->consume()->isAccepted()) {\n    throw new TooManyRequestsHttpException(null, 'Too many attempts. Please wait.');\n}\n
    \n\n\n\n

    Ejemplos

    \n\n\n\n

    Generar un URI de aprovisionamiento (otpauth) que puedan escanear las apps autenticadoras:

    \n\n\n\n
    // Uso en controlador\n$label = $user->getEmail();\n$issuer = $request->getHttpHost();\n$secret = $totpService->generateSecret();\n$uri = $totpService->getProvisioningUri($label, $issuer, $secret);\n$qrSvg = $totpService->generateInlineSvgQr($uri, 180);\n
    \n\n\n\n

    Verificar un código recibido del usuario (ventana de tolerancia = 1 período por defecto):

    \n\n\n\n
    // Verificación\nif ($totpService->verifyCode($storedSecret, $submittedCode)) {\n    // Aceptado: marcar sesión como verificada\n    $session->set('2fa_verified', true);\n} else {\n    // Código inválido\n}\n
    \n\n\n\n

    Checklist

    \n\n\n\n
    1. Agregar totp_enabled y totp_secret en la entidad y migrar.
    2. Registrar TotpService en el contenedor y probar generación/verificación localmente.
    3. Crear formularios y controladores para setup/disable/verify.
    4. Implementar EventSubscriber para forzar verificación tras el login.
    5. Configurar rate limiting y proteger rutas sensibles.
    6. Planificar recuperación: backup codes o proceso de soporte.
    \n\n\n\n

    Conclusión

    \n\n\n\n

    Implementar TOTP sin dependencias externas ofrece control y entendimiento del flujo de MFA. El patrón mostrado (servicio TOTP + suscriptor de eventos + verificación en sesión) es ligero y fácil de adaptar a requisitos adicionales como backup codes o WebAuthn.

    \n\n\n\n

    Priorice siempre: comparación en tiempo constante, limitación de intentos y proteger los secretos en reposo. Después de implementar, pruebe el flujo completo en entornos de staging antes de desplegar en producción.

    \n\n
  • Notificaciones PHP en tiempo real con Redis y WebSockets

    Notificaciones PHP en tiempo real con Redis y WebSockets

    Resumen: guía práctica para enviar notificaciones en tiempo real desde PHP usando Redis como bus de mensajes y WebSockets para entrega instantánea. Incluye ejemplo mínimo, buenas prácticas y checklist de despliegue.

    Introducción

    Las notificaciones en tiempo real son fundamentales para la experiencia de usuario moderna. En vez de pollings periódicos, una combinación de Redis (Pub/Sub o Streams) y un servidor WebSocket permite entregar mensajes al instante desde PHP.

    Prerrequisitos

    • PHP con soporte para ejecución de procesos permanentes (p. ej. Workerman, Swoole o Ratchet)
    • Redis (instancia accesible desde el servidor WebSocket)
    • Cliente Redis para PHP (Predis o phpredis) y composer
    • Clientes Web que soporten WebSockets y lógica de reconexión

    Desarrollo

    La arquitectura básica separa responsabilidades: la aplicación PHP publicará eventos en Redis; un proceso WebSocket suscrito a Redis retransmitirá esos mensajes a los clientes conectados.

    Procedimiento

    Pasos esenciales:

    1. Publicar un evento en Redis desde la app PHP cuando ocurre la acción (por ejemplo, comentario nuevo).
    2. El proceso WebSocket está suscrito al canal Redis y recibe el evento.
    3. El proceso WebSocket envía el payload a los clientes conectados (filtrando por usuario o canal si aplica).
    4. El cliente maneja el mensaje (mostrar notificación, solicitar detalles, marcar como leído, etc.).

    Ejemplo mínimo de servidor WebSocket en PHP usando Workerman y Predis (suscripción a Redis y broadcast a conexiones activas).

    <?php
    use Workerman\Worker;
    use Predis\Client as RedisClient;
    
    require 'vendor/autoload.php';
    
    $ws = new Worker("websocket://0.0.0.0:8080");
    $ws->count = 1;
    
    $redis = new RedisClient();
    
    $ws->onWorkerStart = function() use ($ws, $redis) {
        $pubsub = $redis->pubSubLoop();
        $pubsub->subscribe('notifications');
        foreach ($pubsub as $message) {
            if ($message->kind === 'message') {
                foreach ($ws->connections as $connection) {
                    $connection->send($message->payload);
                }
            }
        }
    };
    
    Worker::runAll();
    Lenguaje del código: PHP (php)

    Este proceso mantiene una conexión permanente a Redis y distribuye cada mensaje recibido a todas las conexiones WebSocket. En producción conviene no enviar todo el payload a todos los clientes: filtrar por canal/usuario.

    Ejemplo de publicación de un evento desde la aplicación PHP usando Predis.

    <?php
    use Predis\Client as RedisClient;
    
    $redis = new RedisClient();
    
    $event = json_encode([
        'type' => 'comment_created',
        'user_id' => 42,
        'data' => ['id' => 123, 'excerpt' => 'Nuevo comentario']
    ]);
    
    $redis->publish('notifications', $event);
    Lenguaje del código: PHP (php)

    En el ejemplo anterior publicamos en un canal genérico ‘notifications’. Para evitar sobre-broadcasting, use canales por usuario o por topic (p. ej. notifications:user:42).

    Ejemplos

    Cliente JavaScript: conexión WebSocket básica con reconexión y manejo de mensajes ligeros (solo resumen; adapte según su app).

    function createSocket(url) {
      let ws;
      let retry = 1000;
    
      function connect() {
        ws = new WebSocket(url);
    
        ws.onopen = () => {
          retry = 1000; // reset backoff
          console.log('WS connected');
        };
    
        ws.onmessage = (ev) => {
          const msg = JSON.parse(ev.data);
          // manejar evento: mostrar notificación o solicitar detalles
          console.log('evento', msg);
        };
    
        ws.onclose = () => {
          console.log('WS closed, reconnecting in', retry);
          setTimeout(connect, retry);
          retry = Math.min(30000, retry * 2);
        };
    
        ws.onerror = (err) => {
          console.error('WS error', err);
          ws.close();
        };
      }
    
      connect();
      return () => ws && ws.close();
    }
    
    // uso
    const stop = createSocket('wss://example.com:8080');
    Lenguaje del código: JavaScript (javascript)

    Consejo: mantenga los payloads ligeros. Enviar solo el ID del evento o un resumen reduce latencia y consumo de ancho de banda.

    Redis + WebSockets transforman un backend PHP tradicional en un canal de notificaciones cercano a tiempo real, sin necesidad de reescrituras completas.

    Checklist

    1. Usar canales por usuario o topic para evitar sobre-broadcast.
    2. Autenticar y autorizar suscripciones (gateway o tokens JWT).
    3. Implementar reconexión y reintentos en cliente.
    4. Considerar Redis Streams si necesita persistencia y replay de mensajes.
    5. Monitoreo del proceso WebSocket y alertas para reinicio automático.
    6. Limitar tamaño del payload y evitar enviar datos sensibles por el socket.

    Conclusión

    Combinar Redis y WebSockets permite a aplicaciones PHP ofrecer notificaciones instantáneas sin depender de polling. El patrón reduce latencia y escala bien si se aplica filtrado por canales, autenticación y manejo de fallos.

    Empiece con un prototipo mínimo (un proceso WebSocket suscrito a Redis) y evolucione a Streams, balanceo y autenticación según las necesidades de producción.

  • PHP vs Node.js: cómo elegir el backend en 2025

    PHP vs Node.js: cómo elegir el backend en 2025

    Resumen: Comparativa técnica entre PHP y Node.js para elegir backend según requisitos de rendimiento, ecosistema y experiencia del equipo.

    Este artículo sintetiza ventajas, limitaciones y ejemplos mínimos para ayudar a arquitectos y desarrolladores a decidir qué tecnología usar.

    Introducción

    PHP y Node.js son tecnologías maduras para backend con enfoques distintos: PHP tradicionalmente síncrono y orientado a request-response; Node.js basado en I/O asíncrono y event-driven.

    Prerrequisitos

    Antes de elegir, evalúa: cargas concurrentes previstas, necesidad de tiempo real, experiencia del equipo y requisitos de despliegue.

    1. Definir el patrón de tráfico (p. ej. muchas conexiones concurrentes vs pocas por usuario).
    2. Identificar dependencias y ecosistema (CMS, bibliotecas, integraciones).
    3. Revisar capacidades de hosting y costos operativos.

    Desarrollo

    Comparar en cuatro ejes: rendimiento, ecosistema, curva de aprendizaje y despliegue.

    Procedimiento

    1) Analiza el caso de uso. 2) Mide concurrencia y latencia objetivo. 3) Selecciona la pila que minimice riesgos técnicos y de operación.

    Resumen de diferencias clave:

    • PHP: buen soporte para sitios de contenido, despliegue sencillo en hosting compartido, amplio ecosistema (WordPress, Composer, Laravel).
    • Node.js: mejor para I/O intensivo y tiempo real; ecosistema moderno con npm y frameworks como Express o Fastify.

    Ejemplos

    Ejemplos mínimos que ilustran el modelo de ejecución en cada plataforma.

    <?php
    // Ejemplo PHP: manejador básico en entorno tradicional (request-response)
    header('Content-Type: application/json');
    $response = ['time' => date('c'), 'message' => 'Hola desde PHP'];
    echo json_encode($response);
    Lenguaje del código: PHP (php)

    En PHP el ciclo típico abre, procesa y cierra la petición; es sencillo y efectivo para sitios de contenido.

    const http = require('http');
    
    const server = http.createServer((req, res) => {
      res.writeHead(200, { 'Content-Type': 'application/json' });
      res.end(JSON.stringify({ time: new Date().toISOString(), message: 'Hola desde Node.js' }));
    });
    
    server.listen(3000, () => {
      console.log('Server listening on port 3000');
    });
    Lenguaje del código: JavaScript (javascript)

    Node.js mantiene un loop de eventos que permite gestionar muchas conexiones concurrentes con eficiencia en I/O.

    Checklist

    Lista rápida para decidir tecnología según el proyecto.

    1. ¿El proyecto requiere tiempo real o muchas conexiones concurrentes? → Considerar Node.js.
    2. ¿Priman CMS o contenidos estáticos y despliegue rápido? → PHP/WordPress puede ser suficiente.
    3. ¿El equipo domina JavaScript o PHP/Laravel? → Elegir según experiencia para reducir riesgos.
    4. ¿Necesitas microservicios o arquitectura basada en eventos? → Node.js suele facilitarlo.

    Conclusión

    No existe una respuesta universal: PHP sigue siendo válido para sitios de contenido y despliegues rápidos; Node.js destaca en aplicaciones en tiempo real y arquitecturas orientadas a I/O.

    La recomendación práctica: prioriza requisitos técnicos, experiencia del equipo y costos operativos al seleccionar entre PHP y Node.js.

  • Laravel a escala: 5 trucos para consultar millones de registros

    Laravel a escala: 5 trucos para consultar millones de registros

    Este artículo resume cinco técnicas prácticas para consultar millones de filas en Laravel sin agotar memoria ni tiempo CPU. Cada técnica incluye una explicación breve y ejemplos de código listos para usar.

    Aplica estas prácticas en procesos de reporting, ETL o consultas masivas para mantener tiempos de respuesta y uso de recursos controlados.

    Introducción

    En consultas a tablas masivas, prácticas comunes como SELECT * o cargar todo con get() provocan consumo excesivo de RAM y CPU. Las siguientes cinco estrategias reducen uso de recursos y aceleran procesos.

    Prerrequisitos

    Antes de aplicar las técnicas: asegúrate de contar con un esquema de base de datos razonable y mecanismos de cacheo/colas si vas a usar preagregados o jobs en segundo plano.

    • Acceso al código de tus modelos/Eloquent.
    • Permiso para crear índices y migraciones en la base de datos.
    • Un sistema de cache (Redis, Memcached) y, si procede, soporte para jobs/colas.

    Desarrollo

    Procedimiento

    A continuación se presentan las cinco técnicas: seleccionar columnas esenciales, procesar por chunks o stream, indexar y optimizar WHERE, preagregar datos y cachear resultados pesados.

    1. Seleccionar sólo las columnas esenciales

    Evita SELECT * cuando sólo necesitas unas pocas columnas. Reducir columnas reduce CPU, memoria y ancho de banda.

    <?php
    // ❌ Evitar: carga todas las columnas
    $orders = Order::all();
    
    // ✅ Seleccionar sólo lo necesario
    $orders = Order::select('id', 'amount', 'created_at')
        ->whereBetween('created_at', [$start, $end])
        ->get();
    Lenguaje del código: PHP (php)

    En el ejemplo base, reducir de 20 columnas a 3 puede bajar el consumo de memoria de ~1.2 GB a ~200 MB en un conjunto grande de filas.

    2. Procesar por chunks o con cursor (stream)

    No uses get() para millones de filas. chunk() y cursor() procesan en partes y mantienen baja la memoria.

    <?php
    // Procesar en bloques de 10.000
    Order::where('status', 'completed')
        ->select('id', 'amount')
        ->chunk(10000, function ($orders) {
            foreach ($orders as $order) {
                // generar fila de reporte
            }
        });
    
    // O procesar con cursor para stream perezoso
    $orders = Order::cursor()->filter(fn ($order) => $order->amount > 1000);
    Lenguaje del código: PHP (php)

    En pruebas citadas, procesar 5M de filas con cursor/chunks reduce consumo de memoria a decenas de MB frente a cientos o más.

    3. Índices y cláusulas WHERE eficientes

    Filtros y ordenamientos sin índices provocan full table scans. Indexa columnas usadas en WHERE, JOIN y ORDER BY y evita patrones ineficientes como LIKE ‘%…%’.

    <?php
    // Ejemplo de migración para agregar índices
    Schema::table('orders', function (Blueprint $table) {
        $table->index('status');
        $table->index('created_at');
    });
    Lenguaje del código: PHP (php)

    Un ejemplo reportado mostró una reducción de tiempo de consulta de 120 s a 0.8 s tras indexar columnas utilizadas en filtros.

    4. Preagregar datos para reportes pesados

    Evita agrupar o sumar decenas de millones de filas en tiempo real. Precalcula resúmenes con jobs nocturnos o usa vistas/materialized views si la BD lo permite.

    <?php
    class GenerateOrderSummary implements ShouldQueue {
        public function handle() {
            $summary = Order::selectRaw('
                DATE(created_at) AS date,
                COUNT(*) AS total_orders,
                SUM(amount) AS revenue
            ')
            ->whereDate('created_at', today()->subDay())
            ->groupBy('date')
            ->first();
    
            Report::updateOrCreate(['date' => $summary->date], (array)$summary);
        }
    }
    Lenguaje del código: PHP (php)

    Con preagregados, la carga de un reporte puede pasar de minutos a decenas de milisegundos; en el ejemplo base, un reporte cargó en ~50 ms.

    5. Cachear consultas costosas

    Para consultas repetidas, guarda el resultado en cache con expiración y/o tags. Invalida o refresca la cache cuando los datos subyacentes cambien.

    <?php
    $report = Cache::remember("daily_sales_report:{$date}", 3600, fn() =>
        Order::whereDate('created_at', $date)
            ->selectRaw('COUNT(*) AS orders, SUM(amount) AS revenue')
            ->first()
    );
    Lenguaje del código: PHP (php)

    Usa Redis o Memcached según tu infraestructura y define políticas claras de invalidación para evitar datos obsoletos.

    Ejemplos

    Ejemplos concretos ya mostrados: selección de columnas, chunk/cursor, migración para índices, job para preagregados y cacheo con Cache::remember.

    Adapta los tamaños de chunk y la expiración del cache a la carga real y al patrón de acceso de tu sistema.

    Checklist

    1. Revisar consultas y eliminar SELECT *.
    2. Usar chunk() o cursor() para procesado masivo.
    3. Indexar columnas usadas en WHERE, JOIN y ORDER BY.
    4. Preagregar resultados pesados con jobs o vistas.
    5. Cachear consultas repetidas y definir estrategia de invalidación.

    Conclusión

    Combinando selección precisa de columnas, procesamiento por partes, buenos índices, preagregados y cacheo, puedes consultar millones de registros en Laravel con uso de recursos razonable y tiempos de respuesta aceptables.

    Empieza por los cambios menos invasivos (select, chunk, cache) y mide impacto antes de introducir cambios más estructurales como materialized views o rediseño de esquemas.

  • Evolución de las colas en Laravel: workers, deferred y background

    Evolución de las colas en Laravel: workers, deferred y background

    Este artículo resume la evolución del sistema de colas en Laravel: desde los workers tradicionales hasta el driver background basado en concurrencia. Explica usos, diferencias y ejemplos prácticos de implementación.

    Introducción

    Laravel ha transitado de un sistema de colas basado en workers persistentes a opciones que reducen la complejidad operativa: drivers deferred y background (impulsado por concurrencia).

    Prerrequisitos

    Antes de aplicar cualquiera de las estrategias descritas, confirma lo siguiente:

    • Proyecto Laravel con entorno PHP configurado.
    • Driver de colas definido (Redis, database, etc.) si planeas usar workers tradicionales.
    • En caso de background driver, la configuración de conexiones en config/queue.php debe incluir la conexión ‘background’ (ver ejemplo).

    Desarrollo

    Procedimiento

    A continuación se describen las tres aproximaciones principales y cómo elegir entre ellas según la carga y la tolerancia a fallos.

    1. Workers tradicionales: ejecutan jobs mediante procesos persistentes (p. ej. php artisan queue:work redis), adecuados para cargas pesadas y con control avanzado de reintentos y fallos.
    2. Driver deferred: ejecuta el job después de enviar la respuesta HTTP, sin worker. Adecuado para tareas ligeras y no críticas.
    3. Driver background (concurrencia): ejecuta jobs en procesos PHP separados, sin worker persistente; equilibrio entre robustez y simplicidad operativa.

    Ventajas y limitaciones resumidas:

    • Workers tradicionales: alto rendimiento y control; requieren DevOps para gestionar procesos.
    • Deferred: baja sobrecarga; limitado en reintentos y duración de tarea.
    • Background: sin workers dedicados, más robusto que deferred; útil para tareas de duración media.
    php artisan queue:work redis
    Lenguaje del código: Bash (bash)
    <?php
    return [
        'connections' => [
            'background' => [
                'driver' => 'background',
            ],
        ],
    ];
    Lenguaje del código: PHP (php)

    El ejemplo anterior muestra cómo declarar una conexión ‘background’ en config/queue.php. Su dispatch usa la conexión para ejecutar el job en otro proceso PHP.

    Ejemplos

    Ejemplo de clase Job que implementa ShouldQueue y se serializa para su ejecución en background o por un worker.

    <?php
    namespace App\Jobs;
    
    use Illuminate\Bus\Queueable;
    use Illuminate\Contracts\Queue\ShouldQueue;
    use Illuminate\Foundation\Bus\Dispatchable;
    use Illuminate\Queue\SerializesModels;
    
    class RecordDelivery implements ShouldQueue
    {
        use Dispatchable, Queueable, SerializesModels;
    
        public function __construct(public $order)
        {
        }
    
        public function handle()
        {
            // lógica de procesamiento
        }
    }
    Lenguaje del código: PHP (php)

    Dispatch desde un controlador usando la conexión background para evitar bloquear la petición HTTP.

    <?php
    use App\Jobs\RecordDelivery;
    
    class DeliveryController
    {
        public function store(Request $request)
        {
            $order = Order::create([...]);
            RecordDelivery::dispatch($order)->onConnection('background');
    
            return response()->json(['status' => 'processing'], 202);
        }
    }
    Lenguaje del código: PHP (php)

    Checklist

    1. Determina la criticidad de la tarea (alta → worker tradicional).
    2. Si la tarea es ligera y no crítica, considera deferred.
    3. Para equilibrio operativo (sin workers pero más robusto que deferred), usa el driver background.
    4. Configura reintentos, timeouts y monitoreo según el método elegido.

    Conclusión

    La evolución de las colas en Laravel ofrece alternativas para distintos perfiles de carga operativa. Elegir entre workers, deferred o background depende de la carga, la tolerancia a fallos y la capacidad operativa del equipo.

    Si buscas simplificar la operación sin renunciar a capacidad de procesamiento, el driver background —impulsado por concurrencia— es una opción intermedia interesante.

  • AsyncRequestContext en Laravel: reducir middleware con contexto

    AsyncRequestContext en Laravel: reducir middleware con contexto

    Este artículo explica cómo reemplazar middleware que solo propagan datos por petición con un contexto scoped (AsyncRequestContext) en Laravel. El objetivo: reducir la complejidad de rutas, mejorar la claridad de dependencias y facilitar pruebas unitarias.

    Incluye el diseño del contexto, el enlace en el service container y ejemplos de uso en acciones, controladores, rutas y pruebas.

    Introducción

    El patrón AsyncRequestContext propone un contenedor ligero por petición para almacenar estado que actualmente se inyecta en Illuminate\Http\Request mediante middleware.

    En lugar de ensuciar el Request con claves arbitrarias, se inicializa un contexto por ciclo de petición y se usa desde acciones y bindings del contenedor.

    Prerrequisitos

    Conocimientos básicos de Laravel: service container, providers, uso de acciones/servicios y rutas. No se requiere configuración adicional en el framework más allá de registrar el provider.

    Desarrollo

    La idea central es exponer un RequestContext scoped que se inicializa al enlazar en el container y se limpia en termination. Evita ‘request pollution’ y hace explícitas las dependencias.

    <?php
    namespace App\Context;
    
    class RequestContext
    {
        private static ?array $data = null;
    
        public static function initialize(): void
        {
            self::$data = [];
        }
    
        public static function set(string $key, mixed $value): void
        {
            self::$data[$key] = $value;
        }
    
        public static function get(string $key): mixed
        {
            return self::$data[$key] ?? null;
        }
    
        public static function flush(): void
        {
            self::$data = null;
        }
    }
    Lenguaje del código: PHP (php)

    En el service provider se enlaza RequestContext para inicializarlo por petición y se registra un hook de terminating para limpiar el estado.

    <?php
    namespace App\Providers;
    
    use Illuminate\Support\ServiceProvider;
    use App\Context\RequestContext;
    
    class ContextServiceProvider extends ServiceProvider
    {
        public function boot()
        {
            $this->app->bind(RequestContext::class, function () {
                RequestContext::initialize();
                return new RequestContext();
            });
    
            $this->app->terminating(function () {
                RequestContext::flush();
            });
        }
    }
    Lenguaje del código: PHP (php)

    Procedimiento

    En lugar de usar middleware que sólo añade datos al Request, crea acciones que reciben el Request y escriben en el RequestContext. Eso permite invocarlas donde y cuando hagan falta.

    <?php
    namespace App\Actions;
    
    use Illuminate\Http\Request;
    use App\Context\RequestContext;
    use App\Models\Tenant;
    
    class ResolveTenant
    {
        public function __construct(private RequestContext $context) {}
    
        public function execute(Request $request): void
        {
            $tenant = Tenant::fromDomain($request->host());
            $this->context->set('tenant', $tenant);
        }
    }
    Lenguaje del código: PHP (php)

    Consume el contexto explícitamente en controladores o servicios, lo que revela dependencias y facilita pruebas unitarias.

    <?php
    namespace App\Http\Controllers;
    
    use App\Context\RequestContext;
    
    class ReportController
    {
        public function index(RequestContext $context)
        {
            $tenant = $context->get('tenant');
            return $tenant->reports()->paginate();
        }
    }
    Lenguaje del código: PHP (php)

    Las rutas pueden simplificarse ejecutando acciones de resolución dentro de la closure y pasando el contexto al controlador.

    <?php
    use App\Actions\ResolveTenant;
    use App\Context\RequestContext;
    use App\Http\Controllers\ReportController;
    use Illuminate\Support\Facades\Route;
    
    Route::get('/reports', function (
        ResolveTenant $resolve,
        RequestContext $context
    ) {
        $resolve->execute(request());
        return (new ReportController)->index($context);
    })->middleware('auth:api');
    Lenguaje del código: PHP (php)

    Con este enfoque mantienes middleware para filtrado (autenticación, throttling) y usas acciones/contexto para propagar estado.

    Ejemplos

    Ejemplo de binding contextual que elige una implementación según un valor en RequestContext.

    <?php
    // En AppServiceProvider.php
    $this->app->bind(ReportExporter::class, function () {
        $format = RequestContext::get('export_format') ?? 'csv';
        return match ($format) {
            'excel' => new ExcelExporter,
            'pdf'   => new PDFExporter,
            default => new CSVExporter,
        };
    });
    Lenguaje del código: PHP (php)

    Prueba unitaria sin levantar el HTTP layer inicializando el contexto manualmente:

    <?php
    test('exports tenant reports in excel', function () {
        RequestContext::initialize();
        RequestContext::set('tenant', Tenant::factory()->create());
        RequestContext::set('export_format', 'excel');
    
        $exporter = app(ReportExporter::class);
        $this->assertInstanceOf(ExcelExporter::class, $exporter);
    });
    Lenguaje del código: PHP (php)

    Checklist

    1. Auditar middleware que solo escriben datos en el Request.
    2. Refactorizar esas responsabilidades en acciones (ResolveX classes).
    3. Sustituir $request->get(‘foo’) por $context->get(‘foo’) donde corresponda.
    4. Reducir arrays de middleware en rutas dejando filtros reales (auth, throttle).
    5. Asegurar que RequestContext::flush() se ejecuta en terminating.

    Conclusión

    AsyncRequestContext ofrece una alternativa práctica al uso excesivo de middleware para propagar estado por petición. Mejora la claridad, facilita pruebas y mantiene middleware para filtrado.

    Middleware es para filtrado, no para propagar datos. Deja que el contexto lleve tu estado cross‑cutting.

  • Agentes inteligentes con Sequential Thinking MCP y OpenAI Agents

    Agentes inteligentes con Sequential Thinking MCP y OpenAI Agents

    Resumen: Este artículo muestra cómo combinar Sequential Thinking MCP con el OpenAI Agents SDK para dotar a agentes de una memoria estructurada y razonamiento paso a paso.

    Incluye los prerrequisitos, la arquitectura básica, ejemplos de código y una checklist para empezar rápidamente.

    Introducción

    Los agentes conversacionales tienden a perder el hilo cuando la tarea requiere múltiples pasos o reflexión profunda. Sequential Thinking MCP actúa como una memoria externa donde el agente puede registrar pensamientos, organizar etapas y recuperar resúmenes.

    El enfoque permite transformar respuestas reactivas en procesos de razonamiento sistemático que pueden auditarse y reutilizarse.

    Prerrequisitos

    Antes de integrar Sequential Thinking MCP con un agente necesitas instalar las dependencias y disponer del entorno donde ejecutar el servidor MCP.

    Instala los paquetes necesarios con pip:

    pip install openai-agents
    pip install mcp-sequential-thinking
    Lenguaje del código: Bash (bash)

    Desarrollo

    La integración sigue un patrón sencillo: arrancar el servidor MCP, registrar el servidor como herramienta del agente y usar las funciones de registro y resumen durante la sesión de razonamiento.

    Funciones básicas que ofrece el servidor Sequential Thinking MCP (según el diseño comentado):

    • process_thought — registra un pensamiento con metadatos (etapa, secuencia, importancia).
    • generate_summary — produce un resumen del proceso de pensamiento acumulado.
    • clear_history — borra la memoria de pensamientos para empezar una sesión nueva.

    Procedimiento

    Ejemplo básico de flujo para crear y registrar un servidor MCP en un agente. El snippet ilustra el lanzamiento del servidor y la creación del agente que lo utiliza.

    async def create_thinking_agent():
        # Launch the Sequential Thinking MCP server
        seq_server = MCPServerStdio(
            name="SequentialThinkingServer",
            params={
                "command": "mcp-sequential-thinking",
                "args": []
            }
        )
        
        # Connect and register with agent
        async with seq_server as mcp:
            agent = Agent(
                name="ThinkingAgent",
                instructions=(
                    "You are a methodical problem-solver. Use Sequential "
                    "Thinking tools to organize your analysis step-by-step."
                ),
                mcp_servers=[mcp]
            )
            
            return agent, mcp
    Lenguaje del código: texto plano (plaintext)

    Con el agente registrado, puedes ejecutar tareas complejas pasando la pregunta inicial al Runner del SDK:

    # Definir un agente que use el servidor MCP
    agent = Agent(
        name="TimeTravelPlanner",
        instructions=(
            "You are a planning assistant. Think step-by-step, and use the Sequential "
            "Thinking tools (thought recording, summary) to organize your plan."
        ),
        mcp_servers=[mcp]
    )
    
    # Ejecutar la tarea con Runner
    user_query = "Devise a safe and efficient plan to travel to the year 2050 and back."
    result = await Runner.run(starting_agent=agent, input=user_query)
    print(result.final_output)
    Lenguaje del código: texto plano (plaintext)

    Maneja fallos de conexión al servidor MCP con un fallback sencillo para mantener disponibilidad:

    try:
        async with seq_server as mcp:
            # Lógica del agente que usa MCP
            pass
    except ConnectionError:
        # Fallback a agente sin MCP
        agent = Agent(name="FallbackAgent", instructions="...")
    Lenguaje del código: texto plano (plaintext)

    Ejemplos

    En un caso complejo (p. ej. plan de viaje temporal), el agente con memoria estructurada produce un resumen con etapas claras en lugar de respuestas dispersas.

    Ejemplo simplificado de salida estructurada (ilustrativa):

    Time Travel Plan Analysis:
    Problem Definition: Safe bidirectional time travel to 2050
    Research: Evaluated 3 theoretical methods
    Analysis: Relativistic travel most feasible
    Synthesis: Detailed 30-year mission plan
    Conclusion: Actionable recommendations with safety protocols
    Lenguaje del código: texto plano (plaintext)

    Checklist

    1. Instalar dependencias: openai-agents y mcp-sequential-thinking.
    2. Arrancar el servidor MCP y verificar conexión estable.
    3. Registrar el MCP como herramienta en el agente.
    4. Diseñar etapas de pensamiento (Problem → Research → Analysis → Synthesis → Conclusion).
    5. Implementar manejo de fallos y fallback sin MCP.
    6. Probar con un caso complejo y comparar resultados con el agente tradicional.
    7. Medir y auditar la memoria de pensamientos según necesidades del proyecto.

    Conclusión

    Sequential Thinking MCP aporta una memoria estructurada que permite a los agentes razonar de forma secuencial y coherente. La integración con OpenAI Agents SDK simplifica el patrón: registrar pensamientos, generar resúmenes y continuar la reflexión.

    Empieza por implementar el flujo básico, maneja las conexiones con cuidado y aplica el enfoque solo en problemas que requieran análisis en varias etapas.

  • Registrar cada llamada API sin ralentizar el servidor

    Registrar cada llamada API sin ralentizar el servidor

    Resumen: Cómo registrar cada llamada a tu API sin degradar el rendimiento. Enfoque práctico: logging asíncrono, buffering, offload a workers, enmascarado de datos y rotación.

    Introducción

    Registrar cada petición API es crítico para depuración, auditoría y análisis, pero hacerlo mal puede convertir el logging en el principal cuello de botella.

    La regla de oro: nunca hagas que el manejador principal espere a que los logs se escriban. El registro debe ser asíncrono y, preferiblemente, salir del camino de ejecución principal.

    Prerrequisitos

    Los ejemplos usan Node.js y bibliotecas comunes; adapta las ideas a otros runtimes si es necesario.

    • Dependencia de un logger de alto rendimiento (p. ej. Pino).
    • Mecanismo para buffering o una cola (memoria, Redis, Kafka).
    • Worker o servicio externo para persistencia y envío a sistemas centralizados.

    Desarrollo

    Procedimiento

    Paso 1 — Decide qué registrar: evita sobre-logging. Campos típicos: timestamp, método, ruta, status, latencia, IP, userId, requestId y payload con datos sensibles enmascarados.

    {
      "time": "2025-08-11T14:23:45.123Z",
      "method": "POST",
      "url": "/api/orders",
      "status": 201,
      "responseTimeMs": 123,
      "userId": "usr_1a2b3c",
      "ip": "203.0.113.42",
      "userAgent": "Mozilla/5.0 (Macintosh...)",
      "requestId": "req_abcd1234"
    }
    Lenguaje del código: JSON / JSON con comentarios (json)

    Paso 2 — Usa un logger no bloqueante. Olvida console.log en producción; emplea un logger diseñado para escritura asíncrona y serialización rápida.

    const pino = require('pino');
    const logger = pino({
      level: 'info',
      transport: {
        target: 'pino-pretty'
      }
    });
    
    app.use((req, res, next) => {
      const start = Date.now();
      
      res.on('finish', () => {
        logger.info({
          method: req.method,
          url: req.originalUrl,
          status: res.statusCode,
          responseTime: Date.now() - start
        });
      });
      
      next();
    });
    Lenguaje del código: JavaScript (javascript)

    Paso 3 — Bufferiza en memoria y envía en lotes. Convierte miles de escrituras pequeñas en pocas escrituras grandes.

    let logBuffer = [];
    const BUFFER_SIZE = 50;
    const FLUSH_INTERVAL = 5000; // ms
    
    function flushLogs() {
      if (logBuffer.length > 0) {
        logger.info({ batch: logBuffer });
        logBuffer = [];
      }
    }
    
    setInterval(flushLogs, FLUSH_INTERVAL);
    
    app.use((req, res, next) => {
      const start = Date.now();
    
      res.on('finish', () => {
        logBuffer.push({
          method: req.method,
          url: req.originalUrl,
          status: res.statusCode,
          time: Date.now(),
          latency: Date.now() - start
        });
    
        if (logBuffer.length >= BUFFER_SIZE) {
          flushLogs();
        }
      });
    
      next();
    });
    Lenguaje del código: JavaScript (javascript)

    Paso 4 — Desacopla mediante cola y workers: para tráfico intenso, envía logs a una cola y procesa desde procesos independientes.

    const Redis = require('ioredis');
    const pub = new Redis();
    
    app.use((req, res, next) => {
      const start = Date.now();
    
      res.on('finish', () => {
        pub.publish('api_logs', JSON.stringify({
          method: req.method,
          url: req.originalUrl,
          status: res.statusCode,
          latency: Date.now() - start
        }));
      });
    
      next();
    });
    Lenguaje del código: JavaScript (javascript)
    const sub = new Redis();
    sub.subscribe('api_logs');
    sub.on('message', (channel, message) => {
      const logData = JSON.parse(message);
      // Persistir logData de forma asíncrona (BD, ELK, S3...)
    });
    Lenguaje del código: JavaScript (javascript)

    Paso 5 — Envío remoto asíncrono: si mandas logs a sistemas como ELK o CloudWatch, hazlo en lotes o por workers para evitar latencia en el request path.

    const winston = require('winston');
    require('winston-cloudwatch');
    
    winston.add(new winston.transports.CloudWatch({
      logGroupName: 'my-api-logs',
      logStreamName: 'production',
      awsRegion: 'us-east-1',
      jsonMessage: true
    }));
    
    app.use((req, res, next) => {
      const start = Date.now();
      res.on('finish', () => {
        winston.info({
          method: req.method,
          url: req.originalUrl,
          status: res.statusCode,
          latency: Date.now() - start
        });
      });
      next();
    });
    Lenguaje del código: JavaScript (javascript)

    Paso 6 — Enmascara datos sensibles antes de loguear: nunca escribas contraseñas, números de tarjeta o tokens sin protección.

    function maskSensitive(obj) {
      const clone = { ...obj };
      if (clone.password) clone.password = '******';
      if (clone.cardNumber) clone.cardNumber = '**** **** **** ' + clone.cardNumber.slice(-4);
      return clone;
    }
    
    app.use(express.json());
    app.use((req, res, next) => {
      req.body = maskSensitive(req.body);
      next();
    });
    Lenguaje del código: JavaScript (javascript)

    Paso 7 — Mide y ajusta: compara latencias y uso de CPU/memoria antes y después del cambio. Usa herramientas de carga y APM para validar impacto.

    autocannon -c 100 -d 30 http://localhost:3000
    Lenguaje del código: Bash (bash)

    Paso 8 — Rotación y archivo: configura rotación por tamaño o día y mueve logs antiguos a almacenamiento económico.

    pino server.js | tee >(pino-pretty) | rotatelogs ./logs/api-%Y-%m-%d.log 86400
    Lenguaje del código: Bash (bash)

    Paso 9 — Distribuye a gran escala: evita escribir localmente, usa shippers (Filebeat/Fluent Bit), Kafka para ingesta masiva y sampling para reducir volumen de success logs.

    Paso 10 — Arquitectura de producción (resumen): gateway → collectors/colas → workers → almacenamiento indexable → dashboards y alertas.

    Ejemplos

    Los ejemplos anteriores cubren patrones comunes: logger rápido (Pino), buffering en proceso, publicación a Redis y consumo por workers, y envío asíncrono a servicios remotos.

    Checklist

    1. No bloquear el request path: logging asíncrono.
    2. Usar un logger de alto rendimiento (p. ej. Pino).
    3. Bufferizar y enviar en lotes.
    4. Offload a workers o colas para tráfico intenso.
    5. Enmascarar datos sensibles antes de persistir.
    6. Rotación y archivado de logs.
    7. Medir impacto y ajustar frecuencias/ tamaños de lote.

    Conclusión

    Registrar cada llamada API es viable si se diseña para no bloquear el flujo principal: emplea loggers rápidos, buffering, colas y workers; enmascara datos; y monitoriza el costo del logging.

    Aplica estas prácticas progresivamente y verifica con pruebas de carga y APM antes de desplegar en producción.

  • Identificar y gestionar limitaciones personales en equipos de software

    Identificar y gestionar limitaciones personales en equipos de software

    Resumen: En este artículo técnico explico cómo identificar qué función cumplen las limitaciones personales, cómo analizarlas y opciones prácticas para gestionarlas en contextos personales y de equipo.

    Introducción

    En entornos técnicos, las limitaciones personales suelen manifestarse como creencias internas que condicionan decisiones, prioridades y desarrollo profesional.

    Este artículo adapta conceptos de prácticas como hipnoterapia, counselling y programación neurolingüística para ofrecer un enfoque práctico y aplicable en equipos de software.

    Prerrequisitos

    No se requieren certificaciones específicas para comenzar el autoanálisis; sí es útil disponer de tiempo para reflexión y, cuando procede, el apoyo de un profesional o colega de confianza.

    Desarrollo

    Procedimiento

    El enfoque central es descubrir qué función cumple la limitación: qué evita y qué beneficio oculto proporciona. Una vez identificado, se diseñan alternativas concretas que satisfagan esa función sin la carga negativa.

    1. Identificar la creencia limitante y cuándo aparece.
    2. Analizar qué evita esa creencia (riesgo, esfuerzo, comparación social).
    3. Explorar qué beneficio oculto ofrece (comodidad, protección, coherencia social).
    4. Diseñar alternativas que cubran ese beneficio sin impedir objetivos.

    En equipos, aplicar este procedimiento implica conversaciones estructuradas y retroalimentación orientada a la función de la limitación, no a su moralización.

    #!/usr/bin/env bash
    # script de reflexión rápida
    echo "Describe la limitación (en una frase):"
    read LIMITATION
    echo "¿Qué evita esta limitación? (riesgo/tiempo/vergüenza):"
    read AVOIDED
    echo "¿Qué beneficio oculto ofrece?:"
    read BENEFIT
    echo "Alternativas posibles (3):"
    read ALT1
    read ALT2
    read ALT3
    cat <<EOF
    Limitación: $LIMITATION
    Evita: $AVOIDED
    Beneficio: $BENEFIT
    Alternativas:
     - $ALT1
     - $ALT2
     - $ALT3
    EOF
    Lenguaje del código: Bash (bash)

    Ejemplos

    Ejemplo práctico: la creencia “no soy buen programador en X” puede ocultar la preferencia por dedicar tiempo a otras áreas o el miedo a la exposición.

    Al identificar la función (ahorrar tiempo, evitar crítica), se pueden proponer alternativas: aprendizaje estructurado, tareas pair-programming o revisiones anónimas de código.

    {
      "limitation": "No soy natural en X",
      "when": "Durante revisiones de código",
      "avoids": "Exposición y crítica",
      "hidden_benefit": "Mantener tiempo para otras prioridades",
      "alternatives": [
        "Sesiones de aprendizaje estructuradas",
        "Pair programming con colega de confianza",
        "Objetivos pequeños y medibles"
      ]
    }
    Lenguaje del código: JSON / JSON con comentarios (json)

    Checklist

    1. Registrar la limitación con contexto concreto.
    2. Especificar qué evita y qué aporta.
    3. Evaluar si renombrarla o sustituirla por una alternativa.
    4. Probar la alternativa en pequeño (experimentación controlada).
    5. Revisar resultados y ajustar en equipo.

    Conclusión

    Las limitaciones son, a menudo, estrategias adaptativas con una función concreta.

    Al enfocarnos en la función y diseñar alternativas concretas, podemos cambiar comportamientos sin negar la necesidad subyacente.