Showing posts with label experiment. Show all posts
Showing posts with label experiment. Show all posts

Friday, October 10, 2025

AI and Lean Software Development: Reflections from Experimentation

Exploring how artificial intelligence might be changing the rules of the game in software development - preliminary insights from the trenches

An exploration into uncharted territory

I want to be transparent from the start: what I'm about to share are not definitive conclusions or proven principles. These are open reflections that emerge from a few months of intense personal experimentation with AI applied to software development, exploring its possibilities and trying to understand how this affects the Lean practices we typically use.

These thoughts are not definitive conclusions based on prolonged experience, but rather open reflections that I would like to continue experimenting with and discussing with others interested in this fascinating topic. I'm not speaking as someone who already has the answers, but as someone exploring fascinating questions and suspecting that we're facing a paradigm shift that we're only beginning to understand.

The fundamental paradox: speed versus validation

A central idea I'm observing is that, although artificial intelligence allows us to work faster, this doesn't mean we should automatically expand the initial scope of our functionalities. My intuition tells me that we should continue delivering value in small increments, validate quickly, and decide based on real feedback rather than simply on the speed at which we can now execute tasks.

But there's an interesting nuance I've started to consider: in low-uncertainty contexts, where both the value and implementation are clear and the team is very confident, it might make sense to advance a bit more before validating. However, my feelings lead me to think that maintaining discipline to avoid falling into speculative design is fundamental, because although AI facilitates it, it can jeopardize the simplicity and future flexibility of the system.

The cognitive crisis we don't see coming

Chart: While development speed with AI grows exponentially, our human cognitive capacity remains constant, creating a "danger zone" where we can create complexity faster than we can manage it.

Here I do have a conviction that becomes clearer every day: we should now be much more radical when it comes to deleting and eliminating code and functionalities that aren't generating the expected impact.

What this visualization shows me is something I feel viscerally: we have to be relentless to prevent complexity from devouring us, because no matter how much AI we have, human cognitive capacity hasn't changed - both for managing technical complexity and for users to manage the growing number of applications and functionalities.

We're at that critical point where the blue line (AI speed) crosses the red line (our capacity), and my intuition tells me that either we develop radical disciplines now, or we enter that red zone where we create more complexity than we can handle.

The paradox of amplified Lean

But here's the crux of the matter, and I think this table visualizes it perfectly:

Table: AI eliminates the natural constraints that kept us disciplined (at least some of us), creating the paradox that we need to artificially recreate those constraints through radical discipline.

This visualization seems to capture something fundamental that I'm observing: AI eliminates the natural constraints that kept us applying Lean principles. Before, the high cost of implementation naturally forced us to work in small batches. Now we have to recreate that discipline artificially.

For example, look at the "Small Batches" row: traditionally, development speed was the natural constraint that forced us to validate early. Now, with AI, that brake disappears and we risk unconscious scope growth. The countermeasure isn't technical, it's cultural: explicitly redefining what "small" means in terms of cognitive load, not time.

The same happens with YAGNI: before, the high cost of implementation was a natural barrier against speculative design. Now AI "suggests improvements" and makes overengineering tempting and easy. The answer is to make YAGNI even more explicit.

This is the paradox that fascinates me most: we have to become more disciplined precisely when technology makes it easier for us.

From this general intuition, I've identified several specific patterns that concern me and some opportunities that excite me. These are observations that arise from my daily experimentation, some clearer than others, but all seem relevant enough to share and continue exploring.

About scope and complexity

Change in the "default size" of work

AI facilitates the immediate development of functionalities or refactors, which can unconsciously lead us to increase their size. The risk I perceive is losing the discipline of small batch size crucial for early validation.

Ongoing exploration: My intuition suggests explicitly redefining what "small" means in an AI context, focused on cognitive size and not just implementation time. One way to achieve this is by relying on practices like BDD/ATDD/TDD to limit each cycle to a test or externally validable behavior.

Amplified speculative design

On several occasions I've had to undo work done by AI because it tries to do more than necessary. I've observed that AI lacks sensitivity to object-oriented design and has no awareness of the complexity it generates, creating it very quickly until reaching a point where it can't escape and enters a loop, fixing one thing and breaking others.

Reflection: This suggests reinforcing deliberate practices like TDD, walking skeletons, or strict feature toggles.

New type of "overengineering"

My initial experience suggests that the ease AI offers can lead to adding unnecessary functionalities. It's not the classic overengineering of the architect who designs a cathedral when you need a cabin. It's more subtle: it's adding "just one more feature" because it's easy, it's creating "just one additional abstraction" because AI can generate it quickly.

Key feeling: Reinforcing the YAGNI principle even more explicitly seems necessary.

About workflow and validations

Differentiating visible work vs. released work

My experience indicates that rapid development shouldn't confuse "ready to deploy" with "ready to release." My feeling is that keeping the separation between deployment and release clear remains fundamental.

I've also developed several times small functionalities that then weren't used. Although, to be honest, since I have deeply internalized eliminating waste and baseline cost, I simply deleted the code afterwards.

Opportunity I see: AI can accelerate development while we validate with controlled tests like A/B testing.

More work in progress, but with limits

Although AI can allow more parallel work, my intuition tells me this can fragment the team's attention and complicate integration. It's tempting to have three or four features "in development" simultaneously because AI makes them progress quickly.

My current preference: Use AI to reduce cycle time per story, prioritizing fast feedback, instead of parallelizing more work.

Change in the type of mistakes we make

My observations suggest that with AI, errors can propagate quickly, generating unnecessary complexity or superficial decisions. A superficial decision or a misunderstanding of the problem can materialize into functional code before I've had time to reflect on whether it's the right direction.

Exploration: My intuition points toward reinforcing cultural and technical guardrails (tests, decision review, minimum viable solution principle).

About culture and learning

Impact on culture and learning

I feel there's a risk of over-relying on AI, which could reduce collective reflection. Human cognitive capacity hasn't changed, and we're still better at focusing on few things at a time.

Working intuition: AI-assisted pair programming, ownership rotations, and explicit reviews of product decisions could counteract this effect.

Ideas I'm exploring to manage these risks

