domingo, 3 de mayo de 2026

El cambio de dAcli7 desde Access a SQL Server

Caso real de optimización en dAcli: de Access a SQL Server

Migración desde Access a SQL Server
Migración desde Access a SQL Server

Durante años convivimos con un problema que muchos sistemas legacy conocen demasiado bien: la base de datos funcionaba… hasta que dejaba de funcionar.

Y cuando dejaba de funcionar, no era un bug menor. Era algo peor: corrupción de datos.

El contexto real: software en condiciones no ideales

dAcli es un sistema de gestión utilizado en laboratorios clínicos en Venezuela, un entorno donde existe una variable crítica que muchas arquitecturas no contemplan: los cortes eléctricos.

Durante años, el sistema utilizó Microsoft Access como motor de base de datos, lo que terminó siendo un punto débil en este contexto.

El problema: Access y apagones

Cada vez que ocurría un corte eléctrico, existía una probabilidad real de que la base de datos:

  • Quedara corrupta.
  • No pudiera abrirse.
  • Obligara a restaurar respaldos.

Esto no era un evento aislado. Era recurrente.

La única defensa posible era aumentar la frecuencia de respaldos, lo que generaba otros problemas operativos:

  • Pérdida de datos recientes.
  • Interrupciones en el laboratorio.
  • Reinicios periódicos de la base de datos.

Incluso con pocos usuarios concurrentes (5 o 6), los problemas de concurrencia eran constantes.

El parche: manejar concurrencia desde el código

Para mitigar estos problemas, se implementaron soluciones a nivel de código:

  • Bloqueos manuales.
  • Validaciones adicionales.
  • Control de acceso a datos.

Estas soluciones redujeron conflictos, pero introdujeron un nuevo problema: lentitud generalizada del sistema. Con el tiempo, el código comenzó a crecer de forma desordenada.

20 años de código acumulado

El sistema acumuló una gran cantidad de código innecesario:

  • Funciones no utilizadas.
  • Formularios huérfanos.
  • Lógica duplicada.
  • Loops innecesarios.
  • Implementaciones antiguas mal diseñadas.

Era imposible mejorar el rendimiento sin enfrentar este problema directamente.

El proceso: limpieza manual y refactorización

Se realizó un proceso completo de limpieza de código, de forma manual:

  • Eliminación de código muerto.
  • Simplificación de lógica.
  • Reestructuración de módulos.

Este proceso duró aproximadamente seis meses, trabajando en horarios nocturnos y fines de semana.

Durante este tiempo, múltiples funcionalidades dejaron de funcionar temporalmente. Fue un proceso iterativo de ruptura y reconstrucción.

La decisión clave: migrar a SQL Server

Paralelamente, se decidió migrar el sistema a Microsoft SQL Server.

La migración no fue automática. Se realizó de forma manual, aprovechando para mejorar la estructura de datos.

Cambios realizados

  • Normalización de la base de datos.
  • Cambio de tipos de datos.
  • Creación de índices.
  • Reescritura de consultas.
  • Centralización del acceso a datos en una sola capa.

Se creó una clase que concentra todas las consultas, permitiendo desacoplar el sistema del motor de base de datos.

Function generarCodigo(tabla As String, CampoClave As String)
    Dim rs As New ADODB.Recordset
    rs.Open "SELECT MAX([" & CampoClave & "]) FROM [" & tabla & "]", conexion
    If Not IsNumeric(rs(0)) Then
        generarCodigo = 1
    Else
        generarCodigo = rs(0) + 1
    End If
End Function

Public Function DSQL(ByVal consultaSQL As String) As ADODB.Recordset
    Dim rs As New ADODB.Recordset
    On Error GoTo errorHandler
    rs.Open consultaSQL, MPrincipal.base, adOpenStatic, adLockOptimistic, adCmdText
    Set DSQL = rs
    Exit Function

