10 libros no técnicos que todo techie debería leer

16/01/2019
Artículo original

Imagen Gerd Altmann en Pixabay - CC0 (No se requiere atribución)

En alguna ocasión hemos recomendado “libros clásicos” que todo programador debería leer en algún momento, pues aportan una serie de conocimientos no técnicos que te ayudarán a desarrollar tu carrera profesión.

Sin embargo, como no todo es trabajo, os traemos una serie de libros que consideramos que pueden ser de vuestro interés. Han sido escritos por una serie de tecnólogos, empresarios, científicos, historiadores, investigadores e innovadores.

A continuación encontrarás más de 10 recomendaciones ordenadas por orden alfabético de autor.

Four: El ADN secreto de Amazon, Apple, Facebook y Google de Scott Galloway

Portada del libro Four: el ADN secreto de Amazon, Apple, Facebook y Google

Scott Galloway, uno de los profesores de negocios más famosos actualmente, disecciona las estrategias ocultas bajo la deslumbrante apariencia de estos cuatro gigantes y muestra cómo apelan a las necesidades básicas que han movido a la humanidad desde tiempos ancestrales:

  • Amazon, a la de cazar y recolectar
  • Apple, a la de procrear
  • Facebook, a la de amar y
  • Google, a la de creer en un Dios.

Sapiens. De animales a dioses: Breve historia de la humanidad de Yuval Noah Harari

Portada del libro Sapiens. De animales a dioses: Breve historia de la humanidad

En Sapiens, Yuval Noah Harari traza una breve historia de la humanidad, desde los primeros humanos que caminaron sobre la Tierra hasta los radicales y a veces devastadores avances de las tres grandes revoluciones que nuestra especie ha protagonizado: la cognitiva, la agrícola y la científica. A partir de hallazgos de disciplinas tan diversas como la biología, la antropología, la paleontología o la economía, Harari explora cómo las grandes corrientes de la historia han modelado nuestra sociedad, los animales y las plantas que nos rodean e incluso nuestras personalidades. El libro del Dr. Harari es una lectura fascinante que ayudará a responder las grandes preguntas acerca de dónde venimos, y más importante, hacia donde nos dirigimos como especie.

Breves respuestas a las grandes preguntas de Stephen Hawking

Portada del libro Breves respuestas a las grandes preguntas

Se trata de la obra póstuma del famoso físico teórico y cosmólogo Stephen Hawking. En este libro ofrece su punto de vista personal a las grandes preguntas que desde siempre se hacen los humanos: ¿Hay un Dios? ¿Cómo empezó todo? ¿Es posible viajar en el tiempo? ¿La tecnología nos salvará o nos destruirá?... Hawking hace hincapié en el uso de la ciencia para ayudarnos a resolver los problemas a los que nos enfrentamos, y anima a las generaciones más jóvenes a que definan su propio futuro.

Innovation and its enemies de Calestous Juma

Portada del libro Innovation and its enemies

Aunque no hemos encontrado este título traducido al español, creemos que es una lectura muy recomendable. 

Esta cautivadora panorámica muestra la historia de la innovación y pone de relieve por qué la gente se resiste a las nuevas tecnologías. Calestous Juma, utilizando estudios del caso detallados de café, imprenta, margarina, mecanización de granjas, electricidad, refrigeración mecánica, música grabada, cultivos transgénicos y animales transgénicos, muestra cómo emergen, arraigan y crean nuevas ecologías institucionales que favorecen su establecimiento en el mercado. El libro utiliza estas lecciones de la historia para contextualizar los debates contemporáneos sobre tecnologías como la inteligencia artificial, el e-learning, la impresión 3D, la edición de genes, la robótica y los drones. En última instancia, aboga por transferir una mayor responsabilidad a los líderes públicos para que trabajen con científicos, ingenieros y empresarios para gestionar el cambio tecnológico, realizar los ajustes institucionales asociados y ampliar el compromiso público en asuntos científicos y tecnológicos.

Lo inevitable de Kevin Kelly

Portada del libro Lo Inevitable

En este libro, su autor Kevin Kelly, director ejecutivo fundador de la revista Wired, expone su visión del futuro y cómo la tecnología afectará nuestra vida diaria. Desde la realidad virtual en el hogar hasta la inteligencia artificial incorporada en todos los productos manufacturados, Kelly, examina estas y otras tendencias profundas, mientras explora cómo se superponen y son parte de una red codependiente.

Kelly explica que hay 12 fuerzas tecnológicas que según él revolucionarán completamente la forma en que compramos, trabajamos, aprendemos y nos comunicamos unos con otros. Al entenderlas y adoptarlas, dice Kelly, será más fácil para nosotros estar al tanto de la próxima ola de cambios y arreglar nuestras relaciones cotidianas con la tecnología de manera que produzcan los máximos beneficios.

Machine, Platform, Crowd: Harnessing Our Digital Future de Andrew McAfee y Erik Brynjolfsson

Portada del libro Machine, platforma, crowd

De los mismos autores que “La segunda era de las máquinas” nos llega este ahora este libro que, al menos nosotros, no lo hemos encontrado traducido al español, por eso hemos dejado su título en inglés.

Según sus autores (ambos colaboradores en el MIT de la iniciativa sobre la economía digital) hemos pasado de la primera fase de la revolución digital a una etapa en la que las plataformas y las multitudes de su título se han vuelto tan influyentes como las máquinas. Estamos viviendo en una era de cambios técnicos muy acelerados, pero la humanidad aún está a tiempo de controlar su propio destino. En lugar de hacer redundantes a los seres humanos, la tecnología nos brinda una oportunidad sin precedentes para dar forma a nuestro futuro.

En palabras de los propios autores, “El éxito de una empresa casi nunca se basa en la cantidad de tecnología a la que puede acceder, sino en cómo la gente usa esa tecnología y en los valores que incorporan en la organización".

Armas de destrucción matemática: Cómo el BigData aumenta la discriminación y amenaza la democracia de Cathy O’Neil

Portada del libro Armas de destrucción matemática

Es obvio que vivimos en la edad del algoritmo donde las decisiones que afectan a nuestras vidas no están hechas por humanos, sino por modelos matemáticos. En teoría, esto debería conducir a una mayor equidad: todos son juzgados de acuerdo con las mismas reglas, sin sesgo. Pero en realidad, ocurre exactamente lo contrario. Los modelos que se utilizan en la actualidad son opacos, no regulados e incontestables, incluso cuando están equivocados. Esto deriva en un refuerzo de la discriminación.

O’Neil profundiza en el lado oscuro del Big Data, anima a los que trabajan en la definición de dichos modelos a responsabilizarse de estos problemas y a los ciudadanos a adquirir más conocimientos sobre cómo se utilizan estos algoritmos y a exigir un cambio.

En defensa de la Ilustración de Steven Pinker

Portada del libro En defensa de la ilustración

En este libro, el científico cognitivo y autor Steven Pinker explica que las soluciones a nuestros problemas llegan a través del ideal de la Ilustración de usar la razón y la ciencia. Pinker nos insta a ver con otra perspectiva los titulares alarmistas y las profecías de la perdición que juegan con nuestros prejuicios psicológicos. Haciendo uso de datos empíricos, muestra que la vida, la salud, la prosperidad, la seguridad, la paz, el conocimiento y la felicidad van en aumento, no solo en Occidente, sino en todo el mundo. Él dice que este fenómeno no se debe a fuerzas que están fuera de nuestro control, sino que cree que la razón y la ciencia mejoran la humanidad.

Factfulness: Diez razones por las que estamos equivocados sobre el mundo. Y por qué las cosas están mejor de lo que piensas de Hans Rosling

Portada del libro Factfulness

Hans Rosling, una eminencia del análisis y divulgación de tendencias globales, afirma que tenemos diez instintos que distorsionan nuestra visión. Desde nuestra tendencia a dividir el mundo en dos campos (nosotros contra ellos) a la manera en que consumimos la información de los medios (basada en la explotación del miedo), pasando por el modo en que percibimos el progreso (creyendo que las cosas siempre empeoran). Nuestro problema es que no somos conscientes de lo que no sabemos, e incluso cuando estamos informados nos dejamos llevar por sesgos inconscientes y predecibles.

El autor se explica con gratificante claridad, mediante palabras y sobre todo a través de excelentes gráficos. Además, las fuentes en que se basan son generalmente publicaciones de organismos internacionales que cualquiera puede descargarse de Internet con un clic.

Las mujeres en la tecnología de varios autores

Este no es realmente el título de ningún libro (o quizás sí) sino que en este caso hemos querido recoger varios títulos (todos ellos en inglés) que guardan relación con el tema indicado:

  • The woman who smashed code de Jason Fagone: el libro cuenta la increíble historia de Elizebeth Smith Friedman y su esposo, William, quienes inventaron la ciencia moderna de la criptología y cómo la utilizaron para encontrar espías nazis, ayudando así en la victoria de la Segunda Guerra Mundial.
  • Brotopia de Emily Chang: la periodista de Bloomberg TV Emily Chang descubre lo que sucede entre bambalinas en las empresas de Silicon Valley. La narración se basa en entrevistas realizadas a personas que trabajan para dichas empresas, como Sheryl Sandberg, directora ejecutiva de Facebook, Susan Wojcicki, CEO de YouTube, y Marissa Mayer, ex directora ejecutiva de Yahoo, quienes describen las dificultades que tuvieron para romper el techo de cristal de este centro tecnológico.
  • Geek Girl Rising, de Heather Cabot y Samantha Walravens: este libro se adentra en el mundo de las menos conocidas emprendedoras y tecnólogas que están construyendo la próxima generación de empresas y luchando en el altamente mundo competitivo de Silicon Valley. Los lectores aprenderán sobre modelos a seguir como Debbie Sterling, la inventora de GoldieBox; Tracy Chou, exdesarrolladora líder en Pinterest; y Kathryn Minshew, CEO de The Muse, entre otras.

