Lecture de scoreboards
Transformer la photo d'un tableau des scores en données de match — sans jamais toucher à la base de celui qui les consomme.
Le problème
The Circle saisit ses scores de tournoi à la main. À la fin d'une partie, un organisateur recopie une vingtaine de lignes depuis une capture d'écran de téléphone. C'est long, et on se trompe.
L'idée : envoyer la capture, récupérer du JSON. Mais dès le départ, une contrainte a orienté toute la conception — ce service devait être vendable seul. N'importe quel clan ou organisation esport a exactement le même problème.
La décision qui structure tout : la frontière
Une règle, non négociable, écrite avant la première ligne de code : le service ne touche jamais à la base de The Circle. Il expose une API, il renvoie du JSON. S'il plante, tombe, ou sort une bêtise, The Circle continue de tourner exactement comme avant.
Le piège classique de ce genre de duo, c'est le développeur du service qui demande « juste un accès à la base pour tester ». La réponse est non, et le cahier des charges le dit noir sur blanc. Tout se teste avec des réponses bouchonnées.
Le point de bascule est le pseudo. Un tableau des scores n'affiche que des pseudos en jeu : le service les lit et s'arrête là. La correspondance pseudo → compte se fait uniquement du côté de The Circle, la seule à connaître ses membres. Le service ne reçoit jamais d'identifiant de compte, n'en devine aucun, et ne corrige jamais un pseudo.
Le contrat d'API est figé dès le départ pour que la question ne se pose jamais.
Ce que ça donne, concrètement
Sans état, sans base
Le service ne stocke ni les captures ni les résultats. Il valide et il répond. Seules les clés d'API existent — et sous forme hachée, dans une variable d'environnement.
Un format de sortie neutre
Équipes, classement, pseudo, éliminations, morts, assistances — et un indice de confiance par joueur. Rien qui trahisse le client à qui il est destiné.
Des erreurs uniformes
Corps invalide, clé refusée, tableau illisible, trop d'appels, panne : cinq codes, un seul format. Le client sait toujours quoi faire de la réponse.
Un premier lot qui ne lit rien
Le lot 1 ne fait aucune lecture d'image : il valide un JSON déjà extrait. C'est volontaire — il sert à verrouiller la frontière avant que le sujet difficile n'arrive.
Ma part du travail
Nous sommes deux sur ce dépôt, et je préfère être précis sur qui a fait quoi. J'ai écrit le cahier des charges — la frontière, le contrat de sortie, les jalons — puis sa deuxième version quand on a décidé de piloter la lecture depuis le serveur. J'ai aussi fourni de vraies captures de parties avec leur transcription, pour qu'on puisse mesurer la justesse au lieu de l'estimer.
Côté code, mes contributions portent sur le moteur de lecture : la détection des colonnes par ancrage sur l'en-tête — c'est ce qui a fait passer les éliminations, morts et assistances à 100 % de justesse sur tablette comme sur téléphone, alors qu'une grille figée ne marchait que sur un format d'écran ; la fusion des deux moteurs, navigateur et serveur, en un seul paquet consommable ; un gain de performance en ne reconfigurant le moteur qu'une fois par colonne au lieu d'une fois par cellule ; et la lecture du score de manches, qui permet d'en déduire le vainqueur sans le demander.
Ce que j'en retiens
Travailler à deux sur des dépôts séparés oblige à une discipline que je n'avais pas quand je codais seul : le contrat passe avant le code. Tant qu'on ne sait pas exactement ce qui traverse la frontière, on ne peut pas travailler en parallèle sans se marcher dessus.
Et une limite assumée, documentée dans le dépôt plutôt que cachée : le limiteur d'appels du premier lot compte en mémoire, par instance. Sur un hébergement sans état, c'est approximatif. La signature de la fonction a été écrite pour qu'on puisse brancher un vrai compteur partagé sans rien changer ailleurs — mais tant que ce n'est pas fait, c'est écrit dans le fichier et dans le README.