Deja de esperar por otros
Hasta hace unos años me obsesionaba el tiempo, no me dejaba vivir en paz, siempre sentía que no había tiempo para nada y no disfrutaba ni de un mero paseo o de leer un libro. Irónicamente, me organizaba mucho peor que ahora.
En estos años, tras entender que es muy peligroso intentar controlar demasiado los tiempos, cuando entendí que cada día con vida es una victoria y cada arañazo del bruto de mi gato jugando es vida, el tiempo ya no forma parte de ninguna ecuación. Si algo no llega a tiempo, pues no llega 🤷♂️.
Esto puede sonar especialmente chocante en un consultor. A algunos clientes cuando me piden estimaciones de tiempo les digo “esto podría tomar un mes o un año, quién sabe”. Me miran raro, se supone que si alguien lo sabe soy yo.
No suelo infravalorar los factores no medibles y la experiencia me ha enseñado a no estimar nunca por lo bajo, más vale que sobre el tiempo y darle una alegría al cliente al terminar mucho antes de lo previsto, a que falte y que el cliente se lleve una desagradable sorpresa, aunque eso suponga perder algunos clientes inicialmente a los que tu estimación les parece excesiva.
Cuento esto porque el título de esta entrada podría hacer pensar que para mí es especialmente importante la productividad y no perder el tiempo, pero nada más lejos de la realidad. Ahora bien, eso no significa que no haya notado una fuerte diferencia entre la productividad inherente a algunas metodologías.
Como frikazo del tooling y DX, sé muy bien que hacerte tus propias herramientas puede acelerar tu trabajo de una forma que no te puedes ni imaginar. Que quede muy claro cuanto antes: no me refiero a reinventar la rueda. Tampoco lo considero el mal, reinventar tiene numerosas ventajas y tengo mucho para hablar de ello en otra entrada, pero no, no es de esto de lo que trato aquí.
Caso real: luces de rotación libre en Three.js
Si estás en un dispositivo compatible (requiere navegador no muy viejo, compatible con WebGL al menos) estarás viendo una escena donde hay una luz direccional. Son fuentes de luz usadas para representar iluminación global direccional plana, habitualmente un sol. Es una luz algo modificada respecto a la original. Describo luego.
Verás, en casi toda solución de renderizado 3D, los objetos tienen propiedades de rotación, normalmente asociados a una matriz de transformación. Siempre se puede rotar todo objeto modificando esta propiedad o bien mediante algún método rotate. En Three.js funciona igual.
La primera experiencia de un alumno mío con Three.js fue nefasta. Me contó que tras una hora lo descartó y volvió a otras soluciones como Unity. Concretamente lo que le hizo abandonar es que no entendía por qué al rotar una “DirectionalLight” no cambiaba nada. El objeto que representa la luz, cambiaba, su matriz efectivamente era actualizada, pero no había cambio visible, porque la luz seguía apuntando a donde quería (concretamente hacia la dirección 0,0,0).
En Three.js (hasta la fecha, release 168), por defecto, la rotación del objeto
que representa a la fuente de luz es independiente de la dirección de la luz.
La luz se controla con la propiedad target
. Siempre apunta hacia un objetivo.
Por defecto, hacia las coordenadas 0,0,0, osea, al centro de la escena.
En realidad no supone ningún problema: moviendo el target puedes cambiar la dirección, y el target puede ser un objeto vacío invisible por la escena. De ahí pueden surgir muchas opciones para realmente conseguir el efecto de rotación libre.
Aunque se topó con esto, se convenció de que no tenía sentido y que Three.js era simplemente peor que otras soluciones, que no quería estar apagando fuegos, porque si con algo “tan básico” según él, ya había un obstáculo, ¿qué le depararía el futuro? Atento a esto:
const sun = new DirectionalLight('#ffffff', 2)
sun.target.position.setZ(1)
sun.add(sun.target)
En este caso, sun
es una DirectionalLight
que como todas, siempre apunta a su
target, no cambia, pero ahora el target es hijo de la luz (con add), y además
está en frente de ella (1 en Z, entiendo +Z como el frente). Así, al rotar
sun también rotas el objeto target, haciendo que efectivamente la luz rote
de manera acorde.
¿Básicamente en 2 líneas hemos hecho que una luz rote acorde a su propiedad de rotación?, ¿de pronto tenemos luces como en Unity u otros sistemas? Esto no es precisamente un supertrabajo de ingeniería, pero muchos desarrolladores, juniors y seniors, tienden a creer que las cosas son más complejas de lo que realmente son y demasiadas veces a adoptar un rol de mero usuario.
Esto es común cuando no están acostumbrados a bibliotecas extensibles, libres y bien documentadas. Creen que lo que necesitan debe venir integrado en las bibliotecas que utilizan y se olvidan de que pueden extender cuanto quieran, e incluso contribuir con sus cambios si son muy útiles para alguien.
Me gusta especialmente Three.js porque la mayoría de clases y funciones son bastante obvias. También porque es un entorno con tantos contribuidores (más de 800 a fecha de hoy) y tan laxo, que te ves estilos de diseño muy distintos entre cada parte, haciéndolo muy “multicultural” a su manera.
Siguiendo con el ejemplo, podría abstraerse todo esto y hacer una nueva clase que herede de DirectionalLight:
class RotableDirectionalLight extends DirectionalLight {
freeRotationTarget: Object3D
constructor(color: ColorRepresentation, intensity: number) {
super(color, intensity)
this.freeRotationTarget = new Object3D()
this.freeRotationTarget.setZ(1)
this.add(this.freeRotationTarget)
this.target = this.freeRotationTarget
}
}
Y así, cuando necesitemos una luz de rotación libre hacer:
const sun = new RotableDirectionalLight('#ffffff', 2)
Solo hay que tener cuidado con la propiedad target, si se modifica manualmente ya deja de ser de rotación libre y actua como una direccional normal. Esto está bien, así puede servir de DirectionalLight cumpliendo el principio de sustitución de Liskov en su forma más pura. Guardamos una referencia al nuevo target con esta responsabilidad, para poder restablecerlo en otro momento si se quiere recuperar la rotación libre.
Independencia tecnológica
A lo mejor te preguntas por qué iba a querer alguien hacerse sus propias herramientas cuando ya existen soluciones similares, peor si piensas en la dichosa frase de “reinventar la rueda”. Y puede que te preguntes cómo se supone que todo esto puede ahorrar tiempo.
La siguiente respuesta se aplica con mayor notoriedad cuando eres un profesional con mucha experiencia o formas parte de un equipo muy experimentado, que puede crearse la herramienta adecuada en un momento o adaptar otra preexistente. Si no es así, probablemente te fatigue o incluso no sepas ni por dónde empezar por la mera falta de costumbre.
Cuando las soluciones existentes son privativas la ventaja es bastante obvia: tener libertad y completo control sobre tus herramientas, sin que una empresa de turno venga a decirte de pronto qué puedes o no puedes hacer y cuánto debes pagarle. Además, el soporte a clientes de las herramientas privativas no es tan maravilloso como muchos intentan defender. Te animo a intentar pedirle a Unity que solucione alguno de sus millones de problemas técnicos o mejore algunas de las herramientas más chapuceras que tiene (como las Unity Style Sheets). Por mucho que pagues una licencia, pasarán de ti.
Pero también es muy común hacerse uno mismo herramientas avanzadas pese a que existan soluciones libres. No siempre uno está contento con la arquitectura o el rumbo del proyecto por buenas que sean sus intenciones, y a veces está tan lejos de tus requisitos que sentirás que estarás más tiempo pidiendo ayuda y apagando fuegos que haciendo algo útil.
Cuando no tienes que esperar por nadie para corregir un problema, sino que puedes ser tú mismo (o alguien de tu equipo) quien lo haga, con garantías de que eventualmente se hará y exactamente como uno espera que se haga, no hay pausas salvo las que tú decidas. Te aseguro que en este mundo se notan más las pausas que simplemente “ir lento”, no solo en el tiempo, también en la motivación.
Además, la complejidad de las características de bibliotecas, frameworks o sistemas de naturaleza generalista o multipropósito puede llegar a ser tan grande, que simplemente estudiarte la documentación y un uso básico se vuelve una pesadilla, siendo más viable y habitualmente más eficiente hacerte una herramienta a medida o utilizar bibliotecas orientadas a una única responsabilidad.
Three.js y PixiJS, por ejemplo, no son motores de videojuego, no tienen físicas ni muchas otras herramientas, porque su rol principal es de renderizado. Adaptarlas a un sistema más complejo implica también estudiarlas hasta el punto que más o menos puedes entender bien su arquitectura interna, así que te es muy fácil modificarlas si lo necesitas.
En cualquier caso, el objetivo se puede resumir en buscar independencia. Entre más dependes de soluciones de terceros y entre más completas son sus soluciones, menos margen de maniobra tienes cuando hay un problema serio con esa dependencia. He visto a gente mantener issues en bibliotecas durante más de 5 años preguntando cada 3 meses “¿existe ya una solución?” y quejándose, cuando lo que piden se puede resolver en una tarde o dos.
Por último y no menos importante hay otra razón para querer hacerte tus propias herramientas: por diversión o arte. Estoy algo cansado de los utilitaristas. Insisto mucho en que esto es un arte, y para muchos es nuestro hobby principal además de nuestra profesión. A veces simplemente queremos intentar hacer algo por nuestra cuenta e intentar hacerlo mejor, sin desmerecer cualquier otra solución previa y sin ánimo de aportar nada o competir. A veces es una oportunidad magnífica para mejorar.