Plastic SCM Mergebot: automatizando tu pipeline de desarrollo

16/01/2019
Artículo original

Plastic SCM Mergebot: automatizando tu pipeline de desarrollo

Tradicionalmente muchos equipos de desarrollo cuentan con la figura del integrador o build master encargado de realizar las integraciones de código (merges) de cada una de los feature branch que pueda tener el proyecto que se esté implementando. En otros casos, es directamente cada desarrollador quien se ocupa de realizar sus propios merges de las ramas ya revisadas. En ambos casos la necesidad de la intervención manual representa un freno para la escalabilidad del equipo y su velocidad.

Por ello, la tendencia en el desarrollo de soluciones devops va encaminada hacia automatización de ese proceso. Es decir, un miembro virtual del equipo que juega ese papel de integrador de código. Con ellos podemos automatizar el trabajo manual siguiendo una serie de pasos, lanzar tests y generar los correspondientes checks de forma automática conectándolo a cada uno de los sistemas con los que contemos como repositorios de código, gestores de tareas, sistemas de integración continua e incluso nuestra herramientas de comunicación interna.

Además de la automatización de una parte del proceso, un mergebot incide directamente en la productividad de los desarrolladores ya que evita los temidos cambios innecesarios de contexto. Con un ciclo pull request clásico, alguien debe "pulsar el botón de merge".

Orquestación DevOps automatizada: mergebots

