Curso de Desarrollo de software basado en agile y XP (II edición)
https://www.biko2.com/curso-desarrollo-software/
Fechas | Duración | Plazas | Lugar |
---|---|---|---|
7sept-21dic 2021 | 160 h | 16 | Oficinas de Biko |
En Biko hemos creado el curso al que nos hubiera gustado asistir. Un curso en el que no te vamos a enseñar a programar, sino a convertirte en un excelente desarrollador. Y de eso va esto: de aprender a aprender, de dejar atrás el "qué" para centrarnos en el "cómo".
Aprende a desarrollar software (pero de verdad)
Ser desarrollador/a no implica saber desarrollar (buen) software. Y viceversa. Los lenguajes de programación pueden cambiar, pero si conoces los principios, construirás y entregarás valor continuamente.
Esto no es (solo) un curso, esto es otra historia
El Curso de Desarrollo de Software basado en agile y XP es una formación totalmente práctica, alejada de lo que se aprende en cursos tradicionales o en la universidad.
No se trata de enseñarte a tirar tu primera línea de código, sino de que aprendas cómo desarrollar software de valor, trabajar con metodologías ágiles y de mejorar tu potencial.
Al finalizar, obtendrás una titulación oficial homologada por el Ministerio. Y gracias al Servicio Navarro de Empleo, el curso es totalmente gratuito. Suena bien, ¿eh?
Vocabulario tech
UX, mobile, desktop, asistentes de voz Omnicanalidad, mailing, SEO
TDD, testing, testing unitario Patrones de diseño, buenas prácticas de desarrollo, refactoring, pair programming Integración continua, desarrollo incremental, software craftsmanship, deuda técnica
Agile, Scrum, Lean, Kanban [para gestión de cualquier trabajo creativo] XP (Extreme Programming) [marco de trabajo especializado en desarrollo]
Frontend, backend
HTML, CSS, JS React, Angular, Vue Node, PHP, Symphony
"La diferencia entre librería y framework reside en quién tiene el control" – Pablo
- Librería: tu código llama a la librería. Ej.: ReactJS
- Framework: el framework llama a tu código. Ej.: Symphony, AngularJS
Drupal, Wordpress, CMS
Cloud, AWS, Lambda, CDN, API, API REST, GraphQL
"GraphQL te da los ingredientes y te montas el bocadillo que quieres. Con API REST tienes que elegir tu bocadillo entre los disponibles de la carta" – Aritz
Git
Conceptos
3 estados: modified, staged y committed
3 áreas de trabajo: git dir (.git/
), working dir (copia de una versión) y staging area (próximo commit)
Comandos
git stash
con nombre:git stash push -m "readme updated"
- en versiones nuevas de git,
git checkout -- .
=git restore
ygit checkout branchB
=git switch branchB
Ojocuidao
Detached HEAD: El commit en el que estamos no está en ninguna rama, por lo que si nos movemos de rama este commit se perderá.
git merge
: Solo se puede hacer fast-forward si, en el momento de hacer merge, no hay commits añadidos a la rama sobre la que escribimos (ej: master) después de haber empezado nuestra rama (en la que hemos trabajado).
Javascript
Versión ES6
Arrow functions
const d = () => new Date();
const myConcat = (arr, arr2) => arr1.concat(arr2);
console.log(myConcat([1,2],[3,4,5]));
Template literals
`<li>${arr[i]}</li>`
Destructuring
const {name, age} = user;
const bicycle = {
gear: 2,
setGear(newGear) {
this.gear = newGear;
}
};
bicycle.setGear(3);
Spread operator
arr2 = [...arr1];
Mixin, IIFE
let funModule = (function(){
return {
isCuteMixin: function(obj) {
obj.isCute = function() {
return true;
}
},
...
}
})();
Nullish coalescing
const getName = (animal) => { return animal.name ?? 'Toby' };
Typescript
-
Typescript añade tipado estático. Se transpila a Javascript.
-
Las propiedades por lo general siempre privadas. Si hay que devolver su valor, mejor a través de un método.
-
Everyday types:
- The primitives: string, number and boolean
- Arrays:
Array<string>
- Any
- Union Type:
let aTextOrNumber: number|string = 5;
- Generic types:
function identity<Type>(arg: Type): Type { return arg; }
-
Interfaz: "el contrato que tenemos que cumplir"
interface Animal {
type: string;
name?: string;
}
class Cat implements Animal {
constructor(public type: string) {}
...
}
class Pokemon {
constructor(private nombre: string) {}
}
/* Encadenamiento opcional u optional chaining (?.)
El operador ?. funciona de manera similar a el operador de encadenamiento .,
excepto que en lugar de causar un error si una referencia es casi-nula (null o undefined),
la expresión hace una evaluación de circuito corto con un valor de retorno de undefined.
Puedes saber más en: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#optional-chaining */
const getName = (animal?: Animal) => animal?.name;
"Tell, don't ask"
A una clase le tengo que pedir el nombre (getName()
), no preguntarle por el nombre (this.name
).
NodeJS
Entorno de ejecución de Javascript.
- Con el motor V8 de Chrome, en C++.
- Motor de alto rendimiento de Javascript y WebAssembly.
- Orientado a eventos asíncronos, hilo único.
Gestores de paquetes
npm
npm init # -> package.json
npm install # instala las dependencias de package.json
# npm ci es más rápido
npm run # ejecuta un script definido en package.json
npm start # ejecuta el script "start" de package.json
npm install --save-dev/-D
yarn
yarn utiliza el registro de paquetes de npm
npx
node package manager
npx create-react-app # = npm install create-react-app
package.json
$ cat package.json
{
"name": "proj_node",
"version": "1.0.0",
"description": "",
"main": "index.ts",
"scripts": {
"start": "ts-node index.ts",
/* ^^^ apunta al binario local, que desde node_modules
está en .bin/ts-node -> ts-node/dist/bin.js (16KB)
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"devDependencies": {
"@types/node": "^16.9.2",
// ^^^ para tener los tipos de las librerías (ej: http)
"ts-node": "^10.2.1",
// ^^^ evita que tengamos que hacer el build de ts a js
"typescript": "^4.4.3"
// ^^^ para transpilar
}
}
Introducción a web
"TCP es como el doble check de Whatsapp" (definición 🔝) – Carlos
¿Cómo se sirve el HTML de la aplicación?
Ficheros estáticos almacenados vs. Ficheros generados dinámicamente, bajo demanda
¿Dónde se genera el HTML de la aplicación?
CSR/SPA (Client-Side Rendering / Single Page Application) vs. SSR/MPA (Server-Side Rendering / Multiple Page Application)
Asynchronous JavaScript, or JavaScript that uses callbacks, is hard to get right intuitively. A lot of code ends up looking like this:
fs.readdir(source, function (err, files) { if (err) { console.log(err) } else { files.forEach(function (filename, fileIndex) { console.log(filename) gm(filename).size(function (err, values) { if (err) { console.log('err') } else { console.log(filename + ' : ' + values) aspect = (values.width / values.height) widths.forEach(function (width, widthIndex) { ··· }.bind(this)) } }) }) } })
See the pyramid shape and all the
})
at the end? Eek!
CSS
Son estilos en cascada porque los estilos se aplican de arriba a abajo y, en caso de existir ambigüedad, siguen una serie de normas para resolverla.
Gracias a CSS podemos llevar a cabo el concepto de separación de presentación (diseño, colores, formas - CSS) y contenido (información y datos - HTML).
Espaciado CSS
Así se ve el modelo caja en el inspector de cada navegador (derecha: Chrome, izquierda: Firefox):
- Padding: entre elemento y elementos internos
- Margin: entre elemento y elementos externos
Ejemplo:
.warning {
padding-top: 100px; /* este no aplica,
porque el siguiente lo sobreescribe */
padding: 50px;
}
Los márgenes entre elementos del mismo nivel no se suman, sino que se queda el mayor. Con los paddings sí se suman (son una propiedad interna de la caja).
Recomendación: usar por lo general padding-bottom. Sobre todo mantener un mismo criterio.
Box-sizing
div {
box-sizing: content-box;
height: 200px;
width: 300px; /* width aplica al contenido */
}
Content-box se rompe fácil intentando restar píxeles de margen al % de width.
div {
box-sizing: border-box;
height: 200px;
width: 300px; /* width aplica al margen */
}
Content-box se rompe fácil intentando restar píxeles de margen al % de width.
.page_container {
margin: 0 auto;
}
Recursos habituales
Cascada y herencia
- En igualdad de condiciones, gana lo que está escrito después en el CSS.
- Los hijos heredan estilos del padre. No ocurre con todas las propiedades: No ocurre con border, margins, padding, ... Sí ocurre con color, font, ...
- Se puede forzar la herencia asignando el valor inherit a la propiedad que queremos que aplique:
background-color: inherit;
Clases de utilidad
Nos ayudan a separar responsabilidades (ej.: estilos visuales y estilos de layout).
.margin-bottom-s {
margin-bottom: 12px;
}
Flexbox
#pond { display: flex; }
Al asignarles el tipo flex a los divs, pasan a ocupar solo lo que su contenido necesita.
Posible solución: usar media queries o:
.card_content {
width: 300px;
flex-grow: 1;
}
ReactJS
- Librería, no framework
- Componentes en vez de plantillas. Simplifica el trabajo con el DOM.
- Moderno: ES6, Babel (transpila JS moderno a compatible con todos los navegadores), Webpack (bundelizador, es decir, junta scripts de javascript).
Flujo de datos
- Se inicia un cambio de estado
- Se propaga al padre
- Y se extiende
Las funciones han dejado obsoletas a las clases
import React, {useState} from 'react';
import ReactDom from 'react-dom';
function Counter() { return ...; }
ReactDOM.render(<Counter/>, document.getElementById('root'));
Antes se hacía:
class Welcome extends React.Component {
render() { return ...; }
}
Hook 1: useState
- Coger input (el evento
onChange
recibe la propiedadevent
):
const Header = ({ onFilter, onClear, filter }) => (
<>
<ComicInput onChange={event => onFilter(event.target.value)} value={filter} />
<Button marginLeft="base" onClick={onClear}>
Limpiar búsqueda
</Button>
</>
)
- Inicializar variables:
export const ComicsList = () => {
const [filter, setFilter] = React.useState('')
- Filtrar en lista:
const filteredComics = comics.filter(comic => comic.title.toLowerCase().includes(filter.toLowerCase()))
return (
<Layout>
...
<Header onFilter={setFilter} onClear={() => setFilter('')} filter={filter} />
<List comics={filteredComics} />
...
</Layout>
)
}
ReactJS
Hook 2: useEffect
Es una función que se ejecuta después de useState (ejecuta efectos secundarios).
useEffect( () => {}, [] )
/* ^^ ^^
| └ deps: array de estados de los que depende
└ effect: función a la que aplica
*/
Si la función es una llamada asíncrona,
const fetchComics = async() => {
setComics( await api.allComics() )
}
useEffect( () => { fetchComics() }, [] )
useEffect(() => {
async function fetchComics() {
setComics(await getCommonComics(firstSelectedChar, secondSelectedChar))
// ^^^ es igual que:
//getCommonComics(...).then( data => {setComics(data)} );
}
fetchComics()
}, [firstSelectedChar, secondSelectedChar])
- Por defecto, los efectos se ejecutan después de cada renderizado completo, pero pueden ejecutarse solo cuando ciertos valores han cambiado (deps).
- El modelo mental para entenderlo es pensar con qué parte del estado estamos sincronizando este "efecto". No pensemos en cuándo se ejecuta.
Hay más Hooks adicionales, pero son variantes de los básicos: el Hook de estado y el de efecto.
Arquitectura hexagonal (avance)
- Dominio: tu negocio
- Infra: externo (ej: AWS)
- Servicio: coordina tu dominio con tu infra. También se conocen como "casos de uso"
Routing
ui/views/Root.jsx
:
import { Route, Switch } from 'react-router-dom'
import { routes } from 'ui/routes'
export const Root = () => (
<>
<GlobalStyles />
<Switch>
<Route exact path={routes.COMICS} component={ComicsList} />
</Switch>
</>
)
ui/routes.js
:
export const routes = {
COMICS: '/'
}
Agile
El triángulo de hierro
Valores del manifiesto ágil
Individuos e interacciones sobre procesos y herramientas
Software funcionando sobre documentación extensiva
Colaboración con el cliente sobre negociación contractual
Respuesta ante el cambio sobre seguir un plan
Esto es, aunque valoramos los elementos de la derecha, valoramos más los de la izquierda.
Principios del manifiesto ágil
Elijo algunos de los 12 principios:
- El método más eficiente y efectivo de comunicar información entre el equipos y sus miembros es la comunicación cara a cara (mayor Ancho de Banda).
- Los procesos Ágiles promueven el desarrollo sostenible -> mantener un ritmo constante de forma indefinida.
- Es esencial la simplicidad -> el arte de maximizar la cantidad de trabajo no realizado.
- El equipo reflexiona a intervalos regulares para cambiar su comportamiento en base a lo aprendido.
Prácticas y metodologías ágiles
Maximizar el valor
- Planificación en términos de producto
- Priorización en base a valor
- Colaboración con el cliente
- Visibilidad del proceso
- Detectar y eliminar desperdicio
Desarrollo iterativo e incremental
Agile != Scrum
- 4 people in the room (Cockburn)
- Extreme Programming
- Kanban
- Crystal
- Scrum
Scrum
Panel de scrum (artefacto)
To-Do | Doing | Done |
---|---|---|
XP
"El problema básico del desarrollo de software es el riesgo." – Kent Beck
- Evitar riesgos de software
- Acortar el ciclo de feedback:
- Con compañeros (programando)
- Con clientes (features)
- Con usuarios (a producción)
¿Para qué XP?
-
Calidad -
Código limpio -
Profesionalidad -
Hacer lo correcto -
Economics
-
Felicidad
Testing
Un test es un bloque de código que ejecuta a otro código (SUT1) para validar de forma automática su correcto funcionamiento.
System Under Test
Tipos de tests
- Unitarios
- Integración
- Aceptación o extremo a extremo (end to end, E2E)
- Regresión visual
Estructura de un test
- Preparación / Ejecución / Aserción
- Arrange / Act / Assert (AAA)
Ejemplo de test escrito con Jest:
describe(name: 'Sum', fun: () => {
//^^^ conjunto de tests
/*
LEGIBILIDAD DE LOS TESTS: más importante en los tests incluso que en código de producción.
- Claridad, simplicidad y densidad. Eliminar detalles innecesarios.
- Utilizar el lenguaje de dominio para describirlos
*/
it('should sum two numbers', () => {
// Arrange:
const num1 = 1;
const num2 = 2;
// Act:
const result = sum(num1, num2);
// Assert:
expect(result).toEqual(3);
})
})
Cualidades de un test
FIRST:
- Fast
- Independent
- Repeteable
- Self-validating
- Timely (oportuno: los programamos lo antes posible)
Puntos clave
- En cada test prueba UNA sola cosa que funciona.
- Testea el qué, no el cómo.
- Testea la interfaz pública de cada unidad, no los métodos privados (la interfaz pública solo llamará a los privados).
- Hay que mantener los tests, deben ser fáciles de entender, y sólo deben fallar cuando aquello que quieres probar no funciona.
- No testees código trivial, que no tiene lógica (p.ej. getters, setters).
- Elimina detalles innecesarios (incluso tests que se hayan convertido en innecesarios, una vez han cumplido su labor de ayudarte en el proceso de desarrollo).
- No escribas tests por buscar el 100% de cobertura. Haz test útiles que te ayuden a comprobar que el sistema funciona y te den fiabilidad a la hora de refactorizar.
TDD
Es una técnica de desarrollo de software que consiste en escribir tests antes de escribir el código.
TDD:
- No te guía a hacer un buen diseño.
- No es dogma.
- No es una técnica de testing.
Beneficios TDD
- Obliga a pensar en el comportamiento antes de programar.
- Da feedback rápido.
- Reduce el miedo a cambiar el código. Los tests actúan como red de seguridad.
- Guía la documentación. Usamos los tests como documentación viva.
Las tres leyes de TDD
- No está permitido escribir código de producción sin tener antes un test unitario que falle.
- No está permitido escribir más código de test que el mínimo para hacer que el test falle.
- No está permitido escribir más código de producción que el suficiente para que el test que fallaba ahora pase.
Ciclo de TDD: rojo-verde-refactor
flowchart LR
id1(test que falla) --> id2(test que pasa) --> id3(refactorizar test) --> id1
"TDD es un poco ir a romper tu código, porque así ves dónde lo puedes mejorar." – Stefan
La idea de TDD es ir desarrollando a un ritmo constante, sostenible y controlado.
Predicados y reglas
git clone -b predicates-rules-openclose https://github.com/540/fizzbuzz-ts.git
Predicado: función que devuelve un booleano
Si tenemos una torre de if
s, será mejor convertirlo a una tabla (= conjunto de reglas), porque será más mantenible.
import { otherwise, and, contains, isDivisibleBy, or } from './predicate';
import { Rule } from './rule';
export const createFizzBuzz = () => {
const ruleSet: Rule[] = [
{predicate: and(isDivisibleBy(3), isDivisibleBy(5)), trans: () => 'FizzBuzz'},
{predicate: ..., trans: () => ...},
...
{predicate: otherwise, trans: n => n.toString()},
];
return fizzBuzz(ruleSet);
};
export const fizzBuzz = (ruleSet: Rule[]) => (n: number) =>
ruleSet.find(rule => rule.predicate(n))!.trans(n);
/* ^ no permite que el find devuelva null,
aunque en este caso tenemos el otherwise en las reglas
así que no haría falta.
*/
export interface Rule {
predicate: Predicate
trans: (n: number) => string
}
export type Predicate = (n: number) => boolean
Test doubles
Como los dobles del cine de acción.
También se conocen como "mocks", aunque estos son un tipo concreto de doble ("mocks aren't stubs").
Tipos de dobles
En el Arrange ("dobles de estado"):
- Dummy: objeto que se pasa para cumplir una dependencia. No son comunes en sistemas bien diseñados.
- Stub: reemplaza la respuesta de una llamada a API/BBDD. Siempre devuelve datos.
- Fake: es un stub hecho a mano, además implementando cierta lógica. Ej.: BBDD en memoria, enrutador con con ciertas rutas predefinidas.
En el Assert ("dobles de interacción", más frágiles):
- Mock: valida que un comportamiento deseado se ha cumplido. Ej.: "Espero que el método X se haya llamado. Si no, lanzo error".
- Spy: comprueba que algo ha ocurrido. Hasta que no le preguntas no habla (no falla).
No hay que usar dobles de tests para objetos/métodos simples.
Arquitectura Hexagonal
La arquitectura de software se refiere al conjunto de reglas que decidimos como equipo al definir cómo diseñamos nuestro software.
- the set of design decisions that must be made early
- the decisions that you wish you could get right eary
- the decisions that are hard to change
– Martin Fowler, "Making Architecture Matter"
Design Payoff Line
Complejidad esencial vs. Complejidad accidental
Tipos de arquitecturas
- Old School ("a batalla", sin arquitectura)
- MVC
- Arquitecturas limpias (arquitectura hexagonal)
Arquitecturas limpias
Arquitectura hexagonal
Capa de dominio
A la que pertenece lo relacionado con el problema específico del mundo real que tratamos de resolver desarrollando software
Entidad: Objeto del modelo con identidad propia, distinguible de otros objetos con los mismos atributos (ej.: usuarios).
Value Object: Clase que se identifica por el valor que representa, no por su identidad (ej.: números, fechas, monedas, URLs). Por ejemplo, podrían ser value objects para nosotros dos productos que cuesten 5€, por ser idénticos en cuanto a precio.
Capa de aplicación
Engloba los "casos de uso" o "servicios de aplicación" que representan de forma atómica las funcionalidades del sistema.
Los casos de uso pueden hacer de "barrera transaccional" (o suceden o no suceden, no se quedan a medias) con el sistema de persistencia.
Capa de infraestructura
Es código que cambia en función de decisiones externas (ej.: librerías).
Regla de dependencia
Las capas de fuera conocen lo que hay inmediatamente dentro, pero las capas de dentro no saben nada sobre lo que hay fuera.
Principio de Inversión de Dependencias
Las cosas más importantes (dominio) no deben depender de las cosas menos importantes (infraestructura: frameworks o librerías para el acceso a datos, red, framework de vistas utilizado, etc.).
El principio de inversión de dependencias dice que los módulos de alto nivel no deberían depender de los de bajo nivel, ni las abstracciones de detalles.¹ – Robert C. Martin ("Uncle Bob")
ej.: abstracción: $Invoice
Repository
Queremos conseguir que los Casos de Uso dependan de algo abstracto
ej.: que no dependa de qué base de datos usamos, sino que directamente tengamos un invoiceRepository.save(invoice)
¿Por qué hacer una abstracción cuando solo tienes una implementación? Porque el coste a futuro de cambiar eso es mucho más caro si no hacemos la abstracción de primeras.
Patrón de diseño de Inyección de Dependencias
Un objeto (cliente) recibe otro objeto (servicio) del cual depende.¹ Lo que busca este patrón de diseño es extraer la responsabilidad de creación de instancias de un componente para delegarla en otro. – Martin Fowler
Estructura de carpetas
src/
├── core/
│ ├── domain/
│ ├── infrastructure/
│ └── services/
└── ui/ # ui/ es infraestructura, pero la sacan fuera por ser
│ # muy grande e incluso desacoplada en cuanto a lenguaje
├── components/
├── theme/
└── views/
Naming
⚠️ Desgaste de energía
❌ Nombres acoplados al tipo de variable -> Evitar porque provoca desinformación.
❌ Nombres parecidos (ej.: cust, customer
, getList, getLists
)
-> Hay que hacer que los nombres sean significativamente distintos.
🟢 Nombres pronunciables.
❌ Objetos "dios" (ej: HttpUtils
)
-> ¿Qué responsabilidad tiene?
Es un problema de diseño. Indica que no estamos concretando lo suficiente la responsabilidad de cada método.
"No haces código para ti ni para ahora. Lo haces para el equipo y para el futuro." – Rubén
Porque el mantenimiento del código es el mayor coste.
-
Cuanto hay conceptos de negocio complejos/difíciles de traducir. -> Establecer un diccionario de equipo (lo importante es decidir qué palabra usamos para refernirnos a cada concepto).
-
Ojo Mayúsculas/minúsculas. Ej.: Ficheros en Linux son sensibles a mayúsculas, en Windows no.
-
Reuniones de concerns técnicas (2x30 mins / semana). Después, alguien aplica los cambios retroactivamente.
Biko Insights #5
"Biko Insights es una revista donde recogemos nuestras reflexiones sobre algunas tendencias en tecnología y digitalización."
"Para nosotros es un hito importante haber conseguido publicar esta quinta edición. Jamás imaginamos que compartir nuestra manera de entender este negocio iba a ser tan gratificante."
4 - Una empresa que crea escuela
Un artículo de Aritz Suescun (COO en Biko) y Pablo Albizu (Socio-fundador de 540 y CTO en Biko).
- Los primeros pasos de lo que llamamos Biko School
- Lo que nos movió a lanzarnos
- Comprobar cuánta gente hay fuera de nuestros muros interesada en profundizar en hacer software bien hecho.
- Dar una oportunidad a gente que está empezando en esta profesión para aprender las cosas "bien" desde el principio.
- Compartir nuestra experiencia, con sus cosas buenas y sus cosas malas, para que los demás construyan sobre ella y todos mejoremos por el camino.
- Si le damos tanta importancia a formarnos continuamente... ¿no tiene sentido que aportemos nuestro granito de arena?
- Empezar a enseñar. Probar a enseñar. No sólo individualmente, sino como organización.
- Una breve historia del primer curso
- ¿Cuál fue el resultado del curso? Feedback recibido
- ¿Qué conclusiones sacamos? ¿Qué hemos aprendido?
- ¿Sabemos enseñar? Sí sabemos
- La importancia de aprender y de enseñar en nuestro modelo
- Transformador para la carrera profesional
- Y ahora ¿dónde nos lleva este camino?
- Tenemos claro que queremos repetir el curso e incluso hacer más ediciones dentro de un mismo año.
- Nos gustaría ampliar el público objetivo, enfocándonos también en líderes de equipo, managers y formadores.
- Vamos a asentar las bases de un espacio de aprendizaje y enseñanza dentro de Biko, de una escuela.