CTOs, développeurs : comment choisir une bonne API externe ?

De nos jours trouver une API externe permettant d’améliorer la qualité de son service est devenu très facile. De plus en plus d’entreprises mettent à disposition des APIs. Problème : de nombreux développeurs/CTOs se lancent dans l’intégration alors que cette dernière ne devrait être en fait que l’ultime étape ! Avant cela il vous faut déterminer si la qualité de l’API répond à un minimum d’exigences. Voici comment je m’y prends personnellement. J’espère que cela aidera d’autres CTOs et développeurs.

Qualité des données

De nombreuses APIs exposent des données vous permettant d’enrichir votre propre système (ce n’est pas le cas de toutes les APIs bien entendu, Stripe n’est pas une API d’enrichissement par exemple). Il est essentiel que vous vous assuriez de la qualité de ces données. Cela va vous prendre pas mal de temps et je sais déjà que vous n’aimez pas les tests ! Moi non plus, mais néanmoins vous ne pouvez pas vous soustraire à la création d’un scénario de test rigoureux ici. Si vous réalisez que la qualité des données fournies n’était pas suffisamment bonne seulement deux semaines après avoir fini votre intégration, croyez-moi vous le regretterez…

Documentation

Je suis récemment tombé sur une API qui exposait des données de grande qualité (bien meilleure que ce proposait la concurrence selon moi), mais sa documentation était… un cauchemar ! En réalité il n’y avait pour ainsi dire pas de documentation. Par ailleurs l’API ne respectait pas toutes les conventions REST. Comment pouvez-vous réussir à intégrer une API externe si les codes erreurs ne sont pas proprement documentés ? Hé bien l’unique solution qu’il vous reste dans ce cas est de tester encore et encore l’API afin de comprendre son fonctionnement. Le rétro-engineering peut être amusant mais demande aussi beaucoup de temps. Rappelez-vous que dans le cas d’une API vous n’avez pas de dépôt GitHub à explorer puisque le code source n’est pas disponible… Une mauvaise doc est systématiquement synonyme de perte de temps pour les devs, et sûrement de mauvaises surprises à moyen terme.

Bibliothèques

Est-il possible d’intégrer l’API via une bibliothèque disponible dans votre langage favori ? En tant que développeur Python et Go je suis toujours enchanté lorsque je tombe sur une API qui offre une lib Python (je sais qu’en ce qui concerne Go je peux oublier pour l’instant). Cela peut vous faire gagner pas mal de temps, mais avant tout assurez-vous que la bibliothèque est mûre et couvre toutes les fonctionnalités de l’API (pas toujours le cas).

Notoriété de l’éditeur

La notoriété peut vous aider à éclairer votre choix et éviter les mauvaises surprises avec votre API à l’avenir. Par mauvaises surprises j’entends interruption de service, régression de certaines fonctionnalités, ou même arrêt définitif du service… Vous pouvez en partie éviter ces écueils en vous posant les questions suivantes :

  • est-ce que cette API est connue sur le net (en général si vous trouvez peu d’information, fuyez) ? Trouvez-vous de nombreux articles/tutoriels parlant de l’API ? Ces articles sont-ils élogieux ?
  • des entreprises réputées l’utilisent-elles ?
  • si l’entreprise a développé des bibliothèques dédiées, sont-elles bien notées sur GitHub ? Les problèmes remontés sur GitHub sont-ils traités régulièrement ?
  • y a-t-il eu des mises à jour récentes de l’API ou la dernière mis à jour a-t-elle eu lieu il y a longtemps ?

Support technique

Assurez-vous que quelqu’un réponde à vos questions rapidement par email lorsque vous rencontrez un problème et que la réponse est pertinente. Si vous êtes basé en Europe et que l’API est éditée par une entreprise américaine, assurez-vous que le décalage horaire ne pose pas de problème.

Respect des conventions

A mon humble avis, toute API sérieuse aujourd’hui se doit d’être une API RESTful. Si l’API que vous affectionnez ne respecte pas les conventions REST, alors soyez méfiant. Toutefois gardez à l’esprit quel le standard REST n’est pas clair sur tous les sujets et que chaque API peut avoir sa propre déclinaison (codes HTTP, encodage des requêtes POST, …). Il n’empêche que vous devez lire la documentation attentivement et vous assurer que vous ne constatez pas de pratique trop originale. L’originalité vous ralentira…

Prix