Ya hemos hablado sobre Plastic SCM en Genbeta Dev, un control de versiones distribuido desarrollado por Codice Software en el Parque Tecnológico de Boecillo (Valladolid). La solución que proponen para solventar todos esos problemas son los los mergebots](https://www.plasticscm.com/mergebot-devops), los cuales permite automatizar merges y orquestar las tareas necesarias hasta la creación final de la build final.

Es decir, por un lado, son capaz de automatizar hasta el 30 % de las integraciones de código que hasta ahora eran manuales y lo hacen en base a reglas previas; por otro lado, son capace de lanzar builds, lanzar integración continua, ejecutar todos los tests y una vez el proceso ha concluido satisfactoriamente, integrarlo en la rama principal.

Todo esto de manera automatizada apoyándose en el stack tecnológico que ya cuenta con xmerge y semanticmerge para evaluar la viabilidad de la integración.

Mergebot Integrar Branch

Dos ejemplos de mergebots: TrunkBot y ConflictsBot.

TrunkBot está conectado al servidor central de Plastic SCM esperando a que una rama sea etiquetada como reviewed. Está diseñado para cumplir con la metodología Trunk Based Development cuyo éxito se basa en las ramas por funcionalidad de vida muy corta. Es decir, una funcionalidad por branch.

Cuando un desarrollador ha terminado su tarea puede marcarla como resuelta lo que avisará al revisor para que revise el código y valide la tarea. El TrunkBot confirmará que el código ha sido revisado y lanzará los pasos configurados en el CI integrando la rama si puede resolver los conflictos. Recordemos que Plastic SCM utiliza una serie de motores de merge sintacticos y semanticos para facilitar la tarea. Si por el contrario no es posible, avisará al responsable para resolver los conflictos de forma manual y volver arrancar el proceso.

Conflictsbots está orientado a inspeccionar cualquier problema relacionado con los merges automáticos de las tareas marcadas como "resolved". Si existe algún conflicto notificará a los usuarios y reabrirá la tarea. Si todo es correcto, es el encargado de validar el proceso previo de Trunkbot para poder hacer la integración.

Cada mergebot cuenta con una configuración específica para definir los pasos que debe seguir que podéis ver en la documentación de Plastic SCM.

Entendiendo el posicionamiento absoluto de elementos HTML con CSS

15/01/2019
Artículo original

El comportamiento de un elemento con position:absolute; quizá te puede parecer errático de entrada, especialmente si no tienes suficiente experiencia maquetando con CSS y no conoces el detalle fundamental que te voy a contar en este post. Así que, si alguna vez te has desesperado al intentar posicionar un elemento de forma absoluta, mejor sigue leyendo.

Pero empecemos por el principio. La propiedad position, tal y como su propio nombre indica, define cómo se va a posicionar un elemento.

Por defecto su valor es static y sigue el flujo normal del documento, esto es, que se va colocando de arriba a abajo y de izquierda a derecha en función de si es un elemento de línea o bloque, y del espacio y de los elementos que encuentren antes que él.

Pero también puede tomar otros valores, que pueden ser: relative, absolute, fixed o sticky.

A partir de que se haya definido su posicionamiento, su posición exacta se puede modificar usando las propiedades top, bottom, left y right, que harán que se desplace como en un sistema de coordenadas.

Un ejemplo probando position

Ahora veremos un ejemplo práctico donde lo entenderemos todo mejor:

Tenemos cuatro divs, y dentro del último, un enlace con la clase .logo que alberga una imagen que es el logo de campusMVP. Los cuatro con position:static;, que es el valor que el navegador le aplica por defecto a cualquier elemento:

<div class="div1">
	<h2>Div 1</h2>
	<div class="div2">
		<h2>Div 2</h2>
		<div class="div3">
			<h2>Div 3</h2>
			<div class="div4">
				<h2>Div 4</h2>
				<a class="logo" href="https://www.campusmvp.es">
					<img src="img/logo-campusmvp.png" alt="logo de campusmvp"/>
				</a>
			</div>
		</div>
	</div>
</div>

Ejemplo inicial donde todos los elementos son static

Ahora posiciono el enlace del logo como absoluto en las coordenadas 0,0 arriba a la izquierda, a ver qué pasa:

a.logo {
	position:absolute;
	top:0;
	left:0;
}

Vemos que se ha colocado en las coordenadas 0,0 con respecto a la ventana del navegador:

Ejemplo de position:absolute; con ancestros static

Probamos a aplicarle a un div position:relative;

Ahora bien, ¿qué pasa si le pongo al .div1 un position: relative;?:

.div1 {
	position:relative;
}

 

Ejemplo de position:absolute; y contenedor position:relative;

Mmmm, interesante. Se posiciona respecto a él.

Position: fixed;

¿Y si le ponemos al .div2 un position: fixed;?:

   

.div2 {
	position:fixed;
}

Ejemplo de position:absolute; y un ancestro position:fixed;

¡Vaya! Ocurre lo mismo con este.

Position: absolute;

Otro más, ahora le ponemos al .div3 un position: absolute;

 

.div3 {
	position:absolute;
}

Ejemplo de position:absolute; y un ancestro position:absolute;

También se posiciona el logo respecto a él.

Position: sticky;

Y para rematar, le ponemos al .div4 un position: sticky;

.div4 {
	position:sticky;
}

Ejemplo de position:absolute; y un ancestro position:sticky;

De nuevo ocurre lo mismo.

En conclusión...

Pues bien, parece que hemos averiguado que nuestro logo con position: absolute; se posicionará en función de su ancestro más cercano que tenga un valor distinto a static. O lo que es lo mismo, que esté posicionado (se le llama así, si no es static se dice que está posicionado). Y si no encuentra ningún ancestro posicionado, se colocará en función de la ventana del navegador.

Esta es la norma general. En la práctica, la fórmula que más se usa es ponerle position:relative; al contenedor del elemento que queremos posicionar en absoluto, porque es la que menos problemas de posicionamiento con el resto de elementos te va a dar. Sin embargo este tema tiene más sutilezas que debes controlar (sistemas de referencia, unidades, colapso de elementos, flotaciones...) y que se explican con detalle en nuestro curso online de online de HTML5 y CSS3.

Espero haberte aclarado un poco el panorama. Aquí te dejo el ejemplo para que sigas jugando tú con él, o mejor aún, créate tu propio ejemplo.

La maquetación web no es difícil, pero está llena de sutiles trampas como esta. Si quieres aprender a evitarlas con conocimientos sólidos, échale un vistazo al curso anterior y, si ya dominas los fundamentos de HTML y CSS, seguro que te puede interesar aprender en serio cómo hacer maquetación Responsive con HTML5, Flexbox, CSS Grid y Bootstrap.

Qué pasa cuando escribes la dirección de una web en tu navegador y cómo simular cualquier dominio en tu equipo local para desarrollo

13/01/2019
Artículo original

Imagen ornamentalLo que voy a contar hoy es algo de "cultura general", pero también es cierto que muchas personas lo desconocen todavía, sobre todo si están comenzando en el mundo de la programación web.

Empezaré repasando cómo es el funcionamiento básico desde el momento en el que escribimos una dirección web en un navegador y hasta que empieza a solicitarse la página de verdad, y luego explicaré un sencillo proceso que te permite simular que cualquier dominio de internet, exista o no, está en cualquier servidor que tu quieras. Por ejemplo, si te interesara por el motivo que fuese, podrías hacer que cuando escribas en un navegador www.google.com en vez de ir a Google vaya a, por ejemplo, una página de prueba en tu servidor local de desarrollo.

Quizá lo de Google no sea muy útil, claro, pero sí que puede resultar de gran ayuda para poder probar de la manera más realista posible una web o aplicación, utilizando para el desarrollo o las pruebas no solo el mismo código, incluso el mismo dominio que usaríamos en la realidad.

Por ejemplo, vamos a suponer que tenemos una aplicación web funcionando en el dominio misuperapp.com y que en nuestra máquina local queremos seguir trabajando en ella para aumentar sus características, y utilizar directamente ese dominio en lugar de localhost o 127.0.0.1 (que es siempre la dirección de la máquina actual).

Cómo funciona la resolución de nombres de dominio

No quiero extenderme demasiado con esto, porque además da para mucho, pero es importante que cualquiera que se dedique a la programación (y no solo a la programación Web) conozca el funcionamiento básico de Internet. Así que ahí va una pequeña explicación...

Cada máquina conectada a Internet dispone de una o varias direcciones IP asignadas que la identifican de manera única. Estas direcciones pueden ser de dos tipos IPv4 o IPv6. Las direcciones IPv4 son cuatro numeritos entre 0 y 255 separados por puntos y son las más comunes. Cuando se empezaron a usar hace unas décadas parecía suficientes para que duraran siempre. Sin embargo hay tantas máquinas conectadas a Internet que empiezan a escasear y es uno de los motivos de que hace tiempo se diseñasen las direcciones IPv6, que son una cadena larga e ininteligible que designa de manera única también a un dispositivo conectado a Internet.

De este modo, a través de su dirección IP, es posible encontrar cualquier dispositivo que esté conectado a la Red de redes.

Cuando un usuario abre su navegador y escribe la dirección de una web o aplicación, lo que ocurre por debajo es que el nombre de dominio (por ejemplo misuperapp.com) se transforma en la dirección IP del servidor encargado de servir las páginas web correspondientes a esa web o aplicación. De este modo el navegador sabe a qué servidor debe conectarse y solicitarle la página.

¿Cómo se sabe cuál es esa dirección IP? Gracias a un protocolo específico que se llama DNS (Domain Name System, Sistema de Nombres de Dominio). Un servidor DNS es un dispositivo especial conectado a Internet y que es responsable de almacenar un base de datos de parejas "dominio / IP" de modo que sabe encontrar uno a partir del otro y viceversa. De este modo si buscas un dominio te sabe decir su IP y si buscas una IP te sabe decir qué dominio o dominios tiene asociados.

Nota: en realidad a un dominio o subdominio se le pueden asignar muchos más registros que el que devuelve su dirección IP, y se utiliza también para crear alias entre dominios, entradas para saber cómo enviar y recibir correo electrónico, datos textuales de diversa utilidad (como la validación de dominios ante terceros o firma digital de correo), y muchas cosas más. Pero a os efectos de lo que quiero explicar hoy, lo anterior es suficiente.

La resolución de nombres con DNS es jerárquica. Existen unos servidores DNS raíz que contienen una base de datos con todos los dominios de primer nivel (TLD: Top Level Domain) del mundo y los servidors DNS que son a su vez responsables de las entradas para cada uno de ellos.

Tu operador de acceso a Internet también dispone de unos servidores DNS que se encargan de intermediar entre tu máquina y la jerarquía de servidores DNS para averiguar la dirección IP final al a que debes conectarte. Esto es algo a lo que no se le da mucha importancia habitualmente, pero que es crítica por varios motivos:

  • Si el servidor DNS de tu proveedor fuese hackeado podrían hacer que, por ejemplo, al introducir la dirección de tu banco fueses a parar al servidor de un ladrón que simula ser tu banco, y no podrías enterarte.
  • La resolución de nombres puede llevar bastante tiempo, por lo que si el servidor DNS de tu proveedor es lento esto lo acabarás notando en la velocidad aparente de conexión que tienes a la web.
  • El que controla tu servidor DNS controla también exactamente a qué sitios web te conectas, y esto es información muy valiosa.

De hecho muchas empresas, como por ejemplo Google, ofrecen servidores DNS para que puedas utilizarlos en lugar de los que te da tu proveedor de Internet, ofreciéndote más velocidad, más seguridad o ambas cosas. Lo que seguro que no te ofrece Google es más privacidad, así que aunque son muy rápidos igual no son los más recomendables (aunque si ya usas Chrome poco te importará que sepan a dónde te conectas ¿verdad?)

Yo personalmente uso los servidores DNS de Cloudflare (1.1.1.1) porque son muy rápidos, son seguros y sé que Cloudflare no va a guardar ni utilizar mi información.

En resumen, cuando escribes una dirección en tu navegador lo que ocurre es esto:

  1. Se consulta el servidor DNS de tu proveedor.
  2. Este consulta a uno de los servidores raíz para averiguar cuáles son los servidores DNS responsables para tu dominio de primer nivel (.es, .com, etc...).
  3. Se consultan esos servidores para averiguar qué dirección IP le corresponde a tu navegador.
  4. Devuelve a tu sistema operativo (y éste a tu navegador) la dirección IP a la que debe conectarse.

Y todo esto sin que los usuarios lo veamos.

Nota: esto sería así sólo cuando se pida ese dominio por primera vez. Si ya lo ha pedido alguien antes lo más probable es que lo tengan en caché (ya que no cambian a menudo) y que te lo devuelva inmediatamente sin más consultas. Por eso cuando cambias la IP asociada a un dominio éste tarda en "propagarse": mientras todos los servidores DNS que ya lo habían anotado no borren sus cachés no se verá la nueva IP en todo el mundo. Esa caché puede durar desde unos minutos hasta varias horas.

Se puede ver esta información con una utilidad de línea de comandos llamada nslookup. Por ejemplo, estas son las IPs asociadas al dominio del buscador de Google:

Resolución de nombres de Google

Como vemos tiene dos direcciones asociadas, una IPv6 (2a00:1450:4003:801::2004) y la otra IPv4 (172.217.17.4). Poríamos usarlas directamente para conectarnos a ese dominio.

Por cierto, ese "Server" que aparece al principio es el servidor DNS que se está utilizando para la consulta inicial, que en tu caso probablemente sea el de tu proveedor de acceso a Internet y en mi caso, como decía, es el servidor DNS de Cloudflare.

También podemos ver cuáles son los servidores de dominio (DNS) que se encargan de devolver las entradas DNS de ese dominio (recuerda: no solo tenemos IPs en esas bases de datos asociadas a un dominio). En el caso de Google son:

Los DNS responsables de Google

En este caso vemos que el principal es ns1.google.com y que indica que se debe conservar en caché la resolución durante un minuto (default TTL).

El archivo hosts: la excepción en la resolución de DNS

Todo lo anterior es "cultura general", ya digo, pero conviene saberlo.

Ahora bien, existe una excepción a todo lo que he explicado antes: el archivo hosts.

Se trata de un archivo de texto sin extensión que existe en todos los sistemas operativos de escritorio y que permite especificar parejas de dominio / IP cuya relación queremos forzar. Este archivo se consulta antes de hacer cualquier consulta al servidor DNS real como acabamos de ver.

Por lo tanto, si introduces cualquier relación entre dominio y dirección en este archivo podrás hacer que el dominio apunte a donde quieras a los efectos de tu máquina. Es como "hackear" el servicio DNS pero haciéndolo para ti mismo.

El archivo hosts en Windows está ubicado en la carpeta C:\Windows\System32\drivers\etc:

El archivo hosts

Para poder escribir en él debes hacerlo con permisos de administrador. Así que abre por ejemplo el bloc de notas como administrador y navega hasta esa carpeta para abrirlo. Tendrás que mostrar todos los archivos en el selector del bloc de notas:

El selector de archivos del bloc de notas

Al abrirlo generalmente estará vacío y verás unas instrucciones sobre cómo utilizarlo:

El contenido de hosts

Ahora, volviendo a nuestro ejemplo inicial, vamos a asociar el dominio misuperapp.com a la máquina local:

La nueva entrada DNS falsa

Como ves solo hay que poner la dirección IP (en este caso la local), un tabulador y el nombre de dominio (no te olvides de las www si lo necesita).

A partir del momento en que grabes los cambios, si vas a un navegador cualquiera e intentas acceder al dominio, te responderá el servidor local (que obviamente tiene que tener asociado dicho dominio para poder responder. Eso o responder a cualquier dominio, cosa que pasa con muchos de desarrollo local como Fenix o Live Server, pero no con IIS).

Nota: los navegadores basados en Chromium (Chrome, Opera, Brave, Vivaldi...) hacen una caché propia de DNS. Por eso, es posible que si ya habías pedido anteriormente el dominio y es un dominio real, no pille los cambios ue acabas de hacer ni aunque entres en modo privado. Debes borrar el histórico de navegación o esperar el tiempo que considere Chrome para ver el cambio reflejado. Si el dominio no existe no hay problema.

De hecho, y este es un truco poco conocido, puedes forzar la descarga de la caché de DNS de Google para un determinado dominio utilizando esta página especial de la propia empresa, para cuando te urja que los usuarios de Chomium (la mayoria) vacíen sus cachés de tu dominio cuanto antes.

Como ves es algo muy sencillo de hacer, y siento el "rollo" previo que te he contado, pero creo que conocer estos detalles del funcionamiento de la Red es indispensable para los desarrolladores y por eso me gusta divulgarlo.

¡Espero que te resulte útil!

Bootstrap 4.2 - spinners, notificaciones "toast", interruptores y otras novedades

10/01/2019
Artículo original

Imagen ornamental con el logo de Bootstrap

Hace unos días Bootstrap liberó su versión 4.2.0, con algunas novedades dignas de mención que vamos a repasar a continuación.

Además también han incluido unas cuantas mejoras de características ya existentes.

Vamos a verlas.

Spinners o indicadores de carga y progreso

En muchas ocasiones hay procesos que pueden tardar algo de tiempo en llevarse a cabo: la carga de una parte de la página o incluso de la página entera, una llamada a través de AJAX en segundo plano... Este tipo de cosas. En estos casos, para que el usuario tenga la sensación de que algo está funcionando y que debe esperar, lo normal es proporcionar algún tipo de feedback visual con una animación. Y para esto precisamente es para lo que sirve el primero de los componentes que se lanzan con la versión 4.2: el spinner.

Existen dos tipos de spinners que generan dos tipos de animaciones diferentes:

  • El borde giratorio: que muestra un círculo dando vueltas.

Imagen animada de un spinner de tipo borde giratorio

  • El punto creciente: que utiliza un punto que aparece poco a poco creciendo desde la nada.

Imagen animada de un spinner de tipo punto creciente

Añadir uno a la página es realmente sencillo: basta con asignarle a cualquier elemento contenedor (por ejemplo un div) la clase .spinner-border o .spinner-grow respectivamente, siendo en su expresión más sencilla algo como esto:

<span class="spinner-border"></span>

que crearía un spinner de tipo borde giratorio con el tamaño y color por defecto, como el de la animación anterior.

Se puede controlar su color usando cualquiera de las clases predefinidas para colores de Bootstrap, es decir, text-primary, text-success, y similares... Por ejemplo, para tener un punto creciente de color rojo como indicador sería:

<span class="spinner-grow text-danger"></span>

Disponen de dos tamaños predefinidos: el que tienen por defecto y uno más pequeño que se consigue añadiéndoles el sufijo -sm (de "small") a su nombre de clase, aplicando ambas (la del spinner y la del tamaño), así:

<span class="spinner-border spinner-border-sm"></span>

Esto es útil, por ejemplo, para meterlos dentro de botones y elementos similares, que nos permite dar un efecto muy chulo cuando se genera alguna acción en segundo plano, como una llamada AJAX por ejemplo:

<button class="btn btn-primary text-nowrap" type="button">
  <span class="spinner-border spinner-border-sm mr-2"></span>
  Enviando datos...
</button>

que nos da algo como esto:

Imagen animada de un spinner de tipo borde giratorio dentro de un botón de color azul

Ten en cuenta que para crear este tipo de spinners se utilizan por debajo animaciones CSS, no JavaScript. En ambos casos lo que se hace es poner los bordes redondeados, con uno de los cuatro bordes transparentes en el caso del que gira.

Esto tiene dos implicaciones importantes:

  1. No es necesario incluir bootstrap.js para que funcionen.
  2. Si le aplicas estas clases a un elemento que no esté vacío, afectará a todo lo que tenga dentro. Por ejemplo, si usas .spinner-border en un elemento que tiene texto se convertirá todo, en un elemento redondo y girará todo el contenido. Por ejemplo, si tenemos esto:
<div class="spinner-border text-danger">
    <div>Girando!!</div>
</div>

veremos por pantalla una animación como esta:

Imagen animada de un spinner de tipo borde giratorio con un texto dentro

que en este caso es un poco "chusquera" pero que te dará una idea de las posibilidades.

Mediante JavaScript podemos iniciarlos y pararlos simplemente colocándoles o quitándoles la clase correspondiente. Así que, no tiene misterio alguno y son súper-fáciles de utilizar.

Accesibilidad de los spinners

Para conseguir que este tipo de elementos sean accesibles para lectores de pantalla deberíamos hacer dos cosas:

  • Marcarlos con un atributo aria de tipo role
  • Incluir un texto dentro que solo se vea en lectores de pantalla, que indique para qué valen

Por ejemplo, así:

<div class="spinner-border text-primary" role="status">
    <span class="sr-only">Procesando...</span>
</div>

En este caso el atributo role="status" indica que la función de este spinner es simplemente la obvia: un indicador de estado. La clase sr-only del texto que contiene es una clase de accesibilidad de Bootstrap que indica que solamente se verá para lectores de pantalla (screen readers, de ahí el prefijo). Con esto sería suficiente.

Notificaciones de tipo "toast"

Otro de los nuevos componentes que se incluyen con Bootstrap 4.2.0 es el que nos permite gestionar notificaciones de tipo "Toast", de las que aparecen cuando queremos dar un mensaje durante unos segundos.

Para construirlos necesitamos utilizar tres clases CSS específicas:

  • .toast: para el elemento contenedor que encierra los contenidos de la notificación.
  • .toast-header: para la cabecera de la notificación.
  • .toast-body: para el cuerpo.

Por lo tanto, para construirla necesitamos como mínimo 3 elementos, aunque es recomendable incluir algunos más para darle un aspecto más agradable. La estructura básica de una notificación sería la siguiente:

<div class="toast">
    <div class="toast-header">
        Título de la notificación
    </div>
    <div class="toast-body">
        Cuerpo de la notificación
    </div>
</div>

Con esto llegaría, pero es interesante, por cuestione estáticas y funcionales, incluir algunos elementos más, por ejemplo:

<div class="toast">
    <div class="toast-header">
        <div class="rounded mr-2" style="height: 16px;width: 16px;background-color: red;"></div>
        <strong class="mr-auto">Título de la notificación</strong>
        <small>Hace 2 segundos</small>
        <button type="button" class="ml-2 mb-1 close" data-dismiss="toast">
          <span aria-hidden="true">&times;</span>
        </button>
    </div>
    <div class="toast-body">
        Cuerpo de la notificación
    </div>
</div>

En este caso dentro de la cabecera hemos incluido un pequeño div con los estilos en línea que lo único que hace es crear un cuadrado rojo para dar un toque estético más bonito. A continuación va el título, esta vez en negrita y con margen automático a la derecha (clase .mr-auto), ponemos en pequeño texto para indicar cuánto hace desde que se mostró (puesto a mano para el ejemplo), y finalmente un elemento con un aspa (&times; representa una cruz de multiplicar en HTML) que se usará para cerrar la notificación.

Nota: el efecto de cerrar la notificación cuando se pulse sobre el aspa se consigue de manera automática decorando el elemento que contiene el aspa (el botón) con el atributo data-dimiss="toast" que es reconocido por Bootstrap y añade automáticamente esta acción.

Este sería el aspecto de esta notificación básica:

Aspecto de la notificación anterior una vez mostrada

Pero si incluyes el código anterior en una página no verás nada. Por defecto las notificaciones están ocultas (para ser exactos, tienen la opacidad a 0, por lo que son transparentes y hacen un efecto de difuminado sobre lo que tengan debajo).

Para lograr que se vea una notificación hay que usar un poco de JavaScript. En concreto lo único que hay que hacer es localizarlas, inicializarlas y mostrarlas, para lo cual usaremos jQuery, que como sabrás es una dependencia que tiene Bootstrap. Sería algo así:

//Durante la inicialización de la página
$('.toast').toast();

....

//Cuando la queramos mostrar
$('.toast').toast('show');

El método .toast() tiene varias sobrecargas y sirve tanto para inicializar las notificaciones como para mostrar y ocultarlas. Por defecto, cuando se inicializan, aplican una animación de la opacidad para mostrarse y se auto-cierran al cabo de medio segundo. Si queremos cambiar este comportamiento podemos pasarle al método toast() durante la inicialiación un objeto en formato JSON con tres posibles propiedades:

  • animation: que por defecto vale true, pero que si le ponemos false hará que se muestre de inmediato sin animación alguna.
  • autohide: para decidir si se quiere ocultar automáticamente o no. Si no queremos que se oculte le pasaremos false.
  • delay: el tiempo en milisegundos para que se oculte automáticamente en caso de que lo anterior tenga el valor por defecto o true.

Así, por ejemplo, si queremos que se oculten solas pero al cabo de 2 segundos en vez de medio, podemos poner:

$('.toast').toast({ delay: 2000 });

y lo mismo con las demás propiedades, separándolas con comas en el objeto literal que le pasamos.

Luego se muestran llamando también al mismo método pero pasándole la cadena 'show'. Si queremos ocultarlas le pasaríamos la cadena 'hide'. Le podemos pasar la cadena 'dispose' que también la ocultará y la dejará en el DOM, pero que evitará que se pueda volver a mostrar la misma notificación aunque llamemos a .toast('show');.

Nota: si queremos que aparezcan colocadas, por ejemplo, en la parte superior de la pantalla y no en el flujo del DOM de la página, las tenemos que posicionar nosotros mismos usando CSS. Dentro de la propia notificación el estilo de visualización que se utiliza es Flexbox, por lo que puedes usar cualquiera de sus propiedades (o utilidades correspondientes de Bootstrap) para posicionar y alinear los sub-elementos dentro de la cabecera o el cuerpo.

El método toast() es asíncrono. Esto quiere decir que en cuanto lo llamas vuelve y, aunque no se haya mostrado la notificación todavía, el código que hubiera a continuación se seguirá ejecutando. Tenlo en cuenta.

La API de JavaScript para este tipo de elementos define además cuatro eventos:

  • show.bs.toast: que salta cuando se llama a toast() para mostrar un elemento.
  • shown.bs.toast que se notifica cuando la notificación se acaba de mostrar, tras la animación (si la tiene).
  • hide.bs.toast: cuando se empieza a ocultar la notificación.
  • hiden.bs.toast: cuando se ha terminado de ocultar la notificación y ha desaparecido por completo de la pantalla.

Interruptores estilo iOS

En los formularios es muy habitual incluir cuadros de verificación para marcar opciones de tipo "Sí o No". Por defecto son unos cuadraditos muy anodinos que podemos marcar o desmarcar, pero cuando se trata de opciones de configuración es mucho más habitual encontrar el tipo interruptor que puso de moda Apple con sus iPhone. Algo como esto:

El interruptor de Boostrap 4.2 en acción

Boostrap define estos interruptores mediante el uso de una clase de control personalizado llamada .custom-switch, por lo que hay que agregarle esta y además la genérica .custom-control. Además, es necesario incluir dentro un control input de tipo check y la etiqueta con el mensaje correspondiente. Algo así:

<div class="custom-control custom-switch">
  <input type="checkbox" class="custom-control-input" id="opcion1">
  <label class="custom-control-label" for="opcion1">
    Marca o desmarca el interruptor de la izquierda
  </label>
</div>

consiguiendo el efecto que se ve en la animación anterior.

Este control no necesita JavaScript para funcionar, arreglándonos tan solo con la hoja de estilos de Bootstrap 4.

Por debajo lo que hace es ocultar el check original y meter el nuevo elemento en un pseudo-elemento ::before de la etiqueta, que cambia de estado en función de la pseudo-clase :checked del control, por lo que no es fácil cambiarle el aspecto.

Otras novedades de Bootstrap 4.2

Aparte de lo que he explicado hasta ahora, hay unas cuantas novedades más en esta versión, entre las que cabe destacar las siguientes:

  • Se da soporte para eventos táctiles en los carruseles, por lo que aparte de usar los controles, en los móviles podemos simplemente arrastrarlos con el dedo para cambiar lo que se muestra.
  • Los campos de los formularios que se estén validando muestran unos nuevos iconos para los estados :valid e :invalid.
  • Los "tooltips" y los desplegables de tipo popover ahora trabajan con el ShadowDOM para mayor rendimiento.
  • Se añaden algunos estilos nuevos para aspecto: .font-weight-lighter, .font-weight-bolder y .text-decoration-none.

Puedes ver a lista completa aquí.

Aquí te dejo un archivo ZIP con los ejemplos que he hecho más arriba (37.3Kb), para que puedas verlos en acción y probarlos.

Y si lo que quieres es aprender Bootstrap te recomiendo que hagas el curso online de Maquetación web multi-dispositivo con Bootstrap, Flexbox y CSS Grid de campusMVP, del que yo soy co-autor y tutor. No solo aprenderás Bootstrap en sus versiones 3 y 4, sino que dominarás el Responsive Web Design utilizando solo CSS además de las tecnologías CSS más recientes como Flexbox y CSS Grid.

¡Espero que te resulte útil!

Más de 10 títulos recomendados para techies

08/01/2019
Artículo original

Imagen Gerd Altmann en Pixabay - CC0 (No se requiere atribución)

En alguna ocasión hemos recomendado “libros clásicos” que todo programador debería leer en algún momento, pues aportan una serie de conocimientos no técnicos que te ayudarán a desarrollar tu carrera profesión.

Sin embargo, como no todo es trabajo, os traemos una serie de libros que consideramos que pueden ser de vuestro interés. Han sido escritos por una serie de tecnólogos, empresarios, científicos, historiadores, investigadores e innovadores.

A continuación encontrarás más de 10 recomendaciones ordenadas por orden alfabético de autor.

Four: El ADN secreto de Amazon, Apple, Facebook y Google de Scott Galloway

Portada del libro Four: el ADN secreto de Amazon, Apple, Facebook y Google

Scott Galloway, uno de los profesores de negocios más famosos actualmente, disecciona las estrategias ocultas bajo la deslumbrante apariencia de estos cuatro gigantes y muestra cómo apelan a las necesidades básicas que han movido a la humanidad desde tiempos ancestrales:

  • Amazon, a la de cazar y recolectar
  • Apple, a la de procrear
  • Facebook, a la de amar y
  • Google, a la de creer en un Dios.

Sapiens. De animales a dioses: Breve historia de la humanidad de Yuval Noah Harari

Portada del libro Sapiens. De animales a dioses: Breve historia de la humanidad

En Sapiens, Yuval Noah Harari traza una breve historia de la humanidad, desde los primeros humanos que caminaron sobre la Tierra hasta los radicales y a veces devastadores avances de las tres grandes revoluciones que nuestra especie ha protagonizado: la cognitiva, la agrícola y la científica. A partir de hallazgos de disciplinas tan diversas como la biología, la antropología, la paleontología o la economía, Harari explora cómo las grandes corrientes de la historia han modelado nuestra sociedad, los animales y las plantas que nos rodean e incluso nuestras personalidades. El libro del Dr. Harari es una lectura fascinante que ayudará a responder las grandes preguntas acerca de dónde venimos, y más importante, hacia donde nos dirigimos como especie.

Breves respuestas a las grandes preguntas de Stephen Hawking

Portada del libro Breves respuestas a las grandes preguntas

Se trata de la obra póstuma del famoso físico teórico y cosmólogo Stephen Hawking. En este libro ofrece su punto de vista personal a las grandes preguntas que desde siempre se hacen los humanos: ¿Hay un Dios? ¿Cómo empezó todo? ¿Es posible viajar en el tiempo? ¿La tecnología nos salvará o nos destruirá?... Hawking hace hincapié en el uso de la ciencia para ayudarnos a resolver los problemas a los que nos enfrentamos, y anima a las generaciones más jóvenes a que definan su propio futuro.

Innovation and its enemies de Calestous Juma

Portada del libro Innovation and its enemies

Aunque no hemos encontrado este título traducido al español, creemos que es una lectura muy recomendable. 

Esta cautivadora panorámica muestra la historia de la innovación y pone de relieve por qué la gente se resiste a las nuevas tecnologías. Calestous Juma, utilizando estudios del caso detallados de café, imprenta, margarina, mecanización de granjas, electricidad, refrigeración mecánica, música grabada, cultivos transgénicos y animales transgénicos, muestra cómo emergen, arraigan y crean nuevas ecologías institucionales que favorecen su establecimiento en el mercado. El libro utiliza estas lecciones de la historia para contextualizar los debates contemporáneos sobre tecnologías como la inteligencia artificial, el e-learning, la impresión 3D, la edición de genes, la robótica y los drones. En última instancia, aboga por transferir una mayor responsabilidad a los líderes públicos para que trabajen con científicos, ingenieros y empresarios para gestionar el cambio tecnológico, realizar los ajustes institucionales asociados y ampliar el compromiso público en asuntos científicos y tecnológicos.

Lo inevitable de Kevin Kelly

Portada del libro Lo Inevitable

En este libro, su autor Kevin Kelly, director ejecutivo fundador de la revista Wired, expone su visión del futuro y cómo la tecnología afectará nuestra vida diaria. Desde la realidad virtual en el hogar hasta la inteligencia artificial incorporada en todos los productos manufacturados, Kelly, examina estas y otras tendencias profundas, mientras explora cómo se superponen y son parte de una red codependiente.

Kelly explica que hay 12 fuerzas tecnológicas que según él revolucionarán completamente la forma en que compramos, trabajamos, aprendemos y nos comunicamos unos con otros. Al entenderlas y adoptarlas, dice Kelly, será más fácil para nosotros estar al tanto de la próxima ola de cambios y arreglar nuestras relaciones cotidianas con la tecnología de manera que produzcan los máximos beneficios.

Machine, Platform, Crowd: Harnessing Our Digital Future de Andrew McAfee y Erik Brynjolfsson

Portada del libro Machine, platforma, crowd

De los mismos autores que “La segunda era de las máquinas” nos llega este ahora este libro que, al menos nosotros, no lo hemos encontrado traducido al español, por eso hemos dejado su título en inglés.

Según sus autores (ambos colaboradores en el MIT de la iniciativa sobre la economía digital) hemos pasado de la primera fase de la revolución digital a una etapa en la que las plataformas y las multitudes de su título se han vuelto tan influyentes como las máquinas. Estamos viviendo en una era de cambios técnicos muy acelerados, pero la humanidad aún está a tiempo de controlar su propio destino. En lugar de hacer redundantes a los seres humanos, la tecnología nos brinda una oportunidad sin precedentes para dar forma a nuestro futuro.

En palabras de los propios autores, “El éxito de una empresa casi nunca se basa en la cantidad de tecnología a la que puede acceder, sino en cómo la gente usa esa tecnología y en los valores que incorporan en la organización".

Armas de destrucción matemática: Cómo el BigData aumenta la discriminación y amenaza la democracia de Cathy O’Neil

Portada del libro Armas de destrucción matemática

Es obvio que vivimos en la edad del algoritmo donde las decisiones que afectan a nuestras vidas no están hechas por humanos, sino por modelos matemáticos. En teoría, esto debería conducir a una mayor equidad: todos son juzgados de acuerdo con las mismas reglas, sin sesgo. Pero en realidad, ocurre exactamente lo contrario. Los modelos que se utilizan en la actualidad son opacos, no regulados e incontestables, incluso cuando están equivocados. Esto deriva en un refuerzo de la discriminación.

O’Neil profundiza en el lado oscuro del Big Data, anima a los que trabajan en la definición de dichos modelos a responsabilizarse de estos problemas y a los ciudadanos a adquirir más conocimientos sobre cómo se utilizan estos algoritmos y a exigir un cambio.

En defensa de la Ilustración de Steven Pinker

Portada del libro En defensa de la ilustración

En este libro, el científico cognitivo y autor Steven Pinker explica que las soluciones a nuestros problemas llegan a través del ideal de la Ilustración de usar la razón y la ciencia. Pinker nos insta a ver con otra perspectiva los titulares alarmistas y las profecías de la perdición que juegan con nuestros prejuicios psicológicos. Haciendo uso de datos empíricos, muestra que la vida, la salud, la prosperidad, la seguridad, la paz, el conocimiento y la felicidad van en aumento, no solo en Occidente, sino en todo el mundo. Él dice que este fenómeno no se debe a fuerzas que están fuera de nuestro control, sino que cree que la razón y la ciencia mejoran la humanidad.

Factfulness: Diez razones por las que estamos equivocados sobre el mundo. Y por qué las cosas están mejor de lo que piensas de Hans Rosling

Portada del libro Factfulness

Hans Rosling, una eminencia del análisis y divulgación de tendencias globales, afirma que tenemos diez instintos que distorsionan nuestra visión. Desde nuestra tendencia a dividir el mundo en dos campos (nosotros contra ellos) a la manera en que consumimos la información de los medios (basada en la explotación del miedo), pasando por el modo en que percibimos el progreso (creyendo que las cosas siempre empeoran). Nuestro problema es que no somos conscientes de lo que no sabemos, e incluso cuando estamos informados nos dejamos llevar por sesgos inconscientes y predecibles.

El autor se explica con gratificante claridad, mediante palabras y sobre todo a través de excelentes gráficos. Además, las fuentes en que se basan son generalmente publicaciones de organismos internacionales que cualquiera puede descargarse de Internet con un clic.

Las mujeres en la tecnología de varios autores

Este no es realmente el título de ningún libro (o quizás sí) sino que en este caso hemos querido recoger varios títulos (todos ellos en inglés) que guardan relación con el tema indicado:

  • The woman who smashed code de Jason Fagone: el libro cuenta la increíble historia de Elizebeth Smith Friedman y su esposo, William, quienes inventaron la ciencia moderna de la criptología y cómo la utilizaron para encontrar espías nazis, ayudando así en la victoria de la Segunda Guerra Mundial.
  • Brotopia de Emily Chang: la periodista de Bloomberg TV Emily Chang descubre lo que sucede entre bambalinas en las empresas de Silicon Valley. La narración se basa en entrevistas realizadas a personas que trabajan para dichas empresas, como Sheryl Sandberg, directora ejecutiva de Facebook, Susan Wojcicki, CEO de YouTube, y Marissa Mayer, ex directora ejecutiva de Yahoo, quienes describen las dificultades que tuvieron para romper el techo de cristal de este centro tecnológico.
  • Geek Girl Rising, de Heather Cabot y Samantha Walravens: este libro se adentra en el mundo de las menos conocidas emprendedoras y tecnólogas que están construyendo la próxima generación de empresas y luchando en el altamente mundo competitivo de Silicon Valley. Los lectores aprenderán sobre modelos a seguir como Debbie Sterling, la inventora de GoldieBox; Tracy Chou, exdesarrolladora líder en Pinterest; y Kathryn Minshew, CEO de The Muse, entre otras.

TRUCO - Depuración con Chrome: convertir archivos JavaScript en "cajas negras" para evitar depurarlos

07/01/2019
Artículo original

Por regla general, cuando estamos depurando una aplicación web hay ciertas cosas en las que podemos confiar. Por ejemplo en el hecho de que si se produce una excepción casi seguro que la culpa es de nuestro código y no de alguna biblioteca JavaScript conocida de las que estamos utilizando. Es decir, la excepción puede saltar en una línea de jQuery o de Angular, pero eso no significa que el error esté ahí, sino que se ha producido por una llamada previa hecha por nuestro código.

Por ello, cuando estamos depurando paso a paso, poniendo puntos de interrupción en ciertas líneas para luego ir ejecutando el código función a función, introduciéndonos en ellas con F11 o saltándolas con F10, no tiene mucho sentido que, de repente, se nos abra el código fuente de alguna de estas bibliotecas, pues no vamos a depurarlo. Y mucho menos si lo estamos usando ya minimizado y listo para producción, puesto que encima no veremos gran cosa tampoco:

La imagen muestra el depurador de Chrome parado en una línea ininteligible de jQuery

¡Estupendo que me pare el depurador en esta línea tan interesante! ¿no?

Por suerte los desarrolladores de Chrome ya han tenido en cuenta esta situación y nos permiten evitar que ocurra, permitiéndonos meter en una "caja negra" los scripts que nos interesen. La acción se denomina "blackboxing" y lo que hace es que el script se ejecuta normalmente pero, en lo que respecta a la depuración, actúa como una caja negra de modo que se saben los parámetros que entran y los que salen, pero jamás se accede a su interior.

En la práctica esto significa que el depurador jamás se parará en ningún código de su interior, agilizando nuestra depuración.

Vamos a ver cómo funciona...

Activando la capacidad de "blackboxing"

Por defecto esta característica está deshabilitada, por lo que no podremos utilizarla. Para activarla tenemos que ir a los ajustes de las herramientas del desarrollador y acceder al apartado correspondiente que se llama "Blackboxing", activándola desde allí, como se muestra en esta pequeña animación:

Esta animación muestra cómo activar la característica: F12, F1, Blackboxing y 'Blackbox content scripts'

Añadir scrips a la caja negra

Una vez activada la funcionalidad, tenemos dos opciones para añadir scripts a las caja negra:

1.- Añadir rutas y/o nombres genéricos

Por ejemplo, generalmente no querremos que el depurador se pare en ninguna biblioteca minimizada, de producción. Como los archivos de estas bibliotecas suelen terminar con el nombre .min.js, podemos agregar un patrón como este al nombre de archivo usando el botón que tenemos debajo del check que acabamos activar:

La animación muestra cómo agregar un patrón de archivos para convertir en caja negra

Fíjate en un par de cosas importantes:

  • El patrón es una expresión regular. Por lo tanto puedes usar toda la potencia que te dan éstas (que es mucha) para localizar todo tipo de archivos, Además, como se ve en la animación, en una expresión regular un punto representa cualquier carácter, por lo que si queremos indicar un punto de verdad hay que "escapearlo" poniéndole una barra inclinada delante, como se ve en la animación. Tenlo en cuenta. Gracias a esto podemos añadir muchos archivos de golpe. En la animación todos los que estén minimizados.
  • Estos patrones no solo nos permiten convertir archivos en cajas negras, sino lo contrario: deshabilitar la caja negra para ciertos archivos que estuviesen incluidos en alguno de los patrones que hayamos añadido. Así que vale para las dos cosas.

2.- Convertir individualmente cada archivo en una caja negra

Si además (o en lugar) de usar patrones queremos convertir en cajas negras archivos concretos, al cerrar el diálogo anterior y haber habilitado la funcionalidad, tendremos una nueva opción en los menús contextuales de los archivos en la pestaña de código fuente, Sources. Basta con que abramos cualquier archivo y pulsemos con el botón derecho en cualquier parte del archivo abierto para ver una nueva opción Blackbox Script que nos permite hace precisamente eso:

Animación que muestra el nuevo menú contextual con la opción de caja negra

A partir de ese momento el depurador no se detendrá en ese archivo, que es lo que queríamos.

Fíjate en como en la animación anterior, una vez convertido el archivo en una caja negra, podemos volver a habilitarlo para depuración con el mismo menú contextual, que ahora pone Stop blackboxing. ¡Súper-fácil!

En resumen

El "blackboxing" de scripts puede ser muy útil para ir más rápido a la hora de depurar y ser más productivo ya que raramente nos interesa entrar en el detalle del código de una biblioteca de terceros o que esté muy probada (salvo quizá con objetivos didácticos), y haciendo que actúen como cajas negras lo evitaremos. Además es muy fácil de deshabilitar si lo necesitásemos en un momento determinado.

Mi consejo sería que deshabilitases directamente la depuración de bibliotecas para producción (.min.js) y quizá las que. aunque no estén minimizadas, estén ampliamente utilizadas y probadas por miles de desarrolladores (frameworks y cosas así). Ahorrarás tiempo y te despistarás menos, fijándote tan solo en la información que trasiega tu programa y las acciones que se gestionan con éste, considerando tan solo los efectos que producen las otras bibliotecas de terceros.

¡Espero que te resulte útil!

jQuery.pan: Un plugin para jQuery, gratuito y Open Source, para hacer zoom y pan de imágenes

27/12/2018
Artículo original

Imagen ornamentalHace unos días os hablaba de un plugin sencillo para jQuery que había creado que permite añadir un tooltip basado en CSS a las imágenes y otros objetos, que va siguiendo el puntero del ratón para facilitar dar información sobre éstos. Otro añadido que hice al blog al mismo tiempo fue un sistema para realizar zooms a las imágenes y que puedes comprobar en cualquiera de los artículos que tengan alguna imagen un poco más grande del espacio disponible. Esta funcionalidad se la otorga un plugin de jQuery llamado jQuery.pan.

Originalmente, la versión 1.0 de este plugin la creó el programador turco Samir Hazir, pero llevaba mucho tiempo abandonado. Como era de lo mejor que había encontrado por ahí para hacer esto, y hacía exactamente lo que yo quería pero todavía no se adaptaba del todo, yo lo he retomado y he creado la versión 2.0 del mismo.

Entre sus características están:

  •  Añade capacidades de zoom y de "pan" (o sea, moverse por una imagen grande para poder verla) a cualquier imagen de una página o a cualquier elemento que contenga una imagen.
  • Puede hacer zoom de cualquier imagen que sea más grande que su tamaño actual, o bien usando un atributo data-big para indicar cuál es la imagen grande que queremos mostrar al hacer el zoom.
  • El efecto de pan es automático, de modo que solo con mover el cursor se desplaza la imagen, tal y como se ve en la animación un poco más abajo.
  • Se puede aumentar o disminuir su tamaño usando un par de botones a tal efecto, o bien moviendo la rueda del ratón.
  • Tiene soporte para dispositivos móviles, de modo que puedes hacer "pan" arrastrando la imagen grande con un dedo.
  • Soporta IE8 o posterior y, por supuesto, todos los navegadores modernos.
  • Funciona con cualquier versión de jQuery, incluyendo versiones antiguas 1.x

Puedes verlo en funcionamiento en este mini-video:

Puedes probarlo pulsando en esta imagen:

Esta foto es bastante grande. Hecha por mi, por cierto :-)

