Archivo de la categoría: Desarrollo

Temas relacionados con programación y herramientas de desarrollo.

Como hacer que tu código sea legible

Reconozco que no soy el mejor programador del mundo, pero intento mejorar cada día. Puede sonar tonto, pero una de las mayores complicaciones del trabajo de un programador es conseguir que el código que escribes un día sea entendible por otras personas !y nosotros mismos! en el futuro.

Casi todo lo que voy a contar a continuación lo he aprendido a partir de diversas lecturas y por propia experiencia.

Algunos de los consejos más útiles que creo que son importantes tener en cuenta son:

Utiliza el inglés al programar

Puede resultar extraño para algunos que dominando un idioma (español, portugués, francés, etc.) tengan que programar en inglés. El motivo es sencillo, la mayor parte de la documentación en nuestro sector está en inglés por lo que acabamos mezclando idiomas. ¿A quién no le ha pasado tener funciones llamadas setPeso o getNombre?

Por otra parte, el inglés permite abrirnos puertas en dos sentidos:

  • Para empezar como programadores que dominemos el inglés podremos acceder a más puestos de trabajo. La globalización no es sólo para las empresas; nosotros también podemos aprovecharnos de ella.
  • Podemos contratar programadores extranjeros que no tendrán problemas al leer el código.

Utiliza normas de codificación

Esto en algunas metodologías se llama gestión de configuración del software. También se puede encontrar como convención de codificación (code convention) o guías de estilo. La idea es tener una serie de normas que indica cómo debe ser la nomenclatura de codificación y tabulación para que todo el desarrollo sea consistente. Con esto conseguimos un programa más homogéneo y fácil de entender.

Tampoco es necesario inventarse una propia porque ya existen muchas. A modo de ejemplo:

Elige bien los nombre

La verdad es que el tema de los nombres es uno de los más complejos. Sobre todo para funciones y clases. Por norma los nombres de métodos, funciones, clases, etc. deben ser descriptivos y breves. Por ejemplo, un nombre como ApplicationModelAbstractObjectFactory quizá sea un poco largo, pero parece descriptivo. Igualmente, algo como EntraSola (caso real) es muy breve, pero no dice nada; no es posible determinar realmente su funcionalidad (además, puede inducir a tener pensamientos impuros).

Utiliza comentarios

Este punto es bastante controvertido porque mucho programadores piensan eso de «el código bien escrito se entiende solo». Y es verdad. El problema es cuando el código no hace lo que toca.

No hay que llenar el código de comentarios, deben ser lo mínimo posible y sólo para cosas que claramente lo necesiten. Esto suele ser para aquellas construcciones que sean muy abstractas y que cueste mas comprender.

Se organizado

Otro punto que quizá no sea obvio, pero es cierto que tener bien organizado el código ayuda mucho a su legibilidad. Es más fácil entender una función que está dividida en partes (preparación de datos, realización de acciones, generación de resultado) que otra en la que se vayan mezclando todas estas tareas.

Refactoriza

Por mucho que nos esforcemos en que nuestro código sea claro y legible, con el tiempo acabará por convertirse todo en algo caótico. Es normal, porque conforme evoluciona un software se añaden nuevas funcionalidades para las que no estaba inicialmente pensado.

Para evitar esto hay que hacer ciclos de refactorización que mantengan el código limpio y optimizado.

Conclusiones

Está claro que una parte importante para evitar la deuda técnica reside en la calidad del código un código más claro facilita el mantenimiento al poder ser entendido fácilmente (ojo, que a parte de esto es necesario un buen diseño del mismo). El uso de unas normas que permitan que éste sea entendible ayudan mucho para evitar este problema.

Finalmente me gustaría comentar que este artículo es meramente introductorio y faltan muchas cosas que contar. Estoy más que seguro que habrá discrepancias con algunas cosas; si es así, te animo a que dejes un comentario.

Bibliografía

La mayor parte de lo que he expuesto aquí procede del libro The Art of Readable Code de O’Relly.

