jueves, 25 de agosto de 2022

Mejorar la velocidad de equipos de desarrollo

version en video:


 

 

hace poco lei un articulo que me pareció muy poderoso titulado “teams that finish early accelerate faster” traducido los equipos que terminan temprano se aceleran más rápido, me pareció bacano por que nos presenta algunas estrategias para equipos de desarrollo que trabajan con scrum para que estos generan más valor.

Empieza poniendo unos criterios para aplicar estas estrategias entre ellos que los equipos sigan buenas prácticas y que sean estables, después de esto empiezan a comentar unas estrategias que pueden ayudar a generar equipos de alto desempeño.

El buffer comenta que si el equipo espera soporte o tareas inesperadas se debe tener un buffer(no mayor al 30% de la capacidad del equipo) que le permita atacar estas tareas si aparecen. para la implementación de este buffer es común que se asigne y rote una persona que está en soporte encargada de estos inesperados.
VInculado a este buffer comentan la necesidad de un procedimiento de emergencia si estas tareas inesperadas(y mas importantes que las tareas del sprint actual por que sino no entrarían) superan el 30%, se deben seguir unos pasos (no necesariamente todos).
cambiar la forma como se trabaja para que esto no sea un 30% extra(intentar encontrar innovación)
buscar alternativas aka intentar entregar este trabajo a otros.
cancelar el sprint y re planearlo informando las nuevas fechas a los jefes.

yesterday weather(el clima de ayer) consiste en determinar la capacidad que el equipo tiene basado en los 3 o mas ultimos sprint, se promedia la velocidad de estos como base para determinar los compromisos de los próximos sprints(rolling average)

Swarming esta nunca la he visto ni de cerca implementada, consiste en que se trabaje 1 historia de usuario a la vez, donde una persona hace de capitán y el resto del equipo lo ayuda en todo los que necesita.

Mantener un equipo estable sin gente que deje el equipo, e incluyendo una persona cada 6 meses a un año para traer ideas nuevas (un equipo de entre 4 y 6 personas). esto ciertamente es dificil de conseguir una recomendación que hacen el artículo es rastrear la felicidad del equipo( con algunas preguntas por escala numerica ) y se disminuye la felicida mucho investigar a fondo hablando con las personas del equipo que esta pasando.

Con estas estrategias el articulo reporta que hubo mejoras drasticas en los equipos del x40 o mas de la velocidad previa, ante esto supongo que habra que ver para creer.

Del lado de la implementacion de estas estrategias en http://scrumbook.org/ se habla de kaizen-pulse como un meta patron para hacer mejora continua (por ejemplo para ir implementando alguna de las estrategias anteriores) la idea es reunir data de como se comporta el equipo antes del cambio su velocidad y la varianza en esta velocidad, teniendo esto se procede a realizar un cambio (esto no lo dicen pero es mas probable que se acepte un cambio si se presenta de manera que parezca una propuesta del equipo mismo y no una descision unilateral) se procede entonces a medir como cambia la velocidad y la varianza(en la velocidad) del equipo a lo largo de varios sprints ( por que la apdatacion a cualquier cambio toma tiempo) posiblemente realizando pequeñas adaptaciones segun las discuciones del equipo. un cambio fue bueno si disminuye la varianza del equipo o si aumenta la velocida del mismo(sin aumentar drasticamente la varianza). en kaizen pulse este proceso se repite por cada cambio.


jueves, 30 de junio de 2022

Types, Covarianza y Contravarianza


 

Los tipos (types)


empezando por los tipos son una definición del grupo de propiedades que algo tiene por ejemplo si tenemos un String este tiene algunas propiedades como su tamaño y tal vez un método substring que nos permite tomar parte del string etc. mediante el tipo se define esto, cuando usamos tipos en lenguajes tipados lo que buscamos es que sepamos con que estamos interactuando y que operaciones son válidas/qué valores tiene algo.

Estos los vemos por ejemplo cuando declaramos una variable como String, Int, Boolean o cuando creamos una instancia de una interfaz o de una clase.

tipos genéricos (Generic types)