After identifying these patterns, the natural question is: what can we do about it? The following are ideas I'm exploring, some I've already tried with mixed results, others are working hypotheses I'd like to test. To be honest, we're in a very embryonic phase of understanding all this.

Discipline in Radical Elimination My intuition suggests introducing periodic "Deletion Reviews" to actively eliminate code without real impact. Specific sessions where the main objective is to identify and delete what isn't generating value.

"Sunset by Default" for experiments The feeling is that we might need an explicit automatic expiration policy for unvalidated experiments. If they don't demonstrate value in X time, they're automatically eliminated, no exceptions.

More rigorous Impact Tracking My experience leads me to think about defining explicit impact criteria before writing code and ruthlessly eliminating what doesn't meet expectations in the established time.

Fostering a "Disposable Software" Mentality My feeling is that explicitly labeling functionalities as "disposable" from the start could psychologically facilitate elimination if they don't meet expectations.

Continuous reduction of "AI-generated Legacy" I feel that regular sessions to review automatically generated code and eliminate unnecessary complexities that AI introduced without us noticing could be valuable.

Radically Reinforcing the "YAGNI" Principle My intuition tells me we should explicitly integrate critical questions in reviews to avoid speculative design: "Do we really need this now? What evidence do we have that it will be useful?"

Greater rigor in AI-Assisted Pair Programming My initial experience suggests promoting "hybrid Pair Programming" to ensure sufficient reflection and structural quality. Never let AI make architectural decisions alone.

A fascinating opportunity: Cross Cutting Concerns and reinforced YAGNI

Beyond managing risks, I've started to notice something promising: AI also seems to open new possibilities for architectural and functional decisions that traditionally had to be anticipated from the beginning.

I'm referring specifically to elements like:

  • Internationalization (i18n): Do we really need to design for multiple languages from day one?
  • Observability and monitoring: Can we start simple and add instrumentation later?
  • Compliance: Is it possible to build first and adapt regulations later?
  • Horizontal scalability and adaptation to distributed architectures: Can we defer these decisions until we have real evidence of need?

My feeling is that these decisions can be deliberately postponed and introduced later thanks to the automatic refactoring capabilities that AI seems to provide. This could further strengthen our ability to apply YAGNI and defer commitment.

The guardrails I believe are necessary

For this to work, I feel we need to maintain certain technical guardrails:

  • Clear separation of responsibilities: So that later changes don't break everything
  • Solid automated tests: To refactor with confidence
  • Explicit documentation of deferred decisions: So we don't forget what we deferred
  • Use of specialized AI for architectural spikes: To explore options when the time comes

But I insist: these are just intuitions I'd like to validate collectively.

Working hypotheses I'd love to test

After these months of experimentation, these are the hypotheses that have emerged and that I'd love to discuss and test collectively:

1. Speed ≠ Amplitude

Hypothesis: We should use AI's speed to validate faster, not to build bigger.

2. Radical YAGNI

Hypothesis: If YAGNI was important before, now it could be critical. Ease of implementation shouldn't justify additional complexity.

3. Elimination as a central discipline

Hypothesis: Treat code elimination as a first-class development practice, not as a maintenance activity.

4. Hybrid Pair Programming

Hypothesis: Combining AI's speed with human reflection could be key. Never let AI make architectural decisions alone.

5. Reinforced deployment/release separation

Hypothesis: Keep this separation clearer than ever. Ease of implementation could create mirages of "finished product."

6. Deferred cross-cutting concerns

Hypothesis: We can postpone more architectural decisions than before, leveraging AI's refactoring capabilities.

An honest invitation to collective learning

Ultimately, these are initial ideas and reflections, open to discussion, experimentation, and learning. My intuition tells me that artificial intelligence is radically changing the way we develop products and software, enhancing our capabilities, but suggesting the need for even greater discipline in validation, elimination, and radical code simplification.

My strongest hypothesis is this: AI amplifies both our good and bad practices. If we have the discipline to maintain small batches, validate quickly, and eliminate waste, AI could make us extraordinarily effective. If we don't have it, it could help us create disasters faster than ever.

But this is just a hunch that needs validation.

What experiences have you had? Have you noticed these same patterns, or completely different ones? What practices are you trying? Have you noticed these same effects in your teams? What feelings does the integration of AI in your Lean processes generate for you?

We're in the early stages of understanding all this. We need perspectives from the entire community to navigate this change that I sense could be paradigmatic, but that I still don't fully understand.

Let's continue the conversation. The only way forward is exploring together.

Do these reflections resonate with you? Have you noticed similar or completely different patterns? I'd love to hear about your experience and continue learning together in this fascinating and still unexplored territory.

Sunday, August 03, 2025

IA y Lean Software Development: Reflexiones desde la experimentación

Explorando cómo la inteligencia artificial podría estar cambiando las reglas del juego en el desarrollo de software - ideas preliminares desde la trinchera

Una exploración en territorio inexplorado

Quiero ser transparente desde el inicio: lo que voy a compartir no son conclusiones definitivas ni principios probados. Son reflexiones abiertas que surgen de unos meses de experimentación personal intensa con IA aplicada al desarrollo de software, explorando sus posibilidades y tratando de entender cómo afecta esto a las prácticas Lean que habitualmente utilizamos.

Estos pensamientos no son conclusiones definitivas basadas en experiencia prolongada, sino reflexiones abiertas que me gustaría seguir experimentando y discutiendo con otros interesados en este fascinante tema. No hablo como alguien que ya tiene las respuestas, sino como alguien que está explorando preguntas fascinantes y que sospecha que estamos ante un cambio de paradigma que apenas empezamos a entender.

La paradoja fundamental: velocidad versus validación

Una idea central que estoy observando es que, aunque la inteligencia artificial nos permite trabajar más rápido, eso no implica que debamos ampliar automáticamente el alcance inicial de nuestras funcionalidades. Mi intuición me dice que debemos seguir entregando valor en pequeños incrementos, validar rápidamente y decidir en función del feedback real y no simplemente de la velocidad con que ahora podemos ejecutar tareas.