Errata

  • 2017-02-01: Corrijo la parte de la deuda técnica a sugerencia de Fioddor.

Control de errores

Uno de los grandes problemas al programar es realizar el control de los errores. Por diversos motivos me ha tocado hacer un trabajo de estudio sobre qué opciones tenemos para realizar el control de los errores de una aplicación.

La verdad es que hubiese estado muy bien hacer una tormenta de ideas al respecto, pero en estos momentos estamos todos demasiado atareados.

El siguiente texto, sin pretender ser una guía exhaustiva de lo que es el control de errores, sí busca poner un punto de partida para todo aquel que quiera ver qué mecanismos hay para este fin.

Seguir leyendo Control de errores

Tratamiento de cadenas en C

Cuando empezamos a programar en C una de las cosas que probablemente más nos cuesta controlar es el tratamiento de cadenas.

El lenguaje C es de bastante bajo nivel, por lo que sus tipos de datos básicos son muy cercanos a lo que la máquina puede utilizar. Los ordenadores no entienden de cadenas, pero sí de direcciones de memoria. De hecho, no existe un tipo de cadena, sino que se usan arrays de bytes. Y lo mejor de todo, ¡en C es el programador el que debe conocer la longitud del array! (cuanto bien, y a la vez cuánto mal, han hecho los lenguajes modernos).

Todas las funciones del estándar de C presuponen que una cadena es un array de bytes donde el último carácter es un nulo.

No voy a entrar en más detalles sobre que son las cadenas. Si quieres aprender cómo se programa con ellas puedes leer Cadenas de caracteres del manual Programación en C de  wikibooks.

Quien más y quien menos habrá visto que muchos problemas de seguridad en bibliotecas en C viene precisamente del tratamiento de cadenas. Casi se podría decir que si tienes que hacer un programa que tenga que manejar cadenas es aconsejable que mires cualquier otra cosa que no sea C. Aún así, esto no siempre será posible.

En el lugar en el que trabajo hemos empezado a cambiar el modo en que utilizamos las cadenas. Para facilitar su uso y reducir la cantidad de errores que pueden producirse, hemos creado un nuevo tipo de cadena. Bueno, se parece más a un StringBuilder de java. Las motivaciones son varias:

  • Queremos un tratamiento de cadenas más rápido (tener que recorrer una cadena cada vez que quieres conocer su longitud no es óptimo).
  • Queremos poder trabajar con cadenas sin preocuparnos por reasignar constantemente memoria. ¿Habéis probado un strcat cuando estás usando arrays y el buffer de destino es más pequeño que la cadena a concatenar?
  • Para ciertas operatorias queremos poder tener cadenas con caracteres nulos. Las cadenas de C no se llevan bien con esto, pero nosotros creamos cadenas para mandar a dispositivos que no sólo aceptan el nulo, sino que ciertas operaciones lo requieren. Con las funciones estándar sería muy complicado esto.

Personalmente no creo que seamos los primeros en hacer algo así; no tengo la más mínima duda de que hay muchas personas con los mismos problemas.

Las ventajas de usar esta biblioteca son inmediatas:

  • Simplificamos el código por no tener que preocuparnos en si hay o no espacio para almacenar la cadena y en la gestión de este espacio.
  • Minimizamos errores pues no nos pasaremos nunca del buffer (siempre podemos quedarnos sin memoria).
  • Agilizamos el desarrollo al poder escribir directamente código que haga lo que queremos (es una consecuencia del primer punto).
  • Se pueden optimizar ciertas operaciones. Esto se debe a que no hay que estar constantemente recorriendo cadenas para medirlas o que al «liberar» una cadena, no hacemos free y malloc, sino que marcamos la cadena como de longitud 0 (que es más rápido y permite aprovechar la memoria).

¡Mucho ojo con esto! Que en el día a día de mi trabajo esta haya sido una buena solución no significa que lo sea para todos los casos. Siempre hay que evaluar convenientemente cada situación antes de decantarse por una u otra solución.