en algunos casos queremos garantizar que algo tenga un tipo pero no nos importa cuál sea este por ejemplo para las lista queremos que las listas solo contengan un tipo de elementos, pero no queremos decir especificamente cual, o queremos crear una funcion que nos permita aplicar validaciones pero no sabemos cuáles van a ser los campos, aquí vienen los Generic types que usualmente se definen en algo como esto.


public static <A> List<A> createListWithTwoElements(A element, A element2){
   return List.of(element,element2);
}

usarlos estos generic types tiene varias ventajas entre ellas:

  • que podemos reusar mas el codigo: por ejemplo el método anterior nos sirve para crear una lista de 2 elementos de varios typo llamándola con diferentes A’s
  • que el compilador nos va a chequear si hacemos algo mal si de repente quiero crear una lista con el primer elemento de un tipo, pero el segundo de otro se va generar un error que queremos que pase para estar seguros de que tenemos en esa lista que crea ese método que tipo

 

Covarianza y ContraVarianza.

Existen 2 tipos de herencia que me parecieron interesantes la covarianza y la contravarianza pero antes de eso vamos definir un árbol de herencias para poder explicar mejor :p

open class LivingThing

open class Animal(val name:String):LivingThing(){
    fun yell(){}
    fun eat(){}
}

data class Sloth(val slothName: String, val isTwoFingered: Boolean):Animal(slothName) {

    fun sleep(){}
}

data class Panda(val pandaName:String):Animal(pandaName){

    fun sleep(){}
}

con esto tenemos más o menos una estructura asi.

 

Donde tenemos a LivingThing(cosa viva) como supertipo de Animal, y Animal como supertipo de Sloth(oso peresozo) y Panda.
Al mismo tiempo Sloth y panda son subtipos de Animal, y Animal es un subtipo de LivinThing.

Covarianza.

en la covarianza decimos que recibimos un tipo o sus subtipos por ejemplo:

  • si recibimos un animal también recibió un Sloth o un Panda en ese parametro.
  • si recibimos un Sloth solo recibimos el Sloth porque este no tiene subtipos.
  • Si recibimos un LIvingThing podemos recibir cualquier cosa en el grafo.


la covarianza es la más natural y sencilla de entender desde mi punto de vista, por que se ve también cuando se castea de un tipo mas especifico a uno mas generico.

en kotlin la covarianza se establece por defecto sin que le pongamos nada simplemente un tipo es covariante por defecto, pero si queremos podemos usar la palabra clave out
<out Animal>.
en el caso de java para obtener covarianza se usa extends
<? extends Animal>
 

Contravarianza.

la contravarianza implica que algo puede recibir un tipo o sus supertipos, ejemplos:

  • si se define contravarianza de Sloth, el parámetro puede tener tipo de un Sloth, un Animal o un LIvingThing
  • si es Animal, puede ser de tipo Animal o LivingThing
  • si es un LivingThing puede solo ser un LivingThing


en kotlin la contravarianza se define así
<in A>
y en java
<? super A>

este es para mi fue mas dificil de entender principalmente por que parece contrario a la intuición, lo pude entender cuando vi un ejemplo de uso.

un árbol de herencias para poder explicar mejor :p
 
fun copy(src: List<out Animal>,dest: MutableList<in Animal>){
    for(a in src){
        dest.add(a)
    }
}


en este podemos ver un método que se encarga de copiar lo que está en input(parámetro src) al output(parámetro dest), en el input recibe una lista de Animales out o covarianza esto nos indica que la lista puede ser una lista de Animales con Sloths y Pandas, por el contrario en la salida tenemos un in de Animal o contravarianza lo que quiere decir que en esta lista vamos a tener Animales o LivingThing

y ahora sí todo empieza a tener sentido recibimos algo específico o su hijo, y generamos ese algo o algo más general

la regla para usar covarianza es para definir productores o get( en el ejemplo anterior estamos sacando de src y por eso lo definimos como covarianza).
y la regla para usar contravarianza es para definir consumidores o put( en el ejemplo anterior estamos guarda o consumiendo en dest).

Esto me lleva a creer más que la herencia es complicada :D .

