Blog

  • Ensayo de un SDLC en casa

    Plan completo (práctico) para llevar una pequeña aplicación PHP desde el inicio hasta producción

    A continuación tienes un plan detallado, paso a paso, pensado para ensayar en casa todo el ciclo: diseño, desarrollo, control de versiones, pruebas, integración continua, empaquetado, despliegue, monitorización y operativa en producción. Incluyo ejemplos concretos (comandos y ficheros mínimos) que podrás copiar/pegar para practicar.


    1. Definición mínima del proyecto

    Objetivo: una app PHP pequeña (por ejemplo: una “libreta de notas” con autenticación básica y API REST).

    Estructura sugerida:

    project/
    ├─ src/                # código PHP (PSR-4)
    ├─ public/             # index.php, assets
    ├─ config/             # configs (no secretos)
    ├─ tests/              # PHPUnit
    ├─ migrations/         # migraciones DB (Phinx o similar)
    ├─ docker/             # nginx/php config
    ├─ composer.json
    ├─ .env.example
    └─ README.md
    

    Tecnología mínima:

    • PHP 8.1+ (recomiendo 8.2/8.3)
    • Composer
    • MySQL/MariaDB o PostgreSQL
    • Nginx + PHP-FPM (producción)
    • PHPUnit para tests
    • Docker + Docker Compose para reproducibilidad local
    • Git para control de versiones

    2. Inicializar proyecto y herramientas de desarrollo

    Comandos iniciales:

    mkdir project && cd project
    composer init               # crea composer.json
    git init
    echo "vendor/" > .gitignore
    cp /dev/null .env.example   # variables de ejemplo: APP_ENV, DB_*
    

    Dependencias típicas:

    composer require vlucas/phpdotenv        # manejo .env
    composer require slim/slim:"^4.0"       # (o tu micro framework preferido)
    composer require doctrine/dbal          # acceso DB ligero (opcional)
    composer require illuminate/database    # ORM ligero (opcional)
    composer require symfony/console        # comandos CLI (migrations)
    composer require firebase/php-jwt       # auth JWT (opcional)
    composer require monolog/monolog        # logging
    composer require league/oauth2-server   # si quieres oauth (opcional)
    composer require robmorgan/phinx --dev # migraciones (o usar doctrine/migrations)
    composer require phpunit/phpunit --dev  # tests
    composer require phpstan/phpstan --dev  # análisis estático
    

    Configura PSR-4 en composer.json para autoload.


    3. Flujo Git y buenas prácticas

    • Rama principal: main (o master) representa producción.
    • Desarrollo: develop o ramas por feature: feature/*.
    • Pull Requests, revisiones de código y CI obligatorio antes de merge.
    • Tags semánticos: v1.0.0.

    Ejemplo flujo:

    main <- develop <- feature/nota-CRUD
    

    Añade .gitattributes y código de estilo (PHP-CS-Fixer, phpcs).


    4. Tests y calidad (local)

    Implementa:

    • Unit tests (PHPUnit).
    • Integration tests (base de datos en Docker).
    • Static analysis (PHPStan/Psalm).
    • Linter/formatter (PHP-CS-Fixer).

    Comandos típicos:

    vendor/bin/phpunit --colors=always
    vendor/bin/phpstan analyse src --level=max
    vendor/bin/php-cs-fixer fix --dry-run
    

    Incluye tests básicos que cubran endpoints principales y lógica.


    5. Contenerización para desarrollo y para producción

    Crear Dockerfile para la app y docker-compose.yml para orquestar servicio localmente.

    Ejemplo Dockerfile (PHP-FPM, minimal):

    FROM php:8.2-fpm
    
    # Instala extensiones necesarias
    RUN apt-get update && apt-get install -y \
        libzip-dev zip unzip git \
      && docker-php-ext-install pdo_mysql zip
    
    # Composer
    COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
    
    WORKDIR /app
    COPY composer.json composer.lock ./
    RUN composer install --no-dev --optimize-autoloader
    
    COPY . /app
    
    # Opcional: crear usuario no-root
    RUN useradd -m appuser && chown -R appuser:appuser /app
    USER appuser
    
    CMD ["php-fpm"]
    

    Ejemplo docker-compose.yml para desarrollo:

    version: "3.8"
    services:
      app:
        build: .
        volumes:
          - ./:/app
        environment:
          - APP_ENV=development
          - DB_HOST=db
        depends_on:
          - db
      web:
        image: nginx:stable
        ports:
          - "8080:80"
        volumes:
          - ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
          - ./:/app:ro
        depends_on:
          - app
      db:
        image: mysql:8
        environment:
          MYSQL_DATABASE: app
          MYSQL_ROOT_PASSWORD: root
        ports:
          - "3306:3306"
        volumes:
          - db_data:/var/lib/mysql
    
    volumes:
      db_data:
    

    Nginx config mínimo apuntando a public/index.php.


    6. Migraciones y gestión de esquema

    Usa Phinx o Doctrine Migrations:

    • Mantén migraciones en migrations/.
    • En CI y deploy: ejecutar migraciones con flag --no-interaction.
    • Versiona migraciones con el repositorio.

    Ejemplo comando:

    vendor/bin/phinx migrate -e production
    

    Practica: crear migración, poblar datos de prueba y ejecutar rollback.


    7. Integración continua (CI)

    Configuración mínima (ejemplo GitHub Actions) con etapas:

    1. checkout
    2. instalar dependencias
    3. ejecución de linter + phpstan
    4. tests (unit + integration con DB en contenedor)
    5. build image (opcional)
    6. security scan (dependabot/composer audit)
    7. push al registry y despliegue (si procede)

    Ejemplo (workflow simplificado): .github/workflows/ci.yml

    name: CI
    
    on: [push, pull_request]
    
    jobs:
      build-test:
        runs-on: ubuntu-latest
        services:
          mysql:
            image: mysql:8
            env:
              MYSQL_DATABASE: app
              MYSQL_ROOT_PASSWORD: root
            ports: ["3306:3306"]
            options: >-
              --health-cmd="mysqladmin ping --silent"
              --health-interval=10s
              --health-timeout=5s
              --health-retries=3
        steps:
          - uses: actions/checkout@v4
          - name: Setup PHP
            uses: shivammathur/setup-php@v2
            with:
              php-version: 8.2
              extensions: mbstring, pdo_mysql
          - name: Install deps
            run: composer install --no-progress --prefer-dist
          - name: Run static analysis
            run: vendor/bin/phpstan analyse src --level=7
          - name: Run tests
            run: vendor/bin/phpunit --coverage-text
    

    Incluye secrets en el repo para despliegue (DOCKER_HUB, SSH key, API keys).


    8. Build y registro de artefactos

    Dos enfoques:

    • Imágenes Docker: Build + push a un registry (DockerHub, GitHub Container Registry).
    • Artefactos: crear tarball con versión.

    Ejemplo: docker build -t myapp:${GITHUB_SHA} . → docker push.

    CI se encarga de versionar la imagen con tag semántico y latest.


    9. Entornos: staging y producción

    Recomendación:

    • Staging: réplica de producción para pruebas end-to-end y smoke tests antes de producción. Tener la misma infra (mismos tipos de instancias/containers).
    • Production: configuraciones seguras, SSL, monitorización.

    Variables de entorno:

    • Mantén .env.example en repo. NO cometas .env real ni secretos.
    • En producción, usa gestores de secretos: Vault, AWS Secrets Manager, GCP Secret Manager o variables de entorno en el servidor/CI.

    10. Estrategias de despliegue y disponibilidad

    Opciones para practicar:

    • Despliegue en VPS (Docker Compose): SSH → pull imagen → docker-compose up -d (simple).
    • Rolling / Blue-Green: despliegue de nueva versión en paralelo, luego cambio de tráfico.
    • Zero-downtime: usando un reverse proxy (Traefik/Nginx+upstream) y healthchecks.
    • Despliegue en PaaS: Heroku/Render/Platform.sh (más sencillo para practicar).

    Practica los pasos:

    1. CI construye y publica image.
    2. SSH/CD runner en el servidor tira la nueva image.
    3. Ejecuta migraciones.
    4. Ejecuta smoke tests.
    5. Si todo ok, marcar release.

    Rollback: mantener la imagen anterior y reimplementarla si falla la versión nueva.


    11. Configuración de producción PHP

    • PHP-FPM con pm = dynamic o static según carga.
    • Opciones: OPCache enabled, realpath_cache_size, memory_limit, max_execution_time ajustados.
    • Logs: separar logs de acceso/error, enviar a central (syslog / log driver / ELK).
    • Timezone, locale en php.ini.

    12. Seguridad y prácticas operativas

    • HTTPS (Let’s Encrypt + Certbot o automatizado por reverse proxy).
    • Firewall mínimo (ufw) y cierre de puertos innecesarios.
    • Gestionar dependencias: composer audit o dependabot.
    • Secrets off-repo.
    • Rate limiting, CSRF tokens, validación de inputs y escaping.
    • Backups automáticos de la base de datos (fecha, retención).
    • Auditoría de logs y rotación (logrotate).

    13. Monitorización, logging y alertas

    • Logs: Monolog a ficheros y/o envío a un servicio (Papertrail, Loggly) o ELK stack.
    • Métricas: Prometheus + exporters (o servicios SaaS).
    • Health checks: endpoint /health que compruebe DB y dependencias.
    • Uptime/Alertas: UptimeRobot o Pingdom.
    • Alertas por correo/Slack/Telegram ante errores críticos y subida de latencia.

    14. Observabilidad y diagnósticos

    • Correlación de request-id (añadir header X-Request-ID).
    • Traceo: integrar OpenTelemetry (opcional) y enviar traces a Jaeger/Zipkin.
    • Perf profiling: xdebug/Blackfire en staging, no en producción.

    15. Respaldo y recuperación (DR)

    • Plan de backups DB diario, con retención.
    • Pruebas periódicas de restore (restore drill) en entorno de staging.
    • Documentar runbook para incidents: cómo revertir, cómo regenerar certificados, cómo restaurar DB.

    16. Checklist para tu sesión práctica en casa

    1. Crear repo y estructura básica.
    2. Implementar un endpoint mínimo y tests unitarios.
    3. Crear Dockerfile + docker-compose y levantar localmente.
    4. Configurar GitHub Actions CI para ejecutar tests y static analysis.
    5. Build y push de imagen a registry local (DockerHub/GHCR).
    6. Montar un servidor VPS (puede ser una VM local) y desplegar con Docker Compose.
    7. Ejecutar migraciones desde CI o manualmente en el servidor.
    8. Configurar nginx/Let’s Encrypt y habilitar HTTPS.
    9. Configurar logs y una herramienta de monitorización simple (UptimeRobot).
    10. Simular rollback: desplegar versión con bug y revertir a versión anterior.
    11. Hacer una “incidente” de prueba: borrar datos y restaurar desde backup.

    17. Ejemplos prácticos rápidos (copiar/pegar)

    Comando para levantar entorno local:

    docker-compose up --build
    # luego visitar http://localhost:8080
    

    Comando para migraciones (ejemplo Phinx):

    vendor/bin/phinx create CreateNotesTable
    vendor/bin/phinx migrate -e development
    vendor/bin/phinx rollback -t 0
    

    Build y push (local):

    docker build -t myuser/myapp:1.0.0 .
    docker push myuser/myapp:1.0.0
    

    Despliegue simple por SSH (script):

    ssh user@server 'docker pull myuser/myapp:1.0.0 && docker-compose -f /srv/myapp/docker-compose.prod.yml up -d'
    

    18. Métricas de éxito y KPIs para tus pruebas

    • Tiempo de despliegue (build → producción).
    • Porcentaje de tests cubiertos por CI.
    • Tiempo medio de respuesta (p99, p95).
    • Disponibilidad (uptime %).
    • Tiempo medio de resolución frente a un fallo.

    19. Siguientes pasos

    Puedo generar ahora mismo (en esta misma respuesta) archivos de ejemplo listos para copiar:

    • Dockerfile completo (producción/dev).
    • docker-compose.yml (dev) y docker-compose.prod.yml.
    • nginx config.
    • phinx migration template.
    • .github/workflows/ci.yml completo.
    • Un Makefile con comandos útiles (make up, make test, make build).

  • Chat TCP/IP

    En este proyecto vamos a crear una aplicación de chat simple basada en sockets TCP/IP que permita la comunicación en tiempo real entre múltiples clientes conectados a un servidor central. El propósito es comprender la concurrencia y la sincronización de mensajes entre varias conexiones a un mismo punto.

    Este proyecto está hecho en Go ya que ofrece una interfaz apropiada para este tipo de trabajo, aunque también sería posible realizarlo con C, por ejemplo, pero al tener una librería estándar más simple con un nivel de abstracción más «bajo», entre otras cosas, creo que haría la lectura del código menos comprensible que con Go.

    No descarto en un futuro hacer el mismo proyecto en C para ver la diferencia.