1 on 1: Reflexiones sobre dedicarle tiempo a escuchar a tu equipo :)

Cuando hablamos de programar y de trabajar en desarrollo de software, es muy muy fácil pensar en la necesidad de aprender y perfeccionarse en los conocimientos y herramientas técnicas. Pero una parte que no es tan “natural” pensar, y que a la mayoría nos cuesta desarrollar, es la parte más “humana”.

Sin embargo, los/as mejores developers y managers que conozco, no son sólo (muy) buenos/as en lo técnico, sino que además tienen MUY entrenadas y desarrolladas habilidades que se suelen considerar “blandas”. De todas esas, la que a mí me parece fundamental, y la que más vengo entrenando últimamente, es la habilidad de escuchar.

Mucho de lo que hacemos como líderes es un trabajo de ayudar a organizar las tareas de manera que podamos cumplir con los objetivos de todas las partes. Esto implica escuchar, entender y muchas veces negociar alcances y tiempos con otros equipos de IT, de producto… pero también con los intereses y capacidades de cada uno de los miembros del equipo.

¿Cómo encontrar el balance entre “lo que hay que hacer” y “lo que nos gusta/tenemos ganas de hacer”? Sobre todo, cuando “lo que nos gusta hacer” varía de persona a persona y cada quien en momentos específicos de su vida/carrera profesional está más entusiasmado/a por alguna cosa en particular…

1 on 1 – ¿y eso con qué se come?

Son reuniones periódicas con cada miembro del equipo individualmente. La clave de este tipo de reuniones es que el/la líder no tendría que hablar mucho, todo lo contrario: es una oportunidad para ejercitar el skill de escuchar y prestar atención a lo que cada persona nos cuenta. ¿Por qué? Porque no es una instancia de catch-up operativo, si no que tiene dos objetivos:

  • conexión humana (porque nuestra vida personal influye en nuestro trabajo, y porque trabajamos con personas)
  • que la otra persona tenga una instancia para charlar privadamente con nosotros lo que sea que necesite discutir.

Son reuniones que pueden darse con los reportes directos o, en caso de los managers de managers, con los reportes indirectos (es decir, los reportes de sus reportes, o skip-level).

¿Más reuniones? ¡No de nuevo decía! ¿por qué tener 1 on 1s?

En mi experiencia personal, me parece que este tipo de reuniones es un gran termómetro de cómo se va sintiendo cada uno, y de si las expectativas que tenemos están alineadas (y los por qué).

Por ejemplo, si yo pienso:
“X viene desempeñando muy bien, y además está mostrando aptitudes para ser Tech Lead más adelante en su carrera”.
Podría tomar una “decisión ejecutiva” y darle tareas que trabajen esos aspectos de liderazgo que me parecen adecuados… pero la otra persona podría querer tener desafíos puramente técnicos, y no sentirse cómoda con esas tareas; incluso hasta sentirse frustrada. La 1 on 1 es EL momento (para mí) de investigar cuál es la proyección de carrera de la otra persona, a través de preguntas del tipo “¿te gustan las tareas que llevas haciendo?” “¿te ves más haciendo cosas técnicas?” “¿te gustaría ser líder de un equipo?”.

Seguro charlamos con cada uno/a en las instancias de feedback de desempeño, pero no debería ser el único momento. Por un lado, porque la instancia de feedback es más hablar “con el diario del lunes”: qué cosas de las esperadas se cumplieron y cuales no. Es una conversación muy puntual, y en algún punto más “vertical”, en el sentido de que es el/la líder quien comunica una evaluación sobre una persona, y es mucho menos debatible que otra instancia de feedback.

Por otro lado, y relacionado con lo anterior, deberíamos dedicarle tiempo específicamente a charlar con nuestra gente sobre cómo se sienten en su vida personal, con el trabajo que hacen, con su desarrollo de carrera… en una palabra, ¡conocerles! Trabajamos con personas, y es importante cuidarlas y que realmente sepan que nos preocupamos no solo por el delivery, sino porque estén contentos/as con su trabajo y por cómo están por fuera de él.

Tomarnos ese tiempo tiene como beneficio adicional que nos llevamos información valiosísima para a) construir, replantear y consensuar objetivos más “on point” con cada uno/a, y b) reconocer aquellas cosas que van por el buen camino, y anticipar y corregir conductas/actitudes que podrían alejarnos de esos objetivos que pusimos. Si somos prolijos y tenemos estas reuniones con una periodicidad razonable, cuando llegamos a la instancia de feedback de desempeño, no debería haber ninguna sorpresa (para ninguna de las dos partes). Y no sólo eso: la otra persona tiene un sentido de “progresión” en su desarrollo profesional mucho más firme, ya que no solo puede preguntar y recibir feedback sobre cómo está y hacia dónde quiere ir, sino que las metas a alcanzar se ven menos lejanas.

¿Con cuánta frecuencia deberíamos juntarnos?

Bueno, eso depende mucho de cada par de personas 🙂 , y del contexto general de la empresa. Camille Fournier en su libro “The Manager’s Path“, deja 5 tips:

  • ¿Qué tan frecuentemente hablás con la otra persona?

Con mi equipo hablamos casi todos los días, desde “vamos a tener que cambiar esta api” a “¿che, hacemos un pedido de helado?” 😛 Entonces, a mí me funciona tener reuniones un poco más espaciadas (trato de tener al menos 2 por semestre). Si por algún motivo detecto que necesitamos más, tenemos más. Me gusta ser flexible al respecto de la frecuencia y la voy trabajando con cada uno, ya que la idea es que sea un espacio que ambos sintamos que nos sirve y no una obligación.
Si bien está bueno alentar al equipo a que busquen instancias de 1 on 1 ellos, aparte de las que agende uno/a como líder, no vale decir “no tuvimos 1 on 1 porque ellos no me buscaron”. Es normal que la mayor parte del tiempo no sientan que tengan algo particular que decirnos y no busquen tener este tipo de reuniones; pero nosotros sí deberíamos buscarlas.

  • ¿Qué tanto coaching necesita la otra persona?