Pseudo lambdas con C

Últimamente se lleva mucho el tema de las funciones lambda y los closures en los lenguajes modernos. Un ejemplo es que en C++, desde la versión de 2011, ya las incorpora. Igualmente, otros lenguajes, como Ruby, Groovy o Rust ya han sido diseñados con esta característica.

Advertencia: el código que vamos a mostrar a continuación hace uso de extensiones de GNU C. Para ser exactos, hace uso de Statement Expressions y de Nested Functions. Estas extensiones solo son soportadas por unos pocos compiladores a parte de GNU C (ver Compiler support of GNU Statement Expressions en stackoverflow).

Aunque en C no existe nada parecido es factible simularlo. Todo sea dicho, el código puede ser muy feo. En el siguiente ejemplo tenemos una función que recorre un conjunto de elementos. Esta función la llamamos proccess. Por cada elemento procesado llama a una función de callback. Lo normal es crear la función de callback a parte, lo que hace que en ciertos casos sea incómodo el mantenimiento del código.

Otra opción es declarar la función de callback in situ justo en el momento que la necesitamos:

Como puede verse, nos aprovechamos de ‘({‘ y ‘})’ para declarar un bloque de código que devuelve el valor de la última expresión para crear nuestra función de callback. Como en este caso el nombre no nos importa, la llamamos ‘_’. Lo mejor de todo es que podemos repetir este nombre sin que por ello suponga que vaya a haber conflicto en la compilación ni en la ejecución.

Actualización 1: incluimos advertencia sobre compiladores.

Agilismo mal aplicado

Recientemente una gran empresa española ha decidido dar el salto a desarrollos ágiles. Esto viene motivado por un gran proyecto que abarca una gran cantidad de áreas dentro de la empresa.

Para tener claro lo que es el agilismo, estas son los pilares sobre los que se va a apoyar la empresa para usar las metodologías ágiles:

  • La planificación es lo más importante y no puede ser cambiada de ningún modo.
  • El cliente/usuario es lo menos importante y hay que interactuar lo mínimo con él.
  • La documentación debe ser muy exhaustiva.

Hay que añadir, además, que se ha retroplanificado: primero se ha definido la fecha de fin de proyecto y a partir de ahí se planifica el resto del proyecto.

Comparemos esto con el manifiesto ágil (extraído del manifiesto por el desarrollo ágil de software):

  • Individuos e interacciones sobre procesos y herramientas
  • Software funcionando sobre documentación extensiva
  • Colaboración con el cliente sobre negociación contractual
  • Respuesta ante el cambio sobre seguir un plan

Ahora comparen; cualquier parecido es pura coincidencia. Prefiero no entrar en más detalles porque el asunto me enoja mucho (por más motivos que quizá en un futuro lejano cuente).

Concurso de C «a pelo»

Recientemente surgió en una newsletter de O’Reilly un concurso que me pareció bastante curioso. Las bases eran las siguientes:

It seems like a new language is spawned every day, but the old tried-and-true warriors of the code world continue to soldier on. This week, the specification for C++ version 14 was frozen, and it includes such features as enhanced lambda expressions (because all the cool languages are doing it). In honor of this momentous event, we’re running a little contest to test your chops with the bare C. Sure, anyone can create awesome code using all the libraries that modern operating systems come with, but what can you do close to the metal?

Your challenge is to create a nontrivially useful application that will compile on a Linux system using the following command:

 gcc -nostdlib -o sample sample.c startstub.S

startstub.S is an i386 assembler file that can call main() without the standard libraries and can be downloaded here. You need to have a function main defined as «int main()» that can return a value, which will end up in $?. Only C is allowed, no assembler or other funny business. Entries will be judged on two criteria: binary size and functionality of the program. For reference, a program that does nothing weighs in at 1502 bytes.

