Buenas práctias en el desarrollo de software con javascript. Código limpio, mantenible, escalable, tests,... Charla perteneciente al evento Betabeers Murcia del día 9 de Mayo de 2014
4. if (wl && wl.length) {
for (i = 0, l = wl.length; i < l; ++i) {
p = wl[i];
type = Y.Lang.type(r[p]);
if (s.hasOwnProperty(p)) { if (merge && type == 'object') {
!
Y.mix(r[p], s[p]);
} else if (ov || !(p in r)) {
r[p] = s[p];
}
}
}
}
if (wl && wl.length) {
!
for (i = 0, l = wl.length; i < l; ++i) {
p = wl[i];
type = Y.Lang.type(r[p]);
!
if (s.hasOwnProperty(p)) {
if (merge && type == 'object') {
Y.mix(r[p], s[p]);
} else if (ov || !(p in r)) {
r[p] = s[p];
}
}
}
}
5. GUÍA DE ESTILOS
¿Por qué? Mantener el mismo estilo de código en un equipo de desarrollo
es fundamental por varios motivos. Cualquier miembro del equipo puede
comprender fácilmente código escrito por otro miembro, permitiendo un
desarrollo más eficiente y evitando tener que gastar tiempo en comprender
un estilo de código diferente. Los errores son más fáciles de detectar.
Inicialmente el equipo debería mantener una reunión para decidir cuál será
su estilo de código, ya que todos los miembros tendrán sus preferencias
personales y todos intentarán mantenerlas. Hay que lograr un compromiso.
6. ESTILOS: HERRAMIENTAS
• JSLint: Desarrollado por Douglas Crockford, esta herramienta nos informa sobre
errores potenciales hallados en nuestro código, así como advertencias sobre el uso
de ciertos patrones de estilo no recomendados.
• JSHint: Es un derivado de JSLint mantenido por Anton Kovalyov.Ayuda a mantener
las convecciones de código así como evitar errores en nuestro código JS. Mozilla,
Facebook,Twitter, Bootstrap, jQuery,..., todos ellos utilizan esta herramienta, por lo
que es una buena opción ya que su mantenimiento está prácticamente asegurado.
Incluir una herramienta de este tipo en nuestro proceso de construcción es una
práctica muy recomendable, ya que forzamos a que se cumplan las convecciones de
código, así como evitar errores potenciales (p.e. uso de eval).
7. ESTILOS: PRINCIPALES
ELEMENTOS ATENER EN CUENTA
• Identación: La primera decisión a tomar es el estilo de identación. Dos alternativas: tabs o
espacios. Normalmente 4 espacios.
• Terminación de sentencias: JS permite terminar lineas con y sin ; debido al ASI (Automatic
Semicolon Insertion). La recomendación aquí es insertarlo siempre, así evitaremos que el
código interpretado tenga un comportamiento totalmente diferente al deseado. (Ver ejemplo
siguiente). JSLint y JSHint lanzan warnings en estos casos.
• Tamaño de línea: Estrechamente relacionado con la identación. La mayoría de convecciones
indican 80 caracteres por línea (Android 100). Establecer un ancho máximo evita tener que
hacer scroll horizontal cada vez que queremos modificar el código, mejorando la producción.
• Rotura de líneas: Cuando una línea alcanza el máximo de caractéres, ésta debe ser dividida en
dos. La norma aquí suele ser partirlas después de un operador y que la siguiente línea tenga dos
niveles de identación. (ver ejemplo).
8. • Líneas en blanco: La norma en este caso es introducir una
línea en blanco para separar partes de código que no estén
relacionadas, quedando el código como si fuesen párrafos en
lugar de una secuencia de símbolos. Entre métodos, entre
variables locales de un método y su primera sentencia, antes
de un comentario, entre secciones lógicas de un método.
• Nombramiento: Se recomienda siempre utilizar el estilo
propio que tenga el lenguaje de programación. En el caso de JS
es camel case.Variables = nombres, funciones = verbos,
objetos con capitalización.
• Constantes: En mayúsculas y con barra baja para los espacios.
9. // Código original
function getEvento() {
return
{
event: "Betabeers MurciaVI",
place: "You&Co"
}
}
Ejemplo:Terminación de sentencias.
//Tras el ASI, quedaría así
function getEvento() {
return;
{
event: "Betabeers MurciaVI",
place: "You&Co"
};
}
10. Ejemplo: Rotura de líneas
// MAL: Sólo un nivel de identación.
callAFunction(document, element, window, "some string value", true, 123,
navigator);
// MAL: Línea partida antes de operador.
callAFunction(document, element, window, "some string value", true, 123
, navigator);
// Bien: Después de operador seguido de dos niveles de identación.
callAFunction(document, element, window, "some string value", true, 123,
navigator);
11. Ejemplo: Líneas en blanco.
if (wl && wl.length) {
for (i = 0, l = wl.length; i < l; ++i) {
p = wl[i];
type =Y.Lang.type(r[p]);
if (s.hasOwnProperty(p)) {
if (merge && type == 'object') {
Y.mix(r[p], s[p]);
} else if (ov || !(p in r)) {
r[p] = s[p];
}
}
}
}
if (wl && wl.length) {
!
for (i = 0, l = wl.length; i < l; ++i) {
!
p = wl[i];
type =Y.Lang.type(r[p]);
!
if (s.hasOwnProperty(p)) {
!
if (merge && type == 'object') {
Y.mix(r[p], s[p]);
} else if (ov || !(p in r)) {
r[p] = s[p];
}
}
}
}
12. Ejemplo: Nombramiento.
// Bien
var limite = 30;
var asuntoMensaje = "Mantenimiento";
var encontrado = true;
var MAX_PERSONAS = 100;
!
// Mal: Fácil confusión con funciones
var getLimite = 10;
var isEncontrado = true;
!
// Bien
function getName() {
return myName;
}
!
// Mal: Fácil confusión con variable
function theName() {
return myName;
}
13. PRÁCTICAS DE
PROGRAMACIÓN
Las prácticas de programación son otra convección
de código, ayudándonos a resolver problemas
cotidianos y estableciendo pautas para la reutilización
y mantenibilidad del código.
A continuación veremos conceptos que para
muchos posiblemente sean ya conocidos pero que
son dignos de destacar.
14. .box {
width: expression(document.body.offsetWidth + "px");
}
<button onClick="doSomething()" id="boton">Haz clic aquí</button>
var element = document.getElementById("boton");
elememt.style.color = "red";
element.style.float = "left";
element.innerHTML = "<strong>CLICA AQUÍ</strong>;
var boton = document.getElementById("boton");
boton.addEventListener("click", doSomething, false);
#boton {
color: "red";
float: "left";
}
// tres alternativas vistas más adelante
15. ACOPLAMIENTO UI & LÓGICA
Un factor determinante para la
mantenibilidad de un proyecto
es reducir el acoplamiento
tanto como sea posible.
En la imagen podemos ver las
capas de una aplicación web
(client-side).
Siguiendo una serie de consejos
podemos mantener estas capas
lo más desacopladas posible.
16. REDUCCIÓN DE
ACOPLAMIENTO
• Mantener JavaScript fuera de CSS.
• Mantener CSS fuera de JavaScript.
• Mantener JavaScript fuera de HTML.
• Mantener HTML fuera de JavaScript.
17. MANTENER HTML FUERA DE
JS
Este es el punto más importante e interesante de esta
sección, ya que el resto de puntos son relativamente
lógicos.
En muchísimas ocasiones, ya sea por la falta de
conocimiento o por comodidad, empezamos a desarrollar
aplicaciones JS y empezamos a embeber código HTML en
la lógica de la aplicación. Hay unas alternativas que
podemos utilizar para evitarlo:
18. • Cargar HTML desde el servidor.
• Plantillas simples en el lado del cliente.
• Plantillas complejas en el lado del cliente.
19. function loadDialog(name, oncomplete) {
!
var xhr = new XMLHttpRequest();
xhr.open("get", "/js/dialog/" + name, true);
!
xhr.onreadystatechange = function() {
!
if (xhr.readyState == 4 && xhr.status == 200) {
var div = document.getElementById("dlg-holder");
div.innerHTML = xhr.responseText;
!
oncomplete();
} else {
// handle error
}
};
!
xhr.send(null);
}
Alternativa 1
20. <li><a href="%s">%s</a></li>
!
function sprintf(text) {
var i=1, args=arguments;
!
return text.replace(/%s/g, function() {
return (i < args.length) ? args[i++] : "";
});
}
!
// uso
var result = sprintf(templateText, "/item/4", "Fourth item");
Alternativa 2
22. GLOBALIZACIÓN
El entorno de JavaScript tiene su ejecución asociada
a un objeto global (en los navegadores web window).
Por lo tanto, cualquier variable definida fuera de una
función se considera una variable global.
23. var numero = 6;
!
function mostrarNumero() {
alert(numero);
}
!
alert(window.numero); //=> 6
alert(typeof window.getNumero); //=>"function"
fichero1.js
...
ficheroN.js
24. • Entre los problemas de la globalización
podemos nombrar los siguientes:
• Colisión de nombres.
• Fragilidad del código.
• Dificultad en el testeo.
• Globales accidentales.
25. AUTOMATIZACIÓN
• Primeras páginas web sólo contenían algunos ficheros JS, los scripts eran
desarrollados por una persona.
• Hoy en día las aplicaciones web contienen gran cantidad de scripts, con miles de
líneas de código programadas, en ocasiones, por más de una docena de
programadores.
• Con este contexto, las metodologías utilizadas hace años dejan de servir.
• Incluir JS en los procesos de automatización es un paso muy importante para
conseguir proyectos que puedan ser mantenidos.
26. VENTAJAS
• El código de desarrollo no
tiene que estar "reflejado" en el
entorno de producción.
• Pueden ser ejecutados análisis
automáticos para detectar
fallos o errores.
• Procesamiento de los ficheros
JS.
• Fase de testeo es
automatizada.
• Desplegado en los servidores
de producción es sencillo.
• Tareas comúnes pueden ser
ejecutadas numerosas veces de
manera sencilla.
• Desarrolladores podrían
necesitar ejecutar un "build"
mientras hacen cambios en
entornos de desarrollo.
(Preferencia por refrescar en el
navegador).
• El código de producción no es
exactamente como el
desarrollado. Seguimiento de
bugs.
• Desarrolladores menos
técnicos podrían tener
problemas usando los sistemas
de construcción.
DESVENTAJAS
27. AUTOMATIZACIÓN: ESTRUCTURA DE
DIRECTORIOY FICHERO
El primer paso es establecer una estructura de directorios y
ficheros. La estructura dependerá directamente del tipo de
proyecto que estemos desarrollando. Esta etapa es
importante ya que el proceso de construcción dependerá
directamente de la organización de ficheros y directorios,
por lo que tener una estructura lógica es fundamental.
28. INDEPENDIENTE ALTIPO DE
PROYECTO...
• Un objeto por archivo: Al igual que otros lenguajes de programación. Reduce el riesgo de trabajar varias
personas en el mismo fichero.
• Agrupar archivos relacionados en directorios: Si existen objetos relacionados es aconsejable mantenerlos
en un mismo directorio (p.e. Utilidades)
• Mantener librerías separadas del código: Cualquier código no desarrollado por nosotros debería
mantenerse apartado del nuestro. De hecho, lo ideal es hacer uso de un CDN.
• Determinar la ruta de construcción: Debe ser un directorio completamente separado del directorio fuente,
ya que este no debería ser tenido en cuenta en ningún chequeo u otro proceso automatizado.
• Mantener código de testeo cerca: Esto ayuda a los desarrolladores a echar en falta cualquier código de
testeo que todavía no haya sido implementado.
• En cualquier caso: son recomendaciones, la estructura debe ser decidida por el equipo de desarrollo ya que
en muchos casos dependeremos del framework utilizado en el lado del servidor.
29. LAYOUT BÁSICO
Un layout básico suele ser el siguiente:
• build: Para los ficheros construidos finales.
• src: Para todos los ficheros desarrollados,
conteniendo subdirectorios.
• test o tests: Para todos los tests de nuestra aplicación.
31. ANT
La elección de un sistema de construcción
depende de las herramientas con las cuales
los desarrolladores sean más familiares.Ya que
las aplicaciones web suelen ir acompañadas
de un back-end (posiblemente) desarrollado
en Java, esta herramienta resulta muy
apropiada para la tarea en cuestión.Aunque
hay muchas alternativas a día de hoy, nosotros
nos vamos a centrar en esta herramienta por
su larga trayectoria y su sencillo uso.
Nos centraremos en la construcción de
JavaScript, pero las técnicas vistas podrían ser
aplicadas a otros lenguajes.
32. En primer lugar, lo básico:
• task: Es un paso del proceso (p.e. copiar
archivos, ejecutar un comando, etc).
• target: Un grupo de tareas con un orden
secuencial.
• project: Contenedor de todo lo anterior.
34. En nuestro fichero de construcción podemos
necesitar cierta información como, por ejemplo,
el directorio src de nuestro código, el directorio
que contiene las librerías o el directorio
resultante del fichero JavaScript final.
Toda esta información puede definirse mediante
propiedades y ser cargadas posteriormente
desde nuestro fichero de construcción.
36. VALIDACIÓN
• JavaScript no es un lenguaje compilado, por lo que
los desarrolladores no tienen la información que
proporciona ese paso.
• Añadiremos JSHint (visto anteriormente) a
nuestro proceso de construcción.
37. • Encontrar archivos a validar: <fileset> &
<filelist>.
• Ejecutar JSHint a través de Rhino.
• Detener la construcción en caso de error.
39. CONCATENACIÓNY
"BAKING"
• Si hemos seguido la recomendación de un objeto por
archivo, tendremos docenas de ficheros.
• La concatenación reduce peticiones al servidor, reduciendo
así el overhead producido por las cabeceras HTTP.
• Cada proyecto tendrá su propia directiva a la hora de
concatenar los ficheros, veremos que con Ant es una tarea
bastante sencilla de realizar.
40. CONCATENACIÓN: PASOS
• Encontrar ficheros a concatenar, teniendo en
mente cuál debería ser su orden.
• Determinar el fin de línea.
• ¿Necesitamos cabeceras o pie de página?
42. BAKING: DESCRIPCIÓNY
PASOS
• Esta etapa de la construcción puede ser util para
añdir información de licencias o reemplazar cierto
texto en nuestros ficheros JS.
• El ejemplo visto anteriormente es un tipo de
baking.
• Lo veremos más claro con el siguiente ejemplo.
44. MINIMIZACIÓN &
COMPRESIÓN
La minimización y compresión son los últimos pasos
mas relevantes de la construcción. La minimización
consiste en eliminar espacios en blanco, comentarios
y realizar algunas mejoras en los ficheros para
hacerlos tan pequeños como sea posible.
Por otro lado podemos comprimir los ficheros (gzip,
etc).
47. MINIMIZACIÓN
Hay dos tipos: con parseo de código o mediante
expresiones regulares. Los primeros son más seguros y
suelen producir menor número de fallos de sintaxis.
• YUI Compressor: Julien Lecomte.
• Closure compiler : Google engineers.
• UglifyJS : Mihai Bazon.
48. COMPRESIÓN
Una vez los ficheros han sido minimizados es hora
de comprimirlos para transmitir el menor número
de bytes como sea posible.
• Compresión en tiempo de ejecución.
• Compresión en tiempo de construcción.
49. UNIENDO PIEZAS
El proceso de construcción debería tener, al menos, tres
targets diferentes:
• Develop:Target ejecutado en la etapa de desarrollo.
• Integration:Target ejecutado por el CI.
• Production:Target ejecutado al realizar un release.
52. CONCLUSIONES
El mantenimiento de un proyecto software es determinante,
tanto para los propios desarrolladores, como para la entidad
para la que trabajen, ahorrando tiempo, esfuerzo y dinero.
Hay numerosas alternativas y elegir las apropiadas es igual de
determinante.
Siguiendo una serie de consejos sencillos, podemos aumentar
nuestra productividad y ser más eficientes, mejorando así
nuestra faceta profesional.