most read
Building Stories
Modo Rua: Redefiniendo el desarrollo de aplicaciones mediante iteración centrada en el usuario Ago 23
Culture & Values
Cómo los valores y la cultura de Nu dan forma a los productos que creamos Ago 7
Building Stories
NuStories: Adaptación de productos para clientes fanáticos en varios países Oct 30
Careers
We are building diverse teams with the most creative and innovative professionals for each position we open.



Escrito por: Breno Oliveira
Cuando un desarrollador necesita modificar un servicio crítico, el cambio solicitado parece sencillo, pero entender sus consecuencias es otra historia.
El servicio ha acumulado años de lógica de negocio. En este contexto, algunas dependencias están mal documentadas, ciertos comportamientos son difíciles de reproducir localmente y las pruebas disponibles ofrecen solo una seguridad parcial. Por eso, una modificación que parece aislada podría afectar una parte completamente distinta del sistema.
El desarrollador, entonces, tiene dos opciones: investigar más a fondo, aceptando un plazo de entrega incierto, o implementar el cambio y esperar que las salvaguardas existentes detecten cualquier consecuencia no deseada. Dicho esto, ninguna de las dos opciones resulta especialmente atractiva.
Lo que hace interesante esta situación es que el problema podría no tener nada que ver con la cultura del equipo. El ingeniero podría trabajar junto a colegas que lo apoyan, sentirse cómodo haciendo preguntas y tener total libertad para proponer mejoras. Aun así, el propio código sigue siendo difícil de confiar.
Esta distinción revela una dimensión de la experiencia del desarrollador que a menudo se pasa por alto. La confianza en la ingeniería no depende solo de las relaciones entre las personas, sino también del grado en que los sistemas de software pueden comprenderse, probarse y modificarse de forma segura.
Y, a medida que los sistemas crecen, esa confianza se vuelve cada vez más dependiente de las decisiones de arquitectura que se toman a lo largo del camino.
La incertidumbre detrás de un cambio aparentemente simple
La complejidad del software no es, en sí misma, un problema. Un sistema que maneja miles de reglas de negocio, se integra con servicios externos o procesa transacciones financieras críticas naturalmente implicará complejidad.
La dificultad comienza cuando los ingenieros no pueden determinar dónde reside esa complejidad ni cómo las distintas partes del sistema se influyen entre sí.
Pensemos en un servicio responsable de varias capacidades de negocio. Un desarrollador que modifica una de esas capacidades puede necesitar entender flujos no relacionados, porque comparten estado interno, dependencias o supuestos.
Incluso cuando el cambio en sí requiere solo unas pocas líneas de código, evaluar su impacto puede convertirse en una investigación considerable. Aquí es donde la incertidumbre técnica empieza a influir en el comportamiento de la ingeniería.
Un ingeniero que no puede identificar con confianza las consecuencias de una modificación podría posponer una refactorización, introducir una abstracción adicional para evitar tocar la lógica existente o solicitar una revisión exhaustiva a alguien más familiarizado con el sistema.
Estas decisiones pueden ser totalmente racionales. En un entorno de producción, la cautela suele ser preferible a introducir riesgos innecesarios. Sin embargo, cuando este patrón se vuelve rutinario, una organización puede perder gradualmente la capacidad de evolucionar su software de manera eficiente.
Las estimaciones de entrega empiezan a reflejar no solo el esfuerzo de implementación, sino también el tiempo necesario para investigar dependencias desconocidas. El onboarding se vuelve más exigente, porque entender el sistema depende de conocimientos que rara vez están documentados. Los ingenieros senior pueden convertirse en puntos de consulta recurrentes, lo que limita su disponibilidad para otros desafíos técnicos.
El problema de fondo no es necesariamente que los ingenieros carezcan de autonomía. Es que ejercer esa autonomía implica demasiada incertidumbre.
Check our job opportunies
La arquitectura determina cuánto necesita saber un ingeniero
Una de las decisiones de arquitectura con mayores consecuencias es dónde establecer los límites entre responsabilidades. Un límite bien definido permite a los ingenieros razonar sobre un componente a partir de su comportamiento documentado y sus interfaces, sin necesidad de entender cada detalle de su implementación.
Por ejemplo, un equipo que trabaja en un producto relacionado con tarjetas debería, idealmente, poder consumir las capacidades comunes de tarjetas sin tener que entender las reglas de negocio que rigen todos los demás productos que las utilizan, pero lograr esta separación rara vez es sencillo.
Las capacidades que parecen independientes desde la perspectiva del producto pueden compartir datos, restricciones operativas o supuestos históricos. Extraerlas en componentes reutilizables requiere entender esas relaciones y decidir qué responsabilidades realmente deben ir juntas.
Aun así, el beneficio va más allá de la organización del código. Cuando los límites de arquitectura corresponden a responsabilidades significativas, los ingenieros pueden acotar el alcance de sus investigaciones. Pueden identificar qué componentes se ven afectados por un cambio, qué contratos deben mantenerse estables y qué pruebas deberían aportar evidencia de que la modificación se comporta como se espera.
Esto hace que la incertidumbre sea más fácil de localizar y gestionar, y la distinción se vuelve especialmente importante cuando una arquitectura diseñada originalmente en torno a un pequeño número de productos debe adaptarse a un negocio en expansión.
Lo que la evolución de la arquitectura de Nubank revela sobre el cambio
La historia de la ingeniería de Nubank ofrece un ejemplo útil de cómo los requisitos de arquitectura evolucionan junto con una organización. Como se describe en el artículo La evolución de la arquitectura de Nubank hacia una plataformización consciente, la arquitectura original de la empresa fue diseñada para soportar un único producto, una tarjeta de crédito, en un único mercado, Brasil.
Esa arquitectura reflejaba los requisitos de negocio de su época, pero, a medida que Nubank se expandió hacia otros servicios financieros y países, los mismos supuestos de arquitectura ya no podían adaptarse con la misma naturalidad a cada nuevo requisito.
Distintos productos necesitaban acceder a capacidades comunes, mientras que cada mercado introducía sus propias reglas de negocio y consideraciones regulatorias. El desafío era distinguir lo que debía compartirse de lo que necesitaba seguir siendo específico de un producto o de una geografía.
Un ejemplo que presentó la Principal Engineer Bel Gonçalves durante el Nubank Engineering Meetup #12 fue la evolución de la plataforma de tarjetas. Anteriormente, la funcionalidad estaba concentrada en servicios como CreditCardAccounts, cuyas responsabilidades estaban estrechamente asociadas con las tarjetas de crédito.
A medida que el negocio se expandió, Nubank evolucionó hacia servicios más flexibles, capaces de soportar funcionalidades de débito y crédito en distintos productos y mercados. El relato publicado destaca dos contextos distintos: las tarjetas combinadas de Brasil y las tarjetas de débito y crédito separadas de México.
Soportar estas distintas configuraciones requirió una arquitectura capaz de separar las capacidades reutilizables de tarjetas de las reglas que rigen productos y países específicos. Aquí es donde la plataformización se vuelve relevante para la cuestión más amplia de la confianza en la ingeniería.
Una arquitectura organizada principalmente en torno a un producto específico puede obligar a los ingenieros a lidiar con supuestos propios de ese producto al adaptar funcionalidades a un contexto diferente. Separar las capacidades reutilizables de la lógica localizada puede hacer que esos supuestos sean más explícitos.
En principio, esto permite a los equipos razonar sobre el comportamiento compartido de forma independiente de las reglas de negocio de cada producto. Lo relevante no es simplemente que los servicios se vuelvan reutilizables. Es que sus responsabilidades pueden volverse más fáciles de identificar. Para los desarrolladores, esa claridad ofrece una base más sólida para entender el impacto potencial de un cambio.
El relato publicado por Nubank no establece que los ingenieros que trabajaban en CreditCardAccounts carecieran de confianza, ni que la transformación mejorara de forma medible su experiencia. El logro documentado es de arquitectura: un cambio hacia servicios con mayor flexibilidad entre productos y mercados.
Sin embargo, el caso ilustra un principio de ingeniería importante. A medida que las responsabilidades se separan con mayor claridad, la cantidad de comportamiento no relacionado del sistema que los desarrolladores deben entender para realizar un cambio concreto puede disminuir.
Los trade-offs detrás de una evolución de arquitectura más segura
Los límites más claros son valiosos, pero crearlos puede introducir riesgos propios. La plataformización de Nubank implicó extraer funcionalidades críticas de servicios legados que ya estaban en producción, además de reescribir componentes fundamentales. Estas actividades requieren algo más que decidir dónde debe vivir el código.
Mover funcionalidades entre servicios puede afectar la propiedad de los datos, las garantías de consistencia, las dependencias, la coordinación de los despliegues y el manejo de fallas. Una abstracción que simplifica el trabajo de un equipo podría introducir complejidad adicional para otro.
También existe el riesgo de generalizar demasiado pronto. Una plataforma diseñada para dar cabida a todos los productos futuros imaginables puede volverse difícil de entender precisamente porque expone demasiadas opciones de configuración y puntos de extensión. En esa situación, la flexibilidad se convierte en otra fuente de incertidumbre.
El relato de junio de 2025 identifica explícitamente la necesidad de equilibrar generalidad y simplicidad, y de evitar una complejidad de ingeniería innecesaria. Ese equilibrio importa porque la mantenibilidad no puede medirse por el número de servicios creados ni por la cantidad de código extraído.
Un límite de arquitectura útil debería hacer más claras las responsabilidades sin obligar a los desarrolladores a navegar por una red de abstracciones innecesariamente complicada.
A veces, mantener las funcionalidades juntas es la opción más simple y segura. Otras veces, separarlas crea las condiciones para una evolución independiente. El desafío de ingeniería es saber qué trade-off requiere realmente el sistema.
La confianza proviene de la evidencia, no de los supuestos
La arquitectura determina gran parte de lo que los ingenieros necesitan entender antes de hacer un cambio. Pero entender la estructura de un sistema es solo una parte del problema.
Los ingenieros también necesitan formas de verificar sus supuestos. Las pruebas automatizadas son especialmente valiosas cuando describen comportamientos significativos, en lugar de limitarse a ejercitar detalles de implementación.
Una prueba que establece cómo responde un servicio ante una condición de negocio específica puede cumplir dos propósitos. Verifica si el software se comporta correctamente y comunica lo que las modificaciones futuras deben preservar. Esto se vuelve especialmente importante al cambiar componentes fundamentales que soportan varios productos.
Sin embargo, las pruebas no pueden ofrecer una seguridad ilimitada. Una suite que pasa dice poco sobre los escenarios que no cubre, y las pruebas demasiado acopladas a detalles de implementación pueden hacer que una refactorización legítima sea innecesariamente difícil.
La confianza depende de entender tanto lo que verifican las pruebas como dónde termina su cobertura. La depuración y la observabilidad aportan otra capa de evidencia. Los logs, las métricas, los traces y los entornos de desarrollo reproducibles pueden ayudar a los ingenieros a entender cómo se comporta un sistema más allá de lo que sugiere su código fuente.
Las herramientas de desarrollo interactivo también pueden acortar la distancia entre una hipótesis y su verificación. En Clojure, por ejemplo, el REPL permite a los desarrolladores evaluar expresiones e inspeccionar el comportamiento durante el desarrollo.
Estas capacidades son complementarias, no intercambiables. Un REPL puede ayudar a explorar el comportamiento de una función, mientras que el tracing distribuido puede ser necesario para entender cómo se mueve una solicitud entre servicios en producción.
La documentación aborda un problema distinto: la pérdida del contexto de arquitectura con el paso del tiempo. Una interfaz puede explicar cómo interactuar con un componente sin revelar por qué se diseñó de esa manera. Los Architecture Decision Records (ADRs) pueden preservar el razonamiento detrás de decisiones relevantes, incluidas las alternativas consideradas y las restricciones que influyeron en el resultado.
Estos registros son especialmente útiles cuando futuros ingenieros se encuentran con un diseño que parece innecesariamente restrictivo hasta que su contexto histórico se aclara. Los estándares técnicos compartidos pueden reducir aún más el esfuerzo necesario para moverse entre sistemas.
El mismo artículo de Building Nubank describe el uso que hace la empresa de Clojure, microservicios y comunicación asíncrona como una base común de ingeniería. También aborda la inmutabilidad como un principio de diseño importante.
La consistencia no garantiza la mantenibilidad, pero los patrones conocidos pueden reducir la cantidad de convenciones que los ingenieros deben aprender al trabajar con distintos servicios. En conjunto, estas prácticas hacen que los cambios sean más explicables y sus consecuencias más observables. No pueden eliminar el riesgo operativo, pero sí pueden ayudar a los equipos a tomar decisiones con mejor evidencia.
Las consecuencias humanas de las decisiones de arquitectura
Existe una distinción importante entre sentirse seguro con los colegas y sentirse confiado para modificar un sistema. La seguridad psicológica, en su sentido organizacional establecido, se refiere a si las personas se sienten capaces de expresarse, reconocer errores, hacer preguntas y asumir riesgos interpersonales sin miedo a la humillación o al castigo.
La confianza técnica se refiere a algo distinto: si los ingenieros cuentan con el entendimiento, las herramientas y la evidencia suficientes para hacer cambios con un nivel de riesgo aceptable.
Un equipo puede tener una sin la otra. Un ingeniero podría reconocer abiertamente su incertidumbre durante un code review y, aun así, no ser capaz de determinar si una modificación afectará a un servicio no relacionado. Por el contrario, un ingeniero podría conocer a fondo una base de código, pero trabajar en un entorno donde cuestionar decisiones se desalienta. Ambas condiciones importan, y ninguna sustituye a la otra.
La expresión seguridad psicológica del código, explorada por Breno Salgado, ofrece una metáfora útil para la relación entre los desarrolladores y los sistemas que mantienen. Su valor radica en llamar la atención sobre las consecuencias humanas del diseño técnico, sin dejar de distinguirla de la seguridad psicológica interpersonal. Esas consecuencias van más allá de la comodidad individual.
Cuando el comportamiento de un sistema es difícil de entender, el conocimiento puede concentrarse en un pequeño grupo de ingenieros con experiencia. Su participación puede volverse necesaria para cambios rutinarios, lo que crea dependencias que afectan tanto la entrega como el desarrollo profesional.
Los ingenieros recién llegados pueden dedicar más tiempo a aprender excepciones no documentadas que a desarrollar una comprensión más amplia del producto. Los ingenieros con experiencia pueden dedicar una atención desproporcionada a explicar comportamientos históricos en lugar de abordar problemas emergentes.
Un sistema mantenible no puede eliminar la necesidad de colaborar, ni debería hacerlo. El trabajo de ingeniería complejo depende de la experiencia compartida, pero una buena arquitectura puede ayudar a que la colaboración sea una elección motivada por el problema y no un requisito recurrente impuesto por un código opaco.
La verdadera medida de la mantenibilidad
Una base de código puede ser estable en producción y, aun así, ser innecesariamente difícil de cambiar. Esa distinción importa porque la estabilidad operativa describe cómo se comporta un sistema hoy, mientras que la mantenibilidad influye en la eficacia con la que podrá evolucionar mañana.
La evolución de la arquitectura de Nubank ilustra la importancia de revisar las decisiones de diseño a medida que cambian los requisitos de negocio. Una arquitectura que sirve bien a un único producto puede necesitar límites diferentes cuando empieza a soportar varios productos, países y contextos regulatorios.
El objetivo es que la complejidad sea lo suficientemente comprensible como para que los ingenieros puedan trabajar con ella de manera deliberada. Eso implica diseñar límites en torno a responsabilidades significativas, preservar el razonamiento detrás de las decisiones, invertir en mecanismos de retroalimentación confiables y reconocer cuándo una abstracción genera más incertidumbre de la que elimina.
En definitiva, una organización de ingeniería no debería tener que elegir entre proteger sus sistemas existentes y dar a los desarrolladores margen para mejorarlos. La arquitectura más mantenible no es necesariamente la que cambia con mayor facilidad. Es la que ayuda a los ingenieros a entender qué afectará un cambio, verificar sus supuestos y gestionar los riesgos que persisten.
Al final del día, la confianza en la ingeniería de software proviene de tener suficiente conocimiento y evidencia para avanzar con responsabilidad, incluso cuando algo podría salir mal.
Check our job opportunies