Básicamente, cuando una persona es nueva en el equipo, probablemente necesita que un senior o el líder esté un poco más encima. O por ahí alguien más senior con alguna tarea compleja también necesita más atención.
Yo acá le pongo una lupa a lo que dice Camille, en cuanto a que se puede dividir la parte operativa de adaptarte a un nuevo equipo/tarea compleja y la parte más “personal” del asunto (¿cómo se siente con esos desafíos? ¿está incómodo/a? ¿le estresa, le preocupa?). La parte operativa es más straight-forward de manejar, la parte personal quizás no: ahí podría ser valiosa una 1 on 1 con esa persona, independientemente de su nivel de coaching (puede ser que alguna situación de índole más personal esté afectándole, o que esté estresado/a, y eso no se correlaciona directamente con la autonomía, la tarea en sí misma, o el seniority).

  • ¿Cuánta información te da esa persona?

Esto apunta a gente que quizás le cuesta más dar visibilidad con el/la líder sobre lo que está haciendo, y quizás una charla más personal le ayude (o le ayude al líder a entender por qué).

  • ¿Qué tan buena relación tenés con la persona?

“Buena relación” entendida como “no hay problemas serios”. Muchas veces tendemos a dedicar tiempo y atención a las relaciones problemáticas dentro de nuestro equipo (razonable, ya que seguramente necesitan que estemos encima); pero descuidamos las que no lo son, entendiendo que “está todo bien”, “hablamos seguido”, etc. Me sumo al consejo de Camille de no dar por sentado que “no hay problema manifiesto” equivale a “está todo bien”: no sólo porque podría haber un problema no explícito, sino porque como decía antes, es importante que la otra persona sepa que estamos preocupados y ocupados por que siga estando lo mejor posible.

  • ¿Qué tan estable/inestable está el contexto de la empresa?

Cuanto más inestable el contexto, más hay que estar atento a la necesidad de tener 1 on 1s más frecuentes, porque la incertidumbre de lo que va a pasar puede generar malestar y/o dudas que como líder querés evacuar y ayudar a tranquilizar. No hay ningún secreto sobre este punto: tenemos que acompañar a nuestra gente y tratar que estén lo más tranquilos/as y seguros/as posible.

Contenido: ¿Cómo es una 1 on 1?

ANTES: ¿cómo preparar la reunión?

Si bien te podrías reunir tomándote un café en el pasillo, me parece que dice un montón sobre la importancia que le das y que predispone mejor a una charla más abierta si la reunión se da en una sala cerrada en la que haya clima de “confidencialidad” entre las partes. Otra cosa que hago es aclarar que me llevo la computadora a la reunión para tomar nota sobre lo que estamos charlando, en caso de tener temas para escalar o accionables a trabajar. Un consejo acá: tratá (sé que es difícil) de desconectarse de los chats y mails antes de empezar (y obviamente durante) la reunión. Fundamentalmente por respeto a la otra persona, pero también porque escuchar con la atención completamente puesta en lo que nos están contando nos ayuda a detectar cosas que quizás en el día a día no vamos a percibir sobre el clima del equipo en general, la empresa, y la persona en particular.

DURANTE: ¿qué charlamos en la reunión?

Cada líder encuentra su manera de llevar la reunión… hay algunos/as que prefieren tener de antemano una lista priorizada de temas a conversar, otros/as prefieren que sea más como un catch-up en el que la otra persona te cuenta lo que quiera, y se va armando desde ahí. Otros/as las usan como instancias de seguimiento. Yo coincido nuevamente con Camille (les juro que esto lo pensaba antes de leer el libro) que está bueno hacer un mix de todo eso, y además dejar un espacio para hablar de la persona.

A mí me gusta arrancar mis 1 o 1s preguntando ¿cómo estás? 🙂 y me gusta ser relajada respecto al contenido de la reunión, porque me parece que propicia una charla más abierta.

En cuanto a la conversación en si misma, a veces la otra persona trae temas puntuales, y en ese caso es es lo primero que hay que escuchar. Una vez agotados esos temas, o si no hay temas particulares, podemos nosotros hacer algunas preguntas para disparar la charla.
Friendly reminder: el foco no está en el líder, sino en la otra persona.

Algunas preguntas que se me ocurren:

  • operativas: ¿qué dificultades estás teniendo? ¿las pudiste resolver? ¿qué te parece la dinámica del equipo? ¿te gustan/te sentís cómodo/a con los problemas que estás resolviendo? ¿te sentís agobiado/a con las tareas que tenés?
  • “personales”: ¿cómo estás? ¿cómo va la familia? ¿cómo fue el fin de semana? ¿qué tal las vacaciones? (acá depende de cuánto conozcas o te cuente la persona, claramente).
  • career path: ¿te gustan más los desafíos técnicos? ¿te gustaría ser líder? ¿conocés los esperados de tu rol? ¿cómo sentís que estás respecto de esos esperados? ¿sentís que tenés todas las herramientas para cumplir con los esperados de tu rol? ¿cómo te ves en 1 año?¿Cuál es tu próximo paso? ¿cómo te puedo ayudar?
  • otras: ¿te interesa aprender algún tema/tecnología en particular? ¿hay alguna situación que te genere incertidumbre/dudas? ¿sentís que podés organizar bien tu tiempo en el trabajo? ¿querés que tengamos estas reuniones más seguido?
  • EDIT: Para alguien nuevo en el rol (me sugieren desde una posición más senior que la mía): ¿en qué estás usando tu tiempo? ¿Hay algo que no estés haciendo y creés que deberías? ¿Cómo te puedo ayudar?

Otra cosa que me funciona es pensar en los feedbacks y las charlas con líderes/referentes que he tenido yo que me han servido y enseñado: pensar qué cosas me dijeron o me preguntaron esas personas que me ayudaron a crecer y a destrabar problemas.
En mi caso, lo que valoro la honestidad: sobre lo que hago bien, lo que puedo hacer mejor, cosas del big picture que me pasé por alto, etc. También valoro la disposición a escucharme sin tratar de imponer una idea, y la voluntad de trabajar sobre las cosas que me generan incomodidad o no me ayudan a desarrollarme (trabajar conmigo puntualmente, pero también con la organización si hace falta). En el plano no laboral, valoro el preguntar cómo van mis cosas, el alegrarse con mis buenas noticias y acompañarme en las malas.

