javisantana.com

Agroguia En 2013

Agroguía en 2013

Agroguía es una pequeña empresa que monté hace ya 7 años y medio que se dedica a hacer productos para agricultores. Siendo realistas solo se dedica a hacer un producto que es lo que vendemos, sin embargo hemos hecho y hacemos otras cosas.

Agroguía nació como un “y si probamos” y todos y cada uno de los años, al finalizar la temporada agrícola, sabía que aquel año sería el último. Cómo iba una empresa de 3 fulanos en un poblado de Valladolid a sobrevivir, vendiendo sólo por internet. Todos y cada uno de estos años me he confundido, lo que viene a significar que hemos superado las espectativas con creces.

Hay que tener en cuenta que hace 8 años lo de las startup y estas modernidades no se llevaba, aquí salías a pecho descubierto, sin tener ni puta idea pero con un par de huevos. Agroguía nunca ha sido una startup, cuando la cree no fue para crecer mucho, ni para venderla al cabo de 3 años, ni para captar un VC. Símplemente necesitaba dinero por que básicamente estábamos [muy] metidos en la mierda (es lo que tiene ser hijo de agricultor sin tierras).

Es cierto que este año no hemos dedicado mucho tiempo, pero algunas cosas hemos hecho:

Me gustaría destacar que sólo hay una persona dedicada 100% en Agroguía, los demás (@jatorre, @saleiva y yo) hacemos cosas puntuales y echamos un cable en momentos concretos pero nos dedicamos a @vizzuality la mayor parte del tiempo.

Este año vamos a trabajar duro en la parte de tracking, consiguiendo más distribuidores y dando el puto mejor servicio que puedas tener si compras un sistema de guiado GPS agrícola.

Finding Binding Leaks

Finding binding leaks in Backbone applications

If you are developing a medium/big javascript frontend application you do want to have a isolation mechanism for views. This is the only way to keep your application working as expected as its size grows.

For the moment HTML does not provide a native way to do this so you have to rely on javascript side to do this. Ok, that’s not totally true, iframes are a sandbox (spotify uses them to isolate views) and shadow DOM is not still there.

There are a lot of libraries to do this, in CartoDB we use Backbone, it’s simple, small enough to fully understand it, no magic, no extensions on top of HTML and provide a evented system to comunicate model and views.

The typical Backbone view looks like this:

var View = Backbone.View.extend({
  initialize: function() {
  	this.model.bind('change:attr', this.render, this);
  }
})

So every time attr changes the view is rendered. In current Backbone version you have listenTo method which tracks which objects are attached to a given one but when we started to use Backbone that method didn’t exist so we use the 3rd argument to know what object a signal is attached to. Easy but you have to remember to pass the this always you link a signal.

When a view is removed all those links must be removed, if it’s not done those views are going to last forever. That’s “only” a memory use but imagine the view have some side effects like saving another model to the server…

During last chrismas I decided to do a big refactor in some part of CartoDB (wizards if you know it), and some views had binding leaks. Find them is really hard and sometimes it takes hours to find them even if you have tools. For example, we have a checker to detect leaks while the app is running, just execute cdb.core.View.runChecker int the console and it will show a list of “missing bindings”. It works but only with the views that are currently created.

I though it would be better to find them in testing stage so I created a jasmine (*) helper:

  it("should not have leaks", function() {
    expect(view).toHaveNoLeaks();
  });

What basically does is:

Really simple, easy to use in all the view tests. It saved me hours of in-app testing. The function itself is defined here if you want to take a look. It can be improved with a more smarter logic but it’s enough for the moment.

NOTE: this post is actually a mail I was going to send to frontend cartodb developers but I though it may result useful for someone else

(*) we use jasmine but I totally hate it

Repaso 2013

repaso 2013 y visión para el 2014

Ya casi hemos terminado el 2013 y toca la típica review y objetivos para el año que viene.

Este año ha sido intenso, algunas de las razones son las siguientes:

No creo ni en la suerte ni en el destino pero mi intuición me dice que el año que viene va a ser movidito. Sólo espero sacar los productos que tengo pendientes y ver como evolucionan, mejorar mi puto inglés de una vez por todas (objetivo recurrente durante los últimos 6 años), pasarmelo lo mejor posible y quizá cerrar alguna que otra herida del pasado.

