Cosas que volví a hacer desde que programa la inteligencia artificial
Hoy vengo con un artículo ligeramente distinto. Llevo semanas metiendo código en diferentes proyectos que no tienen nada que ver entre si y empecé a notar un nuevo patrón de conducta en mi forma de trabajar.
En esta publicación ya hablamos largo y tendido de cómo el uso de inteligencia artifical generativa cambió el día a día de los programadores, pero nunca nos detuvimos en concreto sobre qué cosas cambiaron.
El cambio quizás más notorio y radical es que de forma natural volví a escribir mis propias abstracciones. Durante muchos años para resolver patrones de diseño comunes supe depender casi enteramente de npm y su gran oferta de librerías. Esto tiene como ventaja que el código suele ser robusto y su funcionalidad bien probada. Y sobre todo nos ahorra mucho tiempo que podemos aplicar al resto del desarrollo.
Pero entre los ataques a npm y la situación general del ecosistema cada vez me veo más reacia a sumar librerías porque si. Apoyada por la IA me resulta mucho más simple crear abstracciones y probarlas en tiempos que no resultan prohibitivos para cumplir con los tiempos del proyecto.
Vamos con algunos ejemplos.
Wrappers para todo
No sé si decir que “volví” a hacer wrappers. Ellos siempre estuvieron ahí como primera linea de abstracción en el momento que el código se empieza a complejizar. Creo que la diferencia es que ahora mis wrappers son mucho más intencionales no solo en su uso particular sino como patrón para que la IA use de ejemplo.
Un wrapper es, como su nombre en inglés lo indica, un “envoltorio”. Código que envuelve a otro código (una función, librería, clase) para darnos una interfaz más cómoda, segura o adaptada a un contexto sin modificar lo que tenemos adentro.
Un ejemplo típico de uso es cuando consumimos una API externa. Sabemos que siempre vamos a tener que enviar una solicitud, recibir una respuesta y manejar errores. El wrapper maneja toda esa lógica de forma genérica, agnóstico a lo que vaya a consumir. Si mañana cambia nuestro proveedor de API no pasa nada, lo envolvemos y listo. Plug and play.
Y justamente este es el valor que tienen cuando dejamos que la IA haga parte del trabajo. Podemos soltarle la mano en la implementación sabiendo que está utilizando la misma abstracción. Y si encima tenemos tests a mano, mucho mejor.
Máquinas de estado
Una máquina de estados es un modelo en el que un sistema puede tener un solo estado a la vez. Los estados son definidos en un conjunto finito de posibilidades y pasamos de uno a otro mediante transiciones disparadas por eventos.
Los tres ingredientes:
Estados: las situaciones posibles del sistema (ej: idle, loading, success, error)
Eventos: lo que provoca un cambio (ej: FETCH, RESOLVE, REJECT, RETRY)
Transiciones: las reglas que dicen “si estás en X y pasa Y, vas a Z”
Lo importante es que al igual que los estados las transiciones son explícitas y limitadas. Desde loading podemos ir a success o error, pero no volver a idle directamente si la regla no existe. Eso elimina estados imposibles.
En React encontramos bastante este patrón:
const [isLoading, setIsLoading] = useState(false);
const [isError, setIsError] = useState(false);
const [data, setData] = useState(null);
El gran problema en ese ejemplo de código es que sin querer podemos generar situaciones sin sentido, por ejemplo: (isLoading: true con isError: true)
En lugar de una máquina de estados tenemos una máquina de generar bugs.
La versión con máquina de estados sería algo así:
type FetchState =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'success'; data: MyType[] }
| { status: 'error'; error: string };
Para casos simples, un useReducer con un switch sobre el estado actual ya es una máquina de estados y alcanza para evitar mayores problemas.
Yo sé que estarán pensando que esto no es un patrón nuevo y se usaba mucho antes de que la IA asistiera en la creación de apps. Pero como en el punto anterior, hoy en día las estoy usando de forma explícita, incluso indicándole a Claudio que por favor siga este patrón y no deje booleanos sueltos por ahí.
Son detalles que se me pueden pasar en una revisión sobre todo si los componentes son chicos. No se a ustedes, pero si un componente es simple suelo perdonarle tener antipatrones, por eso establecer este tipo de reglas me ayuda a evitar errores a futuro.
Factories
Una factory es una función cuyo trabajo es crear objetos, encapsulando la lógica de construcción. Su objetivo principal es centralizar y automatizar la creación de elementos sin que el código que los usa tenga que saber exactamente cómo se construyen.
En React es común ver factories de componentes, donde dejamos que, según el caso, nuestro código pueda decidir qué componente tiene que ser utilizado.
Por ejemplo para un sistema de alertas podríamos hacer:
import React from 'react';
// 1. The Factory Function
// It takes a configuration object (type and style configuration)
function alertFactory({ type, backgroundColor, defaultIcon }) {
// 2. It defines and returns a brand-new, customized component
return function AlertComponent({ message, onClose }) {
return (
<div style={{
padding: '16px',
borderRadius: '8px',
backgroundColor: backgroundColor,
display: 'flex',
justifyContent: 'space-between',
color: '#fff',
marginBottom: '10px'
}}>
<span>{defaultIcon} <strong>{type}:</strong> {message}</span>
{onClose && <button onClick={onClose} style={{ background: 'none', border: 'none', color: '#fff', cursor: 'pointer' }}>✕</button>}
</div>
);
};
}
// 3. Using the Factory to manufacture specific components
export const SuccessAlert = alertFactory({
type: 'Success',
backgroundColor: '#2e7d32',
defaultIcon: '✅'
});
export const ErrorAlert = alertFactory({
type: 'Error',
backgroundColor: '#d32f2f',
defaultIcon: '🚨'
});
export const WarningAlert = alertFactory({
type: 'Warning',
backgroundColor: '#ed6c02',
defaultIcon: '⚠️'
});En este caso alertFactory centraliza toda la lógica de construcción de alertas. Todas las configuraciones para diferentes casos, validaciones y dependencias viven en el mismo lugar.
En desarrollo web este patrón aparece constantemente. Como en los ejemplos anteriores, no es que no usara factories antes, pero ahora la decisión de diseño aparece como una necesidad de centralizar donde la IA va a sumar código.
Es más probable que, si el caso es simple y no ametira sumar una librería extra, escriba y pruebe mi propia factory para asegurarme detalles de implementación.
Todos estos ejemplos sirven al mismo fin: si podemos delegar el esfuerzo de escribir cada caracter lo que cobra importancia es que, donde y como se va a implementar ese código. Estos patrones son buenas prácticas y funcionan como guardrails para que el agente sume código de calidad y sobre todo facil de leer y revisar.
Es mucho mejor leer código ajeno cuando sabemos que las implementaciones se hacen siempre sobre la misma base y no reinventando la rueda o duplicando funcionalidad o mezclando patrones.
No pretendo con este texto darles consejos sobre patrones de diseño sino tomarlo como ejercicio para reflexionar como las herramientas provocan cambios en nuestro modelo mental a la hora de trabajar.
¿Ustedes también notaron cambios en sus procesos? ¿Me cuentan cuales?
Espero que hayas disfrutado el artículo de hoy. Te cuento que este newsletter se sostiene de la gente que lo lee. Si querés recibir los próximos textos y bancar este trabajo, podés suscribirte gratis o pasar a una suscripción paga.
O también podés:
💌 Compartilo
Ahora sí ¡Nos vemos la próxima!


