Stiamo costruendo infrastrutture che devono quasi mai fallire. Per raggiungere questo obiettivo, ci muoviamo velocemente mentre spediamo software di qualità straordinaria senza scorciatoie o compromessi. Questo documento delinea gli standard ingegneristici che ci guideranno fino al 2026 e oltre.
Struttura del Team
La nostra organizzazione ingegneristica è composta da cinque team principali, ciascuno con responsabilità distinte:
Team di Fondazione: Si concentra sulla creazione e il mantenimento di standard di codifica e modelli architetturali. Questo team lavora in collaborazione con gli altri team per stabilire le migliori pratiche in tutta l'organizzazione.
Team Consumatori, Imprese e Piattaforma: Team focalizzati sui prodotti che rilasciano funzioni rapidamente mantenendo gli standard di qualità stabiliti in questo documento. Questi team dimostrano che velocità e qualità non sono mutuamente esclusive.
Team Comunitario: Responsabile di revisionare rapidamente le PR dalla comunità open source, fornendo feedback e guidando quel lavoro verso la fusione. Questo team garantisce che i nostri contributori open source abbiano una grande esperienza e che i loro contributi soddisfino i nostri standard di qualità.
I Nostri Risultati Finora
I dati parlano da soli. Negli ultimi diciotto mesi, abbiamo trasformato radicalmente il modo in cui costruiamo software:

Abbiamo approssimativamente raddoppiato la nostra produttività ingegneristica migliorando contemporaneamente la qualità. Ancora più impressionante è il cambiamento di ciò che stiamo costruendo:

Siamo riusciti a riallocare approssimativamente il 20% dell'impegno ingegneristico dalle correzioni alle funzioni, ai miglioramenti delle prestazioni, ai rifattori e alle attività. Questo cambiamento dimostra che investire nella qualità e nell'architettura non ti rallenta. Ti accelera.
La Fondazione di Cal.com Abilita l'Eccellenza per coss.com
Cal.com è un'azienda stabile e redditizia che continueremo a far crescere.
Questo successo ci dà un vantaggio unico mentre costruiamo coss.com. A differenza dei primi giorni di Cal.com, dove dovevamo muoverci rapidamente per stabilire l'adattamento del prodotto al mercato e creare un business sostenibile, coss.com parte da una posizione di forza.
Non abbiamo bisogno di affrettare coss.com.
La stabilità di Cal.com significa che possiamo permetterci di costruire coss.com nel modo giusto fin dal primo giorno. Abbiamo il lusso di implementare questi standard ingegneristici senza la pressione delle immediate esigenze di mercato o dei vincoli di finanziamento. Questa è una posizione di partenza fondamentalmente diversa.
La "lentezza" è un investimento, non un costo.
Sì, seguire questi standard potrebbe sembrare più lento inizialmente e può addirittura essere frustrante per alcuni ingegneri. Scrivere DTO richiede più tempo rispetto al passaggio diretto dei tipi di database al frontend. Creare astrazioni adeguate e iniezione delle dipendenze richiede più progettazione iniziale. Mantenere una copertura del test del 80%+ per il nuovo codice richiede disciplina. Ma questa apparente lentezza è temporanea e il rendimento è esponenziale.
Considera i rendimenti composti...
Il codice architettato correttamente dall'inizio non necessita di massicci rifattori successivamente
Una copertura elevata dei test previene bug che altrimenti consumerebbero settimane di debugging e correzioni urgenti (vedi 2023 a metà 2024)
Le astrazioni corrette rendono l'aggiunta di nuove funzioni drammaticamente più veloce nel tempo
I confini puliti e i DTO prevengono l'erosione architetturale che alla fine richiede riscritture complete
La traiettoria Cal.com mostra cosa succede quando ottimizzi per la velocità immediata. Alta velocità iniziale che gradualmente degrada man mano che il debito tecnico si accumula, le scorciatoie architetturali creano colli di bottiglia e più tempo viene speso per risolvere problemi che per costruire caratteristiche (vedi grafico precedente dove spendevamo il 55-60% dell'impegno ingegneristico sulle correzioni).
La traiettoria di coss.com abbraccerà il potere di costruire correttamente fin dal primo giorno. Una velocità iniziale leggermente più lenta mentre si stabiliscono i modelli adeguati, seguita da un'accelerazione esponenziale man mano che quei modelli rendono e consentono uno sviluppo più rapido con maggiore fiducia.
Principi Fondamentali
1. Nessuna qualità rimandata
Minimizzeremo i "lo farò in una PR di follow-up" per i piccoli rifattori.
Le PR di follow-up per miglioramenti minori raramente si realizzano. Invece, si accumulano come debito tecnico che ci appesantisce mesi o anni dopo. Se un piccolo rifattore può essere fatto ora, fallo ora. I follow-up dovrebbero essere riservati a cambiamenti sostanziali che meritano veramente PR separate o per casi eccezionali e urgenti.
2. Alti standard nella revisione del codice
Non lasciare passare PR con molti difetti solo per evitare di essere "la persona cattiva."
È esattamente così che i codebase diventano disordinati col tempo. La revisione del codice non riguarda l'essere gentili. Riguarda il mantenimento degli standard di qualità che la nostra infrastruttura richiede. Ogni piccola imprecisione conta. Ogni violazione dei modelli conta. Affrontali prima di fare la fusione, non dopo.
3. Spronarsi a fare la cosa giusta
Ci teniamo reciprocamente responsabili per la qualità
Tagliare angoli potrebbe sembrare più veloce al momento, ma crea problemi che rallentano tutti in seguito. Quando vedi un collega che sta per unire una PR con evidenti problemi, esprimi la tua preoccupazione. Quando qualcuno suggerisce un trucco veloce invece della soluzione adeguata, rispondi. Quando sei tentato di saltare i test o ignorare i modelli architetturali, aspettati che i tuoi colleghi ti mettano in discussione.
Questo non riguarda l'essere difficili o rallentare le persone
Si tratta di una proprietà collettiva del nostro codice e della nostra reputazione. Ogni scorciatoia che una persona prende diventa un problema per tutti. Ogni angolo tagliato oggi significa più sessioni di debug, più hotfix e più clienti frustrati domani.
Rendilo normale sfidare le decisioni sbagliate, con rispetto
Se qualcuno dice "facciamo solo il codice fisso per ora", la risposta prevista dovrebbe essere "cosa ci vorrebbe per farlo nel modo giusto la prima volta?" Se qualcuno vuole commettere codice non testato, il team dovrebbe rispondere. Se qualcuno suggerisce di copiare e incollare invece di creare un'appropriata astrazione, fallo notare con rispetto.
Stiamo costruendo qualcosa che deve quasi mai fallire
Quel livello di affidabilità non accade per caso. Accade quando ogni ingegnere si sente responsabile della qualità, non solo del proprio codice ma dell'intero sistema. Riusciamo come team o falliamo come team.
4. Puntare alla semplicità
Priorità alla chiarezza sulla genialità
L'obiettivo è che il codice sia facile da leggere e capire rapidamente, non di una complessità elegante. I sistemi semplici riducono il carico cognitivo per ogni ingegnere.
Fatti le domande giuste
Sto realmente risolvendo il problema in questione?
Sto pensando troppo ai possibili casi d'uso futuri?
Ho considerato almeno un'altra alternativa per risolvere questo? Come si confronta?
Semplice non significa privo di funzionalità
Solo perché il nostro obiettivo è creare sistemi semplici, questo non significa che dovrebbero sembrare anemici e privi di funzionalità ovvie.
5. Automizzare tutto
Sfruttare l'IA
Genera l'80% del codice base e del codice non critico utilizzando l'IA, permettendoci di concentrarci solo sulla logica aziendale complessa e sulle architetture critiche.
Costruisci un'allerta senza rumore e una gestione intelligente degli errori.
Il testing manuale è sempre più una cosa del passato. L'IA può costruire rapidamente e intelligentemente mega suite di test per noi.
Il nostro CI è il boss finale
Tutto in questo documento sugli standard viene controllato prima che il codice venga unito nelle PR
Nessuna sorpresa entra in main
I controlli sono rapidi e utili
Standard Architetturali
Stiamo passando a un modello architetturale rigoroso basato su Architettura Vertical Slice e Domain-Driven Design (DDD). I seguenti modelli e principi saranno applicati rigorosamente nelle revisioni delle PR e tramite linting.
Vertical Slice Architecture: packages/features
Il nostro codice è organizzato per dominio, non per livello tecnico. La directory packages/features è il cuore di questo approccio architetturale. Ogni cartella al suo interno rappresenta una completa fetta verticale dell'applicazione, guidata dal dominio che tocca.
Struttura:
Ogni cartella delle funzioni è una fetta verticale indipendente che include tutto il necessario per quel dominio:
Logica di dominio: Le regole aziendali di base e le entità specifiche di quella funzione
Servizi applicativi: Orchestrazione dei casi d'uso per quel dominio
Repository: Accesso ai dati specifici delle esigenze di quella funzione
DTOs: Oggetti di trasferimento dati per attraversare i confini
Componenti UI: Componenti frontend relativi a questa funzione (ove applicabile)
Test: Test unitari, d'integrazione e di fine/continua 2e2 per questa funzione
Perché le Fette Verticali Contano
L'architettura tradizionale a strati è organizzata per preoccupazioni tecniche:
Questo crea diversi problemi:
Le modifiche a una funzione richiedono di toccare file sparsi in più directory
È difficile capire cosa fa una funzione perché il suo codice è frammentato
I team si ostacolano l'un l'altro quando lavorano su diverse funzioni
Non puoi facilmente estrarre o deprecare una funzione
L'architettura a fetta verticale è organizzata per dominio:
Questo risolve questi problemi:
Tutto ciò che riguarda la disponibilità si trova in
packages/features/availabilityPuoi comprendere l'intera funzione della disponibilità esplorando una directory
I team possono lavorare su diverse funzioni senza conflitti (se il team ingegneristico di Cal.com cresce ma sicuramente in coss.com avremo team che si occuperanno di pacchetti importanti)
Le funzioni sono debolmente accoppiate e possono evolvere in modo indipendente
Linee Guida per l'Organizzazione delle Funzioni
In teoria, ogni funzione è indipendentemente distribuibile. Anche se potremmo non distribuirli effettivamente separatamente, organizzare in questo modo ci obbliga a mantenere le dipendenze chiare e l'accoppiamento minimo. Questo è il presupposto e il successo dei microservizi, anche se non li stiamo ancora distribuendo.
Le funzioni comunicano tramite interfacce ben definite. Se le prenotazioni necessitano di dati sulla disponibilità, importano da @calcom/features/availability tramite interfacce esportate, non accedendo ai dettagli dell'implementazione interna.
Il codice condiviso vive nei luoghi appropriati:
Utilità agnostiche del dominio e preoccupazioni trasversali (auth, logging):
packages/libPrimitive dell'UI condivisa:
packages/ui(e presto l'ui di coss.com)
I confini del dominio sono applicati automaticamente. Costruiremo un linting che impedisce di raggiungere l'interno delle funzioni dove non dovresti essere autorizzato. Se packages/features/bookings tenta di importare da packages/features/availability/services/internal, il linter lo bloccherà. Tutte le dipendenze tra funzioni devono passare attraverso l'API pubblica delle funzioni.


Nuove funzioni iniziano come fette verticali. Durante la creazione di qualcosa di nuovo, crea una nuova cartella in packages/features con l'intera fetta verticale. Questo rende chiaro cosa stai costruendo e mantiene tutto organizzato fin dal primo giorno.
Benefici
Scopribilità
Stai cercando la logica di prenotazione? È tutto in
packages/features/bookings. Non c'è bisogno di fare la caccia attraverso controller, servizi, repository e utilità sparsi in tutto il codice base.
Test più facili
Testa l'intera funzione come un'unità. Hai tutti i pezzi in un unico posto, rendendo naturale e semplice il testing di integrazione.
Dipendenze più chiare
Quando vedi
import { getAvailability } from '@calcom/features/availability', sai esattamente da quale funzione dipendi. Quando le dipendenze diventano troppo complesse, è evidente e può essere affrontato.
Pattern di Repository e Iniezione delle Dipendenze
Le scelte tecnologiche non devono insinuarsi attraverso l'applicazione. Il problema di Prisma lo illustra perfettamente. Attualmente abbiamo riferimenti a Prisma sparsi su centinaia di file. Questo crea un grande accoppiamento e rende i cambiamenti tecnologici proibitivamente costosi. Stiamo provando la gestione di questo aggiornamento a Prisma v6.16. Qualcosa che dovrebbe essere stato solo un rifattore localizzato dietro repository schermati è stata una caccia meandrica, quasi infinita ai problemi attraverso più app.
Lo standard per il futuro:
Tutti gli accessi al database devono passare per le classi Repository. Abbiamo già un buon punto di partenza su questo.
I repository sono l'unico codice che conosce Prisma (o qualsiasi altro ORM). Nessuna logica dovrebbe essere in loro.
I repository sono iniettati attraverso contenitori di Iniezione delle Dipendenze
Se mai passiamo da Prisma a Drizzle o un altro ORM, gli unici cambiamenti richiesti sono:
Implementazioni di repository
Cablaggio del contenitore DI per nuovi repository
Nient'altro nel codice dovrebbe preoccuparsi o cambiare
Questo non è teorico. Questo è come costruiamo sistemi mantenibili.
Data Transfer Objects (DTOs)
I tipi di database non devono trapelare al frontend. Questo è diventato un ascoltatore popolare nel nostro stack tecnologico, ma è un odore di codice che crea molti problemi.
Accoppiamento tecnologico (tipi Prisma che finiscono nei componenti React)
Rischi di sicurezza (fuga accidentale di campi sensibili)
Contratti fragili tra server e client (questo è particolarmente problematico man mano che costruiamo molte più API)
Impossibilità di evolvere lo schema del database indipendentemente
Tutte le conversioni DTO tramite Zod, anche per una risposta API, per garantire che tutti i dati vengano validati prima di inviarli all'utente. Meglio fallire che restituire qualcosa di sbagliato.
Lo standard per il futuro:
Crea espliciti DTO ad ogni confine architettonico.
Livello dati → Livello applicazione → API: Trasforma i modelli del database in DTO a livello di applicazione, poi trasforma i DTO applicativi in DTO specifici dell'API
API → Livello applicazione → Livello dati: Trasforma i DTO API attraverso il livello applicativo e in DTO specifici del dato
Sì, questo richiede più codice. Sì, ne vale la pena. Confini espliciti evitano l'erosione architetturale che crea incubi di manutenzione a lungo termine.
Pattern del Domain-Driven Design
I seguenti modelli devono essere utilizzati correttamente e consistentemente:
Servizi Applicativi
Orchestrano i casi d'uso, coordinano tra servizi di dominio e repository
Servizi di Dominio
Contengono la logica aziendale che non appartiene naturalmente a un'unica entità
Repository
Astraggono l'accesso ai dati, isolano le scelte tecnologiche
Iniezione delle Dipendenze
Consentono un accoppiamento libero, facilitano i test, isolano le preoccupazioni
Proxy di Caching
Avvolgono i repository o i servizi per aggiungere il comportamento di caching in modo trasparente
Non è l'unico modo per fare caching, ovviamente, ma un bel punto di partenza
Decoratori
Aggiungi preoccupazioni trasversali (logging, metriche, ecc.) senza inquinare la logica del dominio
Coerenza del Codice
I nostri codici base dovrebbero sembrare come se fossero scritti da una sola persona. Questo livello di coerenza richiede l'aderenza rigorosa ai modelli stabiliti, regole di linting complete che applicano gli standard architetturali, revisioni del codice che respingono le violazioni di schema + l'aiuto dei revisori di codice AI.
Spostare le Condizioni al Punto di Ingresso dell'Applicazione
Le istruzioni If appartengono al punto di ingresso, non sparse tra i tuoi servizi. Questo è uno dei principi architetturali più importanti per mantenere codice chiaro e focalizzato che non si trasforma in una complessità ingestibile.
Ecco come il codice si degrada nel tempo: Un servizio è scritto per uno scopo chiaro e specifico. La logica è pulita e focalizzata. Poi arriva un nuovo requisito di prodotto, e qualcuno aggiunge un'istruzione if. Alcuni anni e diversi altri requisiti più tardi, quel servizio è pieno di controlli condizionali per scenari diversi. Il servizio è diventato:
Complicato e difficile da leggere
Difficile da comprendere e motivare su
Più suscettibile ai bug
Violando la responsabilità singola (gestendo troppi casi diversi)
Quasi impossibile da testare a fondo
Il servizio ha superato i suoi limiti in termini di responsabilità e logica.
Una Soluzione: Pattern Factory con Servizi Specializzati
Usa il pattern factory per prendere decisioni al punto di ingresso, poi delega a servizi specializzati che gestiscono la loro logica specifica senza condizionali.
Esempio dalla nostra base di codice:
Il BillingPortalServiceFactory determina se il fatturato è per un'organizzazione, un team o un singolo utente, quindi ritorna il servizio appropriato:
Ogni servizio gestisce poi la sua logica specifica senza bisogno di controllare "sono un'organizzazione o un team?":
Perché Questo è Importante
I servizi rimangono focalizzati
Ogni servizio ha una responsabilità e non ha bisogno di sapere di altri contesti. Il
OrganizationBillingPortalServicenon contiene istruzioni if controllandoif (isTeam)oif (isUser). Sa solo come gestire le organizzazioni.
I cambiamenti sono isolati
Quando devi modificare la logica di fatturazione delle organizzazioni, tocchi solo
OrganizationBillingPortalService. Non rischi di rompere la fatturazione del team o dell'utente. Non hai bisogno di seguire attraverso condizioni annidate per capire quale percorso segue il tuo codice.
Il testing è semplice
Testa ciascun servizio indipendentemente con i suoi specifici scenari. Non c'è bisogno di testare ogni combinazione di condizioni tra diversi contesti.
I nuovi requisiti non inquinano il codice esistente
e.g. Quando hai bisogno di aggiungere la fatturazione enterprise con regole diverse, crei
EnterpriseBillingPortalService. La factory guadagna un'altra condizione, ma i servizi esistenti rimangono intatti e focalizzati.
Come Ottenere Ciò
Sposta le condizioni verso controller, factory o logica di routing. Lascia che questi punti di ingresso prendano decisioni su quale servizio utilizzare.
Mantieni i servizi puri e focalizzati su una responsabilità singola. Se un servizio ha bisogno di controllare "che tipo sono?", probabilmente hai bisogno di più servizi.
Preferisci il polimorfismo ai condizionali
Le interfacce definiscono il contratto. Le implementazioni concrete forniscono le specifiche.
Fai attenzione all'accumulazione delle istruzioni if
Durante la revisione del codice, se vedi un servizio che ottiene condizioni per scenari diversi, questo segnala di rifattorizzare in servizi specializzati.
Progettazione delle API: Controller Sottili e Astrazione HTTP
I controller sono strati sottili che gestiscono solo preoccupazioni HTTP.
Prendono richieste, le elaborano e mappano i dati ai DTO che vengono passati alla logica applicativa centrale. Andando avanti, non dovrebbero essere viste logiche applicative o centrali nelle rotte API o nei gestori tRPC.
Dobbiamo staccare la tecnologia HTTP dalla nostra applicazione.
Il modo in cui trasferiamo i dati tra client e server (sia REST, tRPC, ecc.) non dovrebbe influenzare il funzionamento della nostra applicazione centrale. HTTP è un meccanismo di consegna, non un driver architetturale.
Responsabilità del controller (e SOLO queste):
Ricevere e validare le richieste in entrata
Estrarre i dati dai parametri delle richieste, dal corpo, dalle intestazioni
Trasformare i dati della richiesta in DTO
Chiamare i servizi applicativi appropriati con quei DTO
Trasformare le risposte dei servizi applicativi in DTO di risposta
Restituire risposte HTTP con i codici di stato corretti
I controller NON devono:
Contenere logica aziendale o regole di dominio
Accedere direttamente a database o servizi esterni
Eseguire trasformazioni o calcoli complessi sui dati
Prendere decisioni su ciò che l'applicazione dovrebbe fare
Conoscere i dettagli dell'implementazione del dominio
Esempio di pattern di controller sottile:
Versionamento dell'API e Modifiche di Rottura
Nessuna modifica di rottura. Questo è critico. Una volta che un endpoint API è pubblico, deve rimanere stabile. Le modifiche di rottura distruggono la fiducia degli sviluppatori e creano incubi di integrazione per i nostri utenti.
Strategie per evitare modifiche di rottura:
Aggiungi sempre i nuovi campi come opzionali
Usa il versionamento API quando devi cambiare comportamento esistente
Depreca gli endpoint vecchi graziosamente con percorsi di migrazione chiari
Mantieni la retrocompatibilità per almeno due versioni principali
Quando devi fare modifiche di rottura:
Crea una nuova versione API utilizzando il versionamento specifico per data in API v2 (forse esamineremo anche il versionamento nominato che Stripe ha recentemente introdotto)
Esegui entrambe le versioni contemporaneamente durante la transizione (lo facciamo già in API v2)
Fornisci strumenti di migrazione automatizzati quando possibile
Dai agli utenti ampio tempo per migrare (minimo 6 mesi per API pubbliche)
Documenta esattamente cosa è cambiato e perché
Prestazioni e Complessità dell'Algoritmo
Costruiamo per grandi organizzazioni e team. Ciò che funziona bene con 10 utenti o 50 record può collassare sotto il peso della scala enterprise. Le prestazioni non sono qualcosa che ottimizziamo più tardi. È qualcosa che costruiamo correttamente fin dall'inizio.
Pensa Alla Scala Sin Dal Primo Giorno
Quando costruisci funzioni, chiediti sempre: "Come si comporta questo con 1.000 utenti? 10.000 record? 100.000 operazioni?" La differenza tra algoritmi O(n) e O(n²) potrebbe essere impercettibile nello sviluppo, ma catastrofica in produzione.
Pattern comuni O(n²) da evitare:
Iterazioni annidate degli array (
.mapdentro.map,.forEachdentro.forEach)Metodi di array come
.some,.findo.filterdentro cicli o callbackControllare ogni elemento contro ogni altro elemento senza ottimizzazione
Filtri concatenati o mappature annidate su grandi liste
Esempio reale: Per 100 slot disponibili e 50 periodi occupati, un algoritmo O(n²) esegue 5.000 controlli. Escala a 500 slot e 200 periodi occupati, e stai facendo 100.000 operazioni. Questo è un aumento di 20x nel carico di elaborazione per solo un aumento di 5x nei dati.
Scegliere le Strutture Dati e Gli Algoritmi Giusti
La maggior parte dei problemi di prestazioni sono risolti scegliendo migliori strutture dati e algoritmi:
Sorting + uscita anticipata: Ordina i tuoi dati una volta, poi esci dai cicli quando sai che gli elementi rimanenti non corrisponderanno
Ricerca binaria: Usa la ricerca binaria per ricerche in array ordinati invece di scansioni lineari
Tecniche a doppio puntatore: Per unire o intersecare sequenze ordinate, attraversale entrambe con puntatori invece di cicli annidati
Hash map/set: Usa oggetti o Set per ricerche O(1) invece di
.findo.includessu arrayAlberi di intervallo: Per pianificazione, disponibilità e interrogazioni di intervalli, usa strutture ad albero adeguate invece di confronto a forza bruta
Esempio di trasformazione:
Controlli di Prestazione Automatizzati
Implementeremo più strati di difesa contro le regressioni prestazionali:
Regole di linting che segnalano:
Funzioni con cicli annidati o metodi di array annidati
Chiamate
.some,.find, o.filterannidate multipleRicorsione senza memoization
Noti anti-pattern per il nostro dominio (scheduling, controlli di disponibilità, ecc.)
Benchmark di prestazioni nel CI che:
Eseguono algoritmi critici su dati realistici e su larga scala
Confrontano i tempi di esecuzione con il benchmark su ogni PR
Bloccano le fusioni che introducono regressioni prestazionali
Test con dati su scala enterprise (migliaia di utenti, decine di migliaia di record)
Monitoraggio di produzione che:
Traccia il tempo di esecuzione per percorsi critici
Avvisa quando gli algoritmi rallentano man mano che i dati crescono
Cattura le regressioni prima che gli utenti le notino
Fornisce dati prestazionali reali per informare le ottimizzazioni
La Prestazione è una Caratteristica
Le prestazioni non sono opzionali. Non è qualcosa che affrontiamo "dopo". Per i clienti aziendali che prenotano per team su larga scala, risposte lente significano produttività persa e utenti frustrati (la nostra esperienza con alcuni grandi clienti aziendali può esserne una testimonianza).
Ogni ingegnere dovrebbe:
Profilare il tuo codice prima di ottimizzare, ma pensa alla complessità dall'inizio
Test con dati realistici su larga scala (non solo 5 record di test). Abbiamo già costruito script di seed. Probabilmente dobbiamo estendere.
Scegli algoritmi e strutture dati efficienti in modo predefinito
Fai attenzione alle iterazioni annidate durante la revisione del codice
Metti in discussione qualsiasi algoritmo che scala con il prodotto di due variabili
La Realtà NP-Hard della Pianificazione
I problemi di pianificazione sono fondamentalmente NP-hard. Ciò significa che man mano che il numero di vincoli, partecipanti o slot temporali cresce, la complessità computazionale può esplodere esponenzialmente. La maggior parte degli algoritmi di pianificazione ottimali ha un tempo complesso esponenziale nei casi peggiori, rendendo la scelta dell'algoritmo assolutamente critica.
Implicazioni reali:
Trovare il tempo di incontro ottimale per 10 persone in 3 fusi orari con vincoli di disponibilità individuale è costoso computazionalmente
Aggiungere rilevamento conflitti, buffer e una pletora di altre opzioni amplifica il problema
Scelte algoritmiche pessime che funzionano bene per piccoli team diventano completamente inutilizzabili per grandi organizzazioni
Ciò che richiede millisecondi per 5 utenti potrebbe richiedere molti secondi per le organizzazioni
Strategie per gestire la complessità NP-hard:
Usa algoritmi di approssimazione che trovano soluzioni "abbastanza buone" rapidamente anziché soluzioni perfette lentamente
Implementare il caching aggressivo di orari e disponibilità calcolati
Pre-calcolate scenari comuni durante le ore non di picco
Spezzare grandi problemi di pianificazione in pezzi più piccoli e gestibili
Impostare limiti di timeout ragionevoli e ripiegare su algoritmi più semplici quando necessario
Questo è il motivo per cui le prestazioni non sono solo un "nice-to-have" nel software di pianificazione. È la base che determina se il tuo sistema può scalare alle esigenze aziendali o collassare sotto i modelli di uso reali.
Requisiti di Copertura del Codice
Tracciamento di copertura globale
Tracciamo la copertura complessiva della base di codice come una metrica chiave che migliora nel tempo. Questo ci dà visibilità sulla nostra maturità nei test e aiuta a identificare le aree che necessitano di attenzione. La percentuale di copertura globale è visualizzata in modo prominente nei nostri dashboard.
Copertura dell'80%+ per nuovo codice
Ogni PR deve avere una copertura di test vicino all'80%+ per il codice che introduce o modifica. Questo è applicato automaticamente nel nostro ciclo CI. Se aggiungi 50 righe di nuovo codice, quelle 50 righe devono essere coperte dai test. Se modifichi una funzione esistente, le tue modifiche devono essere testate. Questa è una copertura complessiva dei test. La copertura dei test unitari deve essere vicino al 100 %, specialmente con la possibilità di sfruttare l'IA per aiutare a generare questi.
Rispondendo all'argomento "la copertura non è l'intera storia": Sì, sappiamo che la copertura non garantisce test perfetti. Sappiamo che puoi scrivere test privi di senso che colpiscono ogni riga ma non testano nulla di significativo. Sappiamo che la copertura è solo una metrica tra molte. Ma, certamente è meglio puntare a una percentuale alta che non avere idea di dove sei.
Misurare il Successo
"Velocità" (prendendola dallo Scrum anche se non utilizzeremo Scrum)
Crescita continua nelle statistiche mensili (funzioni, miglioramenti, rifattori)
Qualità
Ridurre l'impegno sulle PR sull'effettuare le correzioni dall'attuale 35 % a 20 % o meno entro la fine del 2026 (calcolato in base alle modifiche ai file e alle aggiunte/eliminazioni)
Salute architetturale
Metriche sull'aderenza agli schemi, accoppiamento tecnologico, violazioni di confini
Efficienza delle revisioni
PR più piccole, revisioni più veloci, meno giri di feedback
Uptime dell'applicazione e dell'API
Quanto siamo vicini al 99,99%?

Inizia subito gratuitamente con Cal.com!
Sperimenta una programmazione e produttività senza interruzioni senza spese nascoste. Iscriviti in pochi secondi e inizia a semplificare la tua programmazione oggi, senza bisogno di carta di credito!


