Lorenzo SanseverinoThe Last Developer
IT EN

The Last Developer

Lorenzo Sanseverino

Nell'apocalisse dell'IA faccio l'ultimo custode del codice: leggo ancora quello che spedisco. Temi Shopify, backend Java e software pensato, ottimizzato, funzionante e pure bello da guardare.

  • Shopify
  • Liquid
  • JavaScript
  • TypeScript
  • Java
  • Spring
  • MongoDB
  • Redis
  • Next.js
  • GraphQL

Skill

Le tecnologie con cui lavoro ogni giorno.

Shopify

Temi Online Store 2.0, Liquid, sezioni, metafield e Storefront API.

{% render 'card', product: product %}

JavaScript

Vanilla e Custom Elements, con il minimo indispensabile nel browser.

customElements.define('x-tabs', Tabs)

TypeScript

Interfacce React e Next.js tipizzate, contratti condivisi con le API.

type View = Partial<Summoner>

CSS

Scroll-driven animation, :has(), container query e @property al posto del JavaScript.

animation-timeline: view()

Java

Record, stream e concorrenza per code, scheduler e servizi su VPS Debian.

record Rank(String tier, int lp) {}

Spring Boot

Controller REST su Tomcat embedded, validazione e job schedulati.

@GetMapping("/profile/{puuid}")

MongoDB

Dati canonici, aggregation pipeline e facet per le statistiche.

db.match.aggregate(pipeline)

MySQL

Schemi relazionali e query scritte a mano. MariaDB in produzione per guild e impostazioni.

SELECT prefix FROM guild WHERE id = ?

Frontend

  • React
  • Next.js
  • GraphQL
  • Astro
  • Vite

Backend e dati

  • Redis
  • MariaDB
  • JDA
  • Maven

Infra e tool

  • Git
  • GitHub
  • Debian
  • Cloudflare
  • Algolia

Progetti

Beebot e Iodid

Un backend Java e un frontend Next.js che formano un’unica piattaforma di statistiche per League of Legends. Scegli uno scenario e guarda cosa succede dietro ogni richiesta.

Backend · Java

Beebot

Bot Discord e motore statistiche per League of Legends, scritto in Java. Le API non aspettano mai Riot: leggono da Redis e Mongo e accodano il resto.

  • Cache-aside: Redis derivato, Mongo come fonte
  • Rate limiter a finestra scorrevole verso Riot
  • Stati PENDING, PARTIAL e READY per il client
STACK
Java · Spring · JDA · MongoDB · Redis
SCOPE
Bot, tracker, job queue, API
STATUS
Attivo, separazione in servizi in corso
Apri il case study
beebot / api trace
Scenario
  1. API
  2. Redis
  3. MongoDB
  4. Riot
  1. GET /api/lol/EUW1/profile/{puuid}
  2. REDIS GET beebot:lol:ls:EUROPE:EUW1:{puuid}:summoner HIT · 2 ms
  3. 200 SummonerView 6 ms
  1. API
  2. Redis
  3. MongoDB
  4. Riot
  1. GET /api/lol/EUW1/profile/{puuid}
  2. REDIS GET beebot:lol:ls:EUROPE:EUW1:{puuid}:summoner MISS · 1 ms
  3. MONGO summoner.findOne({ puuid }) HIT · 9 ms
  4. REDIS SET beebot:lol:ls:EUROPE:EUW1:{puuid}:summoner 1 ms
  5. 200 SummonerView 15 ms
  1. API
  2. Redis
  3. MongoDB
  4. Riot
  1. GET /api/lol/EUW1/profile/{puuid}
  2. REDIS GET beebot:lol:ls:EUROPE:EUW1:{puuid}:summoner MISS · 1 ms
  3. MONGO summoner.findOne({ puuid }) MISS · 8 ms
  4. QUEUE QueueHandler.immediate(...) → RiotScheduler QUEUED
  5. 202 profile_pending 11 ms
  6. RIOT GET /riot/account/v1/accounts/by-puuid/{puuid} 200 · 141 ms
  7. RIOT GET /lol/summoner/v4/summoners/by-puuid/{puuid} 200 · 152 ms
  8. MONGO summoner.insertOne(profile) 7 ms
  9. REDIS SET beebot:lol:ls:EUROPE:EUW1:{puuid}:summoner 1 ms
  10. 200 SummonerView RETRY · 6 ms
  1. API
  2. Redis
  3. MongoDB
  4. Riot
  1. GET /api/lol/EUW1/profile-by-name/Player/EUW
  2. REDIS Riot ID lookup · Player#EUW · EUW1 MISS · 1 ms
  3. MONGO saved summoner lookup by Riot ID MISS · 8 ms
  4. RIOT GET /riot/account/v1/accounts/by-riot-id/Player/EUW 404 · 138 ms
  5. 404 not_found 149 ms