Todas esas cosas son las que trato de aplicar, porque me parecen importantes. Ciertamente no está escrito en piedra, y se aprende mucho con cada reunión y cada persona. Pero creo que está bueno, sobre todo cuando sos nuevo/a en el rol, buscar en la propia experiencia las cosas que te ayudaron y las que no; y siempre recordar que cuando eso falla, podés recurrir a la experiencia de otres: tu managers, referentes de tu área u otras áreas, etc.

DESPUES: ¿qué nos deja la reunión?

Sea cual sea el estilo de la reunión, está bueno que compartamos las notas que tomamos con la otra persona. Ciertamente si hablamos de objetivos y desarrollo de carrera es clave que esté escrito que “esto es lo que consensuamos que tenemos que trabajar”; pero aplica en general: es un buen registro de cuándo nos juntamos, qué se dijo, en qué contexto (y nos sirve para retomar y evaluar cómo estamos respecto a lo charlado).

Expected Profit

Mi expectativa general sobre este tipo de reuniones es principalmente conocer a las personas con las que trabajo. En segundo lugar, es tratar de identificar “a tiempo” situaciones “negativas” con personas puntuales, con el equipo y la empresa, para proactivamente mitigarlas o evitarlas. A veces la información llega tarde y no queda otra que ser reactivo/a, pero trato de que eso pase solamente cuando no puedo evitarlo.

Algo que uno/a claramente no espera que pase, pero debería estar preparado/a para manejar, son las conversaciones difíciles. En este tipo de reuniones me parece que se puede dar más que se toquen temas “ásperos”. Lo importante es tratar que la otra persona no se sienta atacada, y más que nunca de nuestro lado estar dispuestos a escuchar y no intentar imponer una idea. Creo que es clave ser siempre transparentes sobre como funcionan los mecanismos de la organización, los esperados de cada rol, las expectativas puntuales como líderes, y qué cosas podemos/estamos dispuestos a hacer para colaborar a resolver el tema en cuestión; con cuidado de no prometer imposibles ni generar expectativas erróneas.

¿Y por casa como andamos?

Yo puse en práctica las 1 on 1 unos meses después de arrancar como líder del equipo. En ese momento, yo tenía bastante idea de cómo ser developer y como ser referente de una historia, pero tenía muy poca idea de como ser referente de un equipo de personas. Tenía muy presente el hecho de que como líder es mi responsabilidad que mi equipo crezca, y me aterraba pensar que cualquier decisión mal tomada podía arruinar sus posibilidades de crecimiento. Tenía también una idea bastante equivocada de que como la gente era tan nueva en el equipo como yo en el puesto, los compromisos “heredados” convenía que los siguiera yo, mientras ellos se adaptaban.
Grave error, porque yo terminé con un desgaste mental mucho más grande de lo que valía la pena, y porque además ni de casualidad las historias salieron tan bien como podrían haber salido.

Aprendí mucho de esas “malas” decisiones iniciales: me di cuenta de que además de tener muy en claro “qué tenemos que hacer” (los compromisos puntuales con producto y el negocio, la deuda técnica, etc), necesitaba tener lo más en claro posible quién era cada uno, que esperaban de mí, del equipo, de ellos mismos, de sus carreras. También, dejar explícito qué cosas son las que esperamos (como organización) y qué cosas espero yo (como líder); para trabajar en conjunto a dónde queremos llegar y cómo.

Las 1 on 1 se volvieron mis grandes aliadas: charlar con cada uno sobre como estaban me fue dando herramientas para tomar acciones concretas: asignar tareas específicas, agendar reuniones para revisar el plan de carrera, escalar inquietudes, aclarar comunicaciones organizacionales, generar buenos vínculos y construir un buen clima de equipo. Conocerlos me hizo no solo poder establecer expectativas y objetivos adecuados para cada quien, sino que además me hizo crecer muchísimo como líder: me ayuda a entender los tiempos que manejamos, a asignar mejor las tareas y los desafíos, a delegar más eficaz y eficientemente.

Todo eso hace que el equipo se sienta más a gusto con las cosas que están haciendo, y crezcan en ownership y autonomía. Así, puedo enfocarme en ser “facilitadora”, en generar el contexto adecuado para que ellos puedan seguir creciendo. Y en lo personal, siento que generamos la confianza para charlar de cosas laborales pero también de la vida, en las que podemos también aprender unos de otros y acompañarnos (cada uno en la medida que le sale, por supuesto).

Como todo en la vida, uno no nace sabiendo cómo hacer este trabajo, y nos equivocamos un montón. Lo importante es entender y no olvidar que la manera de lograr equipos y soluciones potentes es integrando a las personas con las que trabajamos. El liderazgo que queremos construir no es la idea de “project manager” que establece deadlines y objetivos que cumplir y ya. Queremos preparar líderes que sepan trabajar en equipo, integrando y potenciando a cada persona para que en conjunto podamos pensar y llevar adelante soluciones cada vez mejores.

Y para eso, necesitamos prestar atención y escuchar. Hacerlo bien lleva tiempo, pero creo que los beneficios son enormes, y que vale mucho la pena.

Las ventajas de ser Programador/a

Todas las primeras clases empezamos con la misma pregunta. LA pregunta. Esa que nos hacen todos y nunca podemos contestar “fácil”…

Chiques, ¿qué es programar?

Parece ser que hay un cierto clima de “misterio” y “solemnidad” alrededor de la programación, como si fuéramos les magos y brujas del mundo antiguo. Por suerte para nosotres, programar no es considerado un arte oscura por la cuál morir quemades, pero ciertamente se mantiene el miedo a lo desconocido:

– Qué estudia tu hija?

– Ingeniería en Sistemas

– FAAAAAA y es difícil eso no? Qué hace exactamente?

– Y… si… es difícil…. Exactamente no sé, pero trabaja con computadoras…

Esto me lo ha contado mi madre pero pregúntenle a cualquier estudiante de sistemas, y van a ver que les pasó mil veces: reemplacen “Ingeniería en Sistemas” por cualquier otra carrera relacionada, y voilá.