Bien entendu le prix est de première importance. Mais attention la tarification d’une API n’est pas toujours facile à comprendre. Allez-vous être facturé au mois pour un nombre illimité de requêtes ? Facturé à la requête ? Dans ce second cas allez-vous être facturé deux fois pour 2 requêtes identiques (dans le cas d’une API d’enrichissement) ou la seconde requête sera-t-elle gratuite ? Serez-vous facturé pour une requête ne retournant aucun résultat (HTTP 404) ? Assurez-vous de bien comprendre toutes ces implications.

Qualité de service (QoS)

La qualité de service est très importante. Votre but est de travailler avec une API aussi rapide que possible et avec le moins d’interruptions de service possible. Malheureusement il ne s’agit pas de performances faciles à tester. En effet la qualité de service varie avec le temps, et de nombreuses APIs offrent deux niveaux de QoS différents selon que vous utilisez la version gratuite ou payante… Parfois vous pouvez même choisir entre différents abonnements avec différents temps de réponse.

Support des requêtes parallèles

Selon la façon dont vous prévoyez d’intégrer l’API, vous pouvez avoir envie d’accélérer les choses en consultant l’API via plusieurs requêtes simultanées au lieu du comportement séquentiel classique. Personnellement j’utilise Golang pour cela. Si c’est le cas soyez vigilant : de nombreuses API ne supportent pas les requêtes parallèles et lorsque c’est le cas elles imposent nécessairement une limite. Dans ce cas assurez-vous d’avoir demandé à l’éditeur quelle était cette limite (pas toujours mentionné dans la doc) et adaptez votre script en fonction.

Cet article sera un bon mémo pour moi, j’espère que pour vous aussi !

Also available in English | También existe en Español
Utilisatation d'une API REST : Go vs Python

Les API sont partout de nos jours. Imaginez : vous souhaitez recueillir des informations sur vos prospects à partir de leurs emails. Hé bien il y a une API pour ceci. Vous avez besoin de géocoder une adresse postale mal formatée ? Il existe aussi une API pour cela. Enfin, vous cherchez à effectuer un paiement de façon automatisée ? De nombreuses API font le job bien entendu. En tant que développeur, je suis régulièrement amené à intégrer des API externes à mon système, en Python ou en Go. Les deux méthodes sont assez différentes. Comparons-les sur un cas un peu “borderline” : l’envoi de données JSON encodées dans le body d’une requête POST.

Un exemple de la vie réelle

Récemment, j’ai utilisé l’API NameAPI.org dans le but de découper un nom entier en prénom et nom de famille, et déterminer le genre de la personne.

Leur API attend que vous lui envoyiez les données en JSON encodées dans le body d’une requête POST. De plus, le Content-Type de la requête doit être application/json et non pas multipart/form-data. Il s’agit d’un cas un peu piégeux puisqu’en général les données POST sont envoyées à travers le header de la requête, et si l’on décide de les envoyer dans le body de la requête (dans le cas de données JSON complexes par exemple) le Content-Type standard est multipart/form-data.

Voici le JSON que l’on veut envoyer :

{
  "inputPerson" : {
    "type" : "NaturalInputPerson",
    "personName" : {
      "nameFields" : [ {
        "string" : "Petra",
        "fieldType" : "GIVENNAME"
      }, {
        "string" : "Meyer",
        "fieldType" : "SURNAME"
      } ]
    },
    "gender" : "UNKNOWN"
  }
}

On peut le faire facilement via cURL :

curl -H "Content-Type: application/json" \
-X POST \
-d '{"inputPerson":{"type":"NaturalInputPerson","personName":{"nameFields":[{"string":"Petra Meyer","fieldType":"FULLNAME"}]}}}' \
http://rc50-api.nameapi.org/rest/v5.0/parser/personnameparser?apiKey=<API-KEY>

Et voici la réponse JSON de NameAPI.org :