Esempio con tempi indicativi. Le chiamate a Riot passano sempre dalla coda.

Frontend · TypeScript

Iodid

Statistiche di League of Legends dentro un sistema operativo virtuale: ogni profilo è una cartella, ogni pagina una finestra, ogni percorso un URL.

  • Filesystem virtuale con ls, cd, tree e profile
  • Window manager e terminale sopra Next.js
  • Percorsi VFS indicizzabili come pagine
STACK
Next.js · React · TypeScript
SCOPE
Profili, campioni, build, classifiche
STATUS
In sviluppo
Apri il case study
iodid / terminal
Scenario
  1. Shell
  2. VFS
  3. Beebot API
  4. Window
  1. iodid:~ $ cd /
  2. iodid:/ $ ls
  3. champions/ summoners/ match/ leaderboard/ tier-list/ records/ arena/ htop/ home/
  4. iodid:/ $ cd summoners/Chiaretta-0117
  5. iodid:/summoners/Chiaretta-0117 $ ls
  6. champions/ activity/ matchups/ lp-gains/ matches/ records/ livegame/
  1. Shell
  2. VFS
  3. Beebot API
  4. Window
  1. iodid:/summoners/Chiaretta-0117 $ profile
  2. → GET /api/lol/EUW1/profile-by-name/Chiaretta/0117
  3. ← 200 SummonerView 148 ms
  4. ▣ terminal viewer · Chiaretta#0117
  5. ↗ url /euw1/profile/Chiaretta-0117
  1. Shell
  2. VFS
  3. Beebot API
  4. Window
  1. iodid:/summoners/Chiaretta-0117 $ tree -L 1
  2. ├── champions/ ├── activity/ ├── matchups/
  3. ├── lp-gains/ ├── matches/ ├── records/
  4. └── livegame/
  5. iodid:/summoners/Chiaretta-0117 $ cd lp-gains
  6. → GET /api/lol/EUW1/profile/{puuid}/rank-history
  7. ← 200 Solo/Duo · Flex · Premade 96 ms
  8. ▣ terminal viewer · lp-gains

Ogni comando risolve un nodo del VFS; i dati arrivano dalle API di Beebot.

  • Pomellato
  • Slam Jam
  • Rick Owens
  • Antonioli
  • Montura
  • Kappa

Shopify

Più CSS, meno JavaScript.

Sviluppo di temi, componenti e integrazioni per brand di moda e lusso.

Variant picker a cascata

CSS · Liquid

Filtri con :has() e selettori di attributo. Liquid precalcola variantMap e sold-out, JS resta solo per i reset.

Swatch tra prodotti collegati

Liquid · Algolia

Per Kappa: metafield JSON, swatch con gradienti hard-stop e radio nascosti.

Metafield varianti automatizzati

GraphQL

Per Montura: aggiornamenti bulk dei campi stagione sulle varianti.

Consenso OneTrust e Shopify

JS · Privacy

OneTrust collegato alla Customer Privacy API di Shopify.

Come funziona il gioco: solo CSS

Velocità
  1. Il livello è una griglia

    Ogni ostacolo ha una colonna: left = x × cella. Niente canvas, niente coordinate in JS.

  2. Il mondo scorre, il cubo no

    Una keyframe trasla il percorso di una tile in loop; il cubo resta fermo sull’asse X.

  3. Il salto è una curva

    I keyframe del salto cadono sulle colonne degli ostacoli, con cubic-bezier per la gravità.

  4. Il JavaScript serve solo per esplodere

    Il click imposta un data-attribute: particelle, onda d’urto e restart sono CSS.

