How comparing things is faster and simpler with immutability

The third post of the series about the differences between values and references is focused on a practical example, the same trick that is at the core of React and Redux performance.

In the previous post of the series, I wrote about the nature of value and reference data types, and the differences between shallow and deep operations. In particular, the fact that we need to rely on deep operations to compare things is a major source of complexity in our codebases. But we can do better.

Comparing mutable structures

When working with mutable data structures, things like determining whether an object has been changed or not is not so simple:

var film = {
    'title': 'Piratees of the Caribean', 
    'released': 2003
};

// At some point, we receive an object and one of its properties
// might have changed. But how do we know?
newFilm = doSomething( film );

film === newFilm; // What does a shallow equality yield?

If we are allowed to mutate objects, although film and newFilm identifiers are equal, the payload might have been updated: a shallow equality check won’t suffice, we’ll need to perform a deep equality operation with the original object to know.

Comparing immutable structures

In JavaScript, primitives (numbers, strings, …) are immutable, and reference data types (object, arrays, …) are not. But if mutable structures are the reason why comparing things is difficult, what would happen if we worked with reference data types as if they were immutable?

Let’s see how this would work:

If something changes, instead of mutating the original object, we’ll create a new one with the adequate properties. As the new and the old object will have different identifiers, a shallow equality check will set them apart.

var film = {
    'title': 'Piratees of the Caribean', 
    'released': 2003
};

var doSomeThing = function( film ) { 
    // ... 
    return Object.assign( 
        {}, 
        film, 
        {'title': 'The curse of the Black Pearl'} 
    ); 
}

var newFilm = doSomething( film ); 

film === newFilm; // false

If nothing changes, we’ll return the same object. Because the identifier is the same, the shallow equality check will yield true.

var film = {
    'title': 'Piratees of the Caribean', 
    'released': 2003
};

var doSomeThing = function( film ) { 
    // ... 
    return film; 
} 

var newFilm = doSomething( film ); 

film === newFilm; // true

It is easier to tell what have changed when reference data types are immutable because we can leverage the shallow equality operations.

As a side-effect, it takes less effort to build a whole lot of systems that depend on calculating differences: undo/redo operations, memoization and cache invalidation, state machines, frameworks to build interfaces with the immediate mode paradigm, etc.

Coda

One of the reasons I started this series of posts was to explain how using immutable reference data types was one of the tricks at the core of Redux and React. Their success is teaching us a valuable lesson: immutability and pure functions are the core ideas of the current cycle of building applications – being the separation between API and interface the dominant idea of the previous cycle.

I have already mentioned this some time ago, but, at the time, I wasn’t fully aware of how quick these ideas will spread to other areas of the industry or how that will force us to gain a deeper understanding of language fundamentals.

I’m glad they did because I believe that investing in core concepts is what really matters to stay relevant and make smart decisions in the long term.

How equality and copy operations work

This is the second post of a series about how fundamental operations work depending on the nature of data they work with. JavaScript is used as example.

In the introductory post of this series we talked about the differences between value and reference data types:

  • Value data types store their payload as the contents of the variable.
  • Reference data types store an identifier as the contents of the variable, and that identifier is a reference to the actual payload in an external structure.

Through this post will see how the equality and copy operations use the content of the variable, meaning that they’ll use the payload for data types and the identifier for reference types.

Working with value data types

Let’s say we have the following value variables:

In plain JavaScript, this would be:

var foo = 42;
var bar = 42;
foo === bar; // this yields true

If we were copying variables instead:

var foo = 42;
var bar = foo;
foo === bar; // true

foo = 23;
foo === bar; // false

As the content of the variables is the mere payload, the operations are straightforward.

Working with reference data types

Let’s say now that we are working with reference data type variables:

In JavaScript, this would translate as:

var x = {'42': 'is the answer to the ultimate question'};
var y = {'42': 'is the answer to the ultimate question'};
x === y; // This yields false.

When we create new reference data type variables, they are going to have a brand new identifier, no matter whether the payload is actually the same than other existing variable. Because the language interpreter is comparing identifiers, and they are different, the equality check yields false.

What if we were copying variables instead:

var x = {'42': 'is the answer to the ultimate question'};
var y = x; // Copies x identifier to y.
x === y; // This yields true.

It is important to realize why these are equal: because their identifiers are equal, meaning that both variables are indexing the same payload.

With that in mind, what would happen on modifying the payload?

x['42'] = 'the meaning of life'; // Changes the payload.

x === y; // Still true, the identifiers haven't changed.
console.log(y['42']); // Yields 'the meaning of life'.