referencias:


  • https://www.youtube.com/watch?v=yqB8u9higoI&t=2s
  • https://www.youtube.com/watch?v=A7oPKEj1-Fg
  • https://docs.microsoft.com/en-us/archive/blogs/csharpfaq/covariance-and-contravariance-faq
  • https://medium.com/kotlin-thursdays/introduction-to-kotlin-generics-9d18d3719e1d
  • https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)

 

jueves, 19 de mayo de 2022

ordenamiento bubble sort, quicksort y sin implementar en java.

version en video.


 

ordenar es poner las cosas siguiendo un orden(que viva la redundancia) por ejemplo podemos ordenar un numero de mayor a menor (5,4,3,2,1) o de menor a mayor (1,2,3,4,5) y en programacion existen muchos metodos de ordenamiento siendo uno de los mas sencillos de implementar bubble sort.

Bubble Sort.

es un método de ordenamiento donde iterativa mente vamos por toda la lista de elementos comparando el valor actual con el anterior (y rotandolos cuando no estan en orden) esto se hace cuantas veces como se necesite hasta que ido por todo el arreglo sin tener que rotar nada (significando esto que ya esta en orden).

es un algoritmo muy sencillo y tambien muy ineficiente en promedio toma O(n*n)  *1

en java se puede implementar de la siguiente manera.


 public static void main(String args[]) {
      int[] array = {13, 14, 42, 54, 56, 38, 97, 24, 57};
      bubbleSort(array);
      printArray(array);
  }

  public static boolean printArray(int[] input) {
      if (input.length == 0) {
          return false;
      }
      for (int i = 0; i < input.length; i++) {
          System.out.println(" " + input[i]);
      }
      return true;
  }

  public static boolean bubbleSort(int[] input) {
      if (input.length < 2) return false;
      boolean isInOrder;
      do {
          isInOrder = true;
          for (int i = 1; i < input.length; i++) {
              if (input[i - 1] < input[i]) {
                  int temporal = input[i - 1];
                  input[i - 1] = input[i];
                  input[i] = temporal;
                  isInOrder = false;
              }
          }
      } while (!isInOrder);
      return true;
  }


otro algoritmo muy conocido y que es creo de los que mas se usa es 

 

Quick Sort 


quicksort(ordenamiento rápido) y gusta bastante por que:

  • es rápido en promedio toma O(nlogn)
  • toma menos tiempo si los registros estan parcialmente ordenados(es verda en muchos casos)
  • optimiza bien el uso de cache *3

yo para entender  este algoritmo tuve que ver este video donde se explica muy bien *3.

el algoritmo es mas o menos asi.

  • usted empieza con un array (posiblemente desordenado)
  • tiene un metodo para ordernarlo(quicksort) que recibe este array, un minimo que es 0 el inicio del array y el tamaño del array(el intervalo que va ordenar)
  • este metodo despues de revisar que el min sea menor que el maximo llama a otro metodo que se encarga de encontrar una "particion" y mientras lo hace organiza los mas pequeño que la particion a la izquierda y lo mas grandes a la derecha (ordena no todo sino simplemente lo mayor y menor que donde esta la particion)
  • luego teniendo el index de esa particion se vuelve a llamar el metodo inicial dos veces una del inicio a la particion, y otra de la particion(el index de la misma hasta el tamaño original)
  • esto se sigue repitiendo recursivamente hasta que se acaba organizando todo.