Básicamente dice que hay que hacer un programa en C, que no va a poder enlazar con biblioteca de funciones alguna (no hay libc) y que no se puede hacer uso de ensamblador. Esto último es importante, porque con esta restricción se pierde la capacidad de hacer llamadas al sistema: no se puede reservar memoria con malloc, ni leer ni escribir en disco, etc. Eso sí, el programa debe tener alguna utilidad y te dicen que puede devolver un valor que será podrá ser consultado con $? (variable de la shell con el resultado del último programa ejecutado). Eso sí, el startstub.S que suministran no permite dicho uso (¿despiste quizá?).

Estas premisas tan restrictivas me animaron a tratar de crear algo para el concurso. Tras varias ideas descartadas decidí hacer una aplicación que devolviese la posición de una palabra en una lista de palabras (no parece muy útil) y que con una opción permitiese devolver la palabra de la lista más parecida a la buscada (puede resultar algo más útil).

El problema es que con lo suministrado por la gente de O’Reilly, la función main no recibe argumentos. Así que no tenemos un mecanismo sencillo para comprobar los parámetros.

La solución al problema anterior es tener en cuenta que Linux, cuando ejecuta un programa y pasa le pasa el control deja en la cima de la pila argc, luego argv y después env. Como la función _start suministrada no mueve los punteros de la pila, main tendrá esos mismos datos. Así que sólo hay que buscar un mecanismo para obtener un puntero a la cima de la pila.

En la arquitectura i386, los argumentos de las funciones se pasan por pila. Así, si tenemos una función que sólo acepta un parámetro, en el momento en que entramos en la función nos encontraremos esto en la cima de la pila:

0x000000 Dirección de Retorno
0x000004 Parámetro

Por tanto, la dirección de memoria del primer parámetro será una posición en la pila. En este caso sabemos que se correspondería con argc.

El problema es que en las arquitecturas de 64 bits, no se comporta de este modo: los 6 primeros parámetros son registros de procesador. En este caso, si es una arquitectura de este tipo, creamos una función que acepta 7 parámetros. Los seis primeros que serán registros (y los ignoramos) y el séptimo que ya se encuentra en la pila.

El resto del programa ya no tiene más misterios y lo podéis ver a continuación:

Curiosidades de C++

Ayer trasteando con el compilador de C++ descubrí una cosa nueva sobre cómo funciona por dentro.

Si compilamos el siguiente programa y lo ejecutamos veremos que falla con un bonito SIGSEGV:

El motivo es que, para empezar, hay dos estructuras que son diferentes pero se llaman igual. El constructor de dichas estructuras no deja de ser una función, por lo que en cada fichero objeto se generará una función para construir la estructura. Sí, la función se llamará igual en ambos objetos.

Cuando enlazamos se queda con la primera función que encuentre (en este caso con la de a.cpp). Así que cuando desde run_example_b se trata de instanciar la estructura lo hace con el constructor de a.cpp.

Hasta ayer por la tarde no caí en porque esto funciona así, pero es bastante lógico. Hay muchas clases en las bibliotecas estándar (y de terceros) cuyos constructores están implementados en la propia cabecera. Esto significa que cada fuente que tengamos tendrá su propia implementación del constructor y luego, al enlazar, se reduce a sólo uno.

Si el ejemplo anterior lo compilamos todo con ‘-O2’, el constructor lo convierte en inline para optimizar y funciona correctamente.

Nota mental: hay que mejorar los nombres de funciones in clases según su utilidad y contexto (debe ser más semántico).

Creación de paquetes Debian (1 de 3)

Como prometí en mi anterior entrada, voy a tratar el tema de creación de paquetes para distribuciones de tipo Debian. La explicación va a estar dividida en tres partes:

  1. Motivación de la creación de paquetes
  2. Creación de paquetes
  3. Distribución de paquetes

Motivación

Mucha gente que usa Linux por primera vez se da cuanta enseguida de la gran cantidad de aplicaciones que hay a golpe de una pulsación de ratón. No son necesarios los números de serie, ni los típicos cracks. De hecho, en una distribución como Debian puede encontrarse cerca de 30.000 paquetes preparados para instalar.