{
"matches" : [ {
  "parsedPerson" : {
    "personType" : "NATURAL",
    "personRole" : "PRIMARY",
    "mailingPersonRoles" : [ "ADDRESSEE" ],
    "gender" : {
      "gender" : "MALE",
      "confidence" : 0.9111111111111111
    },
    "addressingGivenName" : "Petra",
    "addressingSurname" : "Meyer",
    "outputPersonName" : {
      "terms" : [ {
        "string" : "Petra",
        "termType" : "GIVENNAME"
      },{
        "string" : "Meyer",
        "termType" : "SURNAME"
      } ]
    }
  },
  "parserDisputes" : [ ],
  "likeliness" : 0.926699401733102,
  "confidence" : 0.7536487758945387
}

Maintenant voyons comment faire cela en Go et en Python !

Réalisation en Go

Code

/*
Fetch the NameAPI.org REST API and turn JSON response into a Go struct.

Sent data have to be JSON data encoded into request body.
Send request headers must be set to 'application/json'.
*/

package main

import (
    "encoding/json"
    "io/ioutil"
    "log"
    "net/http"
    "strings"
)

// url of the NameAPI.org endpoint:
const (
    url = "http://rc50-api.nameapi.org/rest/v5.0/parser/personnameparser?" +
        "apiKey=<API-KEY>"
)

func main() {

    // JSON string to be sent to NameAPI.org:
    jsonString := `{
        "inputPerson": {
            "type": "NaturalInputPerson",
            "personName": {
                "nameFields": [
                    {
                        "string": "Petra",
                        "fieldType": "GIVENNAME"
                    }, {
                        "string": "Meyer",
                        "fieldType": "SURNAME"
                    }
                ]
            },
            "gender": "UNKNOWN"
        }
    }`
    // Convert JSON string to NewReader (expected by NewRequest)
    jsonBody := strings.NewReader(jsonString)

    // Need to create a client in order to modify headers
    // and set content-type to 'application/json':
    client := &http.Client{}
    req, err := http.NewRequest("POST", url, jsonBody)
    if err != nil {
        log.Println(err)
    }
    req.Header.Add("Content-Type", "application/json")
    resp, err := client.Do(req)

    // Proceed only if no error:
    switch {
    default:
        // Create a struct dedicated to receiving the fetched
        // JSON content:
        type Level5 struct {
            String   string `json:"string"`
            TermType string `json:"termType"`
        }
        type Level41 struct {
            Gender     string  `json:"gender"`
            Confidence float64 `json:"confidence"`
        }
        type Level42 struct {
            Terms []Level5 `json:"terms"`
        }
        type Level3 struct {
            Gender           Level41 `json:"gender"`
            OutputPersonName Level42 `json:"outputPersonName"`
        }
        type Level2 struct {
            ParsedPerson Level3 `json:"parsedPerson"`
        }
        type RespContent struct {
            Matches []Level2 `json:"matches"`
        }

        // Decode fetched JSON and put it into respContent:
        respContentBytes, err := ioutil.ReadAll(resp.Body)
        if err != nil {
            log.Println(err)
        }
        var respContent RespContent
        err = json.Unmarshal(respContentBytes, &respContent)
        if err != nil {
            log.Println(err)
        }
        log.Println(respContent)
    case err != nil:
        log.Println("Network error:", err)
    case resp.StatusCode != 200:
        log.Println("Bad HTTP status code:", err)
    }

}

Explications

Comme vous pouvez le voir, nous faisons face à 2 problèmes désagréables :

  • la bibliothèque http n’est pas si simple d’utilisation lorsqu’il s’agit d’encoder des données JSON au sein du body et de changer le header Content-Type. La doc de Go n’est pas très claire à ce sujet. Ainsi nous ne pouvons pas invoquer http.Post qui est plus simple d’utilisation et nous sommes contraints de créer un http.Client puis par la suite d’utiliser la fonction NewRequest() que l’on déclenche avec client.Do(req). C’est l’unique façon de créer un Content-Type personnalisé dans notre cas : req.Header.Add("Content-Type", "application/json")
  • décoder le JSON reçu de NameAPI en Go est assez long et fastidieux (procédé appelé Unmarshalling en Go). Cela vient du fait que, Go étant un langage statique, nous avons besoin de savoir à l’avance à quoi ressemblera le JSON final. Par conséquent nous avons besoin de créer une struct dédiée dont la structure sera la même que celle du JSON et qui recevra les données. En cas de JSON imbriqué tel que celui retourné par NameAPI.org, qui mélange des array et des maps, cela devient très délicat. Heureusement, notre struct n’a pas besoin de mapper tout le JSON mais seulement les champs qui nous intéressent. Une autre approche, si nous n’avons aucune idée de la structure de notre JSON, serait de deviner les types de données. Voici un bon article sur le sujet.

L’input jsonString est déjà une string ici. Mais pour une comparaison encore plus rigoureuse avec Python, cela aurait dû être une struct que l’on aurait par la suite convertie en string. Toutefois cela aurait rallongé l’article inutilement.

Réalisation en Python

Code

"""
Fetch the NameAPI.org REST API and turn JSON response into Python dict.

Sent data have to be JSON data encoded into request body.
Send request headers must be set to 'application/json'.
"""

import requests

# url of the NameAPI.org endpoint:
url = (
    "http://rc50-api.nameapi.org/rest/v5.0/parser/personnameparser?"
    "apiKey=<API-KEY>"
)

# Dict of data to be sent to NameAPI.org:
payload = {
    "inputPerson": {
        "type": "NaturalInputPerson",
        "personName": {
            "nameFields": [
                {
                    "string": "Petra",
                    "fieldType": "GIVENNAME"
                }, {
                    "string": "Meyer",
                    "fieldType": "SURNAME"
                }
            ]
        },
        "gender": "UNKNOWN"
    }
}

# Proceed, only if no error:
try:
    # Send request to NameAPI.org by doing the following:
    # - make a POST HTTP request
    # - encode the Python payload dict to JSON
    # - pass the JSON to request body
    # - set header's 'Content-Type' to 'application/json' instead of
    #   default 'multipart/form-data'
    resp = requests.post(url, json=payload)
    resp.raise_for_status()
    # Decode JSON response into a Python dict:
    resp_dict = resp.json()
    print(resp_dict)
except requests.exceptions.HTTPError as e:
    print("Bad HTTP status code:", e)
except requests.exceptions.RequestException as e:
    print("Network error:", e)

Explications

La bibliothèque Request est une bibliothèque incroyablement pratique qui nous épargne beaucoup d’efforts comparé à Go ! En une ligne, resp = requests.post(url, json=payload), presque tout est fait :

  • construire une requête POST HTTP
  • convertir le dictionnaire Python en JSON
  • passer le JSON au body de la requête
  • passer le 'Content-Type' du header de 'multipart/form-data' à 'application/json' grâce au kwarg json
  • envoyer la requête

Décoder le JSON retourné par NameAPI se fait aussi en une ligne : resp_dict = resp.json(). Nul besoin de créer une structure de données compliquée à l’avance ici !

Conclusion

Python est clairement le gagnant. La simplicité de Python combinée à sa quantité de bibliothèques disponibles nous épargne pas mal de temps de développement !

Nous n’abordons pas la question de la performance ici. Si vous recherchez une intégration d’API hautement performante utilisant la concurrence, Go pourrait être un excellent choix. Mais simplicité et performance vont rarement de paire…

N’hésitez pas à commenter, je serais heureux d’avoir votre avis sur ce comparatif.

Also available in English | También existe en Español
Comment accélérer le web scraping avec Go (Golang) et la concurrence ?

Je développe des web scrapers en Python depuis plusieurs années. La simplicité de Python permet de réaliser des prototypes rapides et ses nombreuses bibliothèques sont très utiles au scraping et au parsing des résultats (Requests, Beautiful Soup, Scrapy, …). Toutefois lorsque l’on commence à s’intéresser à la performance de notre scraper, Python montre certaines limites et Go entre dans la partie !

Pourquoi Go ?

Lorsque l’on essaie d’accélérer la récupération d’informations depuis le web (pour du scraping HTML comme pour du fetching d’API), deux pistes d’optimisation principales s’offrent à nous :

  • accélérer la récupération de la ressource web (ex : télécharger la page http://example.com/hello.html)
  • accélérer le parsing des informations récupérées (ex : récupérer toutes les url présentes sur hello.html)

On peut améliorer la performance du parsing en retravaillant son code, en utilisant un parser plus performant comme lxml, ou en allouant plus de ressources machine à notre scraper. Mais malgré tout cela, on se rend compte que l’optimisation du parsing est souvent négligeable et que le goulot d’étranglement, comme souvent, reste l’accès réseau (c’est à dire le téléchargement de la page web).

La solution est donc de paralléliser le téléchargement des différentes pages web. Et pour cela Go est tout indiqué !

La programmation concurrente est un domaine rapidement compliqué et Go a la capacité de rendre cela plutôt facile. Go est un langage moderne qui a été pensé pour la concurrence dès le début. Au contraire Python est un langages plus ancien qui, malgré de nombreux efforts récents dans ce domaine, reste plus complexe lorsqu’il s’agit d’écrire un scraper concurrent.

Il y a d’autres avantages à utiliser Go, mais ce sera l’objet d’un autre article !

Installez Go

J’ai déjà réalisé un petit tuto sur l’installation de Go sur Ubuntu.

Si vous souhaitez installer l’environnement sur une autre plateforme, référez-vous à la doc officielle.

Un scraper concurrent simple

Notre scraper se contente d’ouvrir une liste de pages web que nous lui donnons en amont et de vérifier qu’il obtient bien un code HTTP 200 (signe que le serveur a retourné la page HTML sans erreur). Pas de parsing HTML ici, le but étant de se focaliser sur la performance liée aux accès réseau. A vous d’écrire la suite de votre scraper !

Code final


/*
Open a series of urls.

Check status code for each url and store urls I could not
open in a dedicated array.
Fetch urls concurrently using goroutines.
*/

package main

import (
    "fmt"
    "net/http"
)

// -------------------------------------

// Custom user agent.
const (
    userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) " +
        "AppleWebKit/537.36 (KHTML, like Gecko) " +
        "Chrome/53.0.2785.143 " +
        "Safari/537.36"
)

// -------------------------------------

// fetchUrl opens a url with GET method and sets a custom user agent.
// If url cannot be opened, then log it to a dedicated channel.
func fetchUrl(url string, chFailedUrls chan string, chIsFinished chan bool) {

    // Open url.
    // Need to use http.Client in order to set a custom user agent:
    client := &http.Client{}
    req, _ := http.NewRequest("GET", url, nil)
    req.Header.Set("User-Agent", userAgent)
    resp, err := client.Do(req)

    // Inform the channel chIsFinished that url fetching is done (no
    // matter whether successful or not). Defer triggers only once
    // we leave fetchUrl():
    defer func() {
        chIsFinished <- true
    }()

    // If url could not be opened, we inform the channel chFailedUrls:
    if err != nil || resp.StatusCode != 200 {
        chFailedUrls <- url
        return
    }

}

func main() {

    // Create a random urls list just as an example:
    urlsList := [10]string{
        "http://example1.com",
        "http://example2.com",
        "http://example3.com",
        "http://example4.com",
        "http://example5.com",
        "http://example10.com",
        "http://example20.com",
        "http://example30.com",
        "http://example40.com",
        "http://example50.com",
    }

    // Create 2 channels, 1 to track urls we could not open
    // and 1 to inform url fetching is done:
    chFailedUrls := make(chan string)
    chIsFinished := make(chan bool)

    // Open all urls concurrently using the 'go' keyword:
    for _, url := range urlsList {
        go fetchUrl(url, chFailedUrls, chIsFinished)
    }

    // Receive messages from every concurrent goroutine. If
    // an url fails, we log it to failedUrls array:
    failedUrls := make([]string, 0)
    for i := 0; i < len(urlsList); {
        select {
        case url := <-chFailedUrls:
            failedUrls = append(failedUrls, url)
        case <-chIsFinished:
            i++
        }
    }

    // Print all urls we could not open:
    fmt.Println("Could not fetch these urls: ", failedUrls)

}


Explications

Ce code est un peu plus long que ce que nous aurions pu obtenir avec un langage comme Python, mais comme vous le voyez cela reste tout à fait raisonnable. Ici nous avons affaire à un langage statique et bien entendu déclarer ses variables prend un peu de place. Mais mesurez le temps d’exécution de ce programme et vous verrez que la récompense est au rendez-vous !

Nous avons pris 10 urls au hasard pour l’exemple.

Ici les mots-clés magiques nous permettant de faire de la concurrence sont go, chan et select:

  • go permet de créer une nouvelle goroutine, c’est à dire que fetchUrl sera à chaque fois exécutée dans une nouvelle goroutine concurrente.
  • chan est le type représentant un channel. C’est par ces channels que l’on va communiquer entre goroutines (main étant en elle-même aussi une goroutine).
  • select ... case est un switch ... case dédié à la réception des messages envoyés via les channels. On ne passe à la suite du programme que lorsque toutes les goroutines en cours d’exécution ont envoyé un message (ici soit pour dire que le fetching de l’url en cours est fini, soit pour dire que le fetching a échoué).

On aurait pu ne créer aucun channel pour ce scraper, c’est à dire se contenter de créer des goroutines et ne pas attendre d’informations en retour de leur part (si par exemple chaque goroutine finissait en stockant le résultat en base de données). Dans ce cas là il est tout à fait possible que notre goroutine principale main se termine alors que d’autres goroutines n’auront pas encore fini de s’exécuter (ce qui n’est pas forcément un problème puisque Go laisse s’exécuter toutes les goroutines, y compris si le main s’est arrêté). Mais en pratique dans la vie réelle il est presque toujours nécessaire d’utiliser les channels afin de faire dialoguer nos goroutines.

Pensez à limiter la vitesse !

Ici la vitesse maximum est recherchée, notamment parce que nous scrapons des urls toutes différentes. Cependant si vous scrapez plusieurs fois la même url (dans le cas du fetching d’une API externe par exemple), vous serez certainement contraint de ne pas dépasser un certain nombre de requêtes concurrentes par secondes. Pour cela la mise en place d’un compteur est nécessaire (objet peut-être d’un prochain article !).

Bon scraping !

Also available in English | También existe en Español
Installer Go (Golang) 1.9 sur Ubuntu 17.10

Voici un petit aide-mémoire pour ceux qui souhaitent installer Go (1.9) sur leur machine Ubuntu (17.10). Pour rappel, Go est un langage compilé, donc pas besoin d’installer Go sur la machine sur laquelle tournera votre application.

Mise à jour des dépôts et des correctifs de sécurité, au cas où :

sudo apt-get update
sudo apt-get -y upgrade

Téléchargement et installation de Go :

sudo curl -O https://storage.googleapis.com/golang/go1.9.linux-amd64.tar.gz  # Téléchargement de l'archive. Changez le nom de l'archive pour une autre version de Go ou une autre architecture système
sudo tar -xvf go1.9.linux-amd64.tar.gz  # Extraction de l'archive
sudo mv go /usr/local  # On déplace les binaires vers /usr/local
echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.profile  # On met à jour notre profile bash pour que Go soit dans le PATH
source ~/.profile  # Actualise le profile

A ce stade, Go est installé ! Maintenant créez votre projet et initialisez les variables d’environnement :

mkdir $HOME/mon_projet
mkdir $HOME/mon_projet/src
mkdir $HOME/mon_projet/src/mon_appli
export GOPATH=$HOME/mon_projet

Puis créez votre appli :

vim $HOME/mon_projet/src/mon_appli/mon_appli.go

Contenant le code suivant :

package main

import "fmt"

func main() {
    fmt.Printf("hello world\n")
}

Compilez l’application :

go install mon_appli

Un executable a été généré dans un nouveau dossier bin. Exécutez-le :

$HOME/mon_projet/bin/mon_appli

Vous devriez obtenir :

hello world

Pour comprendre les différences entre go install, go build, et go run c’est par ici. Et si vous ne souhaitez pas/pouvez pas installer Go sur votre machine, jetez un coup d’oeil à cette image Docker.

Enjoy !

Also available in English | También existe en Español
Pourquoi créer un blog quand on est développeur ?

Hé voilà je saute le pas. Ça fait un moment que l’envie de lancer un blog me titille. Je tenais absolument à ce que ce blog soit multilingue, ce qui m’a obligé à me poser pas mal de questions techniques sur la gestion des langues. En tant que développeur je pense ce blog aura plusieurs intérêts.

Partager

La plupart des développeurs estiment qu’ils n’ont pas l’expertise suffisante pour publier sur internet. C’est une belle preuve d’humilité mais pas toujours une bonne analyse ! Le monde du dev est tellement vaste que l’on trouve toujours des gens plus débutants que soi dans tous les domaines, rassurés de constater que d’autres ont fait face aux mêmes problèmes et capables de les aider dans un langage simple.

Promouvoir la langue locale

Je suis abasourdi par la difficulté à dénicher du contenu traduit en français dans le monde de l’informatique. Je sais bien que l’anglais est devenue la lingua franca en la matière mais bien des gens n’ont pas l’aisance suffisante pour parcourir les blogs anglophones efficacement. C’est dommage que des personnes compétentes et motivées par le dev se heurtent à cette barrière (on n’apprend pas l’anglais du jour au lendemain…). Défendre la langue ça passe par là !

Alors bien sûr internationaliser une appli web c’est beaucoup de boulot supplémentaire, et les outils de localisation existants sont difficiles à utiliser je trouve (objet d’un autre article), donc on comprend fort bien que tout le monde veuille s’en passer.

Structurer ses idées

Il parait que mettre par écrit sous forme de blog les problèmes techniques que l’on a rencontrés aide beaucoup à structurer sa pensée et à assimiler les idées. On va voir ça ! Mais il est vrai que personnellement le premier intérêt lorsque je documente mon code c’est de m’aider à fixer clairement les notions. Voyons donc le blog comme de la doc !

Allez une petite citation pour la route :

Ce qui se conçoit bien s’énonce clairement, et les mots pour le dire arrivent aisément.

– Nicolas Boileau

Also available in English | También existe en Español