entrando mas en detalle en el metodo que hace la particion se encarga de lo siguiente.

  • setea una variable i con el inicio/minimo y una variable j con el maximo/final
  • determina un pivote o elemento en el array para comparar(al inicio o al final del array generamente por facilidad, pero tambien se puede en en cualquier punto.
  • empieza a aumentar el i mientras lo que se encuentre sea menor que el pivote(osea que ya esta organizado con respecto al pivote), y a disminuir j mientras lo que se encuentre sea mayor que el pivote(osea que ya esta organizado en relacion al pivote)
  • si i es menor que j, es decir encontraron uno mayor y uno menor donde no debian estar estos se intercambian.
  • esto se repite y al final el pivote se pone en el "medio"(cambiando la variable en la poscision del pivote y la j) de manera de que queden menores a la izquierda y mayores a derecha del mismo
  • se retorna j, ya que en la poscision de j esta el pivote solo queda organizar las otras dos mitades usandolo.

en java una forma de implementar quicksort seria la siguiente.


public class mainClass {

    public static void main(String[] args) {
        int[] input = {1, 10, 10, 8, 6, 10, 1};
        quickSort(input, 0, input.length);
        printArray(input);
    }

    public static boolean printArray(int[] input) {
        if (input.length == 0) return false;
        System.out.println("");
        for (int i = 0; i < input.length; i++) {
            System.out.print(input[i] + " ");
        }
        return true;
    }

    public static void quickSort(int[] input, int min, int max) {
        if (min < max) {
            int j = partition(input, min, max);

            quickSort(input, min, j);
            quickSort(input, j + 1, max);
        }
    }

    public static int partition(int[] input, int min, int max) {
        int i = min;
        int j = max;
        int pivot = input[min];

        while (i < j) {
            do {
                i++;
            } while (i < input.length && input[i] < pivot);
            do {
                j--;
            } while (input[j] > pivot);
            if (i < j) {
                switchTheVariables(input, i, j);
            }
        }
        switchTheVariables(input, min, j);
        return j;
    }

    public static void switchTheVariables(int[] input, int a, int b) {
        int temporal = input[a];
        input[a] = input[b];
        input[b] = temporal;
    }

}

 implementar quicksort o bubble sort tan literal es interesante como ejercisio pero creo que tiene mas sentido utilizar el ordenamiento que ofrece el lenguaje, para ordenar en java podemos hacerlo asi

 


import java.util.List;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collections;

public class runjava {
    public static void main(String []args){
        Integer[] arrayInt={1,3,2,1,5,3,1};
        Arrays.sort(arrayInt);
        printArray(arrayInt);

        List<Integer> listInt=new ArrayList<Integer>();
        listInt.add(1);
        listInt.add(3);
        listInt.add(2);
        listInt.add(1);
        listInt.add(5);
        Collections.sort(listInt);
        System.out.print(listInt);

    }

    public static <A> boolean printArray(A[] array){
        if(array.length==0) return false;
        for(int i=0;i< array.length;i++){
            System.out.print(" "+array[i]);
        }
        return true;
    }
}

y si tuvieramos que implementar basados en un criterio propio x(por ejemplo ordenar un arreglo de string que sabemos son entero) o usando un campo especifico de una clase, o cualquier logica que quisieramos podriamos crear un comparator para informarle a java como queremos que ordene y hacerlo asi.


 import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class runJava{
    public static void main(String[] args) {

        Comparator<String> customComparator = ((a, b) -> {
            Integer aInt = Integer.valueOf(a);
            Integer bInt = Integer.valueOf(b);
            return aInt > bInt ? 1 : aInt < bInt ? -1 : 0;
        });
        List<String> listInt = new ArrayList<String>();
        listInt.add("1");
        listInt.add("3");
        listInt.add("2");
        listInt.add("1");
        listInt.add("5");
        Collections.sort(listInt);
        System.out.print(listInt);

    }

}

 

Referencias.

1. bubble sort https://www.geeksforgeeks.org/bubble-sort/

2. quicksort explicacion. https://www.youtube.com/watch?v=7h1s2SojIRw

3. por que gusta quicksort https://cs.stackexchange.com/questions/3/why-is-quicksort-better-than-other-sorting-algorithms-in-practice 

4. https://www.opinionatedgeek.com/codecs/htmlencoder

lunes, 28 de marzo de 2022

Scrum, metodologias agiles

Version en video


 

Es una metodología ágil para el desarrollo de software como otras metodologías ágiles nació por la inspiración del manifiesto ágil un manifiesto que se creó casi que como consecuencia del desarrollo por cascada ( primero se planea, luego se desarrolla y por último se prueba) . Se dice que el desarrollo por cascada no funcionaba bien para proyectos de software porque hay mucho descubrimiento en el desarrollo y cambios sobre la marcha inesperados es bastante más parecido al método científico ( hipótesis, ensayo, error o éxito, ajuste y repetir).

Donde he trabajado siempre se dice que se usa scrum, como para determinar cómo se organizan las personal. también he escuchado de conocidos que trabajan en tecnología que lo usan en sus empresas,  por lo que mi impresión es que esta es una metodología para el desarrollo de software muy aceptada actualmente.

scrum busca ciclos cortos de trabajo que se llaman sprint, la idea es dividir el trabajo de manera que se puedan mostrar cosas completas al final de ese sprint, los sprint son cortos en teoría tanto como sea posible para tener una duración de entre 1 y 4 semanas (donde 2 semanas es lo común).

el trabajo se divide en historias de usuario de manera que se puedan hacer por partes, y que estas partes tengan valor para el negocio por ejemplo una historia puede ser “permitir que un usuario pueda recuperar su contraseña” (ese es el título la descripción podría tener como se quiere que se recupere, bajo que condiciones, como se va determinar que se culminó la tarea etc) y una buena historia tiene algunas características (me parece util este acronimo) el acronimo invest.

I: independent (la historia se debe poder cumplir por sí sola)
N: negotiable ( se puede negociar la historia hasta antes de empezar)
V: valuable ( debe ser valiosa para el negocio, generar valor si se completa)
E: estimable ( se debe poder estimar para calcular que tanto esfuerzo va tomar)
S: small ( debe ser lo suficientemente pequeña para que se pueda hacer un sprint)
T: testable ( se debe poder probar de alguna manera que la historia se completo)

qué criterios debe tener si o si los define el equipo pero la historia no deja de ser una forma de dividir y hacer el trabajo por partes manejables

los equipos en scrum están compuestos por el product owner que viene siendo un representante del negocio en el equipo que entiende las necesidades y se las prioriza al equipo( define qué se debe hacer primero) , un scrum master que es una persona que hace de contrapeso al product owner para disminuir la presión que hay sobre el equipo y ayuda a remover bloqueos, y el equipo de desarrollo que es el que se encarga de implementar las soluciones.De estos 3 el scrum master veo como común que se lo pasen por la galleta (que no pongan a ninguna persona con ese rol en el equipo).

el equipo se recomienda que sean 7 +- 2 personas (entre 5 y 9 personas) este conteo son las persona que hacen historias(hacen el backlog del sprint) por lo que si el product owner hace historias cuenta en este rango sino no, lo mismo para el QA( persona que vela por la cálida y hace pruebas) si afecta la capacidad del equipo cuenta sino no, más personas son contraproducente por que aumentan el costo de la comunicación exponencialmente ( entre más gente esté metida en algo más difícil es sincronizar el trabajo, decidir y organizar ) y menos personas tal vez sean muy pocas para conseguir algo significativo en un tiempo razonable además de ser capaces de hacer cosas si alguien sale de vacaciones por ejemplo. En lo personal me gustan mucho más los equipos tirando hacia el lado de pequeño cuando un equipo crece me parece inevitable que se empiezan a tomar temas variados y se pierde la concentración como equipo en algo específico.

el equipo va varían en composición según el producto algunos equipos van a ser solo desarrolladores backend, otros sólo front-end, otros mixtos entre backend y frontend, otros serán científicos de datos etc.

un último detalle aparte de la metodología creo que es importante que en un equipo haya confianza, que la gente se lleve relativamente bien, que todos se puedan concentrar en el trabajo del equipo y esto he visto que se se posibilita cuando.

  • se respetan las curvas de aprendizaje.
  • se saca tiempo para permitir hacer mejoras técnicas ( o deuda tecnica)
  • Se hacen actividades de integración.
  • todos los integrantes trabajan para un solo proyecto ( en ciertos caso se da que una persona tiene x capacidad en un equipo y y en otro, yo creo que eso es contraproducente)
  • se hacen discusiones técnicas en grupo
  • se hace programacion par/mob programming devez en cuando,para iniciar una persona o seguido si al equipo no le molesta

se piden y se respetan las opiniones de todos, aunque se llegue a compromisos


por el contrario cuando esas cositas se dejan de hacer pues el rendimiento y el ambiente empeoran. no se deja de ser humano por más metodologia o processo

fuentes:
opiniones personales y primeros 3 capitulos de:
Scrum the basics de linkeding learning
https://www.linkedin.com/learning/scrum-the-basics