trabajar en remoto, the bad parts

Qué bonito es trabajar desde casa, leer el libro de 37signals y asentir, sacar pecho “yo trabajo desde casa, soy el puto amo” y tantas otras cosas. Pero como todo, generalizar es la receta perfecta para darte la hostia. Cada persona, empresa y situación es diferente.

Estas son algunas de las cosas que he sufrido y sufro durante el tiempo que he trabajado en remoto:

animaciones en javascript

Animaciones en javascript

Ahora que javascript está tan de moda, es tan potente y nos permite hacer lo que código nativo nos permitía hacer en un 486 los desarrolladores estamos empezando a hacer cosas algo más interesantes que poner texto y enlaces como juegos o animaciones. Lo cierto es que a pesar de que javascript sea aún lento para hacer cosas con mucha carga matemática, tenemos a nuestra disposición tarjetas gráficas muy potentes y gracias a las mejoras que últimamente han incluído en los navegadores, podemos usarlas directamente (con WebGL) o indirectamente (CSS3, SVG, canvas)

El objetivo de este artículo no es explicar como renderizar rápido ni de como aprovechar la GPU si no de como hacer una animación.

paso 1: setInterval

Lo primero que se nos ocurre es el típico setInterval. Seguro que conoces la función, pero básicamente lo que hace es llamar a una función que le especificas cada cierto tiempo. Por ejemplo, la animación sería tal que así:

        setInterval(function () {
            update()
            render();
        }, 20)

    

Esto llamará a la función cada 20ms, esto es 50 frames por segundo (FPS). Para la mayoría de casos cumple perfectamente. Pero qué pasa si la función update tarda 30ms en terminar? Realmente no sé que es lo que hace el navegador, supongo que cada uno tendrá su política, pero cabe pensar que con el tiempo se empezarán a aculumar eventos de llamada a esa función ya que no es capaz de terminar en menos de 20ms.

Aunque el navegador fuese muy listo y gestionase eso perfectamente no podemos hacer la animación correctamente porque no sabemos el tiempo que ha pasado de un frame a otro.

paso 2: setTimeout

Para evitar que se nos acumulen llamadas por que la función es lenta vamos a pedir renderizar un frame solo cuando hayamos terminado:

        var logic = function () {
            update()
            render();
            setTimeout(logic, 20);
        };
        setTimeout(logic, 0);

    

Vale, ahora ya no se acumulan eventos y si la función update o render tarda más que esos 20ms no pasará nada, sólo actualizaré la animación cuando el frame anterior haya terminado. Mejor que antes pero aún estamos animando como unos tristes.

paso3: controlando la animación

Imaginemos que estamos controlando una animación de una imagen moviendose por la pantalla. Queremos que en 1 segundo se mueva 500px:

        var img = new Image();
        img.src = 'image.png';
        img.style['position'] = 'absolute';
        var pos = 0;
        img.style['left'] = pos + 'px';

        var logic = function () {

            // update
            pos += 500*0.02;
            img.style['left'] = pos + 'px';

            // browser will render the img on style change
            //render();
            if(pos < 500) {
                setTimeout(logic, 20);
            }
        };
        setTimeout(logic, 0);

    

En un segundo, a 50FPS habremos dibujado 50 frames con lo cual pos será 500 al pasar un segundo. No está mal, pero imaginemos que tenemos una máquina lenta, tan lenta que no es capaz de actualizar a 50FPS. En ese caso tendremos que la imagen llegará a la posición 500px, pero en más de un segundo. No estamos controlando el tiempo.

Para ello podemos usar el tiempo transcurrido desde el último frame. Vamos, lo que se lleva aplicando en videojuegos simples toda la vida:

        var t0 = new Date().getTime();
        var logic = function () {
            var t1 = new Date().getTime();
            var dt = t1 - t0;
            t0 = t1;

            // update
            pos += 500*dt;
            img.style['left'] = pos + 'px';

            // browser will render the img on style change
            //render();
            if(pos < 500) {
                setTimeout(logic, 20);
            }
        };
        setTimeout(logic, 0);

    

Ya está, ahora aunque la máquina sea lenta controlamos la animación correctamente