errorHandler:
    Dim ErrorDescripcion
    ErrorDescripcion = Err.Description & vbCrLf & "#" & Err.number & " source:" & Err.Source
    MPrincipal.Log "sql", "Error:" & ErrorDescripcion & vbCrLf & consultaSQL
    If MPrincipal.RunningInVB Then
        Debug.Print consultaSQL
        MsgBox ErrorDescripcion & vbCrLf & consultaSQL, vbCritical
        Debug.Assert True
    End If
    MsgBox "No puedo completar la operacion"
End Function

Public Function Execute(ByVal consultaSQL As String, Optional silence As Boolean = False) As Boolean
    On Error GoTo errorHandler
    MPrincipal.base.Execute consultaSQL
    Execute = True
    Exit Function

errorHandler:
    Debug.Print Err.Description & vbCrLf & consultaSQL
    MPrincipal.Log "sql", Err.Description & vbCrLf & consultaSQL
    If Not silence Then
        MsgBox "Accion no realizada", vbCritical
    End If
    Execute = False
End Function

Function EscaparComillas(ByVal consultaSQL As String) As String
    EscaparComillas = consultaSQL
    Exit Function
    Dim partesConsulta() As String
    Dim i As Integer
    
    partesConsulta = Split(consultaSQL, " ")
    
    For i = LBound(partesConsulta) To UBound(partesConsulta)
        If partesConsulta(i) Like "*'*" Then
            partesConsulta(i) = Replace(partesConsulta(i), "'", "''")
        End If
    Next i
    
    EscaparComillas = Join(partesConsulta, " ")
End Function

Con la nueva arquitectura, se aplicaron mejoras clave:

  • Uso de stored procedures.
  • Implementación de caché.
  • Reducción de consultas redundantes.
  • Optimización de consultas pesadas.
  • Reducción del tráfico de red.

El sistema dejó de depender de lógica distribuida en el cliente y pasó a aprovechar mejor el motor de base de datos.

Resultados

Antes

  • Lentitud constante.
  • Bloqueos frecuentes.
  • Corrupción de bases de datos.
  • Reinicios periódicos.

Después

  • Fluidez general del sistema.
  • Consultas rápidas.
  • Estabilidad operativa.
  • Eliminación práctica de corrupción por apagones.

El cambio fue significativo tanto a nivel técnico como operativo.

Impacto en clientes

Actualmente:

  • Más del 90% de los laboratorios han migrado.
  • El sistema supera los 200 clientes.
  • Los clientes que inicialmente dudaban por costos terminaron adoptando la nueva arquitectura.

La diferencia en estabilidad y rendimiento justificó completamente el cambio.

Lecciones aprendidas

  • Los problemas de rendimiento muchas veces son problemas de arquitectura.
  • Los parches técnicos generan deuda acumulativa.
  • La limpieza de código es esencial en sistemas de larga vida.
  • Los motores de base de datos tienen límites claros según el contexto.
  • La estabilidad es una característica crítica en entornos reales.

Conclusión

Este proceso no fue simplemente una optimización. Fue una evolución completa del sistema.

dAcli pasó de ser un sistema funcional a convertirse en una plataforma estable y confiable para la operación diaria de laboratorios clínicos.

En entornos donde la continuidad operativa es crítica, este tipo de decisiones técnicas marcan la diferencia.

Página web de dAcli: www.dacli.com

sábado, 2 de mayo de 2026

Hice un Coliseo de IAs en Python para ver quién juega mejor ajedrez

 


¿Qué sucede cuando enfrentas dos lógicas de programación distintas en un entorno de reglas estrictas? Para responder esto, desarrollé un proyecto en Python que funciona como un entorno de ejecución (Engine) donde diferentes scripts de IA pueden competir entre sí.

En este post, explico cómo diseñé el tablero, las reglas y el experimento de enfrentar a white.py contra black.py

1. El Árbitro: Un motor de reglas puras