But:

var x = {'42': 'is the answer to the ultimate question'};
var y = x; // Copies x identifier
x === y; // We already know this is true.

x = {'42': 'the meaning of life'}; // New identifier and payload.

x === y; // This would yield false.
console.log(x['42']); // 'the meaning of life'
console.log(y['42']); // 'is the answer to the ultimate question'

The reason is that x = {'42': 'the meaning of life'} assigns a new identifier to x, that references a different payload – so we’ll be back to the first scenario shown in this block.

(A short aside: in the introduction, I mentioned that references and pointers were different. The above case is a good example of how they’re different: if y was a pointer, it would index the contents of x, so both variables would remain equals after x contents change.)

In computer science, the operations that work with the contents of the variable (be it values or reference identifiers) are called shallow operations, meaning that they don’t go the extra step to find and work with the actual payload. On the other hand, deep operations do the extra lookup and work with the actual payload. Languages usually have shallow/deep equality checks and shallow/deep copy operations.

JavaScript, in particular, doesn’t provide built-in mechanisms for deep equality checks or deep copy operations, these are things that either we build ourselves or use an external library.

An example with nested reference data types

A JavaScript idiom to create new objects by reusing parts of existing ones is using the method Object.assign(target, …sources):

var x = {'42': 'meaning of life'};
var y = Object.assign({}, x);
x === y; // Yields false, identifiers are different.
x[42] === y[42]; // Yields true, we are comparing values.

Object.assign creates a shallow copy of every own property in the source objects into the target object. If the target has the same prop, it’ll be overwritten. In the example above, we’re assigning a new identifier to the variable y, whose own properties will be the ones present in the object x.

This works as expected for objects whose own properties are value data structures, such as string or number. If any property is a reference data structure, we need to remember that we’ll be working with the identifiers.

For example:

var book = {
    'title': 'The dispossesed',
    'genre': 'Science fiction',
    'author': {
        'name': 'Ursula K. Le Guin',
        'born': '1929-10-29'
    }
};

// We are creating a newBook object:
// * the identifier would be new
// * the payload would be created by shallow copying 
//   every book's own property
var newBook = Object.assign({}, book);

newBook === book; // false, identifiers are different

// Compare value data types properties:
newBook['title'] === book['title']; // true
newBook['genre'] === book['genre']; // true

// Compare reference data types properties:
newBook['author'] === book['author']; // true

Both newBook and book objects have the same identifier for the property author, that references the same payload. Effectively, we have two different objects with some shared parts:

If we change some properties, but not the author identifier, both book and newBook will still see the same author payload:

book['title'] = 'Decisive moments in History';
book['genre'] = 'Historical fiction';
book['author']['name'] = 'Stefan Zweig';
book['author']['born'] = '1881-11-28';

newBook === book; // Yields false, identifiers are still different.

// Value variables have diverged.
newBook['title'] === book['title']; // false
newBook['genre'] === book['genre']; // false

// The author identifier hasn't changed, its payload did.
newBook['author'] === book['author']; // true 
newBook['author']['name'] === book['author']['name']; // true 
newBook['author']['born'] === book['author']['born']; // true

For both objects to be completely separate entities, we need to dereference the author identifier in some of them. For example:

book['title'] = 'Red Star';
book['genre'] = 'Science fiction';
book['author'] = { // this assigns a new identifier and payload
    'name': 'Alexander Bogdanov',
    'born': '1873-08-22'
};

newBook === book; // Yields false, identifiers are still different.

// Reference identifier for author changed,
// book.author and newBook.author are different objects now.
newBook['author'] === book['author']; // false

Coda

Humans have superpowers when it comes to pattern matching, so we are biased towards using that superpower whenever we can. That may be the reason why the reference abstraction is sometimes confusing and why the behavior of shallow operations might seem inconvenient. At the end, we just want to manipulate some payload, why would do be interested in working with identifiers?

The thing to remember is that programming is a space-time bound activity: we want to work with potentially big data structures in a quick way, and without running out of memory. Achieving that goal require trade-offs, and one that most languages do is having fixed memory structures (for the value data types and reference identifiers) and dynamic memory structures (for the reference payload). This is an oversimplification, but I believe it helps us to understand the role of these abstractions. Having fast equality checks is a side-effect of comparing fixed memory structures, and we can write more memory efficient programs because the copy operation works with identifiers instead of the actual payload.

Working with abstractions is both a burden and a bless, and we need to understand them and learn how to use them to write code that is simple. In the next post, we shall talk about one of the tricks that we have: immutable data structures.

Value and reference data types

