Aikido

Come evitare di violare i contratti delle API pubbliche: mantenere la compatibilità con le versioni precedenti

Manutenibilità

Regola

Evita di violare i contratti delle API pubbliche.
Le modifiche agli endpoint delle API pubbliche che interrompono
le richieste dei client esistenti costituiscono modifiche strutturali incompatibili.
Considera il contratto API come una promessa — modificarlo
dopo che i client hanno iniziato a farvi affidamento compromette il loro codice.

Linguaggi supportati: PHP, Java, C#, Python, JavaScript, TypeScript

Introduzione

Le API pubbliche sono contratti tra il tuo servizio e i suoi consumer. Una volta che i client dipendono dal formato di richiesta, dalla struttura di risposta o dal comportamento di un endpoint, modificarli compromette il loro codice. Le modifiche non retrocompatibili costringono tutti i client ad aggiornarsi simultaneamente, il che risulta spesso impossibile quando non si ha il controllo diretto sui client stessi. Le app per dispositivi mobili non possono essere aggiornate forzatamente, le integrazioni di terze parti richiedono tempo per la migrazione e i sistemi legacy potrebbero non essere mai aggiornati.

Perché è importante

Disservizio per i client e perdita di fiducia: Le modifiche che rompono la compatibilità delle API causano errori immediati nelle applicazioni client in produzione. Gli utenti riscontrano errori, perdita di dati o interruzioni totali del servizio. Ciò danneggia il rapporto di fiducia tra i fornitori di API e i consumer, oltre a violare il contratto implicito secondo cui le API stabili devono rimanere tali.

Costi di coordinamento: Coordinare modifiche distruttive tra più team di sviluppo client è costoso e lento. Ciascun team necessita di tempo per aggiornare il codice, testare le modifiche e procedere al rilascio. Per le API pubbliche con client sconosciuti (app mobili, integrazioni di terze parti), il coordinamento risulta impossibile.

Proliferazione delle versioni: Modifiche non retrocompatibili gestite in modo inadeguato portano al mantenimento simultaneo di più versioni delle API. Ciascuna versione richiede percorsi di codice, test, documentazione e correzioni di bug separati, moltiplicando esponenzialmente l'onere di manutenzione.

Esempi di codice

❌ Non conforme:

// Version 1: Original API
app.get('/api/users/:id', async (req, res) => {
    const user = await db.users.findById(req.params.id);
    res.json({ id: user.id, name: user.name });
});

// Version 2: Breaking change - renamed field
app.get('/api/users/:id', async (req, res) => {
    const user = await db.users.findById(req.params.id);
    res.json({
        id: user.id,
        fullName: user.name  // Breaking: 'name' renamed to 'fullName'
    });
});

Perché è errato: Ridenominare name in fullName interrompe il funzionamento di tutti i client esistenti che si aspettano il campo name. Il codice del client che accede a response.name riceverà undefined, generando errori. Questa modifica costringe tutti i client ad aggiornarsi contemporaneamente, pena il fallimento.

✅ Conforme:

// Version 2: Additive change - keeps old field, adds new
app.get('/api/users/:id', async (req, res) => {
    const user = await db.users.findById(req.params.id);
    res.json({
        id: user.id,
        name: user.name,           // Keep for backward compatibility
        fullName: user.name        // Add new field (deprecated 'name')
    });
});

// Or use API versioning
app.get('/api/v2/users/:id', async (req, res) => {
    const user = await db.users.findById(req.params.id);
    res.json({ id: user.id, fullName: user.name });
});

Perché questo è importante: Mantenere il vecchio name il campo preserva la compatibilità con le versioni precedenti aggiungendo fullName per i nuovi client. In alternativa, creare un nuovo endpoint con versione (/api/v2/) consente di introdurre modifiche non retrocompatibili senza compromettere i client esistenti che utilizzano ancora /api/v1/.

Conclusione

Evolvi le API attraverso modifiche additive: aggiungi nuovi campi, nuovi endpoint o parametri opzionali. Quando le modifiche che rompono la compatibilità sono inevitabili, utilizza il versionamento delle API per eseguire la vecchia e la nuova versione in parallelo. Depreca i vecchi campi fornendo tempistiche chiare e guide di migrazione prima di rimuoverli.

FAQ

Hai domande?

Quali modifiche sono considerate "breaking changes"?

Rimuovere campi dalle risposte, rinominare campi, modificare i tipi di dati (da stringa a numero), rendere obbligatori parametri precedentemente facoltativi, cambiare i codici di stato HTTP per condizioni esistenti, alterare i requisiti di autenticazione e modificare i formati delle risposte di errore. Anche l'aggiunta di nuovi parametri di richiesta obbligatori o la rimozione completa di endpoint costituiscono modifiche di tipo breaking.

Come posso aggiungere nuovi campi obbligatori senza rompere i client esistenti?

Inizialmente, rendi il nuovo campo facoltativo impostando un valore predefinito sensato. Documenta la modifica e dai ai client il tempo di recepirla. Dopo un periodo adeguato (6-12 mesi per le API pubbliche), rendi il campo obbligatorio in una nuova versione dell'API. Non rendere mai obbligatori campi facoltativi esistenti senza introdurre il versionamento.

Qual è la differenza tra versionamento delle API e deprecazione?

Il versionamento crea un nuovo endpoint (/v2/users) affianco a quello vecchio, consentendo a entrambi di coesistere. La deprecazione contrassegna un vecchio endpoint o campo come obsoleto pur mantenendolo funzionante, stabilendo una tempistica per la sua eventuale rimozione. Usa il versionamento per modifiche importanti e la deprecazione per la dismissione graduale di funzionalità minori.

Per quanto tempo dovrei mantenere le versioni delle API deprecate?

Per le API pubbliche, mantieni le versioni deprecate per almeno 12-18 mesi. Per le API interne, coordinati con i team di sviluppo dei client per definire una tempistica di migrazione. Fornisci sempre un preavviso (almeno 3-6 mesi) prima di rimuovere gli endpoint deprecati. Monitora le metriche di utilizzo per assicurarti che i client abbiano completato la migrazione prima della dismissione.

Posso modificare l'ordine dei campi nella risposta?

Sì, l'ordine dei campi in un oggetto JSON non fa parte del contratto API. I client ben progettati effettuano il parsing del JSON in base al nome del campo e non alla posizione. Tuttavia, effettua test approfonditi poiché alcuni client scritti meno bene potrebbero dipendere dall'ordine dei campi. Per gli array, l'ordine di solito è importante e non dovrebbe essere modificato se non documentato.

Come posso gestire il versionamento delle API senza modificare i percorsi URL?

Usa gli header HTTP: Accept: application/vnd.myapi.v2+json oppure header personalizzati come API-Version: 2. Anche i parametri di query funzionano: /api/users?version=2. La negoziazione del contenuto tramite header è più pulita ma più difficile da testare nei browser. Scegli una strategia e usala in modo coerente.

Metti in sicurezza ora

Metti in sicurezza il tuo codice, il cloud e il runtime in un unico sistema centralizzato.
Trova e risolvi le vulnerabilità rapidamente e automaticamente.

Nessuna carta di credito richiesta | Risultati della scansione in 32 secondi.