Por desgracia no siempre está la herramienta que buscamos o la versión que hay es muy vieja y le faltan características. En ese momento es cuando se complica la cosa y toca preparar el programa por nosotros mismos. Esto suele suponer la ejecución de alguna secuencia de comandos terminada en «make ; make install». Cuando hacemos esto último corremos el riesgo de sobrescribir configuraciones, bibliotecas o programas si  querer. Muchas veces esto no tiene porque ser importante, pero si, por ejemplo, estamos instalando algo en un servidor esto puede suponer un grave problema en el futuro.

Por el motivo anterior en las distribuciones de Linux se utiliza una cosa llamada paquete. Un paquete es un fichero que contiene información sobre los requisitos para instalar su contenido, acciones a ejecutar durante la instalación y los ficheros a instalar. Toda esta información facilita la eliminación y/o sustitución del paquete y comprobación de posibles conflictos con otros paquetes.

Finalmente, utilizando un sistema de distribución de paquetes conseguiremos que al tratar de instalar el sistema sea capaz descargar las dependencias (de otro modo solo comprobaría si hay o no problemas).

En las siguientes entradas veremos el proceso de creación de un paquete y su posterior distribución.

Código ofuscado

Muchos de los que leen este blog seguro que ya saben a qué me refiero por código ofuscado. Si es ese el caso, probablemente lo que cuente a continuación ya lo sepas. Para los demás, si os apetece ver las cosas que un friki de la programación puede hacer cuando se aburre, continúa leyendo.

El código ofuscado es, según la Wikipedia el «[..] acto deliberado de realizar un cambio no destructivo, ya sea en el código fuente de un programa informáticocódigo máquinacuando el programa está en forma compilada o binaria, con el fin de que no sea fácil de entender o leer». De cara al programado que quiere echarse unas risas es hacer un programa correcto pero que no pueda entenderse leyéndolo. ¿Y eso por qué? Porque podemos.

Imaginemos algo sencillo: en C hacer un programa que muestre por pantalla un «Hola, mundo» es algo tan aburrido como lo siguiente:

int main(void)
{
   printf("Hola, mundo");
   return 0;
}

Sin duda algo muy soso, sería más divertido una cosa como la siguiente:

#define __ main
#define ___ printf
int
__(int _, char **b)
{
   return (_ > 0)? ((_==1)?__(_-1, 0):0):___("Hola, mundo");
}

Ahora mismo no sé si funciona, pero como podéis comprobar es muuuucho más clarooooo (entiendase el sarcasmo). Ya lo único que le faltaría es ocultar el mensaje de algún modo estrafalario.

A parte de las connotaciones divertidas que pueda tener la ofuscación de código (hay códigos con forma de avión, círculos, etc.), también sirve para reducir el tamaño del código de ciertos programas. Por ejemplo, en el mundo de la web, los códigos javascript se suelen comprimir utilizando técnicas de sustitución parecidas a las de la ofuscación para que ocupen menos y se transmitan más rápido por la red.

También hay quien utiliza este tipo de códigos para evitar que se copien los algoritmos utilizados y que no se pueda hacer ingeniería inversa.

Distributed Hash Cracker

Ese nombre tan rimbombante es, ni más ni menos, que la base de mi proyecto final de carrera, felizmente entregado ya. Es una herramienta que en su momento se encontraba en internet con licencia BSD y que por algún motivo desapareció. Lo curioso no es que no esté ya el programa, sino toda la web que lo albergaba junto a más proyectos.

Tras hablarlo con mi director de PFC hemos decidido publicar el código con las modificaciones introducidas en los próximos meses (puede que antes haya que hacer aún algún cambio). De momento mi idea es ponerlo en GitHub y aún tengo la duda de si puedo cambiar la licencia de BSD a GNU GPL v3 o si sólo puedo cambiar los ficheros que haya modificado (que creo son bastantes).