The introductory post of a series about how fundamental operations behave depending on the nature of the data they work with. JavaScript will be used as an example.

There are a number of ways to classify data types in computer science. Of all of them, I find that the difference between value data types and reference data types is a useful classification for the daily life of application programmers – knowing the differences results in fewer bugs, less time to understand code, and more confidence to sleep well at night.

One way to think about them is by considering what is the content of the variable for each data type:

  • Value data types store their payload as the contents of the variable.
  • Reference data types store an identifier as the contents of the variable, and that identifier is a reference to the actual payload in an external structure.

Let’s say the FOO variable is a value data type and its payload is 42, while the BAR variable is a reference data type and has 42 as payload. A visual representation of this might look like:

We usually are interested in the payload of the variable (in green), not in their metadata (in red), yet fundamental operations of the languages we use every day have a different behavior depending on whether the variable content is a value or a reference.

For example, JavaScript has value and reference data types: the primitive data types are value data types – number, boolean, or string- and all the rest are reference data types – objects or arrays.

In terms of memory management, it is common for value data types and reference identifiers to be assigned a fixed amount of memory, and to live in a part of the memory called the stack. On the other hand, the reference payload usually doesn’t have a fixed amount of memory assigned so it can grow to any length, and tends to be stored in a different part of the memory sometimes called the heap. This is a generalization and an area that depends heavily on the language and its interpreters, but the reason this distinction exists in some manner is that we want fast and easy operations for an unlimited amount of data: operating with fixed memory variables is easier and faster, but dynamic memory allocation makes a better use of the limited space in memory – it’s a space/time tradeoff.

Boxing and unboxing

Languages with both value and reference data types, tend to provide ways to convert values into references, and vice-versa. This is called boxing and unboxing.

It is common that each value has a reference counterpart. For example, in JavaScript, there is the string primitive and the String object, the number primitive and the Number object, the boolean primitive and the Boolean object.

Also, languages tend to provide automatic boxing and unboxing in some situations. For example, JavaScript primitives don’t have methods or extra properties like the reference objects have; yet, they’ll be automatically boxed to the equivalent reference object when you’re trying to use one of its methods or properties.

This is a source of confusion, and the reason why:

var foo = 'meaning of life';
// Defines foo as a primitive string.
// To define it as the reference object String we'd do
// var foo = new String('meaning of life');

foo.toUpperCase();
// This yields 'MEANING OF LIFE'.
// Although foo is a primitive we can use the object methods
// thanks to the autoboxing.
// We could think of it as a type conversion in other languages: 
// ((String) foo).toUpperCase();

foo.constructor === String;
// This yields true.
// When we call a property or method belonging the object String,
// foo will automatically boxed, so it behaves like the object.

foo instanceof String; 
// This yields false.
// In this case foo is in its natural state (unboxed),
// so we are comparing the primitive to the reference.

typeof foo;
// This yields 'string'.
// In this case, foo is in its natural state (unboxed),
// so we are asking the system what kind of variable it is.

A note about references VS pointers

Some may argue that reference is how Object Oriented languages coined the old pointer data type. They are different things, though. The way I set them apart is by picturing what are the contents of the variables. References contain an identifier of the payload in an external structure; pointers index the content of another variable.

If, for example, a language would allow us to define a variable called Z as a pointer to X, visually it might look like this:

Although the difference between pointers and reference might be subtle, it has deep connotations when it comes to how operations work with them.

Coda

We, applications programmers, are mostly interested in the payload of the variables, but our programs consist of wrangling variables around with operations such as equality checks, copying, and passing arguments to other functions. These operations depend on the nature of the data they work with, so we are bound to deeply understand their inner workings. That will be the topic for the next post of the series.

sum-csv

Reflections on a little thing I made, to learn how to better create the bigger ones.

After the last blog categories reorganization, I realized that I talk less about what I do and more about what others do. That make sense, as this blog is part of my learning process and I’m always looking around to find ways to improve myself. Yet, I’d like to start writing more about the little things I do. Writing helps me to reflect upon the how, so eventually I’ll learn more about my thought processes. These are likely to be very small things.

sum-csv

sum-csv is a small utility I have built to help me to crunch some statistics I was working with. I had a complete dataset in a CSV file, but what I wanted was an ordered list of the number of times something happened.

Original CSV: What I wanted:

A data transformation

This is a small task – my old self whispered. Yet, instead of opening the editor and start coding right away, the first thing I did was drawing things. I am a visual person and drawing helps me to gain understanding. The algorithm I came up with was a succession of mathematical transformations: Which is to say:

  • transpose the original matrix
  • eliminate the rows I was not interested in
  • for each row, group all numerical values (from column 1 onwards) by adding them, to calculate the total
  • sort the rows by the total

