Sunday, August 18, 2024

Good talks/podcasts (Aug 2024 I)



These are the best podcasts/talks I've seen/listened to recently:
  • YOW! 2019 Evolutionary Design Animated (Part1) (James Shore) [Agile, Engineering Culture, Evolutionary Design, Software Design, XP] [Duration: 00:24] (⭐⭐⭐⭐⭐) Modern software development welcomes changing requirements, even late in the process, but how can we write our software so that those changes don’t create a mess? Evolutionary design is the key. It’s a technique that emerges from Extreme Programming, the method that brought us test-driven development, merciless refactoring, and continuous integration. James Shore first encountered Extreme Programming and evolutionary design nearly 20 years ago. Initially skeptical, he’s explored its boundaries ever since. In this session, James will share what he’s learned through in-depth animations of real software projects. You’ll see how designs evolve over time and you’ll learn how and when to use evolutionary design for your own projects.
  • Be Quick But Don't Hurry (Joshua Kerievsky) [Agile, Culture, Inspirational] [Duration: 00:02] (⭐⭐⭐⭐⭐) Inspirational. These two minutes summarize very well why it is important to be fast but maintain control in order to be truly agile.
  • YOW! 2019 Evolutionary Design Animated (Part2) (James Shore) [Agile, Engineering Culture, Evolutionary Design, Software Design, XP] [Duration: 00:24] (⭐⭐⭐⭐⭐) The second part of this great talk. Part1 https://www.youtube.com/watch?v=LtBRvsez8DI
  • The best programmer I know (Dan North) [Agile, Engineering Career, Engineering Culture, Inspirational, Nature of Software Development] [Duration: 00:56] (⭐⭐⭐⭐⭐) Very good talk about the nature of software development and the approach to the profession. Dan talks about software as a medium, continuous learning, teamwork, etc. Highly recommended.
  • The Joy of Building Large Scale Systems (Suhail Patel) [Architecture, Engineering Culture, Microservices, Scalability] [Duration: 00:53] Suhail Patel discusses the art and practice of building systems from core principles with a focus on how this can be done in practice within teams and organisations. Very interesting talk with many details about system implementation, taking into account the changes that have occurred in the hardware.
  • Alan Kay at OOPSLA 1997 - The computer revolution hasnt happened yet (Alan Kay) [Engineering Culture, Evolutionary Design, Inspirational, Nature of Software Development, OOP, Software Design] [Duration: 01:04] (⭐⭐⭐⭐⭐) Classic presentation by Alan Kay talks about the nature of software, the design that systems should have, scalability, and how, to some extent, we could compare it to how biological systems work. Many of the ideas behind Smalltalk can be identified in the talk.. Inspirational.
  • Concurrency Oriented Programming in a Modern World (Francesco Cesarini, Robert Virding) [Architecture patterns, Microservices, Scalability, erlang] [Duration: 00:52] This talk delves into how the Erlang concurrency model, initially developed for telecom systems in the 90s, is now vital for modern cloud-based microservices, mobile apps, and machine learning. Presenters Robert and Francesco emphasize how functional languages and fault-tolerant computing principles are essential for distributed multi-core architectures across cloud, edge, and IoT networks.
  • Continuous Integration: That’s Not What They Meant (Clare Sudbery) [CI, Technical Practices, Trunk Based Development, XP] [Duration: 00:56] (⭐⭐⭐⭐⭐) Very good talk about the benefits of using Trunk Based Development (or in other words, the practice of CI as it was originally created).
  • Generative AI in a Nutshell - how to survive and thrive in the age of AI (Henrik Kniberg) [AI, Generative AI] [Duration: 00:17] Simple explanation of what AI is, Generative AI, and how it works at a high level. It's not a detailed explanation, but it's very useful if you want to explain it to someone without a technological background.
Reminder: All of these talks are interesting, even just listening to them.

Related:

Tuesday, July 16, 2024

El coste basal del software

Traduccion del articulo original Basal Cost of Software
Traduccion realizada por https://x.com/simonvlc y publicada originalmente en su genial newsletter Estrategia de Producto: El Coste Basal del Software
---

En mi opinión, el coste de desarrollo de cada funcionalidad en un producto puede dividirse de la siguiente manera:

  1. El coste directo de desarrollo o coste inicial.
  2. Un coste semanal o mensual únicamente asociado con su existencia en el sistema. Haciendo una comparación con la Tasa Metabólica Basal del cuerpo humano, podríamos llamar a este segundo coste la Tasa Metabólica Basal o Coste Basal.