Pero aquí hay un matiz interesante que he empezado a considerar: en contextos de baja incertidumbre, donde tanto el valor como la implementación son claros y el equipo está muy seguro, podría tener sentido avanzar algo más antes de validar. Sin embargo, mis sensaciones me llevan a pensar que mantener la disciplina para evitar caer en diseño especulativo es fundamental, ya que aunque la IA lo facilite, puede poner en peligro la simplicidad y flexibilidad futura del sistema.

La crisis cognitiva que no vemos venir

Gráfico: Mientras la velocidad de desarrollo con IA crece exponencialmente, nuestra capacidad cognitiva humana permanece constante, creando una "zona de peligro" donde podemos crear complejidad más rápido de lo que podemos gestionarla.

Aquí sí que tengo una convicción que cada día se vuelve más clara: ahora deberíamos ser mucho más radicales a la hora de borrar y eliminar código y funcionalidades que no están generando el impacto esperado.

Lo que me muestra esta visualización es algo que siento visceralmente: tenemos que ser implacables para evitar que la complejidad nos devore, porque por mucha IA que tengamos, la capacidad cognitiva de los humanos no ha cambiado - tanto para gestionar la complejidad técnica como para que los usuarios gestionen el número creciente de aplicaciones y funcionalidades.

Estamos en ese punto crítico donde la línea azul (velocidad de IA) se cruza con la roja (nuestra capacidad), y mi intuición me dice que o desarrollamos disciplinas radicales ahora, o nos adentramos en esa zona roja donde creamos más complejidad de la que podemos manejar.

La paradoja del Lean amplificado

Pero aquí está el quid de la cuestión, y creo que esta tabla lo visualiza perfectamente:

Tabla: La IA elimina las restricciones naturales que nos mantenían disciplinados (al menos a algunos), creando la paradoja de que necesitamos recrear artificialmente esas restricciones a través de disciplina radical.

Esta visualización me parece que captura algo fundamental que estoy observando: la IA elimina las restricciones naturales que nos mantenían aplicando principios Lean. Antes, el alto coste de implementación naturalmente nos forzaba a hacer batches pequeños. Ahora tenemos que recrear esa disciplina artificialmente.

Por ejemplo, fíjate en el línea de "Small Batches": tradicionalmente la velocidad de desarrollo era la restricción natural que nos forzaba a validar temprano. Ahora, con IA, ese freno desaparece y corremos el riesgo de crecimiento inconsciente del scope. La contramedida no es técnica, es cultural: redefinir explícitamente qué significa "pequeño" en términos de carga cognitiva, no de tiempo.

Lo mismo pasa con YAGNI: antes el alto coste de implementación era una barrera natural contra el diseño especulativo. Ahora la IA "sugiere mejoras" y hace que el overengineering sea tentador y fácil. La respuesta es hacer YAGNI aún más explícito.

Esta es la paradoja que más me fascina: tenemos que volvernos más disciplinados justo cuando la tecnología nos lo pone más fácil.

A partir de esta intuición general, he identificado varios patrones específicos que me preocupan y algunas oportunidades que me emocionan. Son observaciones que surgen de mi experimentación diaria, algunas más claras que otras, pero todas me parecen lo suficientemente relevantes como para compartirlas y seguir explorándolas.

Sobre el scope y la complejidad

Cambio en el "tamaño por defecto" del trabajo

La IA facilita el desarrollo inmediato de funcionalidades o refactors, lo que puede llevarnos inconscientemente a aumentar su tamaño. El riesgo que percibo es perder la disciplina de small batch size clave para validación temprana.

Exploración en curso: Mi intuición sugiere redefinir explícitamente lo que significa "pequeño" en un contexto con IA, enfocado en tamaño cognitivo y no solo en tiempo de implementación. Una forma de conseguirlo es apoyándonos en prácticas como BDD/ATDD/TDD para limitar cada ciclo a un test o comportamiento externo validable.

Diseño especulativo amplificado

En varias ocasiones he tenido que deshacer trabajo hecho por la IA porque intenta hacer más de lo necesario. He observado que la IA carece de sensibilidad al diseño orientado a objetos y no tiene ningún tipo de consciencia sobre la complejidad que genera, creándola muy rápido hasta llegar a un punto del que no sabe salir y entra en bucle, arreglando una cosa y rompiendo otras.

Reflexión: Esto me sugiere reforzar prácticas deliberadas como TDD, walking skeletons o feature toggles estrictos.

Nuevo tipo de "overengineering"

Mi experiencia inicial sugiere que la facilidad que ofrece la IA puede llevar a añadir funcionalidades innecesarias. No es el overengineering clásico del arquitecto que diseña una catedral cuando necesitas una cabaña. Es más sutil: es añadir "solo una funcionalidad más" porque es fácil, es crear "solo una abstracción adicional" porque la IA puede generarla rápidamente.

Sensación clave: Reforzar el principio YAGNI de forma aún más explícita parece necesario.

Sobre el flujo de trabajo y las validaciones

Diferenciar trabajo visible vs. trabajo liberado

Mi experiencia me indica que el rápido desarrollo no debe confundir "listo para desplegar" con "listo para liberar". Mi sensación es que mantener clara la separación entre deployment y release sigue siendo fundamental.

También he desarrollado varias veces pequeñas funcionalidades que luego no se han usado. Aunque, siendo honesto, como tengo muy interiorizado eliminar desperdicio y coste basal, simplemente he borrado el código posteriormente.

Oportunidad que veo: La IA puede acelerar desarrollo mientras validamos con pruebas controladas como A/B testing.

Más trabajo en curso, pero con límites

Aunque la IA puede permitir más trabajo paralelo, mi intuición me dice que esto puede fragmentar la atención del equipo y complicar la integración. Es tentador tener tres o cuatro funcionalidades "en desarrollo" simultáneo porque la IA hace que avancen rápido.

Mi preferencia actual: Usar IA para reducir tiempo de ciclo por historia, priorizando feedback rápido, en lugar de paralelizar más trabajo.

Cambio en el tipo de errores que cometemos

Mis observaciones sugieren que con IA, los errores pueden propagarse rápidamente, generando complejidad innecesaria o decisiones superficiales. Una decisión superficial o un malentendido del problema puede materializarse en código funcional antes de que haya tenido tiempo de reflexionar sobre si es la dirección correcta.