paso 4: requestAnimationFrame

Ahora podemos controlar un poco mejor la animación gracias a requestAnimationFrame. Para variar es una función que no es standard y cada navegador la implementa con el nombre que le da la real gana.

En resumen, esta función llama a la función que nostros queramos cuando el navegador vaya a renderizar. Eso es bueno, por que si nosotros llamamos 1000 veces por segundo a esta función como mucho llamará a la animación el máximo que pueda actualizar. Asumamos que esto es bueno, aunque si estás haciendo algo medio serio la actualización de la lógica puede ir parcialmente desacoplada del renderizado (luego veremos un ejemplo).

Así que la cosa quedaría tal que así:

        var logic = function () {
            var dt = //calculate dt
            uddate(dt);
            render()
            requestAnimationFrame(logic);
        }
        requestAnimationFrame(logic);
    

Perfecto, además podemos llamar a requestAnimationFrame desde cualquier lado para que comience la animación.

Normalmente (la ley del copy & paste así lo dice) se define requestAnimationFrame tal que así:

        var requestAnimationFrame = window.requestAnimationFrame ||
                window.webkitRequestAnimationFrame ||
                window.mozRequestAnimationFrame ||
                window.oRequestAnimationFrame ||
                window.msRequestAnimationFrame ||
                function(a){setTimeout(a,20);};
    

aunque en mi opinión debería ser algo así:

        function createRequestAnimationFrame(fn) {
            var rendered = true;
            return window.requestAnimationFrame ||
                window.webkitRequestAnimationFrame ||
                window.mozRequestAnimationFrame ||
                window.oRequestAnimationFrame ||
                window.msRequestAnimationFrame ||
                function(a) {
                    if(rendered) {
                        rendered = false;
                        setTimeout(function() {
                            fn();
                            rendered = true;
                        }, 20);
                    }
                };
        }

        logic = function() {
            update();
            myAnimationRequestFrame();
        }
        myAnimationRequestFrame = createRequestAnimationFrame(logic);
        myAnimationRequestFrame();

        element.onclick = function() { 
            // logic
            myAnimationRequestFrame();
        }
    

De este modo aunque llamemos 1000 veces a myAnimationRequestFrame sólo se llamará a la animación cuando toque

paso 5: casos extremos

Qué pasa si el navegador es muy lento. Bueno, lo ideal es quitar la animación, pero claro, no sabemos a priori si la máquina es lenta (bueno, puedes comprobar si es Firefox). Para evitar que todo se vaya al traste quizá lo mejor sea limitar la animación para que no se vaya de madre.

        var logic = function () {
            var dt = //calculate dt
            dt = Math.min(0.05, dt);
            update(dt);
            render()
            requestAnimationFrame(logic);
        }
        requestAnimationFrame(logic);
    

De esta forma si la animación es muy lenta la ralentizamos pero así estamos seguros de que la lógica no falla. También nos protege del caso en el que el usuario cambie de tab (requestAnimationFrame no asegura que se llame al callback si la pestaña donde se ejecuta el código no está activa), al volver a activarse el dt será muy grande y podría desestabilizar en caso de no controlar el dt. Imagino que habrá algún API para controlar si la pestaña es activa…

Imaginemos que la función update hace algo más complejo que sumar, por ejemplo una integración numérica. En ese caso si el dt es muy grande la integración se puede ir al traste, necesitamos dt suficientemente pequeños. Ese caso quedaría resuelto también.

Por ejemplo, queremos que en el ejemplo anterior la imagen vaya hasta 500 pero suavemente:

        var smooth = 0.1;
        var distance_to_target = 500 - pos;
        pos += distance_to_target*smooth*dt;
    

Si dt es muy grande lo que pasará es que esa función empezará a oscilar haciendo un efecto muelle en vez de llegar suavemente o incluso oscilará hasta el infinito si dt es muy grande (a que os suena de de cuando érais jóvenes y estudiabais? si eres teleco/industrial y no te suena vuelve a la carrera).

y último: haciendo las cosas realmente bien

Mejor que explicarlo aquí, id a este artículo del mítico Javier Arévalo y grabadlo en vuestra mente:

Fixed time step loop

Comentarios y trolleos son bienvenidos

@javisantana