El Coste Basal se compone de dos partes diferenciadas:

  • El impacto directo en la capacidad del equipo debido a la complejidad añadida (nuevas dependencias, más código para entender, más posibilidades de bugs ocultos, etc.).
  • El impacto en el coste de desarrollo o evolución de otras funcionalidades debido a posibles incompatibilidades, acoplamiento, etc.

El coste inicial

Es el coste incurrido por el equipo durante el desarrollo inicial de la funcionalidad. Incluye el coste desde que el equipo comienza a trabajar en la funcionalidad hasta que el cliente la tiene disponible y comienza a usarla. Por supuesto, este proceso debería consistir en múltiples despliegues y lanzamientos parciales para obtener feedback y hacer los ajustes necesarios...

El Coste Basal

Tras el coste inicial de desarrollo, afrontamos también un coste continuo que reduce la capacidad del equipo para el desarrollo de nuevas funcionalidades (innovación).

Este coste, que continúa con el tiempo y solo termina con la eliminación de la funcionalidad o el fin de vida del producto, es el coste que paga el equipo por la existencia de ese código en el producto.

Es importante tener en cuenta que este no se refiere al coste de hacer modificaciones o corregir errores; se refiere al coste de simplemente tener el código allí...

¿Por qué se da este coste? ¿Por qué una funcionalidad que no evoluciona supone un coste? 

  • El equipo tiene que conocer ese código (dónde está, qué dependencias tiene, qué interactúa con él, cómo está diseñado...).
  • El conocimiento y las técnicas del equipo están en constante evolución. Cuando mejora en cualquiera de estos aspectos, deberá actualizar el código.
  • Cuando el equipo diseña una nueva funcionalidad, el código debe ser diseñado de manera que sea compatible con todas las anteriores y no genere problemas o regresiones. Y, por supuesto, este coste es proporcional a todas las que ya teníamos en el sistema.
  • Algo similar ocurre cuando un nuevo miembro se une al equipo. Este nuevo integrante debe aprender sobre todas las funcionalidades, por lo que el coste es proporcional a su número..

Y lo peor de todo, este sobrecoste es continuo hasta la "muerte" de la funcionalidad. Por ejemplo, hasta el fin de la vida del producto, hasta que ningún usuario la use, o hasta el fin del mundo (lo que ocurra primero).

Evolución del coste de una funcionalidad

Como hemos visto, el Coste Basal de una funcionalidad es más o menos constante durante su vida. Pero cada lenguaje, dependencia o tecnología que hayamos usado en su desarrollo, puede alcanzar un punto en el que ya no es utilizable por cualquier razón (dependencias obsoletas, problemas de seguridad, evolución normal que deprecia la versión del lenguaje que usamos, etc.). Desde este momento, el coste puede dispararse porque estamos obligados a actualizarlo, incluso aunque no queramos evolucionar la funcionalidad.

En resumen, el coste real puede ser representado por el siguiente gráfico:

El problema

Un error común es negar el Coste Basal y considerar que si no se hacen cambios, el coste de mantenimiento de una funcionalidad es cero. Supongo que esto proviene de utilizar metáforas de otras profesiones como la construcción, que no son aplicables al desarrollo de software.


La capacidad no es infinita

A pesar de que la capacidad de un equipo cambia (positiva o negativamente) con el paso del tiempo debido a distintos factores (conocimiento del negocio, técnicas, estrés…), esta es finita.

El Coste Basal acumulado de todas las funcionalidades de las que el equipo es responsable reduce la capacidad disponible para desarrollar nuevas funcionalidades (innovar).

Con el tiempo, podemos observar como la capacidad del equipo para innovar decae rápidamente:

Hasta el punto de que cuando la capacidad se agota, el equipo se encuentra en una situación en la que le es imposible innovar porque pasa todo su tiempo “manteniendo” las funcionalidades de las que es responsable.


O bien reduce su velocidad al desarrollar nuevas funcionalidades, acumulando coste basal, pero a menor ritmo.

Conclusiones