Como que hay una idea mal dimensionada sobre lo que hacemos, lo que nos cuesta, quién puede hacerlo. Lo cual genera, por un lado, que muches de les que trabajan en el ramo se sientan Tony Stark (o sea, acreedores de un conocimiento superior, que nadie más puede tener o entender) y por otro, que mucha gente que podría estar haciendo esto ni siquiera se asome a ver de qué se trata.

Entonces, algunas respuestas a las FAQ (frequently asked questions): 

¿Qué es lo que hacemos? ¿Qué es programar?

Programar es sencillamente otra forma de resolver problemas. Lo que hacemos es usar tecnología para modelar un pedacito de la realidad, de manera que nos ayude a resolver un problema puntual. 

¿Qué es un modelo? ¿Qué es modelar?

Un modelo es una representación de la realidad. Las ciencias se sirven de modelos para interpretar, entender y explicar fenómenos naturales y sociales. 

La mayoría de nosotros está familiarizade con modelos matemáticos, físicos, sociales. Los modelos que usamos al programar son un poco distintos, y muchas veces se sirven de modelos ya existentes (en otras ciencias). Modelar para nosotres es elegir de nuestra amplia caja de herramientas, cuáles son más adecuadas para representar el pedacito de realidad que tenemos a mano para resolver un problema puntual. 

¿Qué es un programa? 

Es es una serie de instrucciones escritas para realizar una tarea específica en una computadora.  Nosotres escribimos los programas usando un lenguaje de programación, apto para humanos, que luego se “traduce” a código de máquina, que es el lenguaje que entiende la computadora. Una colección de programas y datos relacionados se conoce como software.

¿Cuántas maneras hay de programar?

La caja de herramientas cambia según el sistema de ideas que elegimos usar. A estos sistemas los llamamos paradigmas. Cada uno se rige por ciertas ideas claves, y determina distintas abstracciones con las que trabajamos a la hora de modelar una solución. Por ejemplo, el paradigma funcional (que está bastante de moda hace ya un tiempo), se rige por ideas que tienen una fuerte base en modelos matemáticos, por ejemplo, la abstracción principal son las funciones. El paradigma de objetos, en cambio, se enfoca más en el comportamiento de las cosas (qué saben hacer/que les puedo preguntar). Hay otros paradigmas, y en cada uno cambia la manera de entender las relaciones entre los componentes de mi solución: como están conformados, como colaboran entre sí, etc. 

Elegir un paradigma es algo así como elegir la forma que tiene la ventana por la que miras el mundo: hay ventanas cuadradas, ventanas redondas, ventanas chicas, ventanales… todas te dejan mirar el mismo cacho de realidad, pero en cada una vas a ver cosas distintas 🙂

¿Es muy difícil programar?

Bueno… yo creo que eso depende de cada une. Hay gente que al toque ve la Matrix y entiende todo…

Yo personalmente no soy una de esas personas: necesito preguntar, y hacer mucho yo misma para entender lo que pasa. Eso es normal, y cada une aprende a su ritmo. Aprender algo nuevo (cualquier cosa, no solo programar) es siempre un proceso en el que no solo interviene el “nuevo conocimiento”, sino que también juega una parte súper importante todo lo que traemos de antes: lo que ya sabíamos, nuestra manera de estructurar el pensamiento, la facilidad (o no) que tengamos de retener/relacionar datos…

Pero todas esas cosas se ejercitan y a la larga empieza a salir.  Programar, como todo, tiene su cuota de dificultad, porque hay mucho conocimiento teórico pero sobre todo hay mucho conocimiento práctico. Este conocimiento práctico se construye haciendo cosas, pero también se construye colectivamente: aprendemos de las experiencias de otres. 

Lo más difícil de programar en mi opinión es que nadie te dice lo mucho que tenés que sentarte a HACER para que te salga. Entonces te asustan un poco cuando te dicen que es “difícil” porque hay que saber matemática, física, lógica, etc… y si, si querés entender que pasa “tras bambalinas”, algo de eso tenés que saber; pero en realidad podes hacer programas y pensar soluciones a problemas con mucho menos 🙂  Pasa que sólo una parte de como hacer eso la vas a encontrar en un libro, el resto, lo más importante, se aprende haciendo y preguntando. 

Otra cosa que nadie te dice es que TE VAS A EQUIVOCAR, Y ESTÁ BIEN. Fundamental. La posta de programar no es que te salga todo bien de una: somos seres humanos, después de todo, y nos equivocamos. Además, modelamos la realidad, que es dinámica… entonces, lo más sano sería internalizar desde el principio que los sistemas cambian porque los problemas cambian, entonces tu mirada, tu enfoque, tu solución también va a cambiar. Lo importante es que tan fácil/rápido nos podemos adaptar a esos cambios (esto vale para la vida también). 

O sea que les programadores no somos ninjas ni tenemos la bola de cristal para anticiparnos a todos los cambios que puede tener una solución, porque sería una locura! Nuestro foco está en pensar nuestras soluciones de manera que sean flexibles ante una realidad que es dinámica. 

Si nos contaran esto antes, nos ahorraríamos muchas frustraciones… o al menos tendríamos expectativas más realistas sobre lo que es programar, no?

¿Por qué programar? 

También es muy subjetivo, pero les dejo algunas de mis motivaciones: saber programar te abre las puertas a un mundo increíble: el de ser creadores de tecnología. Y eso te abre las puertas a poder resolver problemas que nunca imaginaste, a imaginar futuros mejores, a mejorar la propia calidad de vida y la de otros. A viajar, a conocer a otras personas. Ejercita la creatividad haciéndote romper con estructuras… cambia tu manera de percibir y entender lo que te rodea.

¿Qué es lo que más te gusta de programar?

Que puedo resolver problemas, y que no hace falta que lo haga sola. El trabajo en equipo es vital, y desde que elegí esta profesión conocí a muchísima gente que sabe muchísimo y que además está muy dispuesta a compartir y ayudar. Mi profesión me cruzó con gente que me hizo mejor profesional y mejor persona: siempre empujando a dar lo mejor de mí, a superarme cada vez más, y ayudar a otros. Como me dijo un profe alguna vez: lo importante son las personas. 