Now, I was prepared to write some code. Amusingly, the gist of it is almost pure English:

d3-array.transpose( matrix ).filter( isWhitelisted ).map( format ).sort( byCount );

Reflection

Creating production-ready code took me four times the effort of devising an initial solution: finding good and tested libraries for some of the operations not built-in in the language such as reading a CSV into a matrix or transpose the matrix itself, creating the tests for being able to sleep well at night, distributing the code in a way which is findable (GitHub/npm) and usable by others/my future self, and, actually, writing the code.

I am not always able to write code as a series of mathematical transformations, but I find pleasure when I do: it is much easier to conceptually proof whether the code is correct. I also like how the code embodied some of the ideas I’m more interested in lately, such as how a better vocabulary helps you to make things simpler.

My office i4

I’ve been working from home for more than 3 years now, and my setup has gone through several iterations – the current one is i4.

office-i4

After joining Automattic, I was encouraged to think about my office setup. The company sponsors the kind of high-quality office perks that you’ll expect in companies at this level, and I took that opportunity to upgrade my own in ways I had been already thinking about. The fact that you are not in their offices, but in your home adds a different feeling to it. Although I appreciate the company efforts and perks, I’d like to stay frugal within comfortable limits, so I didn’t get anything I wouldn’t buy with my own money. I think of my office setup as a gift for the elder me – I wish he’ll be proud of what his younger self is doing for him.

For i4, these are the new additions to my office:

HAG Capisco 8016 chair, which promotes and supports several seating positions.

DIY kit from Autonomous, to build my own height adjustable desktop so I could change between seating and standing positions.

We, humans, are not designed for seating. A lot has been said about the optimal seating position, but an idea that has gained recognition in ergonomics is that changing positions frequently may be the best long-term strategy.

For the past two months, I’ve been experimenting with that idea to learn what works better for me. I’ve used three main positions -traditional seating, saddle seating, and standing- and a lot of other crazy ones. What I’ve found out is that I change positions through the day as my body asks for it, but I mainly use the saddle position (most of the time, but especially when I need to write) and standing (for consuming information). The traditional seating feels a bit unnatural to me now, although it may be a side effect of using the Capisco which is more tailored for other postures. I also have a more traditional chair at home, but I rarely use it.

This is i4. This setup fits me so well that I cannot imagine what i5 will look like yet.

Grammarly

Como parte de mi refuerzo de aprendizaje del inglés, en los últimos meses he estado escribiendo varios textos: formales, informales, críticas, emails, académicos, informes, etc. Mi herramienta favorita actual para esos momentos es Grammarly. Las sugerencias van más allá de la mera corrección ortográfica y son contextuales al estilo de la redacción, te recomienda sinónimos a palabras usadas en exceso, se integra en mi flujo diario de trabajo digital (email, Simplenote, etc) y me envía informes semanales de los errores más habituales que cometo, en el navegador funciona como tesauro al seleccionar una palabra, etc ¿qué más se puede pedir?

Al poco de experimentar la versión gratuita me hice premium; fue un flechazo a primera vista. Aunque no lo he usado todavía me gusta además que, de manera natural, te invite a contactar con un humano para revisar textos que son devueltos en menos de media hora, 3h o un día. Han visto muy bien que la gente con la sensibilidad para usar este tipo de herramientas son presciptores naturales de un servicio de traducción humano.

Como todo buen software, tiene sus peculiaridades que hace que les tengas cariño: por ejemplo, las comas de Oxford han sido un descubrimiento y todavía estoy decidiendo de qué bando estoy. Por el momento, lo único que verdaderamente me molesta es que sólo pueda usarla con los textos de inglés. Me gustaría ver algo así para el español, gallego o portugués. ¿Una especie del famoso dardo en la palabra actualizado al siglo XXI?

Programación de interfaces basada en componentes

O de la composición de interfaces mediante un lenguaje de programación, no un lenguaje de marcado.

En mi travesía por aprender cómo mejorar lo que hice en mi último proyecto, estoy empezando a apreciar el encaje que tienen ideas como el immediate mode, las funciones puras y la inmutabilidad. Conceptos que transcienden modas y que, al entender su utilidad y trade-offs, se introducen en el conjunto de herramientas que tenemos a nuestra disposición, sea cual sea el contexto en que programamos.

Hay otro concepto al que recientemente le estoy prestando atención: programación de interfaces basada en componentes.

¿En qué consiste?

En la creación de elementos reutilizables que sean autosuficientes. Es decir, estables por sí mismos y que no dependan de estado global.

components_before components_after
Lo que hacemos ahora Lo que necesitamos hacer

Es muy interesante comparar cómo diversas herramientas proponen la creación (o no) de componentes. La selección tecnológica es, en sí mismo, un tema con muchos matices y tonalidades y existen diversas aproximaciones para comparar toolkits de programación. Algunas aportan algo, otras no y otras depende.

Por ejemplo, a la hora de comparar dos toolkits líderes de sus respectivos sectores como Wicket y React podríamos hacerlo de la siguientes maneras:

Sin embargo, si los comparamos en términos de cómo componen la interfaz, vemos que su aproximación es similar: proponen crear componentes que encapsulen conjuntamente HTML, CSS y JavaScript. ¿Cómo lo hacen? Tanto Wicket como React crean los componentes mediante un lenguaje de programación (Java/JavaScript) y no mediante un lenguaje de marcado (HTML).

  • Componente en Wicket. En Wicket, la unidad mínima de reutilización es el Panel, que consiste en varios archivos: panel.java, panel.html y opcionalmente otros de localización como panel.properties.
  • Componente en React. En React, el componente es un archivo JavaScript que devuelve código HTML. JSX es únicamente azúcar sintáctico que ayuda a que el código JavaScript sea más expresivo.

Esta aproximación los diferencia de otros toolkits como Angular, Polymer, JSP o Mako, que serían ejemplos de lo contrario: la composición de la interfaz se hace mediante un lenguaje de marcado -HTML- o derivativos que compilan a él.

¿Esto supone una mejora?

La respuesta rápida: sí, porque en un lenguaje de programación tienes a tu disposición 50 años de investigación en computer science, destilados con más o menos suerte. Hombros de gigantes sobre los que mirar más lejos.

La respuesta más elaborada: las interfaces son sistemas complejos, necesitamos subcomponentes para simplificar su creación y mantenimiento. Hay dos áreas donde un lenguaje de programación supera al de marcado para crear subcomponentes: encapsulación y expresividad.

Encapsulación

La encapsulación consiste en la creación de elementos que podamos (re)usar sin la obligación de entender sus propiedades internas, ni de empezar todo desde cero cada vez. En un lenguaje de programación tenemos herramientas para encapsular elementos y funcionalidades como paquetes, módulos, clases, funciones, herencia, mixins, patrones de diseño, etc. Por el contrario, en un lenguaje de marcado como HTML, las opciones son inexistentes.

Iniciativas como WebComponents se han creado 20 años después del propio HTML. Son bienvenidas, pero no podemos obviar el elefante en la habitación: sólo nos ofrece la creación de paquetes, no todo lo demás. En Wicket y React los componentes son elementos que están programados en Java/JavaScript y, por lo tanto, podemos hacer con ellos lo que normalmente haríamos con cualquier otro trozo de código: herencia, composición, aplicar patrones de diseño, etc.

Expresividad

La expresividad consiste en la capacidad de programar los distintos matices que deseamos. En un lenguaje de programación tenemos a nuestra disposición mecanismos como tipos de datos, control de flujo, inversión de flujo, bucles, paso de mensajes, etc. HTML no tiene nada de esto.

Los toolkits que pretenden componer mediante un lenguaje de marcado -JSP en Java, Mako en python, etc- no poseen esa expresividad. Para solventarlo, tratan de integrar parte de ella en un lenguaje propio que compila a HTML: un sistema de plantillas. Un ejemplo típico que casi todos los sistemas de plantillas poseen son algunas construcciones para controles de flujo y bucles. Por ejemplo, en JSP:

<c:when test="${isThisVariableTrue}">
 <h1><fmt:message key="Title" /></h1>
 <c:if test="${isThisOtherVariableTrue}">
 <fmt:message key="showMessage" />
 <c:out value="${value}" />
 </c:if>
 <c:if test="${isThisOtherSecondVariableTrue}">
 <fmt:message key="aDifferentMessage" />
 <c:out value="${aDifferentValue}" />
 </c:if>
</c:when>

También necesitan crear mecanismos para pasar información del código a la plantilla y suelen ofrecer nuevos tags HTML para realizar acciones que HTML no permite. Interacciones que cualquier lenguaje de programación incluye por defecto pero que un sistema de plantillas integra con esfuerzo, limitaciones y a costa de aprender una nueva sintaxis no reutilizable en otros contextos. Para controlar la complejidad necesitamos más expresividad que la que nos aportan bucles y condicionales. Si lo único que puedes utilizar es un martillo, todos los problemas te parecerán clavos.

Conclusión

Sería simplista decir que Wicket y React se han convertido en líderes de sus respectivos sectores únicamente por la propuesta de creación de interfaces mediante componentes. Es, sin embargo, un fundamento que comparten y plausible para explicar por qué React tiene éxito y Polymer no: como la productividad aumenta al usar esta aproximación, se acaba extendiendo por microdecisiones de agentes interrelacionados que buscan su propio beneficio.

Al pivotar la construcción de componentes sobre un lenguaje de programación y no sobre un lenguaje de marcado tenemos a nuestra disposición todas las herramientas de encapsulación y expresividad disponibles en el lenguaje, lo que facilita domar la complejidad inherente a la creación de interfaces. El aumento de productividad es de órdenes de magnitud.

Para entender en toda su complejidad los efectos del cambio, conviene releer parábola de los relojeros.

Vagrant con Debian Jessie

Estos días he estado creando un entorno de desarrollo con vagrant para un nuevo proyecto que corre sobre Debian Jessie con tecnologías un poco antiguas. Publico aquí unas notas sobre lo que hice como recordatorio para mi yo futuro y como ayuda para cualquiera al que le pueda ser útil.

Estos días he estado creando un entorno de desarrollo con vagrant para un nuevo proyecto que corre sobre Debian Jessie con tecnologías un poco antiguas.

Una de las bases de datos de ese proyecto tiene más de 12Gb de volcado, pero la máquina de debian viene con 10Gb para todo el sistema, así que me puse manos a la obra para redimensionar el espacio asignado en disco en la máquina virtual.

Esto resultó un proceso menos trivial de lo que yo me esperaba, asi que publico aquí unas notas sobre la configuración completa de la máquina para mi yo futuro y como ayuda para cualquiera al que le pueda ser útil.

Instalar el entorno base

Lo primero fue descargarme el binario de vagrant. No usé el propio que viene en los repos de mi sistema porque ellos no lo recomiendan. Luego, me puse a buscar una máquina Debian Jessie de 64 bits y me encontré con la “oficial“. Así que tener un Debian listo para poder jugar fue tan sencillo como:

vagrant init debian/jessie64

Configurar la zona horaria

Instalando ciertas librerías de i18n y l10n para el proyecto, me encontré con problemas porque la VM no tenía la zona horaria correcta, así que me instalé un plugin que me permite configurar la zona horaria de la VMs.

vagrant plugin install vagrant-timezone

En mi caso, he seteado la zona horaria para todas las máquinas, aunque se puede configurar de manera individualizada para cada una. He puesto mi zona horaria como “CET”. Aunque en la docu del proyecto dicen que es posible usar la variable :host para que la tome automáticamente del equipo, a mí no me funcionó.

Así que en mi ~/.vagrant.d/Vagrantfile he incluido:

Vagrant.configure("2") do |config|
  if Vagrant.has_plugin?("vagrant-timezone")
    config.timezone.value = "CET"
  end
end

Aumentar el espacio en disco

En este apartado necesitamos hacer 2 cosas: primero, asignar más espacio a la máquina virtual; luego, configurar internamente las particiones para que reconozca ese espacio. Para la primera parte estuve viendo este tutorial. Para la segunda, lo hice de otra manera inspirado por éste.

Lo primero que hay que saber es que vagrant puede usar diferentes providers, que son los sistemas de máquinas virtuales. El que tenía yo era VirtualBox, así que al crear la base vagrant, lo que ocurre es que se guarda un archivo VDMK en el lugar donde mi VirtualBox almacena las VM. En mi caso, esto fue en un directorio tal que “~/VirtualBox VMS/debian-jessie/”, donde encontré el fichero debian-jessie.vdmk que era el que tenía que ampliar. Al parecer, esta funcionalidad sólo está disponibles en discos con formato VDI nativos, no con el VDMK, así que lo primero que tuve que hacer fue convertir el disco a VDI y luego ampliarlo a unos 52GB:

VBoxManage clonehd --format VDI debian-jessie.vmdk debian-jessie.vdi
VBoxManage modifyhd debian-jessie.vdi --resize 50000

A partir de aquí, la operación es la misma que cuando uno hace particiones, aunque yo no lo tenía muy fresco porque hacía años que no me enfrentaba a ello. Lo primero a recordar es que para manejar particiones, los discos tienen que estar desmontados. La idea es arrancar con una ISO como la de GParted y realizar las operaciones desde ese entorno. ¿Cómo hacer esto con una VM de VirtualBox? Pues:

  • añadiendo la ISO como CD al sistema de almacenamiento de la VM

vm_gparted

  • configurando el arranque indicando que lo intente como CD primero y luego como disco

vm_cd

  • Et voilà!

vm_gparted_iso

Como mi VM no tenía sistema de ventanas, arranqué desde consola luego de aceptar los diversos menús que aparecen e hice las operaciones de edición de tabla de particiones con parted. En mi caso, tenía una partición primaria y una extendida con la SWAP en el disco /dev/sda, así que mi plan fue:

  • borrar la partición extendida
  • reclamar para la partición primaria todo el espacio de disco menos el último giga
  • extender el sistema de ficheros al tamaño total de la partición
  • añadir de nuevo la partición extendida con la SWAP

Que viene a ser lo siguiente:

parted /dev/sda
rm 2
resizepart 1 -1GB
quit
e2fsck -f /dev/sda1
resize2fs -p /dev/sda1
exit

Tuve problemas para crear la SWAP con parted, así que hice este último paso posteriormente con cfdisk ya desde vagrant, que es mucho más sencillo y visual.

Para finalizar, hay que recordar desmarcar el arranque por CD y eliminar la ISO de Gparted  del sistema de almacenamiento de la VM en VirtualBox. Entonces ya podemos arrancar nuestro vagrant de nuevo con normalidad y comprobar  que todo ha ido bien.

vagrant up
vagrant ssh
df -h

Libro sobre desarrollo de software en una PYME

Estos días me he dado cuenta de que en 2015 he escrito un ensayo de 7.400 palabras sobre la vida en una PYME de desarrollo de software del que me siento muy orgulloso. Me gustaría editarlo como libro. ¿Algún consejo sobre edición, longitud, maquetación, las gestiones para conseguir el ISBN, etc?

Estos días me he dado cuenta de que en 2015 he escrito sin proponérmelo un ensayo sobre la vida en una PYME de desarrollo de software del que me siento muy orgulloso. Está en las 7.400 palabras. Para convertirlo en librito convendría incluirle un glosario como anexo, para suplir la falta de enlaces complementarios. También hacer algo de trabajo para homogeneizar la lectura y eliminar la necesidad de los videos. ¿Algún consejo sobre edición, longitud, maquetación, las gestiones para conseguir el ISBN, etc?

Arquitecturas para la participación

Este post cierra la serie que inicié hace unos meses sobre desarrollo de software en una PYME. En el primer post, escribría sobre cómo seleccionar tecnología. En el segundo, sobre un mecanismo para objetivar el diseño y reducir los costes de producción. En este último escribiré sobre cómo la organización del código que escribes habilita relaciones con otros.

Este post cierra la serie que inicié hace unos meses sobre desarrollo de software en una PYME. En el primer post, escribría sobre cómo seleccionar tecnología; en el segundo, sobre un mecanismo para objetivar el diseño y reducir los costes de producción. En este último escribiré sobre cómo la organización del código que escribes habilita relaciones con otros.

Programar es una comunicación entre personas

«Programs must be written for people to read, and only incidentally for machines to execute»

Esta frase extraída del prólogo del mítico SICP es una de las perlas que, entre los hackers, define el buen hacer de la profesión y que pone sobre la mesa toda una declaración de intenciones por parte de Abelson y Sussman: la programación es un nuevo medio de comunicación y expresión de ideas entre personas. De este enfoque se deriva que lo fundamental a la hora escribir programas de software es hacerlo de tal manera que nuestros limitados cerebros puedan navegar rápidamente entre los múltiples detalles, con sus distintos niveles de complejidad.

Al escribir código, un primer nivel de comunicación se daría entre programadores (con otros o con nosotros mismos dentro de unos meses). La buena o mala comunicación de las ideas a través del código tendría un impacto económico que se observaría en los tiempos necesarios para la adaptación, mantenimiento y aprendizaje de un programa. Entender un programa es un acto intelectual donde entra en juego la experiencia previa, la capacidad de relación de ideas y la compresión lectora, pero también la buena maña del que lo haya escrito para hacerlo de un modo inteligible. Al igual que un ensayo, un programa requiere cohesión interna y ritmo para ser entendible.

Un segundo nivel de comunicación se daría entre programadores y analistas del dominio y/o clientes. Ese tipo de conversaciones modela cómo se comporta el sistema y se transmiten al código en forma de estructuras de datos y algoritmos.  Un programa no es más que la declaración de un proceso que tiene entradas y salidas.

El hecho de que el software refleje estas relaciones sociales es conocido desde hace décadas y cristaliza en una de las más populares leyes de la programación, la ley de Conway:

«Any organization that designs a system will produce a design whose structure is a copy of the organization’s communication structure.»

Es decir, las conversaciones, grupos y jerarquías existentes en el proyecto se trasladarán al código de manera inevitable. La arquitectura reflejará tu estructura de comunicación y poder, determinando el tipo de relaciones que puedes tener con tu entorno. Toda una profecía ciberpunk.

Pero … ¿cómo habilita o dificulta relaciones la arquitectura?

Una PYME pequeña funciona como una comunidad: aunque existen roles y división de responsabilidades (diseñador, programador, administrador de sistemas, analista), hay mucho de pluriespecialismo. Además, por el propio tamaño de empresa, en muchas ocasiones existen proyectos que se realizan con otros equipos. Existen arquitecturas o maneras de modularizar el código que te permiten que esa división del trabajo sea más efectiva. Veamos algunos ejemplos:

Diseño orientado al dominio

La programación es fundamentalmente la transcripción de las conversaciones entre programadores y analistas. Es necesario tener un un lenguaje común y existir entendimiento entre ambos para que la cosa salga bien. Una de las prácticas que más me ha ayudado a organizar el código es el diseño orientado al dominio, es decir: organizar el código en torno a la interacción de las entidades que se definen en la conversación analista-programador. Aunque parezca una obviedad, no lo es, tiene sus trampas y se hace menos de lo que parece. El impacto de esta práctica deriva de cómo facilita las conversaciones y el entendimiento del programa.

Separación de API e interfaz

Esta técnica, conocida ya por los pioneros, ha retomado fuerza en la era de la web ubicua y la arquitectura REST. Con este mecanismo de modularización, el API define el acceso a datos y acciones que permiten usar el sistema. El interfaz es un mero usuario del API. Además de beneficios técnicos, esta frontera tiene beneficios sociales: facilita una división del trabajo en aspectos muy distintos de la aplicación, que requieren conocimientos, técnicas y herramientas diferentes. Esto permite que la colaboración diseñador-programador sea más fluida.

Hay 2 ejemplos que ilustran muy bien mi experiencia. En ciertos proyectos donde creamos formularios para la introducción de datos con una aplicación de escritorio, aplicar este principio nos ha permitido que nuestros analistas (repito: analistas, no programadores ni diseñadores) hayan diseñado por sí mismos los formularios que luego los programadores integran en la aplicación. En otros proyectos, hemos contratado a empresas para que nos ayudasen a crear un API mientras nosotros nos centrábamos en el diseño de la interfaz (y viceversa). Ambas situaciones serían muy complejas de delegar (o casi imposibles) si no hubiésemos hecho un uso intensivo de este principio a la hora de desarrollar el producto.

Creación de plugins o módulos

Otra manera muy evidente de crear espacios para la colaboración es permitir añadir nuevas funcionalidades a tu software mediante la creación de plugins o módulos. Este tipo de arquitectura minimiza la barrera de entrada para que nuevos colaboradores puedan ser productivos muy pronto, ya que no necesitan conocer todo el proyecto antes de incluir una funcionalidad, sino que les basta con conocer sólo lo que necesitan.

En nuestra experiencia colaborando con un proyecto empezamos por el desarrollo de pequeñas extensiones o plugins con funcionalidades limitadas. Pasados unos meses, nos sentimos cómodos y con conocimiento suficientes de ciertas partes internas de la aplicación como para modificarlas y enviar mejoras. Los primeros plugins fueron exploratorios, nos permitieron familiarizarnos con el código y el producto; una vez confiamos, nos lanzamos a cosas mayores. Fue precisamente un aspecto técnico (la creación de plugins) el que nos habilitó para iniciar una relación comercial con el proyecto: de no existir esa posibilidad al principio, se nos hubiese hecho muy difícil como PYME invertir todo el tiempo necesario para entender un proyecto tan grande.

Estos son algunos ejemplos de cómo la arquitectura habilita o impide relaciones, pero existen otros miles de pequeños detalles. La modularización del código es fractal, influye en todas las capas de la aplicación.

Conclusión

El programador es, principalmente, un organizador de ideas y un ensayista. Necesita cierta capacidad lógica para analizar y diseñar un sistema, pero también para organizarlo de manera que habilite buenas conversaciones y una división del trabajo efectiva. Necesita entender a las personas con las que trabaja.

Por ello no me parece casual que Kent Beck, el gran recuperador de ideas de nuestra generación, apuntase que uno de los factores con más impacto a la hora de ser un buen programador es la empatía.