Las secciones anteriores destacan varios principios que debemos considerar para mejorar la eficiencia de nuestros equipos de producto.

  • Debemos minimizar el Coste Basal tanto como sea posible, logrando el impacto deseado con la menor cantidad de código posible.
    • Cuando sea posible, incluso sin añadirlo.
    • Iterando la funcionalidad, partiendo de una solución mínima, para adaptarla lo más posible a las necesidades del usuario.
    • Haciendo el software/diseño lo más simple posible. Debe ser fácil de evolucionar sin sobreingeniería (YAGNI).
  • Debemos eliminar cualquier funcionalidad del producto que no tenga el impacto deseado, eliminando así su Coste Basal.
  • Debemos monitorizar continuamente el código del equipo para detectar la obsolescencia de dependencias, lenguajes y tecnologías para evitar que el Coste Basal se dispare.

Veamos un ejemplo teórico sencillo para ayudar a transmitir el mensaje. Supongamos que un equipo acumula un 1% de Coste Basal por semana, lo que significa que, por cada 100 unidades de producto (código, características, mejoras), añade 1 unidad de Coste Basal. A medida que este se acumula, en 52 semanas (aproximadamente 1 año), el equipo solo podrá dedicar el 60% de su capacidad a nuevas funcionalidades/mejoras (innovación). En dos años, será apenas el 35%. Por supuesto, este ejemplo es una simplificación, pero destaca que negar este coste no es una opción viable.


Recordad: La Simplicidad — el arte de maximizar el trabajo no hecho — es esencial.

Relacionado y referencias

Otros artículos interesantes


Monday, July 01, 2024

Decidir lo más tarde Posible: Limites de Producto

Como mencionamos en el artículo anterior de la serie sobre Lean Software Development, continuaremos explorando técnicas que nos permiten tomar decisiones lo más tarde posible.

Empezamos por definir sistemáticamente Limites de Producto.

Al desarrollar un incremento de la solución que estamos implementando, es fundamental establecer límites concretos en todos los parámetros que puedan introducir complejidad. Esto nos permite centrarnos en lo que aporta valor ahora y posponer soluciones más sofisticadas, evitando el coste y la complejidad añadida. Con el tiempo, esos límites irán cambiando y nos obligarán a modificar la solución, pero con esta aproximación podremos decidir cada solución lo más tarde posible y evitar hasta ese momento el coste de desarrollo y evolución de esa solución más compleja.


Es crucial que al definir un límite, este se incluya en el código o solución para que, si se supera, la aplicación se comporte de manera controlada, avisando al equipo y, posiblemente, al usuario.


Ejemplos de límites que he usado:

  • Número de clientes/usuarios totales.
  • Número de clientes/usuarios concurrentes.
  • Tamaño máximo de ficheros que se pueden subir al sistema.
  • Cuotas por usuario (almacenamiento, requests, número de entidades, etc.).
  • Valores numéricos por cualquier concepto de negocio (del problema).
  • Tiempos de respuesta a las distintas peticiones.
  • Resoluciones/Dispositivos para la IU

Si no definimos claramente estos límites de manera numérica, estamos abriendo la puerta al diseño especulativo para resolver situaciones que aún no enfrentamos.


Ejemplos de uso de límites para "decidir lo más tarde posible"

Conocer y definir el número total de clientes en varias ocasiones me ha permitido ofrecer soluciones muy sencillas para la persistencia, que nos han sido útiles durante meses antes de necesitar cambios. Por ejemplo, si el número de usuarios es pequeño, usar persistencia en fichero y cargar la información a memoria es factible. También nos permite usar soluciones tipo SQLite y posponer la decisión de meter un motor de BD independiente.



Al limitar el tamaño de las solicitudes (en cuanto a volumen) y definir los escenarios de conexión, podemos ofrecer soluciones robustas y simples, procesando las solicitudes de forma síncrona. Esto permite posponer la necesidad de tener una solución asíncrona. Como ejemplo, en una ocasión teníamos que aceptar que el usuario subiese ficheros al sistema; la primera implementación solo permitía ficheros muy pequeños, esto nos permitió hacer una implementación sencilla y tener feedback muy rápido (menos de 2 días). Pasadas unas semanas, una vez que vimos que tenía sentido la funcionalidad, mejoramos la implementación para soportar ficheros más grandes.