También me gusta que puedo viajar mucho por mi trabajo, y eso también te cambia mucho la cabeza. Me gusta que puedo enseñar y compartir lo que aprendí como otros hicieron conmigo.

Pero sobre todo me gusta que no hay una sola manera de hacer las cosas, porque hay mil maneras de ver el mundo… entonces une siempre tiene infinito por conocer y aprender 🙂

¿Qué cualidades tiene que tener alguien que quiera aprender a programar?

  •  Ser curiose. No se cansen de preguntar. No hay preguntas tontas ni inútiles, ni hay una sola respuesta para todo. Una solución no está “bien” o “mal”, preferimos discutir ventajas/desventajas en cada contexto.
  • Ser constante y persistente. Como decía antes, no siempre nos sale todo a la primera, y vamos aprendiendo todo con “baby steps”, de modo que hay que practicar MUCHO y no dejarse desanimar.
  • Tener mucha empatía. No trabajamos aislades, siempre alguien está ahí para darnos una mano. Y en el contexto de un equipo de desarrollo, es fundamental poder ponernos en el lugar de les otres para poder ayudarles y para que se genere un clima de colaboración y aprendizaje. Ser amable, tener paciencia, saber preguntar y saber explicar, todas cualidades indispensables 🙂 También nos ayuda a entender mejor las necesidades de los usuarios de nuestros programas, y así poder hacer mejores soluciones. 

En resumen: no somos “bichos raros” ni “superdotados” (al menos no la mayoría! ). Y programar está al alcance de todes… sólo hay que tener mucha curiosidad y dedicación; lo demás lo aprendemos juntes.

Ojalá seamos cada vez más! Espero que nos crucemos pronto!

Clari

To halt or not to halt: that’s the question!

Hi all!

The reason why I (lamely, yeah) paraphrase Shakespeare on this post’s title, is because I want to introduce you to conditional breakpoints.

So basically I have added the possibility of interrupting the execution when a certain condition is met. And what is a condition, then? It is simply a block or an statement with a boolean value (it will fail with anything other than those).

Once you set the condition, the link that is inserted by the breakpoint API is triggered only when that condition is met. Sweet, uh?

A very simple example:

Let’s suppose I want to halt when the value of a counter is non zero. We can use the counter from our past experiments:

Screen Shot 2014-08-28 at 13.57.32

Now, we set the breakpoint:

Screen Shot 2014-08-28 at 14.08.48

We select “halt on condition” from the following menu:

Screen Shot 2014-08-28 at 14.08.58

And we write down the condition, when prompted:

Screen Shot 2014-08-28 at 14.09.38

Now if we execute the following workspace:

Screen Shot 2014-08-28 at 14.28.31

Nothing happens! And that’s cool, because our counter is still zero… Now, if I increment the counter:

Screen Shot 2014-08-28 at 14.32.20

And we execute the workspace again, we get our halt:

Screen Shot 2014-08-28 at 14.37.33

Quite simple, but imagine that instead of checking out a counter, you inspect the current context… That enables a lot of possibilities, and I have to keep on experimenting in that regard. Also it may be cool to change the menus so to make them simpler to use…

And that’s it for now. As always, stay tuned, there are more interesting things on the way!

Clari

SmartBreakpoints: alpha release 1.0

Hello!
I am super proud and happy to announce the first release of SmartBreakpoints 🙂

Features:
– One-shot AST based breakpoints
– Persistent breakpoints
– Integration to SmartSuggestions menu (check out the previous post)
– halts only at base level (for now this is by default, later on it would be nice to have the ability to break in system methods as well).
– Works in Pharo 4.0 (with posibility of being backported to 3.0 eventually)

This is an experimental feature: finally I succeeded on being able to insert breakpoints everywhere (even in system methods) but it is not thoroughly tested. So, now it would be nice if you could use it, test it and let me know if it breaks, and where (it’s likely to break somewhere, but I cannot prevent all possible use cases) 🙂

So, how do you get it? Run this Script:

Gofer it
	smalltalkhubUser: 'ClaraAllende' project: 'SmartBreakpoints';
	configurationOf: 'SmartBreakpoints';
	loadStable .

Break it all!

Salut à tous!

In case you don’t know the song that gives title to this post:

Today I’m going to tell you a little more on those fancy breakpoints we want to get 🙂

Now that Reflectivity is more or less ported and almost stable (I keep on doing small refactorings and improvements, and fixing bugs), I have started to work in a small API for inserting breakpoints. After thinking of it for a while (and thanks to Martin Dias for the brainstorming), I decided to integrate them to SmartSuggestions framework, which provides suggestions (duh!) based in the selected text or the cursor position.Currently there are suggestions for:

  • Temporary/Instance/Class Variable
  • Class
  • Method (when you are in the selector)
  • Source (multiple lines)
  • Message

What I did was just adding a new suggestion which triggers the creation of a new breakpoint. In particular, I am interested in methods and message sends.

So, let’s check out how it looks like:

Screen Shot 2014-07-11 at 14.05.17

 

“Break here” triggers the creation of a one shot breakpoint, that inserts a halt using Reflectivity.

Eventually I am thinking of having either a submenu to choose which kind of breakpoint you want to insert or some sort of pop-up menu. Currently, you can insert two kind of breakpoints: persistent ones, and one-shot. All of them are available at message-send and method level, because they are AST based.

For now this is super alfa: still cannot insert breakpoints anywhere you want (you have the suggestion, but in some cases the image still crashes). But I wanted to do some show off 😛

And then, the roadmap would be:

  • Be able to insert breakpoint everywhere (even in the methods that reflect on the system itself, this is related to the image crashes);
  • Have other kinds of breakpoints: by condition, by exception, etc;
  • Have a nice way to uninstall breakpoints;
  • Have a nice way to pick which kind of breakpoint you want to use.

The first item is the most important to get fixed to make SmartBreakpoints “usable”. Once that is done,  I will create a configuration and start asking for feedback (because the only way of full proving the tool will be getting people to use it).

So, stay tuned! I will have more cool stuff to show soon, I hope! 😀

Clari

Show must go on!

Welcome back… and with a new project!