A diferencia de otros proyectos, aquí el "motor" no es quien juega. Su única misión es ser el sistema operativo del tablero. Está programado para:

  • Validar movimientos legales: Asegurar que ninguna IA intente saltarse las reglas.

  • Gestionar el estado: Controlar enroques, capturas al paso y promociones.

  • Detectar el final: Aplicar las reglas de tablas por insuficiencia de material, triple repetición y la regla de los 50 movimientos.

2. El Experimento: white.py vs. black.py

La verdadera magia ocurre en los archivos externos. El motor actúa como un "Coliseo" que le entrega el estado actual del tablero a cada archivo y espera una respuesta.

Esto permite realizar experimentos de Benchmarking de IA:

  • Lógica de Búsqueda: ¿Quién calcula mejor, un script basado en Minimax o uno basado en heurísticas experimentales?

  • Optimización: Cómo afecta el tiempo de respuesta (time.time()) en la calidad de la jugada devuelta.

  • Heurísticas de Evaluación: Comparar cómo diferentes algoritmos valoran la posición de las piezas en el tablero.

3. Anatomía del Motor (The Backbone)

El código está diseñado para ser modular y transparente. Algunos puntos clave de la implementación:

  • Clonación de Tablero: El motor permite a las IAs clonar el estado actual para simular jugadas futuras sin afectar la partida real.

  • PGN Builder: Generación automática de notación estándar para que las partidas puedan ser analizadas en visores externos.

  • Visualización en Consola: Un sistema de renderizado ASCII que permite seguir la "pelea" en tiempo real desde la terminal.

4. ¿Por qué separar el tablero de la lógica?

Separar el motor de los jugadores (white.py y black.py) ofrece ventajas críticas para el desarrollo de software:

  1. Modularidad: Puedes cambiar la lógica de una IA sin tocar una sola línea de código de las reglas del ajedrez.

  2. Imparcialidad: Ambos scripts reciben exactamente la misma información del estado del juego (get_state()).

  3. Escalabilidad: Es fácil añadir nuevos "jugadores" y enfrentarlos en un torneo tipo Round Robin.


El experimento: Crónica de una masacre algorítmica

El desarrollo de este coliseo no fue lineal; fue una evolución de "supervivencia del más apto". En las primeras pruebas, GPT, utilizando un prompt inicial optimizado, barrió completamente con las versiones básicas de Gemini y superó con claridad a DeepSeek. Parecía que el trono estaba definido.

Sin embargo, el panorama cambió cuando entró Claude en escena. Con una lógica de cálculo mucho más profunda y una gestión del árbol de decisiones superior, Claude destronó a GPT y "destrozó" a todos los demás competidores sin piedad.

Lo más interesante del experimento vino después: al aplicar los principios de optimización y la estructura de código que usaba Claude a las otras IAs, lograron una nivelación. Aunque seguían jugando con menos precisión, estas versiones mejoradas pudieron empezar a forzar tablas por repetición. Pasaron de suicidios tácticos a partidas donde la defensa era lo suficientemente sólida para no perder.


Del código al tablero: ¿Cómo se genera el PGN?

Para que estas partidas no se queden solo en líneas de log en la terminal, el software implementa un PGN Builder. El PGN (Portable Game Notation) es el estándar universal para registrar partidas de ajedrez.

¿Cómo funciona el flujo de datos?

  1. Captura del movimiento: Cada vez que white.py o black.py deciden una jugada, el motor registra la casilla de origen, la de destino y si hubo captura o promoción.

  2. Traducción a Notación Algebraica: El motor convierte las coordenadas internas del array (ej. [7, 4]) a notación estándar (ej. e1).

  3. Detección de estados especiales: El software verifica si el movimiento resultó en jaque (+), jaque mate (#) o si fue un enroque (O-O).

  4. Exportación: Al finalizar el juego, todos estos strings se concatenan siguiendo el formato: 1. e4 e5 2. Nf3 Nc6....

Visualización en Chess.com o Lichess

El resultado final es un bloque de texto que puedes copiar y pegar directamente en herramientas como el Analizador de Chess.com o Chess Compass. Estas plataformas leen el archivo PGN, reconstruyen la partida visualmente y te permiten usar motores como Stockfish para ver exactamente en qué jugada Claude le ganó la partida a Gemini.

 

 

Desafío Open Source

He subido este entorno de competencia a mi repositorio. Si eres desarrollador y quieres probar tu propia lógica de ajedrez, solo tienes que crear tu propio script y enfrentarlo en el tablero.

 Repositorio del proyecto: https://github.com/davdomin/chess_motor

¿Qué estrategia usarías para ganar: un ataque agresivo basado en material o una defensa sólida basada en posición? Te leo en los comentarios.

 

viernes, 1 de mayo de 2026

Keep Code Left (KCL): El arte de escribir código limpio y lineal


 

En el 2015, mientras trabajaba en Software Andina, tuve el privilegio de asistir a una sesión que cambió por completo mi forma de ver un archivo de código fuente. En aquel entonces, recibíamos frecuentemente a desarrolladores de Estados Unidos que venían a compartir su experiencia.

Uno de esos visitantes fue Mick Andrew, Director de Desarrollo en Sage. Mick, amigo cercano de mi jefe de entonces, John Rutherfurd, nos trajo una presentación titulada: "Keep Code Left" (Mantén el código a la izquierda).

El título parecía simple, pero la filosofía detrás era profunda. Como desarrolladores, solemos enfocarnos en que el código funcione, olvidando que el código se lee muchas más veces de las que se escribe. Mick nos enseñó que, con unas pocas reglas de "libro de cocina", podíamos transformar una lógica enredada y profundamente anidada en algo limpio, lineal y extraordinariamente fácil de mantener.

¿Qué es Keep Code Left?

En esencia, Keep Code Left (KCL) es un estilo de programación diseñado para maximizar la legibilidad. El nombre se refiere a la estructura visual: cuanta más sangría (identación) tiene tu lógica, más se empuja el código hacia la derecha, y más difícil le resulta al cerebro humano rastrear el "estado" y el flujo del programa.

Los objetivos de KCL son claros:

  • Simplificar la lógica: Crear un flujo lineal en lugar de ramificado.

  • Mejorar la legibilidad: Permitir que cualquier desarrollador entienda el objetivo de un método de un solo vistazo.

  • Reducir errores de mantenimiento: El código simple es más difícil de romper.

  • Agilizar las revisiones de código: Proporciona un estándar visual que se verifica rápidamente.

Desde aquel día en 2015, he aplicado estos principios en casi todos mis proyectos. Hoy quiero compartir contigo cómo aplicarlo y por qué tu "yo del futuro" te lo agradecerá.

El Problema de la Anidación

Cuando el código se anida profundamente, surgen problemas inmediatos:

  • Es difícil de leer línea por línea.

  • La lógica principal queda "enterrada" en el centro del archivo.

  • Aumenta la carga cognitiva (tienes que recordar qué condición abrió cada llave).

El Principio Maestro

Evita la anidación innecesaria. Prefiere salidas tempranas (early returns) y un flujo lineal.

En lugar de envolver la lógica principal en múltiples bloques if, manejamos los casos de error o excepciones primero y salimos de la función inmediatamente.

Ejemplo 1: Código Anidado (Incómodo)

public void ProcessOrder(Order order)
{
    if (order != null)
    {
        if (order.IsValid())
        {
            if (order.Items.Count > 0)
            {
                // La lógica principal está aquí, escondida
                Save(order);
            }
        }
    }
} 

Ejemplo 1: Aplicando Keep Code Left (Mejor) 

 

public void ProcessOrder(Order order)
{
    if (order == null) return;
    if (!order.IsValid()) return;
    if (order.Items.Count == 0) return;

    // La lógica principal está "a la izquierda" y es visible
    Save(order);
}

Técnicas Clave de KCL

  1. Salidas Tempranas (Early Returns): Si no se cumplen las condiciones necesarias para continuar, sal de la función de inmediato.

  2. Cláusulas de Guarda (Guard Clauses): Protege tu lógica principal de datos nulos o inválidos al inicio del método.

  3. Reducción de Identación: Cada nivel de if o loop añade complejidad. Intenta mantener tu código con el menor número de "escalones" hacia la derecha posible.

  4. Enfócate en el "Happy Path": El camino feliz (donde todo sale bien) debe ser el eje central y más visible del método.

    Beneficios en el Mundo Real

    Aplicar KCL no es solo un capricho estético; tiene impactos reales en el equipo:

  5. Revisiones de código más veloces: Los patrones son fáciles de detectar sin necesidad de conocer todo el contexto del negocio.

  6. Menos bugs: Al reducir la complejidad visual, los errores lógicos saltan a la vista.

  7. Onboarding rápido: Un desarrollador nuevo puede entender qué hace una función en segundos.

Un consejo para trabajar con IA

Hoy en día, cuando usamos herramientas como  Claude o Copilot, notarás que a menudo generan código muy anidado por defecto. Es extremadamente útil pedirles explícitamente: "Escribe este código siguiendo los principios de Keep Code Left" o "Usa cláusulas de guarda para evitar la anidación". Esto te ahorrará mucho tiempo de refactorización posterior.

viernes, 10 de noviembre de 2023

Cómo Crear una Extensión de Google Chrome para Abrir WhatsApp Web con un Solo Clic



 ¿Te gustaría tener la capacidad de abrir WhatsApp Web con un solo clic desde tu navegador? ¡Buenas noticias! Puedes lograrlo fácilmente creando tu propia extensión personalizada para Google Chrome. En esta guía paso a paso, te mostraré cómo hacerlo. Puedes visitar nuestro articulo sobre como crear una extension en chrome

Paso 1: Configuración Inicial

Antes de comenzar, asegúrate de tener un directorio para tu proyecto. En este ejemplo, llamaremos al directorio "WhatsAppWebLauncher".

Paso 2: Archivo manifest.json

Crea un archivo llamado manifest.json en tu directorio y agrega el siguiente contenido:

{
    "name": "WhatsApp Web Launcher",
    "description": "Open WhatsApp Web for a given phone number.",
    "version": "0.1.0",
    "manifest_version": 3,
    "icons": {
        "16": "/images/icon16.png",
        "32": "/images/icon32.png",
        "48": "/images/icon48.png",
        "128": "/images/icon128.png"
    },
    "action": {
        "default_popup": "popup.html",
        "default_icon": {
            "16": "/images/icon16.png",
            "32": "/images/icon32.png",
            "48": "/images/icon48.png",
            "128": "/images/icon128.png"
        }
    },
    "permissions": [
        "storage",
        "activeTab",
        "scripting",
        "tabs"
    ],
    "content_security_policy": {
        "extension_pages": "script-src 'self'; object-src 'self';"
    }
}

Asegúrate de reemplazar "WhatsApp Web Launcher" con el nombre que desees para tu extensión.

Paso 3: Archivo popup.html

Crea un archivo llamado popup.html y agrega el siguiente contenido:

<!DOCTYPE html>
<html>
<head>
  <title>WhatsApp Web Launcher</title>
</head>
<body>
  <h3>Phone number:</h3>
  <input type="text" id="phoneNumber" placeholder="Ej. +51-1234567" value="">
  <button id="openButton">Open in WhatsApp Web</button>
  <script src="popup.js"></script>
</body>
</html>

Paso 4: Archivo popup.js

Crea un archivo llamado popup.js y agrega el siguiente contenido:

document.addEventListener('DOMContentLoaded', function () {
  var phoneNumberInput = document.getElementById('phoneNumber');

  phoneNumberInput.focus();
  document.getElementById('openButton').addEventListener('click', function() {
    openWhatsApp();
  });
  function openWhatsApp() {
    var phoneNumber = phoneNumberInput.value;
    if (phoneNumber) {
      phoneNumber = phoneNumber.replace(/[^\d]/g, '');
      var whatsappWebUrl = 'https://web.whatsapp.com/send?phone=' + phoneNumber;
      chrome.tabs.create({ url: whatsappWebUrl });
    } else {
      alert('Por favor, ingresa un número de teléfono.');
    }
  }
});

Paso 5: Cargar la Extensión

  1. Abre Google Chrome y ve a chrome://extensions/.
  2. Habilita "Modo de desarrollador" en la esquina superior derecha.
  3. Haz clic en "Cargar descomprimida" y selecciona la carpeta de tu proyecto.

¡Listo! Ahora deberías ver el ícono de tu extensión en la barra de herramientas de Chrome.

Beneficios de Crear Extensiones Personalizadas

Crear tus propias extensiones de navegador personalizadas no solo es divertido, sino que también puede mejorar tu productividad. Puedes adaptar tu navegador exactamente a tus necesidades y automatizar tareas comunes.

Contribuye y Mejora

Este proyecto está en GitHub. Si encuentras problemas o tienes sugerencias de mejora, ¡contribuye al desarrollo! https://github.com/davdomin/chrome_whatsapp

¡Espero que encuentres útil esta extensión personalizada! ¡Háganos saber si tienes alguna pregunta o sugerencia!


jueves, 9 de noviembre de 2023

Descubre las Novedades en Quasar v2.13.1: Más Potencia, Menos Errores

 ¡La espera ha terminado! Quasar Framework acaba de lanzar su versión v2.13.1, cargada de emocionantes características y correcciones que mejorarán significativamente tu experiencia de desarrollo. A continuación, exploraremos algunas de las principales actualizaciones que esta versión trae consigo:



Características Destacadas:

1. Atributo "tag" para QField

  • Ahora, en QField, puedes aprovechar el nuevo atributo "tag" para personalizar aún más la renderización del componente. ¡Más flexibilidad para adaptarse a tus necesidades específicas!

2. Mejoras en el Manejo de Errores

  • Correcciones de errores importantes, como el uso incorrecto de etiquetas <label> en QField, que garantizan un rendimiento más suave y consistente.

Soluciones a Problemas Heredados:

1. Problemas de Herencia de v1 Abordados

  • Se han abordado problemas heredados de la versión 1, como el informe de errores en la consola de desarrollo, asegurando una transición sin problemas a la última versión.

Cómo Actualizar:

Si ya eres un usuario de Quasar, actualizar a la última versión es sencillo. Solo sigue estos pasos:

    1. Instalación de la Nueva Versión:

      npm install -g quasar@2.13.1

    2. Actualización de Proyectos Existentes:

      quasar upgrade

    ¡Y eso es todo! Tu proyecto estará listo para aprovechar al máximo las nuevas funcionalidades y correcciones.

    Comunidad y Colaboración:

    Quasar Framework es posible gracias a la increíble comunidad de desarrolladores. Si encuentras problemas o tienes sugerencias, ¡no dudes en contribuir y ser parte de la evolución de Quasar!

    Este lanzamiento marca otro paso hacia adelante en la misión de Quasar de proporcionar un marco de trabajo poderoso y flexible para el desarrollo web. ¡Esperamos que disfrutes de todas las nuevas mejoras!

    Para obtener más detalles sobre esta versión, consulta aquí.

    ¡Feliz codificación con Quasar v2.13.1! 🚀✨

El cambio de dAcli7 desde Access a SQL Server

Caso real de optimización en dAcli: de Access a SQL Server Migración desde Access a ...