En varias ocasiones hemos enfrentado situaciones donde cada usuario acumulaba almacenamiento (archivos/objetos). En estos casos, el definir un límite de producto para el total de almacenamiento para todos los usuarios, otro límite para cada usuario y otro límite para definir cuándo teníamos que empezar a preocuparnos por este problema, nos ayudó a posponer la implementación de cualquier medida de control y gestión de este almacenamiento hasta que se llegase a cualquiera de los límites definidos.



Para ilustrar el uso sistemático de estos límites con un ejemplo concreto, en Alea Soluciones lanzamos un nuevo producto de gestión/control de una red de fibra y los routers de los clientes finales en menos de 3 meses. Sabíamos que nuestros clientes en ese momento no tenían más de 1000-2000 usuarios. Sabíamos que los operadores del sistema de gestión no eran más de 2-3 personas concurrentes. Sabíamos que para tener más usuarios nuestros clientes tenían en muchos casos que desplegar fibra hasta la casa o al menos ir a la casa del usuario a instalar el router; esto implicaba que no era posible que creciesen a más de 2-5 usuarios por semana. Con este contexto, la primera versión del sistema inicial de gestión era un servidor web que procesaba todo de forma síncrona y con la información de usuarios en memoria y que simplemente persistía los cambios a un fichero cuando se necesitaba. Esto permitió que dedicásemos mucho tiempo adicional al resto de componentes del sistema (integración con cabeceras de fibra, sistema de configuración remota de los routers, sistema de monitorización de los routers, etc.). Evidentemente, el sistema fue evolucionando y fuimos mejorando ese sistema, pero siempre esperando al último momento responsable para cada decisión de introducir nueva tecnología.


Otro ejemplo sencillo es en ClarityAI, donde para crear un chat ops en Slack que ofreciera algunas capacidades internas de la plataforma, se definieron ciertos límites, tanto en tiempos de respuesta máximos como en volumen de información tratada. Al definir un tiempo de respuesta máximo alto (2s) pero menor del soportado por slack para la respuesta sincrona a comandos de slack (3s), pudimos posponer bastante tiempo la implementación de una solución asincrona. Esa aplicación trabaja con información de inventario de componentes técnicos (repositorios de código, repositorios docker, etc) y de equipos y personas. Vimos que nos era fácil definir unos maximos para cada uno de los elementos y vimos que en todos los casos el máximo estaba por debajo de 1000 elementos. Estos limites nos permitieron evitar bastante complejidad, simplemente apoyandonos en un backend NoCode (Airtable) como BD, que además nos proporcionaba un frontal de administración básico. Sabemos perfectamente que cuando superemos esos limites, tendremos que pensar en una solución más sofisticada y escalable, pero posponer esa decisión nos ha permitido desarrollar muy rápido en esa aplicación durante más de dos años y medio.


Recursos Relacionados:

Sunday, June 30, 2024

Lean Software Development: Decidir lo Más Tarde Posible

Lean Software Development parte de la premisa de que el desarrollo de software y productos digitales es un ejercicio constante de aprendizaje (ver amplificar el aprendizaje). Con esta premisa, es claro que cuanta más información tengamos antes de tomar una decisión, mayor será la calidad de la misma. Por tanto, decidir lo más tarde posible nos permite ser más efectivos. Al mismo tiempo existe un coste (o riesgo) asociado a no tomar una decisión que crece con el tiempo, por lo que debemos buscar el “último momento responsable”, que es el punto óptimo para tomar la decisión con la mayor información posible, sin que el coste de no tomarla supere el beneficio potencial de obtener más información y seguir postergándola.

Ventajas posponer decisiones y mantener opciones abiertas

Postergar decisiones es una estrategia fundamental en el desarrollo de software lean. Aunque no siempre es fácil y requiere práctica, esta táctica permite la creación de productos sostenibles y fáciles de evolucionar. Una de las principales ventajas de tomar decisiones más tarde es que nos brinda una mejor comprensión tanto del negocio como de la tecnología, lo que a su vez facilita la toma de decisiones más informadas y acertadas.

Además, retrasar decisiones conduce a soluciones más simples y pequeñas, lo cual reduce el esfuerzo necesario para implementarlas y evita el sobrecoste basal. Manteniendo nuestras opciones abiertas, nos enfocamos únicamente en lo que aporta valor real en el presente, evitando la sobre-ingeniería y el trabajo innecesario. Esta flexibilidad nos permite reaccionar de manera rápida y efectiva a cualquier cambio sin temor, lo que es crucial en un entorno tan dinámico como el desarrollo de software.