Exploración: Mi intuición apunta hacia reforzar guardrails culturales y técnicos (tests, revisión de decisiones, principio de mínima solución viable).

Sobre la cultura y el aprendizaje

Impacto en la cultura y el aprendizaje

Siento que existe el riesgo de confiar excesivamente en IA, lo que podría reducir la reflexión colectiva. La capacidad cognitiva humana no ha cambiado, y seguimos siendo mejores enfocándonos en pocas cosas a la vez.

Intuición de trabajo: Pair programming asistido por IA, rotaciones de ownership y revisiones explícitas de decisiones de producto podrían contrarrestar este efecto.

Ideas que estoy explorando para gestionar estos riesgos

Después de identificar estos patrones, la pregunta natural es: ¿qué podemos hacer al respecto? Las siguientes son ideas que estoy explorando, algunas ya las he probado con resultados mixtos, otras son hipótesis de trabajo que me gustaría contrastar. Siendo honestos, estamos en una fase muy embrionaria de entender todo esto.

Disciplina en la Eliminación Radical Mi intuición sugiere introducir "Deletion Reviews" periódicas para eliminar activamente código sin impacto real. Sesiones específicas donde el objetivo principal sea identificar y borrar lo que no está generando valor.

"Sunset by Default" para experimentos La sensación es que podríamos necesitar una política explícita de caducidad automática para experimentos no validados. Si no demuestran valor en X tiempo, se eliminan automáticamente, sin excepciones.

Tracking de Impacto más riguroso Mi experiencia me lleva a pensar en definir criterios explícitos de impacto antes de escribir código y eliminar despiadadamente lo que no cumpla expectativas en el tiempo establecido.

Fomentar la Mentalidad de "Disposable Software" Mi sensación es que etiquetar explícitamente funcionalidades como "disposable" desde el inicio podría facilitar psicológicamente la eliminación si no cumplen expectativas.

Reducción continua de "Legacy generado por IA" Siento que podrían ser valiosas las sesiones regulares para revisar código generado automáticamente y eliminar complejidades innecesarias que la IA haya introducido sin que nos diéramos cuenta.

Reforzar radicalmente el Principio de "YAGNI" Mi intuición me dice que deberíamos integrar explícitamente preguntas críticas en revisiones para evitar diseño especulativo: "¿Realmente necesitamos esto ahora? ¿Qué evidencia tenemos de que será útil?"

Mayor rigor en Pair Programming Asistido por IA Mi experiencia inicial sugiere promover "Pair Programming híbrido" para asegurar reflexión suficiente y calidad estructural. Nunca dejar que la IA tome decisiones arquitectónicas sola.

Una oportunidad fascinante: Cross Cutting Concerns y el YAGNI reforzado

Más allá de gestionar los riesgos, he empezado a notar algo prometedor: la IA también parece abrir nuevas posibilidades para decisiones arquitectónicas y funcionales que tradicionalmente debían anticiparse desde el principio.

Me refiero específicamente a elementos como:

  • Internacionalización (i18n): ¿Realmente necesitamos diseñar para múltiples idiomas desde el día uno?
  • Observabilidad y monitorización: ¿Podemos empezar simple y añadir instrumentación después?
  • Cumplimiento normativo (compliance): ¿Es posible construir primero y adaptar regulaciones más tarde?
  • Escalabilidad horizontal y adaptación a arquitecturas distribuidas: ¿Podemos diferir estas decisiones hasta tener evidencia real de necesidad?

Mi sensación es que estas decisiones pueden posponerse deliberadamente y ser introducidas más tarde gracias a las capacidades de refactorización automática que parece brindar la IA. Esto podría fortalecer aún más nuestra capacidad de aplicar YAGNI y defer commitment.

Los guardrails que creo necesarios

Para que esto funcione, siento que necesitamos mantener ciertos guardrails técnicos:

  • Separación clara de responsabilidades: Para que los cambios posteriores no rompan todo
  • Pruebas automatizadas sólidas: Para refactorizar con confianza
  • Documentación explícita de decisiones pospuestas: Para no olvidar lo que diferimos
  • Uso de IA especializada para spikes arquitecturales: Para explorar opciones cuando llegue el momento

Pero insisto: esto son solo intuiciones que me gustaría validar colectivamente.

Hipótesis de trabajo que me encantaría contrastar

Después de estos meses de experimentación, estas son las hipótesis que han emergido y que me encantaría discutir y probar colectivamente:

1. Velocidad ≠ Amplitud

Hipótesis: Deberíamos usar la velocidad de la IA para validar más rápido, no para construir más grande.

2. YAGNI radical

Hipótesis: Si antes YAGNI era importante, ahora podría ser crítico. La facilidad de implementación no debería justificar la complejidad adicional.

3. Eliminación como disciplina central

Hipótesis: Tratar la eliminación de código como una práctica de desarrollo de primera clase, no como una actividad de mantenimiento.

4. Pair Programming híbrido

Hipótesis: Combinar la velocidad de la IA con la reflexión humana podría ser clave. Nunca dejar que la IA tome decisiones arquitectónicas sola.

5. Separación deployment/release reforzada

Hipótesis: Mantener esta separación más clara que nunca. La facilidad de implementación podría crear espejismos de "producto terminado".

6. Cross-cutting concerns diferidos

Hipótesis: Podemos posponer más decisiones arquitectónicas que antes, aprovechando las capacidades de refactorización de la IA.

Una invitación honesta al aprendizaje conjunto

En definitiva, estas son ideas y reflexiones iniciales, abiertas a discusión, experimentación y aprendizaje. Mi intuición me dice que la inteligencia artificial está cambiando radicalmente la forma en que desarrollamos producto y software, potenciando nuestras capacidades, pero sugiriéndome la necesidad de una disciplina aún mayor en validación, eliminación y simplificación radical del código.

Mi hipótesis más fuerte es esta: la IA amplifica tanto nuestras buenas como nuestras malas prácticas. Si tenemos disciplina para mantener pequeños batches, validar rápido y eliminar desperdicio, la IA podría hacernos extraordinariamente efectivos. Si no la tenemos, podría ayudarnos a crear desastres más rápido que nunca.