Ok so this project is not that new, and not so unrelated to the previous one.
So, let’s catch up! for last year’s GSoC, we managed to:

  • complete the new debugger functionality (to match the old debugger ones)
  • add support for filtering the stack (so that you can hide the contexts you’re not interested in) with a flexible and extensible API.
  • all these changes are integrated in Pharo 3.0 image 😀

Buuuut… (yes, there’s always a but) there were two parts of the project we didn’t get to work in (because of lacking of time and platform migration issues): a) the smart breakpoints API and b) the Object Centric Debugger. So, maybe you have guessed, my current project is actually to solve that “technical debt” 🙂

Well, done with the catching up thing. Now, what’s going on?

Debugging in Pharo

Debugging process in Pharo is still a little bit not straightforward. For example, for inserting a breakpoint in the code, you have to explicitly write it. So what? writing “self halt” is not that hard… But adding even such a little line like self halt to your code generates a dirty package in Monticello… and you are not going to commit that line once you fix your bug. And, on the other hand, you don’t want to change your code by putting information that has nothing to do with your application.  So in an ideal world you want to be able to add debugging information, but without modifying the source code.

And then an interesting question is which kind of information would be nice to add? The most popular IDEs (i.e. Eclipse, IntelliJ IDEA) provide debuggers that allow you to insert several specialized breakpoints: by line, by condition, by exception, to name some. Also they provide expression watchers, to be able to follow the value of a variable dynamically during the debugging process. Currently, even tough Pharo’s reflective capabilities are huge, there’s is no support for such kind of features, which will greatly improve the debugging process.

The heart of the tool: Reflectivity

As I have already mentioned, Pharo’s reflective capabilities are huge. You can introspect and modify objects in runtime, and most parts of the execution process are already reified: execution context, message sends, sender, receiver, etc; so you can inspect and even adapt the method lookup, the compiling process, and anything you like. So, how does this help to solve our current dilemma? Let’s imagine that you have a tool that can intercept,  for example, a message send that is of your interest, add some extra information, and then continue the normal execution process. This looks exactly like what we need, doesn’t it?  And then let’s go an step further, what if this tool did all that stuff by using Pharo’s reflective mechanisms (so that you can virtually add information anywhere and anywhen you want)? Well, such a tool already exists, and it’s called Reflectivity. A little more in detail: Reflectivity introduces a new abstraction: the metalink. A metalink is an object that reifies the information that we want to add, and when we want to add it; among other things, it knows:

  • which object is in charge to perform the operation we want to add, the metaobject
  • which concrete action (that is, which message we have to send to the metaobject)
  • when to insert the code: a control object (so for example you can insert information before, after, or instead a certain piece of code).

Screen Shot 2014-06-20 at 18.32.07

But of course just having the link doesn’t imply anything by itself, we need a way to intercept the execution that is of our interest and add this link. Reflectivity does that by traversing the AST generated in the compilation process, and adding a new node which represents a message send to the metaobject. Since this code is generated and added in runtime, the original source code is not affected; thus providing a mechanism to implement several tools, such as profilers, debuggers, etc… in any granularity we want.

A little example

Let’s suppose we have a class Bar, with an instance-side method #foo. For this example is not very much important what the method does, so let’s keep it simple:


 #Bar>>foo
           ^2.

Now let’s suppose we have a Counter class, with a single message, #inc, which of course increments a counter 😉


#Counter>>inc
count := count + 1

And we want to count how many times #foo is called. Now, you can imagine that I’m going to use a metalink to achieve this, but how? First we identify the method we want to intercept, in this case  #foo. Then, we need a metaobject, and a message: we have this counter object, so we can use it 🙂 . After that, we specify when in the execution we want to call the counter: by default, this is done before the first statement; so we’re going to leave it as is. So, the actual code for creating such a link looks something like this:


counterLink := RFLink metaobject: Counter new selector: #inc

So now we have the link, then we must install it. This is done by generating a method wrapper for the original code, which contains the message send introduced by the link. The implementation details are not much important (and probably may create some confusion), but once you have the wrapper, the message #run:with:in: is sent (which has a very informative comment):


run: selector with: arguments in: receiver
"Called when the wrapper is installed in the method dictionary instead of the compiled method. Generates a new compiled method , install it and call it with the same arguments"
self generateCompiledMethod.
self installCompiledMethod.
self flushCache.
^ receiver perform: selector withArguments: arguments