Algunas de las ventajas específicas de posponer decisiones incluyen:

  • Menos trabajo y desperdicio: Implementar solo lo necesario en el momento reduce el trabajo total y el desperdicio.
  • Menor esfuerzo en rehacer trabajo: Si es necesario cambiar algo, hay menos esfuerzo requerido ya que se ha evitado sobre-ingeniería.
  • Mayor flexibilidad y adaptabilidad: Mantener opciones abiertas nos permite adaptarnos rápidamente a nuevos requerimientos o cambios en el entorno.


Una arquitectura bien diseñada permite posponer decisiones importantes sin comprometer la calidad o la flexibilidad del producto. Esto no solo nos da la capacidad de tomar decisiones más informadas, sino que también facilita la creación de una buena arquitectura que, a su vez, permite posponer otras decisiones críticas en el futuro. En resumen, esta estrategia nos permite movernos con menos carga y mayor agilidad, mejorando nuestra capacidad para entregar valor continuamente.

Decisiones

Mis equipos son equipos empoderados (extremo a extremo) que tienen completa decision sobre como solucionar las problemas y en muchos casos incluso en decidir qué problemas solucionar. En un equipo así, se toman numerosas decisiones todos los días y de todo tipo. Decisiones sobre el problema, sobre potenciales soluciones, sobre implementaciones de esas soluciones, sobre la priorizacion relativa de dónde invertir (en solventar algo, en despejar incertidumbre sobre una decisión técnica o de producto, en decidir siguientes incrementos, etc).

Por ejemplo, al comenzar un nuevo producto o funcionalidad, el nivel de incertidumbre suele ser muy alto. En estos casos, priorizamos reducir esa incertidumbre, dividiendo decisiones y suposiciones críticas en partes más pequeñas, e invirtiendo en incrementos de producto para obtener más feedback y así reducir la incertidumbre.


A medida que recibimos retroalimentación, aprendemos más y ajustamos nuestras suposiciones/hipótesis en consecuencia. Este proceso iterativo nos ayuda a pasar de una gran incertidumbre a una mayor claridad y confianza.

Al validar continuamente los supuestos, reducimos los riesgos y tomamos decisiones más informadas. 

Todas estas decisiones se producen de forma continua y a todos los niveles. Es parte de la naturaleza de nuestra profesión.

Si tuviesemos que clasificar el tipo de decisiones a tomar, lo podriamos hacer de la siguiente forma:

  • Decisiones sobre el problema y oportunidad a explorar
    • Qué: Donde debemos invertir y por qué
    • Cómo: Qué solución o estrategia de implementación creemos que es adecuada para el caso seleccionado.
  • Decisiones sobre tecnologia e implementacion
    • A nivel de arquitectura y tecnologias usadas
    • A nivel de diseño de la solución a diferentes niveles

Es importante tener en cuenta que las decisiones nunca son independientes; siempre deben considerar el contexto y el conocimiento del equipo, así como el sistema o producto existente. Es como si viéramos los problemas y oportunidades como la diferencia en conocimiento o implementación, de lo que conocemos o tenemos y lo que queremos conseguir.

Es decir, que siempre se trata de hacer pequeños incrementos en la dirección y con el objetivo que necesitemos. A veces se trata de aportar valor al usuario, otras veces de reducir la incertidumbre de una decisión que debemos tomar. Siempre, es un incremento en el software (producto/sistema) o en el conocimiento que tiene el equipo.

Dividir decisiones para abordarlas individualmente

Una de las formas principales de incrementar nuestras posibilidades de posponer decisiones es dividir grandes decisiones en pequeñas decisiones y separar muy bien aquellas de facil reversibilidad de las que sean irreversibles (o muy dificiles de revertir).

Por lo tanto, trabajar decidiendo lo más tarde posible implica hacerlo en pasos o incrementos muy pequeños, lo cual está totalmente alineado con la mentalidad Lean (trabajo en lotes pequeños).

Este proceso de dividir es una práctica continua que permite al equipo afrontar desde donde va a invertir en el siguiente cuatrimestre hasta decidir qué es lo que va a hacer en las siguientes horas. Debemos verlo como una forma de pensar sistemática que se aplica de manera recurrente a todos los niveles.

Conclusiones