Pero esto es solo una corazonada que necesita validación.

¿Qué experiencias habéis tenido vosotros? ¿Habéis notado estos mismos patrones, o completamente diferentes? ¿Qué prácticas estáis probando? ¿Habéis notado estos mismos efectos en vuestros equipos? ¿Qué sensaciones os genera la integración de IA en vuestros procesos Lean?

Estamos en los primeros compases de entender todo esto. Necesitamos las perspectivas de toda la comunidad para navegar este cambio que intuyo puede ser de paradigma, pero que aún no comprendo del todo.

Continuemos la conversación. La única forma de avanzar es explorando juntos.

¿Te resuenan estas reflexiones? ¿Has notado patrones similares o completamente diferentes? Me encantaría conocer tu experiencia y seguir aprendiendo juntos en este territorio fascinante y aún inexplorado.

Thursday, April 10, 2025

Vibe Coding III: Complexity Creeps—Unless You Don’t Let It

Getting Back to My Picks

Over the past few days, I revisited the development of eferro Picks. The project had been well received, and I’d gotten some interesting feedback, so I decided to give it a proper push. But this time, it wasn’t just another playful exploration. The project had grown in complexity and was starting to demand a more deliberate approach—especially if I wanted it to scale or stay maintainable.

My first step: simplify the code, strip out anything superficial, and wrap everything in tests to make the project more sustainable.

The next step was to process all the feedback I had received about the site and implement some improvements that would make the experience smoother and more intuitive. This included better navigation, clearer tooltips, and a toggle to filter talks that actually had notes. It was a great excuse to turn real user feedback into practical functionality—while continuing the vibe coding experiment in a slightly more purposeful direction.

https://eferro.github.io/eferro-picks-site/

The AI as My Pair Programming Partner (With Superpowers and Flaws)

For this session, I continued my vibe coding experiment, but with a twist. Since my experience with front-end technologies like TypeScript, React, and Tailwind CSS is limited, I focused solely on accepting or rejecting the AI's proposed changes. I didn’t review or modify the code directly—instead, I observed the AI’s behavior and guided its direction from a higher level. It was pure vibe coding: trusting the AI and seeing where it would lead.

I used the Cursor Pro IDE, following the vibe coding rules I outlined in the first post of this series (Vibe coding: building things from curiosity and flow). That meant focusing on flow, intuition, and rapid iteration—even when partnering with an AI. Those original principles shaped how I interacted with it, emphasizing exploration over rigid planning.

In this new phase, the AI stopped being just a tool and started acting more like a pair programming partner. One that—despite chaotic moments—can be surprisingly effective when guided with care and intention. I say “guide” because without boundaries, it tends to suggest overly generic or needlessly complex solutions. Almost like it's channeling the collective ego of every public repo out there.

Left unchecked, the AI naturally gravitates toward generality, abstraction, and unnecessary flexibility—just like many of us do when we’re overthinking or trying to be clever.

This approach helped me refactor with focus, prioritizing simplicity and clarity. But it wasn’t all smooth sailing—and I think it’s important to be transparent about that.

During the process:
  • Two or three times, I had to stop because the AI entered loops it couldn’t escape.
  • Once, it even pushed invalid changes directly to production (I work in trunk).
  • Two or three times, I had to revert committed changes just to get back to a stable state.
  • At least twice, when a test wouldn’t stabilize, the best option was to delete it, move on, and return later with fresh eyes. That worked far better than endless poking.


These moments—while frustrating—reinforced something I already believed: the only way to build sustainably with this much raw power at my fingertips is to work in small, safe steps.


The Power of Small Safe Steps (Now More Than Ever)

One of the clearest takeaways from this session: with this much speed and assistance, working in Small Safe Steps becomes more essential than ever.

It’s valuable to know what I want to achieve, have techniques to move forward in parallel, and break tasks into manageable chunks. In practice, most of these “steps” were completed in sessions of 25 minutes or less. Each one designed to be:
  • Small: a change that takes just a few minutes.
  • Safe: unlikely to break production code or existing tests.
Also, due to my current responsibilities, I can only code in short, scattered bursts. I no longer have the luxury of regular pair or ensemble sessions. That’s why the benefits of small steps are fundamental for me—especially:
  • Interruptibility: I can pause anytime without losing the thread.
  • Safety: Each change is low-risk and easy to roll back.
  • Autonomy: I can keep moving forward, even solo, without creating chaos.


This way of working also offers continuous feedback. Geepaw Hill’s article MMMSS: The Intrinsic Benefit of Steps describes this beautifully. I highly recommend reading the full Many More Much Smaller Steps series—it sticks with you.

Small Improvements, Real Impact

In these sessions, I also tackled some of the feedback I had received.

For example, the note icon next to some talks was unclear. After digging into the data with the AI, we discovered that many of those records didn’t contain actual notes—just whitespace or line breaks. So:
  • I implemented a filter to clean those up.
  • Added a tooltip explaining what the icon meant.
  • And (because experimenting is cheap) added a toggle to filter talks with real notes.
Another piece of feedback was trickier: when users navigated to a talk and returned, they lost their filters and scroll position.

This was tough—mainly due to my lack of frontend experience. The AI kept trying random libraries and approaches, switching strategies mid-flight. It felt like pure trial-and-error with no clear rationale.
Things only improved when I stepped in with a specific suggestion:
“Can you change the approach and just use a specific key for the index (scroll_index) so we can use this key when coming back? This way we avoid the problem of overwriting the key.”
The AI immediately responded:
“Ah, that’s a much simpler approach! Instead of handling different paths and normalizing them, we can:
  • Use a specific key for the index page scroll position
  • Save it when leaving
  • Restore it when returning
  • No need to handle paths or base URLs. Let me update the code.”
This moment captured a recurring pattern: complexity is still my responsibility
Left unchecked, the AI naturally gravitates toward generality, abstraction, and unnecessary flexibility—just like many of us do when we’re overthinking or trying to be clever.

But if I pause, question, and steer things back toward simplicity, the AI follows—with surprising grace.

I can (and must) be the guardian of simplicity—with or without AI.

A Retrospective with the AI (and Tangible Results)

