Skip to content

← Tornar a la llista

De script a producte

Hi havia un script de 200 línies. Funcionava. Aquí no acabava la història — sense la passa següent, no hi hauria família.

3 min de lectura

Hi havia un script. Dos-centes línies. Funcionava. Aquí no s’acabava la història — sense la passa següent, Lumware no seria una família.

Díptic: a l'esquerra el main.py monolític inicial; a la dreta l'arbre actual de lumware/scripts/ amb core, protocol, transport, importers, server, cli + orchestrator.

El context

El primer host era un main.py de dues-centes línies. Obria un fitxer, calculava colors per a cada LED, escrivia per serial. Sense tests. Sense logs estructurats. Sense separació entre llegir, transformar i enviar.

Funcionava perquè tot estava al cap d’una persona — la meva. El dia que vaig deixar-ho un mes i el vaig tornar a obrir, vaig haver de tornar a entendre què feia. La segona setmana hi vaig afegir una “petita millora” i va deixar de funcionar. Cap test em va avisar perquè no n’hi havia cap.

L’opció fàcil: arreglar la regressió i seguir. L’opció real: parar i tractar-ho com a producte.

La decisió

Tres canvis simultanis, no negociables:

1. Estructura modular. El paquet lumware/scripts/ deixa de ser un fitxer i passa a ser cinc àrees amb fronteres clares:

  • core/ — events, settings, scheduler, cache, colors.
  • protocol/ — codec binari, CRC, definicions de frame.
  • transport/ — sèrie real i loopback per a tests.
  • importers/ — imatges i vídeos a frames RGB.
  • server/ — FastAPI + WebSocket per a la UI web.

Plus cli/ (la capa Unity-stdin) i orchestrator.py (playback en thread propi).

2. Tests reals. No demos. No assertions a if __name__. Tres nivells: unit a core/, integration a server/ i orchestrator/, compat per al contracte Unity stdin. Floor de cobertura del 80 % a core/, ancorada al pyproject.toml (avui rodant pel 98 %).

3. Convencions explícites. Imports relatius dins del paquet. print() prohibit al core (només EventBus). time.perf_counter sempre per al scheduler — mai time.time(). Settings immutables. tools/ queda al marge: pot fer shell=True, però res del runtime n’importa.

Per què aquestes regles i no altres. Cada regla està ancorada a una raó concreta: el print() prohibit ve del contracte Unity (la UI espera events serialitzats); el perf_counter ve d’un bug real de derives amb wall-clock a l’antic codi; les Settings frozen vénen de no fiar-se de mutacions creuades quan el playback i la UI corren a threads diferents. El ruff.extend-exclude està comentat al pyproject.toml amb la story que retira cada exclusió, així els legacy paths no s’eternitzen.

Aquest pas no inventa res nou. Pren el que ja funcionava i el reorganitza al voltant de fronteres ben definides. Però és el que fa possibles Lumware Capture i Lumware Stage — comparteixen vocabulari (frames, transport, host, codec) perquè aquest vocabulari, aquí, té un significat explícit i contrastable.

El que ve

Al pròxim post baixem al nivell físic: tira NeoPixel, font d’alimentació, level shifter, terra comú. Sis components i el motiu real de cadascun — perquè el software pot ser perfecte i el primer LED encara fer-te color random si la massa no està compartida.