La troisième source de migration de notre déploiement Tier-3 est disponible dès aujourd'hui. Collez une clé API Short.io, choisissez le domaine source Short.io (par ex. example.short.gy), sélectionnez le domaine cible Elido, puis cliquez sur Démarrer. Trois à six minutes plus tard, chaque lien se trouve sur votre domaine Elido avec le slug préservé.
Cet article est le compte-rendu technique — ce qui est spécifique à Short.io, ce qui nous a surpris à propos de leur API REST, et pourquoi nous avons fini par exposer des jobs par domaine plutôt qu'un traitement par lot par compte.
Par domaine, et non par compte#
Le modèle de données de Short.io comporte une subtilité qui a façonné toute l'expérience utilisateur du lanceur : les liens sont organisés par domaines, et l'endpoint /links pagine par domaine. Il n'existe pas d'appel de type « donne-moi tous les liens de tous les domaines de ce compte ».
Nous avons envisagé quelques conceptions :
- A. Itérer sur chaque domaine côté serveur, présenter un seul job à l'utilisateur. Plus rapide du point de vue du nombre de clics ; plus difficile à exposer en termes de progression et de choix de stratégie de conflit par domaine.
- B. Un job Elido par domaine source. Plus lent en termes de clics (l'utilisateur exécute N jobs pour N domaines), mais chaque job possède un contrat clair : un domaine source → un domaine cible → une stratégie de conflit.
- C. Lister chaque domaine, permettre à l'utilisateur de faire une sélection multiple, mettre en file d'attente N jobs côté serveur.
Nous avons déployé B et laissé C pour l'itération du plan de déploiement. Le lanceur demande le nom d'hôte du domaine source dans un champ texte (pas de menu déroulant — la liste des domaines de Short.io est peu coûteuse à appeler mais ajoute un aller-retour et l'utilisateur connaît toujours son propre nom d'hôte de domaine). Un job par domaine, mis en file d'attente depuis le tableau de bord, un par un.
Le gain de la taille de page#
Short.io pagine par défaut à 150 liens par appel — la plus généreuse de nos cinq sources de migration. Comparaison :
- Bitly : 100 par page
- Rebrandly : 25 par page
- TinyURL : 100 par page (Pro/Bulk)
- Dub.co : 100 par page
- Short.io : 150 par page
Un domaine Short.io de 5 000 liens nécessite 34 allers-retours. Un compte Rebrandly de 5 000 liens en nécessite 200. Le worker passe la majeure partie de son temps à attendre les réponses HTTP, c'est donc important — Short.io est empiriquement la source de migration la plus rapide que nous supportons.
const shortioPageSize = 150
page := 1
for {
resp, err := w.fetchPage(ctx, opts.Token, opts.DomainID, page)
if err != nil { /* mark failed */ return }
if len(resp.Links) == 0 { break }
for _, link := range resp.Links { /* import */ }
if !resp.HasMore { break }
page++
}
HasMore est un booléen que Short.io retourne explicitement — pas d'analyse de curseur, pas de recherche du dernier identifiant. Leur API est l'une des mieux conçues parmi les cinq fournisseurs que nous supportons.
Liens privés — ce que nous faisons#
Short.io possède un flag « private » par lien. Nous importons les liens privés en tant que liens Elido avec is_active=false afin que le slug ne soit pas résolu à la périphérie (edge). L'utilisateur les active sélectivement depuis le tableau de bord après avoir vérifié l'importation.
Le raisonnement : si un lien Short.io était privé à la source, l'intention de l'utilisateur était de ne pas le rendre résoluble publiquement. L'importer avec is_active=true ferait apparaître des URL qui étaient délibérément restreintes. L'importer avec is_active=false permet de réserver le slug tout en le rendant inaccessible jusqu'à ce que l'utilisateur en décide — strictement plus sûr que l'alternative.
isActive := !link.Private
linkID, err := w.links.InsertImported(ctx, sqldb.InsertImportedLinkParams{
WorkspaceID: job.WorkspaceID,
DomainID: job.TargetDomainID,
Slug: slug,
DestinationURL: link.OriginalURL,
Title: truncate(link.Title, 250),
Tags: append(link.Tags, "imported:shortio"),
IsActive: isActive,
CreatedByUserID: createdByUserID,
})
Il s'agit d'une légère différence de surface par rapport à Bitly (aucun flag équivalent) et Rebrandly (aucun flag équivalent). Cela mérite d'être mentionné dans la procédure post-importation pour que l'utilisateur comprenne pourquoi certains liens importés ne sont pas résolus immédiatement.
Ce que nous ne migrons pas#
Les configurations de split-test A/B de Short.io n'ont pas d'exportation propre — ce sont des outils de création intégrés qui ne présentent pas de structure JSON déterministe via l'API REST. À reconstruire en tant que règles smart-link Elido après l'importation ; la syntaxe est plus expressive mais le modèle mental est le même.
L'historique par clic est la limite universelle pour toutes les sources de migration. Les données par clic de Short.io se trouvent dans leur exportation analytique, qui est réservée au plan Team (accédé le 2026-05-22) et se présente sous forme de compteurs agrégés plutôt que d'événements par clic. Les nouveaux clics atterrissent dans les analyses Elido à partir de la coupure.
Les designs QR et les préréglages UTM par lien — même chose que pour Bitly et Rebrandly. Étiquetés imported:shortio, prêts pour un suivi en masse via les campagnes Elido.
Transfert de domaine#
Le cas d'usage intéressant de Short.io est « J'utilise un domaine de marque sur Short.io et je veux le déplacer vers Elido sans changer l'URL ». La migration gère proprement le côté des liens ; le côté DNS n'est qu'un changement de CNAME.
Nous documentons la séquence de transfert sur la page d'atterrissage /migrate-from/shortio — gardez les deux surfaces en résolution en parallèle jusqu'à ce que votre abonnement Short.io prenne fin, puis pointez le DNS vers Elido. Il n'y a pas d'urgence à désactiver Short.io le jour où l'importation se termine.
Les domaines personnalisés dans Elido utilisent le TLS à la demande de Caddy avec domain-manager comme source de liste blanche, donc la coupure est un changement de CNAME plus un appel API de vérification de domaine. Pas de danse de certificat du côté de l'utilisateur.
Résolution de conflit et contrat du worker#
Identique à Bitly et Rebrandly — les suffixes suivent mylink-2, mylink-3, … en cas de collision ; l'option skip laisse le lien Elido existant intact et journalise la ligne source ; l'option fail abandonne au premier conflit. La recherche est une lecture indexée par ligne.
Le contrat du worker — MaxLinksPerImport=50_000, ImportRunBudget=30*time.Minute, progressEvery=50, errorLogCap=1_000 — est partagé entre les cinq fournisseurs. Ces constantes font la majeure partie du travail et ce ne sont pas des paramètres de configuration. C'est le contrat sur lequel repose l'interface utilisateur de sondage du tableau de bord.
Gestion des jetons (tokens)#
bgCtx := context.WithoutCancel(r.Context())
go h.shortio.Run(bgCtx, job.ID, imports.ShortioJobOptions{
Token: req.Token,
DomainID: req.DomainID,
})
source_token_id reste NULL. Même sémantique de tir unique que pour Bitly et Rebrandly — l'utilisateur colle le jeton une fois, le worker s'exécute, le jeton est supprimé de la mémoire à la fin. Nous ne le persistons pas car la valeur de la persistance (utilisation récurrente) ne s'applique pas aux migrations.
context.WithoutCancel maintient le worker en vie après la requête HTTP qui l'a déclenché. Même modèle que pour tous les autres fournisseurs de migration de ce déploiement.
Comparaison avec la méthode d'exportation CSV#
Short.io expose une exportation CSV sur les plans Team. Nous avons choisi REST plutôt que CSV car :
- REST préserve structurellement les tags Short.io. Le CSV les aplatit en une chaîne séparée par des virgules qui nécessite un fractionnement après analyse.
- REST expose le flag
private. Le CSV ne l'inclut pas de manière cohérente. - REST nous donne une progression déterministe (liens vus / liens restants). Le CSV est un téléchargement de fichier unique sans signal de progression à mi-chemin.
- REST est agnostique au plan — chaque plan Short.io expose
/links. L'exportation CSV est réservée au plan Team.
La méthode CSV reste dans notre poche pour les utilisateurs sur des comptes Short.io hérités dont le jeton API a été révoqué mais qui possèdent toujours un CSV issu de la dernière exportation.
Reprenabilité et problème de déploiement#
Même compromis que pour les deux premières migrations. Le worker est en cours d'exécution ; un déploiement au milieu de l'importation tue la goroutine. Le champ import_jobs.last_progress_at plus la tâche cron de nettoyage des jobs bloqués de 5 minutes fait passer toute ligne running sans progression au cours des 30 dernières minutes à failed. La réexécution est idempotente avec les suffixes et le skip.
Pour les comptes de plus de 10 000 liens répartis sur plusieurs domaines Short.io, la conception de jobs par domaine aide ici — chaque domaine est borné indépendamment par le budget de 30 minutes, donc un déploiement au milieu du troisième domaine ne perd pas le travail effectué sur les deux premiers.
Et ensuite#
Deux fournisseurs supplémentaires à intégrer :
- Dub.co —
GET /api/links?projectSlug=…&limit=100. Les dossiers sont aplatis en tags. L'API la plus propre des cinq. - TinyURL — API REST Pro/Bulk à 100 par page. La version gratuite de TinyURL n'a pas d'API et n'en a jamais eu ; cela reste une méthode manuelle.
Après Dub et TinyURL, le déploiement Tier-3 est terminé. Les cinq pages de destination de migration (/migrate-from/bitly, /migrate-from/rebrandly, /migrate-from/shortio, /migrate-from/dub, /migrate-from/tinyurl) et les cinq articles de blog techniques couvrent toutes les requêtes de fournisseurs sur lesquelles un utilisateur cherchant une alternative à Bitly pourrait atterrir.
Si vous avez hésité sur une comparaison avec Short.io parce que l'histoire de la migration n'était pas documentée, c'est désormais chose faite. Essayez-la — clé API + domaine vers le dernier lien importé en moins de six minutes pour les comptes typiques.