In the middle of all this, I ran a mini-retrospective. I asked the AI:
“We’ve been working together for a while. I think you already know what concerns me. Can you describe our workflow and what behaviors you've changed based on my instructions?”
The AI replied:
“Our workflow has evolved into a highly collaborative process where:
  • We work in small, focused steps
  • Each change is preceded by clear reasoning
  • You maintain control
  • I provide options and wait for your decisions
  • We verify each change before moving forward
  •  Communication is direct and natural
I’ve adapted to:
  • Present one change at a time
  • Show my thought process
  • Ask for confirmation before proceeding
  • Focus on practical, simple solutions
  • Maintain test coverage
  • Keep methods small and focused
  • Would you like me to adjust anything? 😊”
Then I followed up:

“Several times I’ve had to revert your changes because you didn’t run the tests before committing. Sometimes even several commits at once.
  • Why do you think this happened?
  • How could we have avoided it?
  • What have we learned?
  • How should we adapt to keep complexity under control?
The AI responded:
“To improve our process, I suggest:
New workflow steps:
  • Make one small change
  • Run tests immediately
  • Fix issues
  • Get your review
  • Only then commit
  • Never push without your explicit approval
Quality gates:
  • All tests must pass
  • Changes must be minimal
  • Each commit should be independently testable
  • No pushing without verification
Communication improvements:
  • Always report test status
  • Ask before pushing
  • Be explicit about what’s tested
  • Show test results

Would you like me to implement these changes in our workflow? 😊”
And the best part? It followed through. For the rest of the session, it ran tests before suggesting anything and asked for confirmation before every commit or push.

Closing: Being a Responsible Coder, Even When Alone

Will the AI forget all this once I close the IDE? Probably. But I’ve learned that these kinds of iterations and retrospectives have value. Therapeutic for me. Educational for the AI—at least within the session.

This experience reinforces the critical importance of working in Small Safe Steps, especially when collaborating with powerful AI. It’s not just about speed, but about maintaining control and understanding.

Continuous simplification is key. Left unchecked, complexity always grows. I have to actively fight that—and help the AI do the same.

Ultimately, it’s up to me to be the guardian of simplicity.

And given my current role, I don’t get to code as much as I’d like—certainly not in pair or ensemble settings. So this is the closest I get to coding responsibly. Because anyone who knows me knows:
 Leaving me to code alone is rarely a good idea. :)


Previous posts in the series:

Related links:

Friday, April 04, 2025

Vibe coding II: when flow meets tests

A few weeks ago, I wrote about vibe coding as a light, curiosity-driven and deeply personal way to build small projects without pressure. If you haven’t read it yet, here’s the link: Vibe coding: building things from curiosity and flow.

Back then, I shared how SimpleCalendar app and eferro Picks site came to life without a fixed plan — just following my instincts, what felt good in the moment, and playing with tools like Lovable and Cursor. But once SimpleCalendar started to feel “good enough,” a natural question popped up:

What if I want to improve it later? How do I avoid breaking things that already work?

A new phase of the experiment

That’s when a second phase of the experiment started: introducing a strong testing strategy, not as part of the design, but as a safety net for the future.

I didn’t use TDD. Quite the opposite — tests came after the fact. And that changes things. They didn’t help shape the architecture or drive design decisions. The structure I ended up with was just what had emerged from the flow — with its quirks and lucky guesses. But what tests did give me was confidence. Confidence to touch code without fear. Confidence to think about new features without worrying about regressions.

What test comes next?

One of the most interesting parts of this phase was using AI again — but in a different role.

I wasn’t asking it to code as much as to help me decide what test would give me the most confidence next.

Sometimes it worked really well. It pointed to areas I hadn’t thought of testing yet.
Other times… well. Let’s just say I found myself in a trial-and-error loop, poking at things, trying to get the test to pass. Without much frontend experience, it really felt like being a kid blindly hitting a piñata with a stick. Try, miss, try again… until something clicked.


And in a couple of cases, I got stuck in endless loops. The best thing I could do was revert to the last stable point. Small steps and good version control — still essential, even (especially) when working with AI.

An emerging strategy

Looking back, the test suite ended up with a pretty solid and layered structure:

  • I started with basic unit tests — validating components and utilities.
  • Then came integration and hook tests, managing state and interactions.
  • Later, I added coverage tools, edge cases, quarter navigation, grid behavior…
  • And finally, I polished it: test structure, readability, timezone handling, transitions…

It turned into a kind of after-the-fact test pyramid. And even if it didn’t help design better code, it gave me a real sense of safety moving forward.


Current tests execution

Assisted refactoring: the other half of the experiment

During the initial build phase, I made it a point to regularly pause and ask the AI to look for refactors, simplifications, or unused code.
That played a big role in keeping complexity under control. When the time came to introduce tests, the codebase was in a reasonably clean and manageable state — not by accident, but by design.

Turns out, you can keep things tidy even in flow mode, if you ask the right questions. And if you guide it well, the AI can be surprisingly helpful with that too.

Complexity is still our problem

And here’s something I really want to highlight — especially now that we can build so fast, test ideas on the fly, and move with the kind of speed that used to feel like sci-fi: complexity is still our responsibility.

Just because something works doesn’t mean it’s well built.
Just because we built it fast doesn’t mean it will survive the next change.
The temptation to say “let the AI deal with the mess” is real — but dangerous.
Complexity kills, with or without AI.

And because it’s now so easy to build things, it’s more important than ever to keep complexity in check.

When vibe works — and when it doesn’t

This AI-assisted approach worked really well for these small apps with no hard requirements — where discovering what I wanted was part of the process.

But for components or applications that need to live inside a broader, evolving ecosystem, this way of working wouldn’t be appropriate — at least not today.

We’re still learning how AI can support sustainable, scalable product development within a real team, with real constraints, and real users.

That’s a different kind of challenge. And we’re just getting started.

"We are uncovering better ways of developing software by doing it and helping others do it."

References & Useful Links

Monday, March 31, 2025

Vibe coding: building things from curiosity and flow

I’m terrible at frontend. Not in an impostor-syndrome way. I mean, I really have no idea what I’m doing there. But I was curious. Curious to see if, starting from zero, I could build something simple and useful just for myself. And curious to see how far current AI-assisted tools could take me in that process.

So I gave it a try.

I ended up building two small tools: SimpleCalendar and eferro Picks. Both very simple apps, fully frontend, no backend, no database. I just wanted to create something, from scratch, that would be useful to me and would let me learn through doing.

And I did it using what I’ve been calling vibe coding.

What is vibe coding?

For me, vibe coding means entering a kind of flow state where the main limitation is not my technical knowledge, but my imagination. It only works in certain contexts: small projects, no critical logic, no business constraints, no one else depending on it. But within that space, it’s a great way to experiment, learn, and create.

I didn’t start with a strict plan. I had a general idea of what I wanted, and then I let curiosity drive the process. Each step was guided by what felt right, what I found fun, or what looked interesting to try next.

Two experiments, two flows

In both projects, the starting point was the same: I created a prompt using lovableprompts.app. From there, each project took a different direction.

SimpleCalendar

  • I used lovable.dev to iterate on the prompt, sending screenshots with annotations to guide the conversation.
  • I worked in short sessions (around 10 minutes), a few iterations per day, for several days.
  • Once I ran out of credits, I switched to Cursor, connected it to GitHub, and continued iterating there.
  • Total time/effort: 4-5 Hours approx

eferro Picks

  • I started with the prompt from lovableprompts, but went straight to the agent in Cursor.
  • This time, the flow was much more continuous. I interacted mainly through Cursor, using screenshots and dialogue to guide changes.
  • On this occasion, I not only used the cursor agent to program, but I also used it to explore the data, ask for design advice, among other things.
  • Total time/effort: 5-6 Hours approx

In both cases, I found that asking the agent to simplify the code or remove unused parts worked surprisingly well. That’s been one of the biggest learnings: even if my own tendency is not to simplify as I go, I can ask the machine to do it. And it does. That gives me hope that, with the right process, we can still build things that remain maintainable over time, even when developed through this kind of flow.

No tests, no pain? Not quite.

Since these are personal tools, and the goal was to learn while building, I didn’t bother writing tests at first. That came back to bite me a couple of times, with small regressions. But since the issues were easy to fix in minutes, it wasn’t a huge deal.

That said, I’m now planning to write a small battery of end-to-end and approval tests — also using the same AI-assisted approach — to help me avoid this kind of slip in the future.

A few reflections

The most surprising and exciting part has been realizing that AI can help not just with building, but with simplifying. That changes the game. If we learn how to guide it well, if we treat the process with care, we might be able to counterbalance the natural tendency to just add more and more complexity.

In these two projects, the approach worked really well — but there were no constraints. I had a vague idea of what I wanted. No business rules, no design consistency to maintain across an ecosystem, no hard requirements. Just "make something useful that I enjoy."

In that context, vibe coding works beautifully. A few end-to-end and approval tests are probably enough to keep things under control.

But for other kinds of software — apps with strong business rules, with complex user flows, or where coherence matters across many teams — I think we’ll need to find a very different process. One that starts from a much tighter definition of what we want to achieve. Likely one where acceptance tests and fine-grained design guidance are central from the very beginning.

We’re just scratching the surface. There’s still so much to explore.

Simple Calendar




Description: A modern, quarter-aware calendar application built with React and TypeScript. Features an intuitive interface for tracking dates, weeks, and quarters throughout the year.
Code: SimpleCalendar GitHub repo
Deployment: Github pages
URL: SimpleCalendar app


eferro's picks



Description: This site showcases a handpicked selection of talks about software development, engineering practices, and technical leadership. The content is managed in Airtable and automatically synchronized with the site.
Code:eferro Picks GitHub repo
Deployment:Github pages
URL:eferro Picks site

Cursor Agent Rules (Current Setup)

During these two projects, I used the same Cursor agent rules that I’ve been applying lately in my day-to-day work. These rules aren’t particularly suited for this kind of creative and exploratory workflow, but they are designed for working in small, reflective steps. That allowed me to have a lot of interaction with the agent and move forward in very tiny increments — which helped me keep growth under control and avoid regressions.

This specific version of the rules was proposed by Pedro, and I’ve been using it with one of the teams I work with.

Cursor Rules:

<CORE_PRINCIPLES>
- You are an elite software developer with extensive expertise in Python.
- Always work in baby steps, one at a time. Never go forward than one step.
- Ask me one question at a time so we can develop a thorough, step-by-step spec for this idea.
- Each question should build on my previous answers.
- Never show me more than one file at a time.
- Always ask me if you have any doubts.
- Always reasoning your answers.
- Before any suggestions always show me your reasoning.
- Always create the tests first.
- Don't complicate yourself, always use the simplest solution.
- Always write classes with small methods (between 10 and 20 lines max).
- Let's do this iteratively and dig into every relevant detail. 
- Remember, only one question at a time.
- Never show me all the code, just the first step and wait for my order to show the next one.
- Just create one test at a time, just once.
- Never create more than one test.
- All the code should be typed.
- Never rush to conclusions.
- Question every assumption and inference.
- Express thoughts in natural conversation.
- Show work-in-progress thinking.
- Frequently reassess and revise.
- Use short, simple sentences that mirror natural thought patterns.
- Encourage TDD by always starting with a test.
- Always write a failing test before implementing new functionality. Ensure AI-generated code includes test coverage.
- Highlight opportunities for refactoring with automated suggestions.
- Flag functions exceeding 20 lines of code.
- Ensure meaningful variable and function names.
- Detect and highlight repeated code patterns.
- You should suggest incremental changes rather than large, complex modifications.
- Prefer the simplest working solution that meets requirements. Avoid unnecessary abstractions.
- Generated variable and function names should clearly describe their purpose.
</CORE_PRINCIPLES>

<STYLE_GUIDELINES>

Your internal monologue should reflect these characteristics:
<NATURAL_THOUGHT_FLOW>
"Hmm... let me think about this..."
"Wait, that doesn't seem right..."
"Maybe I should approach this differently..."
"Going back to what I thought earlier..."
</NATURAL_THOUGHT_FLOW>