Desde el repositorio de Github puedes ver su código fuente y también descargarte la versión "Release", lista para ser utilizada en producción, minimizada y optimizada. También puedes añadirla como una dependencia a tu proyecto usando npm, en concreto con:

npm i jquery.pan

lo cual lo añadirá a las dependencias de tu proyecto.

Para utilizarla debes incluir en tu página jQuery, obviamente el plugin, y también la CSS de apoyo que necesita, que es extremadamente pequeña (400 bytes gzippeada por el servidor, lo habitual). Esta carpeta CSS contiene también 4 pequeñas imágenes que se usan para la interfaz. En definitiva algo así:

<link rel="stylesheet" href="css/jquery.pan.css" />
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="jquery.pan.js"></script>

<script type="text/javascript">
    $(function(){
        $(".pan").pan();
    })
</script>

Fíjate en que, en este caso, lo que hace el fragmento final de script (que puede ir en cualquier sitio) es, tras haberse cargado la página, localiza todos los elementos marcados con la clase .pan y les aplica el plugin llamando al método pan(). Obviamente se podría aplicar a todas las imágenes usando $('img).pan(), o mediante cualquier selector jQuery que te interesase.

En el caso de imágenes, si no se indica lo contrario (ver más adelante), se usará la misma imagen mostrada para hacer el zoom. Es decir, si la imagen no tiene su tamaño real sino uno menor porque no cabe en el espacio disponible o porque se ha cambiado éste por el motivo que sea, el componente la mostrará a su tamaño real, haciendo zoom efectivamente.

Si le añades un atributo data-big, la ruta que indiques en este atributo es la que se usará para la imagen a mostrar. Por ejemplo, si tienes dos versiones de una misma imagen, una pequeña (un thumbnail o vista previa) y otra grande que es la que quieres que se vea con el zoom, solo tienes que poner algo así:

<img data-big="img/grande.jpg" src="img/pequeña.jpg" >

De este modo la etiqueta de imagen mostrará la foto pequeña, pero al pulsarla se mostrará la foto grande.

No solo funciona con imágenes directamente. Si tienes algún otro elemento que a su vez contenga una vista previa de alguna imagen o que te interese que lance el visor para mostrar una imagen puedes decorarlo con el atributo data-big y funcionará también. Por ejemplo, si quieres que al pulsar un determinado enlace o botón se muestre una imagen con el componente puedes hacer esto:

<a class="pan" data-big="img/grande.jpg" href="#">Pulsa para ver imagen grande</a>

Importante: si no se indica el atributo data-big  y se hace zoom sobre elementos de imagen, es decir, se usa su imagen indicada en el src para hacer el zoom, solo se aplicará el zoom a aquellas imágenes que sean más grandes de lo que se está viendo en la página. Es decir, si tenemos una imagen de tamaño 200x200 pixeles (o sea, pequeña) que cabe perfectamente en la página actual, aunque le intentemos aplicar el zoom con .pan() no funcionará. Esto es así por diseño ya que es absurdo hacer zoom de algo que no lo necesita.

Al aplicar el pan() a un conjunto de elementos de jQuery, se devuelve como resultado el conjunto exacto de elementos que a los que se le haya aplicado el plugin. Es decir, si por ejemplo una imagen es pequeña y no necesita zoom, no se devolverá en el conjunto de resultados. Esto nos sirve para hacer algo con ellas justo a continuación. Por ejemplo, cambiarles el título para añadirle una indicación sobre el zoom y que se vean con el componente YACSSTooltip que presenté el otro día, así:

$(function(){
    $(".main img").pan().each(function() {
        $(this).attr('title', 'PULSA PARA AUMENTAR');
    }).addTooltip();
})

Este fragmento busca todas las imágenes dentro de los elementos con la clase .main, les aplica el plugin de zoom&pan, y a las que finalmente sí se lo haya aplicado, les cambia el título para que ponga "PULSA PARA AUMENTAR". Finalmente usa el plugin YACSSTooltip del otro día para que se vea ese mensaje claramente al pasarles por encima.

¡Espero que te sea útil!

Aprende a decir NO con CSS

26/12/2018
Artículo original

No te alarmes por el título de este post. Esto no va de mindfulness ni de autoayuda, y, por supuesto, no te vamos a tocar los chakras. Es simplemente un ejercicio de pensar un poquito diferente para mostrar lo amplio que es el abanico de posibilidades al definir selectores CSS.

Tampoco es que estemos inventando la pólvora, simplemente se trata de darte pistas para encontrar alternativas eficaces a la hora de resolver tus maquetaciones que quizá no te habías planteado.

Por ejemplo, podemos usar uno de los selectores avanzados de CSS3 que más de uno se suele olvidar de que existe: la pseudo-clase :not()

Cómo usar la pseudo-clase :not()

La pseudo-clase :not() de CSS3 nos ayuda a apuntar con nuestra regla hacia elementos que no coinciden con una lista de selectores. Se lo conoce como la pseudo-clase de negación porque se usa para evitar que se seleccionen elementos específicos. 

Por ejemplo, si tenemos en nuestra página ciertos de elementos (por ejemplo, artículos) y queremos ponerle un borde rojo a todos excepto a los que tienen la clase .loquesea, pues en vez de hacer esto:

article {
   border:5px solid red;
}

article.loquesea {
   border:0;
}

Podríamos usar esta otra regla, mucho más eficiente:

article:not(.loquesea) {
   border:5px solid red;
}

Hay que tener un poco de cuidado con esta pseudo-clase, porque es muy fácil crear un selector que no sirva de nada. Por ejemplo, :not(*) selecciona cualquier elemento que no sea un elemento. Así que, en este caso, obviamente, nunca se va aplicar.

Vale, este es un caso extremo, pero quédate con que es fácil que se te vaya la mano y excluyas más de lo necesario. Pero no te preocupes, con un poco de práctica es fácil evitar estas situaciones. Simplemente ten en cuenta esa posibilidad.

También podemos ponerlo solo, sin modificar a otro elemento. Si usamos :not(.loquesea) coincidirá con cualquier elemento que no tenga la clase .loquesea, incluidos <html> y <body>.

Soporte de :not() en navegadores

En su versión más avanzada (selectores de nivel 4, donde los argumentos son una lista de selectores) en el momento de escribir esto todavía no tiene buen soporte en los navegadores, pero sí tiene buen soporte en el nivel 3 (con un solo selector) que ya es muy útil.

De todas formas, si lo necesitas en navegadores antiguos como IE9 o inferiores, podrías tirar de  http://selectivizr.com/.

Usando :not() para detectar soporte de selectores CSS3

Otro uso curioso que se le puede dar a :not() es usarla como una especie de detector de soporte de selectores avanzados de CSS3. En este post hay un buen ejemplo. Lo he adaptado al español y lo he reformulado de forma que se vea mejor. Puedes descargar el ejemplo desde aquí. Este código tiene la misma licencia CC que el original.

Se parte de un formulario varias opciones en radio-buttons:

En la imagen se ven tres selectores de tipo radio, ninguno de ellos seleccionado

En el momento que se marca la tercera opción, se usa el truco de evaluar en CSS con :checked() si está marcado para así poder modificar otro elemento. En este caso, si está marcada la opción "Otra" se le muestra un input del tipo text para ampliar la información:

En esta imagen se ha seleccionado el último botón de radio, mostrándose automáticamente una interfaz relacionada


<form>
  <div class="opciones opcion1">
    <inputclass="opciones" id="opcion1" type="radio" name="opcion1" value="Opción 1">
    <label for="opcion1">Opción 1</label>
  </div>
  <div class="opciones opcion2">
    <inputclass="opciones" id="opcion2" type="radio" name="opcion2" value="Opción 2">
    <label for="opcion2">Opción 2</label>
  </div>

  <div class="opciones otra-opcion">
    <inputid="otra-opcion" type="radio" name="otra-opcion" value="Otra opción">
    <label for="otra-opcion">Otra</label>
      <div>
        <label for="especifica">Por favor, especifica:</label>
        <input id="especifica" name="especifica" type="text">
      </div>
  </div>

</form>

Con este CSS:

.opciones {
padding:5px;
}

.otra-opcion div {
margin:5px5px5px26px;
}

.otra-opcion div{
    display : none;
}

.otra-opcion input:checked + label + div{
    display : block;
}

En el caso de que un navegador no soporte :checked nunca se vería el formulario, pero podemos arreglarlo de forma sencilla con :not().

Si un navegador no soporta :checked tampoco va a soportar :not (ni ningún otro selector avanzado de CSS3) así que, en este caso se reformulan las reglas para que :not() esté involucrado. Esto es, sólo se ocultará el input (y luego se mostrará cuando corresponda) en funcion de :checked() si su ancestro NO tiene la clase "old". Como esta clase no la usa ningún elemento, se cumplirá siempre, así que el navegador siempre esconderá el input hasta que el radio button esté marcado.

Ahora bien, si el navegador no soporta :not(), nunca se ocultará el input, así que no habrá problema tampoco porque no soporte :checked.

.otra-opcion:not(old) div{
    display : none;
}
.otra-opcion:not(old) input:checked + label + div{
display : block;
}

Exclusiones sin :not()

A pesar de que la pseudo-clase :not es muy potente y con muchas aplicaciones, hay otras formas muy interesantes de discriminar elementos, como usar pseudo-clases que son similares entre sí, pero que se aplican de forma totalmente opuesta.

Quizá esto te suene extraño ahora mismo, pero me refiero a casos como el descrito en este artículo. En ese artículo, José Manuel Alarcón (autor y tutor del curso de HTML5 y CSS3 para desarrolladores, entre otros cursos de front end de campusMVP) se vale de :nth-child() y de :nth-last-child() combinados de tal forma que nos sirven para elegir a qué elementos de un listado aplicar ciertos estilos y a cuáles no.

Te recomiendo leer y practicar lo mismo que se hace en ese post con calma, porque es un ejercicio muy interesante.

Lo visto en este post es solo una pequeña muestra de lo que se puede hacer, pero espero que te haya picado la curiosidad lo suficiente como para querer aprender un poco más sobre cómo jugar con selectores avanzados, o, quién sabe, incluso para apuntarte a nuestro curso de HTML5 y CSS3.

Cómo priorizar las características de un producto de software

20/12/2018
Artículo original

Foto ornamental libre de derechos hecha por Kaleidico en Unsplash

Nota: este artículo es una traducción de esta respuesta en inglés en Quora de Michael J. Mehlberg que hemos querido compartir contigo. ¡Cualquier comentario es bienvenido!

Nos cabreábamos la mitad. La otra mitad estaba molesta.

"Tenemos que dejar que los usuarios puedan hacer uso de la función 'deshacer'. ¡El producto es inútil sin él!"

"¡Pero nadie va a comprar nuestro producto simplemente porque no tiene la opción de deshacer!"

Así que discutimos (mucho más tiempo del que deberíamos) sobre si nuestro equipo de programación debería dedicar tiempo o no a desarrollar la función "deshacer" para nuestros clientes.

Finalmente, se tomó una decisión... a través de la falta de decisión.

No se agregó ninguna función "deshacer" porque no podíamos ponernos de acuerdo para hacerlo. Y de todas las formas de priorizar las características de un producto, ésta fue (y sigue siendo) la peor forma de hacerlo.

Lo que hay que saber antes de priorizar las características de un producto de software

Para priorizar las características de un producto, tienes que considerar 3 cosas:

  1. ¿Cuál es el objetivo de tu producto? ¿Cómo encaja en la misión de tu empresa?
  2. ¿A qué mercados y clientes sirve tu nuevo producto y cómo les beneficia?
  3. ¿Cuál debe ser el retorno de la inversión (ROI) de tus productos?

Sí, antes de plantearse cualquier otra cosa, se necesita saber de qué manera exactamente el producto está contribuyendo a la consecución de los objetivos y la misión de la empresa. No todos los productos generan beneficios. Algunos productos están diseñados para impulsar la adopción, aumentar el reconocimiento de la marca o recopilar datos de los clientes. Si no sabes lo que está intentando conseguir tu producto será imposible priorizar las características del mismo, ya que no sabrás cómo se supone que cada característica debe servir de ayuda durante el proceso.

Una vez que se tiene un objetivo de producto específico alineado con la misión de la empresa, se necesita saber qué mercados se van a atender y, sobre todo, cómo puede el producto ayudar a los clientes a resolver sus verdaderos problemas. Sin entender los problemas de los clientes y de su mercado objetivo será imposible priorizar las características del producto, ya que no sabrá cómo se supone que cada una de las mismas debe ser de utilidad.

Finalmente, se necesita conocer el retorno de la inversión de los productos. Tanto si son datos de clientes, difusión por Internet o (en la mayoría de los casos) beneficios económicos, es necesario saber cuál es la única métrica por la que se medirá nuestro producto. Sin conocer el retorno de la inversión será imposible priorizar las características del mismo, ya que no sabrás cómo puede contribuir cada una de ellas a la consecución de ese indicador.

Cómo utilizar los tres valores críticos para dar prioridad a las características

La priorización de cualquier característica se puede hacer cuantificando dos cosas:

  1. Valor de negocio
  2. Esfuerzo de desarrollo

El cálculo del valor de negocio

Para saber cómo calcular el valor del negocio, tendrás que remitirte a los objetivos de tus productos y compararlos con los de tu mercado objetivo. En otras palabras, en el contexto del desarrollo de productos, el valor del negocio es una consecuencia de la cantidad de mercado o de cuántos clientes puedes ayudar multiplicado por la forma en que cada cliente contribuye a los objetivos de tus productos.

Valor del Negocio = Número de Clientes Atendidos x Valor (Ingresos o de otro tipo) por Cliente

Si eres del tipo de personas que necesita cuantificar las cosas, aquí tienes un ejemplo: si puedes conseguir el 10% de tu mercado objetivo formado por 100 clientes con la característica A, y cada cliente nuevo vale 1.000 euros, el valor comercial de la característica A es 100 * 0,1 (10%) * 1.000 euros = 10.000 euros.

Una manera más rápida de calcular el valor del negocio es trazar su valor relativo en comparación con otras características que estés contemplando destacar (más información a continuación).

El cálculo del esfuerzo de desarrollo

En cuanto al esfuerzo de desarrollo, suele ser más fácil de cuantificar, pero no siempre es así. Puedes hacer un análisis exhaustivo de toda la investigación, el tiempo y otros costes de desarrollo necesarios para programar cada característica. Pero yo recomendaría no hacerlo.

En cambio, traza el esfuerzo de desarrollo de manera relativa, es decir, en relación con el esfuerzo de desarrollo de otras características.

Me gusta usar la sucesión de Fibonacci para asignar un "valor" de esfuerzo de desarrollo a cada característica... es decir, ¿llevaría 1, 2, 3, 5, 8, 13, 21 semanas o más de esfuerzo de desarrollo para hacerla? Es lo suficientemente impreciso como para tener que reflejar el esfuerzo de desarrollo con tu instinto, pero aún así tiene algún valor que puedes usar para compararlo con otras características.

Consejo: Si utilizar semanas para estimar con en la sucesión de Fibonacci resulta demasiado largo o demasiado corto, simplemente cambia esa unidad a minutos, horas, días, meses, etc.

El Cuadro de Priorización

Uniendo todo esto, vamos a hacer un gráfico de 4 cuadrantes con el Valor de Negocio medido en el eje horizontal y el Esfuerzo de Desarrollo medido en el eje vertical. La gente de ProductPlan lo ilustra bien en este gráfico:

Diagrama cortesía de ProductPlan: 7 Estrategias para elegir las mejores características para su producto

A partir de aquí, es fácil ver qué características se deben priorizar primero. Descarta las características de bajo Valor de Negocio / alto Esfuerzo de Desarrollo (cuadrante de abajo a la derecha) y enfócate en las funciones que son fáciles de implementar y que aportan mucho valor.

Pero aún no hemos terminado.

Incluso en la tabla anterior, hay algunas características en el cuadrante de alto valor/bajo esfuerzo (cuadrante 1) que podrían ser debatidas por toda la eternidad. Aquí es donde se puede asignar un valor "P", o prioridad de desarrollo, a cada una de esas características.

  • P1 | Máxima prioridad: DEBE hacerse durante el próximo ciclo de desarrollo... el producto no se considerará completo o actualizado a menos que esté terminado, probado y funcionando a pleno rendimiento.
  • P2 | Prioridad media: DEBERÍA completarse durante el próximo ciclo de desarrollo.... el producto puede considerarse completo sin él, pero carecerá de una característica muy deseada y valiosa.
  • P3 | Lista de deseos: PODRÍA ser completada durante el siguiente ciclo de desarrollo si hay tiempo. Si se completa, será la guinda del pastel. De lo contrario, no hay problema, se volverá a establecer una nueva prioridad para el próximo ciclo de desarrollo.

Una nota rápida sobre herramientas

Parte de la priorización de funciones radica en las herramientas que se utilizan junto con este procedimiento que te recomiendo. Aquí están algunos de mis favoritos:

  1. Excel. De eficacia probada, Excel (o Numbers si tienes un Mac) es una forma sencilla pero efectiva de priorizar las cosas. Puedes organizar cada característica en filas, y tener columnas para el valor "P", nombre de la característica, valor del negocio, esfuerzo de desarrollo, notas, etc
  2. Trello. Esta es mi favorita en este momento. Es como una pizarra digital cubierta de notas adhesivas. Cada nota adhesiva lleva un nombre de característica con una descripción y se les pueden asignar otros valores. Muévelos a través del proceso, de izquierda a derecha, hasta que estén listas para ser liberadas. Impresionante.
  3. Notas adhesivas. No, no la aplicación para Windows o Mac.... notas adhesivas de papel en una pizarra blanca. Esto no solamente es increíblemente eficaz a la hora de priorizar características en un contexto de grupo, sino que además favorece la actividad física de las personas, lo que tiene un efecto unificador sobre el equipo de trabajo.

Espero que esto ayude. Este es un proceso que he visto que se ha utilizado y que yo mismo he utilizado con gran éxito en el transcurso del desarrollo de casi 20 productos.

Página Siguiente