Piensa en el proyecto más difícil en el que tu equipo haya trabajado, o quizás en el proyecto que has evitado porque crees que es imposible.
A veces aún no sabes qué será lo difícil. Si estás construyendo un producto o una función completamente nuevos, probablemente no puedas predecir cuál será el factor limitante, y está bien; solo deberías construir algo y seguir desde ahí. Pero a veces hay una parte obviamente difícil. Quizás otras personas ya lo han intentado antes y revelaron la parte difícil. O tu primera versión llega a producción y te das cuenta cuando empieza a tener uso real.
Supongamos que conoces la parte difícil y no te alejas de todo el proyecto, que es lo que muchos equipos, lamentablemente, hacen. ¿Cuál será tu enfoque ante la existencia de esta parte particularmente difícil? A grandes rasgos, tienes un par de opciones. Está la del enfoque seguro e incremental. Intentas minimizar el riesgo, y muchos equipos eligen este camino. Pero probablemente haya alguna razón oculta por la que ese enfoque en realidad no va a funcionar. En la práctica, lo que realmente estás haciendo es evitar la parte realmente difícil. Así que tienes que preguntarte: ¿podemos evitar la parte difícil y aun así tener éxito? Si la respuesta es sí, entonces sin duda evítala. Pero si la respuesta es no, entonces el camino incremental no es realmente seguro. Solo estás retrasando el fracaso inevitable.
Hay otra opción en la que atacas la parte particularmente difícil de frente, lo que probablemente requiera que des un gran y arriesgado paso. Uno de mis enfoques favoritos para resolver problemas es mantener una cosa constante y evaluar el resto. En este caso, supón que das el gran y arriesgado paso y realmente funciona. ¿El resto del plan es sólido como para que el proyecto en general tenga probabilidades de éxito? Si la respuesta es sí, y si realmente quieres abordar este proyecto y tener éxito, deberías dar el gran paso.
Esto suena muy simple, pero un par de cosas importantes deben ser ciertas para que los equipos puedan hacerlo. Profundizaré en eso, pero primero, un par de historias donde vi esto desarrollarse.
Una historia: Stripe v2 Accounts
Cuando estaba en Stripe, trabajé en Connect, donde permitíamos a las plataformas integrar pagos y servicios financieros. Creíamos que sería increíblemente poderoso si una plataforma y sus usuarios subyacentes pudieran desbloquear beneficios de red en todo el conjunto de productos de Stripe. Quizás tienes un usuario al que necesitas cobrarle una tarifa de suscripción (cliente), que también quiere aceptar pagos (comerciante) y recibir dinero (destinatario). Podemos asumir que este usuario tiene un saldo en Stripe porque ha aceptado un pago con tarjeta por algo que vendió y ha recibido una transferencia de otro comerciante de Stripe. Y quiere usar ese saldo para pagar tu tarifa de suscripción. Sabíamos que este usuario era el mismo en todos estos contextos diferentes, pero nuestras abstracciones no permitían una representación unificada de ese usuario. Teníamos v1/accounts, v1/customers, etc., por lo que no podíamos desbloquear esto sin una nueva abstracción importante. Esta fue la motivación detrás de v2 accounts: una abstracción única que pudiera manejar múltiples configuraciones.
Muchas personas y equipos antes de mi tiempo en Stripe habían imaginado esto y habían hecho intentos de abordarlo. Era ambicioso. Y si preguntaras "¿qué lo hace desafiante?", la respuesta simple es que la totalidad de las abstracciones y la lógica de negocio de Stripe tendrían que aprender a trabajar con una cuenta v2. Incluso si primero abordabas una parte, como reemplazar comerciantes con cuentas v2 o clientes con cuentas v2, las áreas de superficie eran cada una enormes, porque es literalmente cada API y cada bit de lógica de negocio dentro del código base de Stripe que contempla pagos o facturación. Además, solo obtienes los beneficios que buscábamos si reemplazas al menos 2.
Cuando comencé a trabajar con el equipo, el plan registrado era crear la nueva abstracción y modelo de datos, y hacer que coexistieran con los antiguos, con sincronización bidireccional. De esta manera, la mayor parte de la lógica de negocio de Stripe no necesitaba entender la nueva abstracción y podía seguir usando las antiguas. Considera esto como el camino seguro e incremental, que en gran medida evitaba resolver la parte difícil. El problema oculto con este camino era que la sincronización sería frágil, y tendríamos un problema de escala muy rápido. Si alguien creaba una cuenta v2 configurada tanto como comerciante como cliente, tendríamos 3 modelos subyacentes que mantener sincronizados, cada uno con copias de los mismos datos (como su nombre y correo electrónico).
El equipo exploró varios caminos para intentar que esto fuera viable, pero la realidad era que todas las opciones evitaban el problema oculto que probablemente resultaría ser un factor determinante. Reunimos a un pequeño grupo de ingenieros para una reunión fuera de la oficina donde discutimos cómo manejarlo. Nos encontramos dando vueltas alrededor de otro camino, donde atacaríamos el desafío de frente. Podríamos construir una interfaz que reemplazara cada sitio de llamada de lectura y escritura y, en última instancia, escribiera en nuestro nuevo modelo de datos. Lo llamamos "encapsulación". Se había sugerido en el pasado, pero todos lo ignoraron como una solución real porque venía con una estimación increíblemente alta en meses de ingeniería (en realidad, años de ingeniería). Esta vez, lo consideramos por un segundo: ¿y si mantuviéramos constante que podríamos lograr la encapsulación? ¿El proyecto en general tendría éxito? La respuesta fue sí. Pero era una locura.
Muchas personas evitan un problema como este a toda costa, pero otras se sienten atraídas por las cosas más difíciles. Resulta que teníamos un par de ingenieros que se emocionaron muchísimo con un desafío absolutamente loco como este, y los empoderamos para resolverlo. Consideramos algunas formas de ejecutar la migración, como federar el trabajo a cada equipo que poseía sitios de llamada, pero terminamos manteniéndolo centralizado y optando por codemods, porque manejar a tantos equipos con sus propias prioridades claramente iba a ser una montaña tonta de escalar. Durante todo esto, trabajé con el equipo para seguir adelante a pesar de la posibilidad muy real de que la encapsulación se convirtiera en un cuello de botella demasiado largo, porque realmente llegamos a creer que era el único camino viable.
Esto fue a principios de la década de 2020, antes de los agentes de codificación, por lo que la estimación realmente era de múltiples años de trabajo de ingeniería. A pesar de una montaña rusa de desafíos, el equipo lo logró. No creo que las cuentas v2 hubieran podido lanzarse sin ello.
Otra historia: Claude Managed Agents
En Anthropic, mi equipo lanzó recientemente Claude Managed Agents en beta, un conjunto de API para ejecutar agentes. Cuando construimos nuestra primera versión, no estaba claro cuál sería la parte difícil, así que hicimos lo que debes hacer con un producto nuevo: construimos lo más simple que pudiera funcionar. La API generaba un sandbox e iniciaba Claude Code dentro de él, y todo sobre la sesión de un agente vivía en ese contenedor. Lo usamos internamente, dimos acceso temprano a un pequeño grupo de clientes y comenzamos a recopilar comentarios.
Los problemas aparecieron bastante rápido. La latencia no era buena, porque un contenedor tenía que iniciarse antes de que Claude pudiera siquiera empezar a pensar. Si el contenedor fallaba, toda la sesión moría con él, lo que hacía que la confiabilidad fuera deficiente. Y no nos gustaba que el código que Claude escribía se ejecutara justo al lado de las credenciales de MCP.
Durante un tiempo, tratamos estos como problemas separados e intentamos parchear cada uno. El equipo mejoró en reanimar contenedores fallidos y pasó largos períodos depurando sesiones atascadas donde no podías saber si el arnés, el flujo de eventos o el contenedor habían fallado. Finalmente nos admitimos a nosotros mismos que cada uno de estos problemas se remontaba a la misma elección: todo se ejecutaba en un solo contenedor. Ahí estaba nuestro problema oculto. Arreglarlo significaba separar el cerebro (el bucle del arnés) de las manos (donde se ejecuta el código), y eso significaba construir un sistema distribuido real, que era una complejidad significativa que el diseño original evitaba.
Estábamos cerca de una beta pública en ese momento, y retrasar un lanzamiento para reconstruir una arquitectura es una decisión dolorosa. Pero nos hicimos la misma pregunta: ¿podríamos ir a beta con la arquitectura de un solo contenedor y aun así tener éxito? Sabíamos que no podíamos. Así que retrasamos e hicimos una re-arquitectura completa. Cuando trajimos de vuelta a los clientes de acceso temprano, los problemas de credenciales y confiabilidad habían desaparecido y el tiempo hasta el primer token había disminuido significativamente. Llevamos este sistema a beta.
Quizás podríamos haber predicho que la confiabilidad a escala sería la parte difícil que tendríamos que resolver para tener éxito. Pero para un producto nuevo, iterar hasta la respuesta fue mejor que dejar que lo perfecto fuera enemigo de lo bueno desde el principio. El error real habría sido ir a beta de todos modos después de que lo descubrimos.
Permitir que un equipo dé el gran paso
La razón por la que los equipos evitan el camino difícil es bastante racional. El camino difícil viene con una estimación masiva y de baja confianza, y un equipo necesita asumir una gran responsabilidad y riesgo para recorrerlo. El camino que lo evita tiene problemas ocultos que aparecen más tarde, de maneras que pueden llevar al fracaso total de todo. Pero ese camino suena seguro y, por lo tanto, es bastante tentador. Así que tomas el camino seguro, a veces durante años, incluso mientras un montón de personas creen que fracasará pero siguen adelante de todos modos.
Si quieres ser un equipo que da el gran y arriesgado paso, 2 cosas deben ser ciertas.
Primero, necesitas personas que puedan emocionarse muchísimo con el desafío. No necesitas consenso, y definitivamente no necesitas que todos crean que funcionará. La mayoría de las personas mirarán la parte difícil y asumirán que es imposible, y está bien. Necesitas a aquellos que vean el desafío más interesante que quizás hayan tenido la oportunidad de abordar. Dale el problema a esas personas y permíteles tomar mucha propiedad sobre cómo resolverlo.
Segundo, como líder, debes estar dispuesto a asumir la responsabilidad y cubrir al equipo para que lo persiga. Si no funciona, es tu responsabilidad, y hay una serie de consecuencias que podrían venir con eso. Asumir este riesgo requiere que sientas 2 cosas personalmente: primero, que realmente quieres que este proyecto tenga éxito, y segundo, que realmente crees que este gran y arriesgado paso es necesario.
La línea se está moviendo: algunas cosas difíciles se están volviendo más fáciles de lo que solían ser. Si mi equipo en Stripe estuviera escribiendo código con Claude Fable, quizás no habríamos encontrado la encapsulación tan desalentadora. Lo que realmente está sucediendo es que la IA nos está dejando a todos con las partes verdaderamente más difíciles. Así que realmente, la pregunta crucial no ha cambiado: ¿puedes evitar la parte difícil y aun así tener éxito? Si la respuesta es no, el camino seguro solo está posponiendo tu fracaso. Si realmente quieres tener éxito, tienes que dar el gran paso.





