Resumen

Agregar funcionalidades nuevas a un sistema sin romper lo que ya existe es una de las habilidades más valiosas en el desarrollo de software. La extensibilidad permite exactamente eso: incorporar cambios de forma sencilla, comprensible y ordenada, apoyándose en herramientas como los contratos y las interfaces. A continuación se explican los fundamentos, los retos y un ejemplo práctico que muestra cómo aplicar estos principios en código real.

¿Qué es la extensibilidad y por qué importa cada día?

La extensibilidad es la capacidad de agregar nuevas funcionalidades sin modificar de forma importante lo ya existente [0:22]. En el día a día del desarrollo, los requerimientos cambian constantemente; como se menciona en la clase, "el cambio es la única constante" [0:05]. Escribir código que soporte esos cambios sin reescrituras masivas es fundamental.

Lo ideal es que el sistema ofrezca puntos de extensión claros, donde los cambios sean sencillos y comprensibles [0:30]. Para lograrlo, las reglas de cambio se definen a través de contratos e interfaces [0:42].

¿Qué significa un contrato o interfaz en programación?

Un contrato —o interfaz, tratados aquí como sinónimos— es una serie de reglas que cualquier clase que desee implementarlo debe cumplir por completo [0:48]. Funciona como una firma: si quieres ser parte del acuerdo, debes respetar todas sus cláusulas. Esto garantiza que cada implementación mantenga una estructura predecible y consistente.

¿Cuáles son los retos principales de la extensibilidad?

  • Comprender el problema antes de escribir código. Detenerse a pensar si la solución tiene sentido y es sencilla, en lugar de saltar directamente a programar [1:10].
  • Encontrar consenso en estándares y reglas de equipo. Definir qué elementos considerar al agregar nuevas funcionalidades, documentarlos y hacer tracking de las convenciones acordadas [1:33].
  • Crear documentación si no existe. Sin ella, el equipo pierde la referencia de cómo extender el sistema de forma ordenada [1:45].

¿Cómo aplicar la extensibilidad con un ejemplo práctico?

Partiendo del ejemplo de reusabilidad de clases anteriores, se tiene un servicio de usuarios que depende de un HTTP gateway, el cual a su vez usa un módulo HTTP para hacer peticiones al backend [1:58]. Todo funcionaba con REST por defecto.

El nuevo requerimiento cambia las reglas: ahora algunos servicios usarán GraphQL mientras otros seguirán con REST [2:14]. La pregunta clave es cómo garantizar orden cuando coexisten dos implementaciones distintas.

¿Cómo se define un contrato gateway?

Se crea una interfaz llamada Gateway que estipula los métodos obligatorios: fetchMany y fetchOne, junto con los parámetros que deben recibir [2:38]. Toda clase que quiera llamarse gateway debe implementar ambos métodos sin excepción.

A partir de ahí se crean dos clases concretas:

  • RestGateway: implementa la interfaz Gateway con la lógica propia de REST [2:55].
  • GraphQlGateway: implementa la misma interfaz pero con la lógica específica de GraphQL [3:18].

Ambas clases cumplen el contrato, pero su implementación interna es diferente. Ahí radica el poder de las interfaces: solo estipulan las reglas, no cómo se resuelven [3:28].

¿Cómo se integra en el servicio de usuarios?

El servicio de usuarios declara un atributo llamado serviceGateway cuyo tipo de dato es Gateway —la interfaz, no una clase concreta— [3:38]. En el constructor se instancia, por ejemplo, un GraphQlGateway. Si mañana se necesita volver a REST, el cambio es una sola línea, porque el contrato es el mismo: fetchMany y fetchOne están garantizados en ambas implementaciones [3:50].

En el diagrama final, el servicio de usuarios está compuesto por un Gateway, y tanto GraphQlGateway como RestGateway lo implementan [4:02]. Esta relación de composición, combinada con la dependencia en abstracciones en lugar de implementaciones concretas, es lo que hace al código verdaderamente extensible.

¿Por qué depender de abstracciones y no de implementaciones concretas?

Cuando el código depende de una interfaz abstracta como Gateway en vez de importar directamente GraphQlGateway, se gana la libertad de intercambiar implementaciones sin afectar al resto del sistema [4:15]. Si aparece un tercer tipo de gateway en el futuro, basta con crear una nueva clase que implemente el contrato. No hay que modificar el servicio de usuarios ni las clases existentes.

Esta práctica —conocida como inversión de dependencias— refuerza tanto la extensibilidad como la reusabilidad. El código crece de forma ordenada, predecible y fácil de mantener.

¿Ya habías trabajado con interfaces o contratos en tus proyectos? Comparte tu experiencia en los comentarios para que todos podamos aprender de diferentes contextos y lenguajes.