[data-obstacle] {
  left: calc(var(--x) * var(--cell));
}
[data-world] {
  animation: world var(--period) linear infinite;
}
@keyframes hop {
  22%, 41% { translate: 0 0 }
  31% { translate: 0 calc(-2.2 * var(--cell)) }
}
[data-lab]:has(#lab-grid:checked) [data-x]::after {
  content: "x " attr(data-x);
}

Filosofia

La filosofia del codice.

Il codice va pensato, ottimizzato, deve funzionare ed essere bello da leggere. Anche dopo l'apocalisse dell'IA.

  1. Le IA sono strumenti

    Fanno ormai parte del lavoro e lo rendono più veloce. Il controllo però resta umano: ogni riga va capita, verificata e scelta.

  2. Ownership

    Chi scrive il codice ne risponde. Ogni logica ha un solo proprietario e ogni decisione ha una ragione.

  3. Pulizia

    Nomi espliciti, responsabilità chiare, nulla di superfluo. Il codice pulito costa meno a ogni modifica.

  4. Poche righe, ma capibili

    La brevità ha valore solo se resta leggibile. Meno codice significa meno errori e meno da mantenere.

naming/no-type-in-name JAVA

Il tipo è già nel tipo

La dichiarazione dice già int: ripeterlo nel nome è rumore. Un intero locale si chiama count, non itemCounterInt.

int itemCounterInt = items.size();int count = items.size();
naming/explicit-unit JAVA

L’unità va nel nome

Un long può contenere secondi, millisecondi o nanosecondi: qui il nome deve dirlo. Meglio ancora, un tipo che lo sa già.

long start = System.currentTimeMillis();long timeout = 30;long startTimeMillis = System.currentTimeMillis();Duration timeout = Duration.ofSeconds(30);
naming/no-abbreviations JS

Niente abbreviazioni

Il codice si scrive una volta e si legge cento. Tre lettere risparmiate non valgono un dubbio.

const usrCfg = getCfg(req);const btn = el.querySelector('.btn');const userConfig = getConfig(request);const button = element.querySelector('[data-button]');
comments/name-over-comment JS

Un nome al posto del commento

Se una condizione ha bisogno di un commento, merita una costante con un nome.

// controlla se l’utente è adminif (user.role === 'admin' && user.active) {const isAdmin = user.role === 'admin' && user.active;if (isAdmin) {
flow/early-return JAVA

Early return, niente nesting

Prima si escludono i casi che non servono, poi il percorso principale resta piatto.

if (player != null) {    if (player.isRanked()) {        return player.getRank();    }}return Rank.UNRANKED;if (player == null || !player.isRanked()) return Rank.UNRANKED;return player.getRank();
size/readable-lines JS

Poche righe, ma leggibili

La versione corta vince solo se si legge meglio di quella lunga.

let r = false;for (let i = 0; i < p.v.length; i++) {  if (p.v[i].a) r = true;}return r;return product.variants.some((variant) => variant.available);

Prima gli sviluppatori. Poi il prodotto. Infine i clienti.

È una battuta. Più o meno. Prendersi cura di chi sviluppa è il primo modo per prendersi cura del prodotto e di chi lo usa.

Gaming

Quando chiudo l’editor, apro il client.

Le mie statistiche di League of Legends, servite dall’API di Beebot, più qualche clip. Altri giochi in arrivo.

Stagione 2026 · Support main

Win rate47,7%21 V · 23 S
KDA medio2,16ultime 20 partite
Forma5V · 5Sultime 10

Campioni più giocati

Solo/Duo
Thresh 14 partite50,0%
Brand 5 partite60,0%
Galio 3 partite33,3%
Singed 3 partite66,7%
Nautilus 3 partite100,0%

Ultime partite

  1. Sconfitta
    GalioArena · 23:26
    2/6/163,00 KDA
    — 08/11
  2. Vittoria
    AmumuOther · 22:52
    1/3/72,67 KDA
    — 08/11
  3. Vittoria
    KaynOther · 21:03
    3/1/47,00 KDA
    — 08/11
  4. Sconfitta
    NasusOther · 22:04
    5/5/62,20 KDA
    — 08/11

Contatti

Parliamo del tuo progetto.