What happens then, is that the code generated by the wrapper is executed reflectively (by sending #perform:withArguments: ) with the original arguments. Then the wrapper is uninstalled, so the original method is still there. Of course, I’m omitting a lot of the details, but to prove this works, I can inspect the counter to see if it has increased after sending #foo to an instance of Bar class:

Screen Shot 2014-06-20 at 20.03.17

And a more fine grained example: if I just generate and install the wrapper, and I have a collection of instances of #Bar:

Screen Shot 2014-06-20 at 20.14.17

The initial state of the counter and the collection, if we inspect them:

Screen Shot 2014-06-20 at 20.16.40       Screen Shot 2014-06-20 at 20.18.03

if now I use #collect: to send the message #foo to the elements of the collection, the counter should be increased to 3, but #foo should still just return 2:

Screen Shot 2014-06-20 at 20.22.19         Screen Shot 2014-06-20 at 20.22.09

Of course this approach is not fool proof: there are some implementation issues (such as what happens if the message you want to intercept is sent not only at base level, but in the meta-level as well? This generates an infinite recursion that we should handle), and also all this information should be hidden for the end-user (this is just a little explanation of how it works, but you won’t have to explicitly create a link, nor install it yourself).

But it provides a simple yet powerful way to introduce meta-level information in runtime; which comes in handy, for example, for interrupting the execution without changing the application code (which is basically what a breakpoint does 😉 )

State of the art

What it’s done:

  • Currently there are two implementations of Reflectivity, so the first step was to “merge” them. My approach is to try and reuse most of them as possible, and then write the most simple version I can (so that we can build on top of it later, adding whatever it is necessary).
  • Writing tests. But I should write more 🙂

Future work (aka the Roadmap):

  • Write an API for Smart Breakpoints. We need a model that is flexible (to allow different applications and frameworks to customise them as needed) as well as a simple way to integrate it to the current debugger (ideally, the end user shouldn’t be aware of link installation/uninstallation, but instead just say something like “break here”, and the framework will do the magic)
  • Add support for setting execution watchers, to follow how the value of a variable/expression changes during the execution
  • Highlight hot methods on source: provide a way to identify which methods are highly likely to break everything if you change them.
  • Heimdall: the implementation of the Object Centric Debugger on top of Reflectivity. Sadly it is not possible to reuse Bifröst implementation because after several tries we weren’t able to migrate it from old versions of Pharo image.

TL:DR (you wrote so much and I’m too lazy to read everything…)

Long story made short: My project right now is to provide a simple way to set up breakpoints in the debugger, without changing the application code. Furthermore, we want these breakpoints to be Smart: the ability to break when a condition is met, to create custom breakpoints, and to create them not only from the UI, but programatically as well, is desired. After that, we want to be able to use the debugger but scoped to one specific object, and with dynamic-based abstractions (the objects are dynamic, but the concepts used in debugging tools are rather statical and more related to the source code, and not to the live objects). And we will use Reflectivity, a framework to insert meta-level information into the AST nodes, to build such tools.

Aaaaand… that’s it. Thanks for reading 🙂 Stay tuned!

Filtering the stack – Episode II: Refactor strikes back.

So, last post I introduced you to a new filter API. After some iterations, we have arrived to a nice, flexible filter implementation 🙂 As we wanted filters to be rich objects, able to combine themselves to produce new filters, we designed a hierarchy: Image

  • StackFilter defines methods #and: and #or: , which allow filter combination. Both messages return a BooleanFilter
  • BooleanFilter is a filter subclass, which know how to actually combine two filters.
  • SelectorFilter is a filter for an specific selector or collection of selectors.
  • KernelClassesFilter filters out common classes message sends (like those from Boolean, the Collection Hierarchy, etc)
  • BlockFilter provides a way to create filters from any (boolean) block.

Filters handle one context at a time. Their most important responsibility is to know whether a given context should be displayed or not, through #shouldDisplay: message. For instance:

#BlockFilter>>shouldDisplay: aContext

^self block value: aContext.

And then you can combine a couple of filters, like this:

fromBlockFilter := [:ctx | ctx isNotNil] asFilter.
doItFilter:= SelectorFilter forSelector: #doIt.

doItFilter and: fromBlockFilter.

Which in turn return a BooleanFilter that can be combined again, and so on. Then, the responsibility of filtering the stack relies in the debugger:

filterStack
^self class filterCommonMessageSends ifTrue:[self stack  reject: [:aContext | (self enabledFilters anySatisfy:[:aFilter | aFilter shouldDisplay: aContext])and: [aContext = self currentContext ] ]

But then we realized that we needed not filtering the top context, because we usually want to know where we actually are; and also the first n contexts prior to the top one. So finally the code looks like this:

filterStack
^self class filterCommonMessageSends ifTrue:[
activeFilters := self enabledFilters.
newStack := OrderedCollection new.
activeFilters do: [ :aFilter |
"first loop: add to the newStack those contexts that should not be displayed"
self stack do: [ :aContext | [aFilter shouldDisplay: aContext ] whileFalse: [ newStack add: aContext. ctx := aContext ] ].
"second loop: add the top contexts to keep track of where in the execution we are"
(self stack dropWhile: [:each | each ~= ctx ]) do: [ :context | (aFilter shouldDisplay: context) whileTrue: [ newStack add: context ]]]
newStack
 

I think the most interesting filter is BooleanFilter, which represents the combination of two filters, by a boolean operator (namely #and: and #or: messages). At first I thought of having #AndFilter and #OrFilter, but the I realized that the only difference between them was the boolean operation; so I decided to generalize that behavior by using reflection:

#BooleanFilter>>shouldDisplay: aContext
 ^ (self filters first shouldDisplay: aContext) perform: booleanOperator with: [self filters last shouldDisplay: aContext]

That code could easily be extended to handle more than two filter combination, by folding #shouldDisplay: message send results.

And then, the next challenge was to expose the filter API to the user. We wanted to have default filters (kernel classes, do it, nil messages filters) as global configuration, but we wanted to let the users create new filters and publish them as global configuration if they wished to. So we decided to create a dictionary, whose keys would be the filter classes and booleans as values (meaning if the given filter is enabled or not). So, for global configuration, you’ll see something like this on the Settings Browser:

PharoScreenshot.1

So finally, to avoid having lots of class variables in SpecDebugger to toggle filters, we decided to create a new widget, whose responsibility would be to handle filtering actions. But for the moment, that responsibility still remains on the debugger.

And that’s pretty much it.

Now, a few words, not so unrelated. This year’s Google Summer of Code is ending, and even tough i didn’t make it to the final goal (remember I told you we’d call Heimdall to open the Bifröst?) due to some technical restrictions, I want to say that I’ve learnt a lot, and that I am really thankful to my mentors and to all the Pharo community for the support they’ve given me through this months. I will keep on working on the debugger, but since it won’t be for gsoc anymore… thanks.

So, thank you for reading, and stay tuned!

Cheers!

Clari

Filtering the stack (Episode 1: A new filter)

Hi there! Before telling you anything of what I am doing right now, I want to tell you some great news: the new debugger has been integrated to Pharo3.0 build 🙂 So now we have a prettier and more extensible debugger, and every change and improvement we make it’s going to be integrated faster.

So now, back to business. Now most of the actions available in the old debugger are present in the new one, except for filters.  So the new challenge is to implement that functionality.

In the old debugger, all the logic for filtering the contexts of the stack was all together in one method…  In the new debugger, on the other hand, all the debugging actions are done on the full stack. So my first approach was to create a new filter class, and parametrize the filtering criteria:

#StackFilter
>>filterWith: aCriteria
stack select: aCriteria

Here stack is a reference to the stack, and aCriteria is one (or more than one, that’s the intention) of the filters we want to apply on the stack. The first decision I made was to model the criteria as blocks. So, for example, you could use it to filter common messages like this:

context := [Set new] asContext.

process:= Process
forContext: context
priority: Processor userInterruptPriority.
session := DebugSession process: process context: context
filter := StackFilter new stack: session stack.
filter filterWith: filter commonSelectorsFilter.

This code snippet produces a new collection with those contexts which aren’t common selectors.

Now, what about filtering by more than one criteria? Well, my first approach, in a workspace, was something like this:

filter := StackFilter new stack: session stack.
filtered := #(#doItSelectorFilter #commonSelectorsFilter #kernelClassesFilter) inject: Set new into: [:acc :symbol | acc union: (filter filterWith: (filter perform: symbol))].

But after discussing it with my mentors, we arrived to certain conclusions:

  • We’d rather pass the stack as an argument instead of holding it as internal state (this is, filters should be stateless).
  • Flattening the resulting collection isn’t good since it’s more flexible to work with different stacks for each filter, instead of folding filter applications.
  • Most filtering operations are done by intersection rather than union of filtered stacks. Also #union:  wouldn’t preserve the order of the collection.

Considering this, I did a refactor:

context := [ (Set with: 1 with: 2) collect:[:e | e*2]. self halt. ] asContext.
process := Process
forContext: context
priority: Processor userInterruptPriority.
session := DebugSession process: process context: context.

filter := StackFilter new.
filtered := #(#doItSelectorFilter #commonSelectorsFilter #kernelClassesFilter) collect: [ :symbol |  filter filter: session stack with: (filter perform: symbol)].

Which returns

an Array(an OrderedCollection() an OrderedCollection([ :e | e * 2 ] in UndefinedObject>>DoIt) an OrderedCollection())

This is, instead of folding, I just mapped filter application, and return an array of filtered stacks 🙂 Then we can decide if I want to intersect each one of those, or leave them separated. This would depend on how we want to plug in/out the filter from the debugger window (I mean,  the decision is closely related to how are the users going to activate/deactivate the filters, if they’re to be set as global options or in context menu, or both; etc).

Still, I am not sure if this is the way I want to combine the filters. I felt that I could still refine the idea, and Nico Passerini suggested me to change the filters so that instead of receiving the stack and returning a new, filtered one, they receive a message, and return a boolean… Something like


blocks := #(#doItSelectorFilter #commonSelectorsFilter #kernelClassesFilter) collect: [:symbol | filter perform: symbol]
filter filter: session stack with: [:message | filters allSatisfy: [:block | block value: message]

This way it’s simpler to combine filters using and/or, and then generate one resulting stack. Using and/or as combinators is a more powerful approach, but perhaps it’s not that common… So I chose to have a collection: enabling/disabling a filter is just a matter of adding/removing an element from this collection, and if you want combined filters, you could add a filter created by using and/or combinators.

And I am most content with the plot twist 🙂 Now I just have to refactor it again, and see what comes up of it.

I mentioned before that we have yet to decide how we want to expose the filters settings to the users. That is an important part of the discussion, and I think it deserves that I write another post to explain it 🙂

So, stay tuned!!

Cheers!

Debugger Model Overview

Hello there! In this post I will do my best to sum up the new debugger essentials. So let’s get to it!

In the old debugger all the actions and UI logic were all together. This makes it difficult to maintain and scale the debugger, and don’t even think of having new debuggers. So the new debugger came up to do some serious cleanup.

The intention is to separate the model from the UI, so any UI implementation, let’s say Glamour, Morphic, whatever, can be attached to the model, and use it. This is, the model does not know of how the stack is represented (a list, a table, a widget) , nor of the existence of the inspector, the selection handling, or the widgets that are being used.  The model only knows of processes and contexts.

So let’s see a little class diagram…

Debugger Model - New Page

The cool thing of this approach is that now you could take away everything above the dotted line (namely the UI components) and plug in any other implementation you may like. The default is one made in Spec.

Some considerations:

  • The SpecDebuggerProxy right now just adds the SpecDebugger button to the PreDebugWindow. That’s why it knows the old Debugger. This component should be removed once the new debugger is made the default for Pharo 3.0
  • A DebugSession has all the information of the current debugging process, this includes the interrupted process, the current context, and the possible actions for them.
  • A DebugContext right now just has helper methods to handle contexts. In the future, we should return a stack made of DebugContext, which would have more information (top context, active program counter, for instance) and more intelligence than a plain context.

So now about the model… A DebugSession is created for a process in a method context. The following code illustrates the creation of an interrupted process with a newly created context as top context:


context := [Set new] asContext

process:= Process
          forContext: context
          priority: Processor userInterruptPriority

And, to create a Debug session

session:= DebugSession process: process context: context

So now you have a new DebugSession, to which you can ask things like #stepOver or #stepInto to move between contexts and the stack.

And that’s pretty much it. Stay tuned in!

Cheers!

The Hammer of the Gods…

The hammer of the gods will drive our ships to new lands,
To fight the horde, singing and crying: Valhalla, I am coming!

Led Zeppelin – Immigrant song

Well there, welcome.

I am here to tell you a little bit of my experience during my Google Summer of Code project.

And what is it about? The not despicable process of improving Pharo‘s Debugger. There’s a lot to be done, but me and my mentors think that it’s a very important task, being the debugger a tool to introspect the system and which provides (or would be able to provide) so much information… our true Hammer of the Gods.

So, first things first: the actual state of the art of the debugger.

There are already some things done: the old debugger was (is) quite a mess,  there wasn’t a separation between de UI and the model. So Pharo guys started a new debugger, with a new and fresh UI, separated from the model. The idea is to provide a robust, smart debugger model that can be attached to any UI you can think of; being the default the one made in Spec.

And here is where I came in 🙂  The first step towards a better debugger is, as you might realize, to continue cleaning up the code. As a part of it, there are also some features the old debugger had that are still missing in the new one, so they have to be added. For instance, at the moment I’ve added tests, and I’m adding support for handling post-mortem contexts (contexts that have no longer a process attached).

In that regard, I had to get to know the internals of the debugger. It’s proving to be an interesting challenge for me to think of processes and contexts and getting my hands into them, since I’m much more used to work with other abstractions, closer to the “bussiness logic”. Also I have to get to be friends with Spec, since my little incursions into UI design in smalltalk have all been done in Morphic.

And then, once we get the new debugger as good as the current one, comes the fun(nier) part: calling Heimdall to open the Bifröst.

But that’s another post’s tale 🙂