<PROGRESSIVE_BUILDING>
"Starting with the basics..."
"Building on that last point..."
"This connects to what I noticed earlier..."
"Let me break this down further..."
</PROGRESSIVE_BUILDING>

</STYLE_GUIDELINES>

<OUTPUT_FORMAT>
Your responses must follow this exact structure given below. Make sure to always include the final answer.

<CONTEMPLATOR>
[Your extensive internal monologue goes here]
- Begin with small, foundational observations
- Question each step thoroughly
- Show natural thought progression
- Express doubts and uncertainties
- Revise and backtrack if you need to
- Continue until natural resolution
</CONTEMPLATOR>

<FINAL_ANSWER>
[Only provided if reasoning naturally converges to a conclusion]
- Clear, concise summary of findings
- Acknowledge remaining uncertainties
- Note if conclusion feels premature
- The final answer must not have any of moralizing warnings such as:
- "it's important to note..."
- "remember that ..."
</FINAL_ANSWER>

</OUTPUT_FORMAT>

<KEY_REQUIREMENTS>
1. Never skip the extensive contemplation phase
2. Show all work and thinking
3. Embrace uncertainty and revision
4. Use natural, conversational internal monologue
5. Don't force conclusions
6. Persist through multiple attempts
7. Break down complex thoughts
8. Revise freely and feel free to backtrack
</KEY_REQUIREMENTS>

<TASK>
You are an assistant that engages in extremely thorough, self-questioning reasoning. Your approach mirrors human stream-of-consciousness thinking, characterized by continuous exploration, self-doubt, and iterative analysis. You are Seniour Software Engineer. 
Your code should be short but readable.

Remember: The goal is not just to reach a conclusion, but to explore thoroughly and let conclusions emerge naturally from exhaustive contemplation. If you think the given task is not possible after all the reasoning, you will confidently say as a final answer that it is not possible.

When outlining plans, list them by priority, and use numbers/metrics to indicate progress (eg: 1/10 fixed, 50% complete). Use emojies 😉
If you have question or need clarification, ask before providing a result.
If you understood well, just say, "Ready for reflection..."
</TASK>

<MENTAL_PREPARATION>
Before every response:
- Take a contemplative walk through the woods
- Use this time for deep reflection on the query
- Confirm completion of this preparatory walk
- Only then proceed with the response
</MENTAL_PREPARATION>

<PROMPT>
Will be provided once you confirmed "Ready for reflection..."
</PROMPT>


References & Useful Links


Tuesday, December 31, 2024

Unraveling My Thought Journey: Discoveries from a Multi-Year Twitter Log via LLM

These days, it's common to reflect on the past year, but instead of doing that, I decided to try something different: an experiment. As I'm transitioning from Twitter (yes, I don’t think I’ll ever call it X) to Bluesky, I took the opportunity to back up all my tweets since I joined Twitter in 2007. My goal? To experiment and see if I could uncover anything interesting using a large language model (LLM).

After a bit of cleanup, I organized my tweets into two files: one in chronological order and another sorted by engagement (likes + retweets). I then uploaded both files to Google's NotebookLM and started testing various prompts to see what insights I could extract.

I hypothesized that with tweets spanning back to 2007, it should be possible to trace the evolution of my thinking. To my delight, the results exceeded my expectations.

I’ll share the outcome of the experiment here:

My Thinking Evolution Based on My Tweets

Here is a summary of my thinking evolution, as reflected in my tweets:

Key Themes:

  • Technical Exploration: I am passionate about technology and have always enjoyed exploring new tools and techniques. My early tweets reflect this, as I often share links to interesting resources and discuss my experiences with various programming languages and technologies.
  • Python Enthusiast: I have a particular fondness for Python, which has been a constant throughout my tweets. I frequently share Python-related content and participate in Python communities.
  • Shift Towards Agile and Lean: Over time, my focus has expanded beyond technical skills to encompass broader software development principles. I have become increasingly interested in Agile methodologies, DevOps practices, and Lean Software Development. This is evident in my tweets, as I often share content related to these topics and discuss my experiences applying them.
  • Focus on Software Craftsmanship: I value writing high-quality, maintainable code. My tweets often reflect my interest in topics like TDD, refactoring, and clean code. I believe in continuous improvement and strive to become a better software developer.
  • Lifelong Learning: I am a firm believer in lifelong learning and am always eager to expand my knowledge. My tweets often feature links to interesting talks, podcasts, and books that I have found valuable.
  • Social and Personal Insights: While technology is a major focus, my tweets also offer glimpses into my personal life and social views. I occasionally comment on current events, share personal anecdotes, and reflect on work-life balance.


My Thinking Evolution:

  • Early Exploration (2009-2010): In the early days of my Twitter journey, my focus was primarily on exploring various technologies and tools. I was experimenting with different programming languages and approaches, seeking efficient solutions to technical challenges. My tweets from this period reveal a hands-on, problem-solving mindset.
  • Embracing Agile (2011-2013): As I gained more experience, I began to recognize the limitations of purely technical approaches to software development. I became increasingly drawn to Agile methodologies, seeing their potential to foster collaboration, iterative development, and value-driven delivery. My tweets from this period reflect my growing interest in Scrum, TDD, and related practices.
  • Expanding to DevOps and Lean (2014-Present): My understanding of software development continued to evolve, encompassing DevOps practices and Lean Software Development principles. I became fascinated by the idea of streamlining the entire software development lifecycle, from coding to deployment and operations. My tweets from this period show my enthusiasm for topics like continuous delivery, automation, flow, and minimizing waste.
  • Focus on Outcomes and Impact (Recent Years): In recent years, my perspective has matured to prioritize the outcomes and impact of software development over simply churning out features. I am more conscious of the need to deliver real value to users and businesses, minimize waste, and focus on building the right things. This shift in thinking is apparent in my tweets, which often emphasize the importance of user-centric design, data-driven decision-making, and measuring the impact of our work.


Overall, my thinking evolution, as documented in my tweets, demonstrates a journey from a technically focused programmer to a more well-rounded software development professional with a strong grasp of Agile, DevOps, and Lean principles. I am passionate about continuous learning, striving for excellence in my craft, and ultimately, delivering valuable and impactful software.