Decidir lo más tarde posible es una estrategia clave en el Lean Software Development que maximiza la calidad de las decisiones al aprovechar la mayor cantidad de información disponible. Esta práctica contribuye a la creación de productos más sostenibles, flexibles y adaptables.

Las principales ventajas son:

  • Reducción del desperdicio: Enfocarse solo en lo necesario minimiza el trabajo redundante y evita la sobre-ingeniería.
  • Mayor flexibilidad: Mantener opciones abiertas permite adaptarse rápidamente a cambios en el entorno o requisitos.
  • Decisiones más informadas: Postergar decisiones hasta el último momento responsable resulta en decisiones más acertadas y efectivas.
  • Incremento en la adaptabilidad: Facilita la implementación de soluciones simples y pequeñas, reduciendo el esfuerzo para realizar cambios y mejoras.

Es fundamental que los equipos empoderados adopten esta mentalidad y se acostumbren a dividir las decisiones en pasos manejables. Al hacerlo, pueden reducir la incertidumbre de manera incremental, validando continuamente sus supuestos y ajustando estrategias según el feedback recibido. Este enfoque iterativo disminuye riesgos y refuerza la capacidad del equipo para entregar valor continuo.

En los siguientes artículos, exploraremos estrategias y ejemplos concretos sobre cómo posponer decisiones de manera efectiva, proporcionando herramientas prácticas para implementar esta filosofía en tus proyectos de desarrollo de software.


Recursos relacionados


Sunday, June 23, 2024

Good talks/podcasts (June 2024 I)




These are the best podcasts/talks I've seen/listened to recently:
  • (Spanish) Impulsando el crecimiento organizacional a través de la Developer Experience (Angélica Lozano, José Rodríguez Huerta) [Devex, Engineering Culture] [Duration: 01:07] Interesante conversación sobre el funcionamiento y organización de MLean con Angélica CTO y fundadora. Interesantes ideas sobre como conectar la experiencia de desarrollo con las necesidades de la compañia, la organizacion de equipos, las particularidades del negocio, como balancean las distintas inversiones en ingenieria. 
  • Software Engineering F&*K Up Behind The Passport E-gate Failure" (Dave Farley) [Architecture, Resilience, Software Design] [Duration: 00:17] In this episode, Dave Farley talks about the issue, how poor software engineering led to this, how distributed systems come into it all, how to avoid something like this happening again and at the end of the video asks for answers on some concerning issues around the whole story.
  • Perspectives on effective product development culture (Jason Yip) [Culture, Engineering Culture, Product] [Duration: 00:39] Good description of what an efficient product development culture should be like. Jason talks about the pillars of that culture, the guiding principles, and even the main practices, including technical, product, and organizational practices.
  • Lean Code (Kevin Kelly) [Agile, Lean Software Development] [Duration: 01:05] (⭐⭐⭐⭐⭐) Interesting approach to lean software development focused on how to tackle agile development with this lean mindset. It contains a few interesting points.
  • Designing for Habitability (Sam Newman) [Platform, Platform engineering] [Duration: 00:27] Tips and tricks for creating platforms that truly accelerate the pace of innovation in companies.
  • Fighting User Requirements That CONSTANTLY Change (Dave Farley) [Nature of Software Development, Product] [Duration: 00:13] (⭐⭐⭐⭐⭐) Great description of the true nature of software development as an adaptive learning process of customer needs. A must.
  • Continuous Delivery vs. Gitflow & CD At Scale | Bryan Finster In The Engineering Room Ep. 11 (Bryan Finster, Dave Farley) [Continuous Delivery, Engineering Culture, Engineering Scalability] [Duration: 01:09] Interesting conversation about how to introduce CD as part of the engineering strategy in large companies and how having CD as part of the vision (not as a goal in itself) can generate a healthy engineering culture that continuously improves.
Reminder: All of these talks are interesting, even just listening to them.

Related:

Sunday, June 02, 2024

Talk slides / Lean Software Development: A more effective Agile

Yesterday, I had the pleasure of giving a talk at https://eventos.paradigmadigital.com/agile-real-life. My talk "Lean Software Development: A more effective Agile" explained how the teams I have been part of work. We use a combination of Lean Software Development, with Lean Product Development, and Extreme Programming.

I leave the presentation slides here in case someone might find it useful:

Link to the original doc

Related content and references: