Blog // Exirel.me

Nuit du 4 août 1789

Par Florian Strzelecki - 16:16 - 04.08.2015

Tags : Société, Ma vie, Sexisme, 4 Août, Racisme

En bon élève que je suis, j'ai appris sur les bancs de l'école que la nuit du 4 Août 1789, l'Assemblée constituante mit fin au système féodal - voici l'introduction tirée de Wikipédia :

La nuit du 4 août 1789 est un événement fondamental de la Révolution française, puisque, au cours de la séance qui se tenait alors, l'Assemblée constituante met fin au système féodal. C'est l'abolition de tous les droits et privilèges féodaux ainsi que de tous les privilèges des classes, des provinces, des villes et des corporations, à l'initiative du Club breton, futur "Club des Jacobins".

Rien que ça : l'abolition de tous les droits et privilèges de ceux que l'on pourrait appeler "les dominants". Non, je ne vais pas me lancer dans un exercice complexe de définition et de description de ce que sont des "dominants" et des "dominés", pas plus que je ne vais sortir d'analyse quelconque de ce qui s'est passé lors de la Révolution française.

Mais tout de même, je suis songeur.

Je lis, je m'interroge, et j'apprends beaucoup, sur la sociologie entre autre, et sur des maux qui rongent notre société : sexisme, racisme, homophobie, l'injustice sociale, la "crise", la corruption. La haine des uns pour les autres, le conservatisme, les positions extrêmes des uns et des autres.

J'ai une longue route à faire pour comprendre tous les tenants et aboutissants des problèmes que j'aimerais résoudre, et je ne doute pas un seul instant que je n'aurai jamais une vision assez objective de la situation, ni la prétention de détenir une quelconque vérité sur ce qu'il faut faire, sur ce qu'il ne faut pas faire, et encore moins croire ou ne pas croire.

J'essaie, humblement, et avec beaucoup d'erreurs, d'accepter ce que je ne sais pas, d'écouter au-delà de ma conception forcément limitée du monde, en cherchant à dépasser mes préjugés. Parfois je soutien une initiative personnellement, parfois je ne fais que partager un lien sur Twitter, ou une photo sur Tumblr.

Alors, cette nuit du 4 Août, qu'est-ce qu'il nous en restera dans 100, 200, ou 300 ans ?

Remote

Par Florian Strzelecki - 22:02 - 16.07.2015

Tags : Ma vie, Travail, Remote, Rythme de vie

C'est la fin d'après-midi, et je suis dans le train qui me ramène à Rennes, après deux jours à Paris. Ces aller-retours sont devenus fréquents - toutes les deux semaines exactement - et ce depuis bientôt un an : je suis en télé-travail pour la société Oscaro.com, et je travaille (principalement), sur le site oscaro.be.

Cet article me trotte dans la tête depuis quelques temps - depuis le premier jour en réalité - et j'ai attendu d'avoir suffisamment d'expériences à partager pour l'écrire.

Plutôt que parler de mon travail, je souhaite surtout parler de ce qui a changé dans ma vie.

Mise à jour

Par Florian Strzelecki - 20:47 - 14.07.2015

Tags : Bonjour, Django, Web, Ma vie, Divers

Heh, ça fait longtemps, non ? Depuis Novembre dernier, je n'ai rien publié ici. Je n'en avais pas l'envie la plupart du temps, et pas la motivation le reste du temps. J'avais envie de mettre à jour le site, le blog, le serveur, l'application, etc. comme une excuse avant de publier à nouveau (et, quelque part, avant d'écrire à nouveau).

Un peu de style

Pour commencer, j'ai donc regardé comment "améliorer" le style du blog. Rien de très folichon : j'ai opté pour un design encore plus sobre, encore plus épuré, avec une grosse police de caractères. D'ailleurs, je n'ai indiqué aucune police en particulier : c'est du sans-serif pour les titres, et du serif pour le texte. C'est un choix délibéré de ma part.

Je vais sans doute retravailler un peu cela prochainement, si l'envie m'en prend. Mais pour le moment, cela me suffit ainsi.

Un peu de script

Il y avait pas mal de JavaScript inutile sur ce blog (des reliquats d'un temps ancien). J'en ai donc supprimé une bonne partie pour ne garder que l'essentiel (ie. un plugin pour les images, qu'il faut que je change aussi).

Au passage, je suis passé à Grunt pour gérer tous mes fichiers "frontend" (ie. le CSS et le JavaScript). Ce n'était pas très compliqué, et une fois les bases posées, c'est allé assez vite.

J'ai presque envie de dire que j'ai apprécié cette expérience.

Un nouveau serveur

Parce qu'il n'était pas question de garder un vieux Ubuntu 10.04 en production, j'ai opté pour un nouveau serveur, avec Ubuntu 14.04 - je reste donc sur des LTS.

Je suis aussi passé de Apache à nginx, et de Gunicorn à uwsgi. Enfin, j'ai laissé de côté runit pour utiliser upstart.

Le plus difficile a été de configurer l'ensemble correctement, en utilisant des fichiers de configuration valides, avec des variables d'environnements qui contiennent les données sensibles avec un accès en lecture seule pour l'utilisateur root seulement.

Il me faut encore configurer proprement le système de log, et ajouter une supervision à mes nouveaux serveurs (j'avais déjà migré d'autres applications sur un autre serveur auparavant).

Django avec Postgres

Auparavant, j'utilisais exclusivement MySQL. J'ai, depuis, décidé de braver les épreuves pour apprendre à installer et utiliser correctement PostgreSQL. Je suis loin d'en maîtriser les arcanes, mais je sais au moins créer les utilisateurs, les bases de données, et configurer proprement les droits d'accès.

Enfin, il n'était pas question de rester sur une version non-supportée de Django, et j'ai donc tout naturellement migré vers Django 1.8.

J'ai donc pu :

Lâchez vos com'

C'est la grande nouveauté en terme de fonctionnalité. C'est une petite révolution pour moi : j'ai accepté d'utiliser Disqus. J'avais choisi à l'origine de ne pas mettre de commentaires sur mon blog, car la gestion du spam est particulièrement pénible, et que, la plupart du temps, la qualité des commentaires n'étaient pas suffisantes pour justifier le travail supplémentaire.

Avec Disqus, j'ai ouvert les commentaires en déléguant cette partie à un prestataire. Je verrai bien à l'usage ce qu'il en est. Alors n'hésitez pas.

J'autorise les anonymes à écrire des commentaires, mais tous emails non encore validés devra attendre une modération avant de voir ses commentaires publiés. L'avenir nous dira s'il faut apporter des modifications à ce système.

Bref, allez-y, vous pouvez commenter. Mais n'espérez pas trop de mises à jour, je publie souvent par paquet de billets d'un coup, sans aucune fréquence particulière.

Framework overflow

Par Florian Strzelecki - 00:02 - 10.11.2014

Tags : Framework, Web, Bonne pratique, twitter bootstrap, Angular.js

Je mets toujours beaucoup de temps avant d'adhérer à un framework. Contrairement à ce que l'on pourrait croire, je ne suis pas du genre à adhérer à une nouvelle technologie qui vient à peine de sortir, pas plus que je ne me jette à corps perdu dans un nouveau framework ou une nouvelle méthode. Il faudrait sans doute que j'écrive un long article sur le sujet à l'occasion, car je suis presque sûr que mes amis me voient comme le plus hipster d'entre eux en la matière.

Si vous ne l'avez pas encore fait, je vous invite à lire cet excellent article "Stop Breaking the Web", qui aborde un problème récurrent dans le monde du développement web : l'usage abusif de frameworks et de solutions qui ne résolvent pas les bons problèmes.

Si sur la forme je retrouve la rengaine habituelle de ce que nous devrions faire et que nous ne faisons pas (faire du progressif au lieu de chercher à supporter tous les navigateurs de la même façon), sur le fond, il y a plusieurs points intéressants qui sortent de l'ordinaire ; et avec lesquels je suis tout à fait d'accord.

Dans les passages qui ont attiré mon attention il y a celui-ci :

You should be delivering the content in human-viewable form first, [...] then you can add your fancy JavaScript magic on top of that.

Je ne peux qu'insister lourdement sur cette approche qui n'apporte pas seulement quelques bénéfices : cette approche est la base même de tout ce que nous devrions faire.

Nous devrions faire un premier rendu, sans JavaScript et avec un CSS si possible minimal, de sorte à pouvoir donner l'accès au contenu. C'est le contenu que les utilisateurs, que les êtres humains, viennent chercher en premier. Et il n'est jamais trop tard pour ajouter une couche de JavaScript par dessus.

De plus, cela permet d'avoir un socle solide, unique, et stable, sur lequel s'appuyer pour faire tout le reste : la vue mobile, la vue tablette, la vue desktop, avec ou sans JavaScript, avec ou sans la dernière mise à jour du navigateur.

C'est la base même du web, et cet article nous rappelle à quel point nous l'avons oublié.

Robustesse et anti-fragile

Il y a quelques temps déjà un de mes amis, Sylvain, me disait que nous ne devrions plus penser avec des applications monolithiques, que nous devrions repenser notre façon d'aborder les tests, mais surtout, que nous ne devrions plus chercher à faire des applications robustes, mais penser nos applications avec une approche anti-fragiles.

L'idée, c'est que quoi qu'il arrive, un ensemble minimal doit toujours rester fonctionnel. Que ce qui ne marche pas ne devrait pas avoir d'effet sur le reste, autre que retirer une partie des fonctionnalités.

J'ai un exemple très récent en tête, une expérience désastreuse avec Angular.js : suite à un bug dans la gestion de l'état de l'application, une erreur impromptue bloquait toute l'application, la rendant complètement inutilisable à moins de recharger la page. Certes, il y avait des contrôles pour que l'application soit robuste, mais à la première erreur imprévue, plus rien du tout ne fonctionnait.

Ce genre de problème ne devrait pas arriver : les bugs sont toujours possibles, mais nous devrions être capable de faire en sorte qu'ils aient le moins d'effets de bords possible, et de toujours garder une base fonctionnelle quoi qu'il arrive.

Les choses à l'envers

Le second point soulevé par cet article est que nous faisons les choses à l'envers. Nous ne devrions pas chercher à supporter toutes les nouvelles fonctionnalités dans les vieux navigateurs : à la place, nous ne devrions activer les fonctionnalités que si le navigateur les supporte :

We are doing things backwards. We are treating modern browsers as "the status quo", and logically if someone doesn't conform to "the status quo", we'll be super helpful and add our awesome behavioral polyfills.

La mise en exergue est de moi.

Là encore, c'est pourtant quelque chose qui devrait nous sembler évident. Si quelqu'un utilise un vieux navigateur, il est aussi tout à fait possible qu'il utilise un vieux PC. Ou pour une raison ou une autre, il a des limitations sur son environnement de navigation (peut-être qu'il utilise un bloqueur de pub un peu trop agressif, certes, mais il faut bien dire que les pubs sont souvent très agressives aussi).

Qu'est-ce qui intéresse vraiment l'utilisateur ? Un super système de routage d'URL côté client ? Ou le contenu ? Les textes ? Les images ? Ou appuyer le bouton "ajouter au panier" ? Remplir le formulaire de contact ?

Tous ces problèmes de développement pourraient se résumer à "faire les choses biens". J'aimerais ajouter cependant un petit conseil, donné par ma compagne : nous devrions créer nos applications en se basant sur les conditions d'accès de la zone Afrique - Asie du Sud ; des connexions lentes et du matériel dépassés.

Car quand on y pense, entre les connexions en Edge (ou même la 3G ce n'est pas toujours parfait), et la flotte de vieux terminaux Android et les iPhone 4 devenus trop lent avec les dernières mises à jours, ce sont, peu ou prou, les conditions de nos utilisateurs au quotidien. Et je ne parle même pas de tous ces PCs dans les entreprises qui n'utilisent que les versions spécifiques de Firefox (parfois bloquées dans une version ancienne) ou d'IE (avec un Vista ou un Seven jamais mis à jour et IE8 ou 9).

Un peu de bon sens ! Voici ce dont nous aurions bien besoin.

Subtile compréhension de listes

Par Florian Strzelecki - 23:59 - 04.11.2014

Tags : Python, Documentation, Programmation, list-comprehension, generator expression

Si vous faites du python, un jour ou l'autre, vous tomberez sur ce genre de structure :

new_list = [int(x) for x in list_of_string_values]

Il s'agit de l'expression très connue appelée list-comprehension. Puissante et pratique, elle permet d'améliorer la lisibilité (dans certains cas), et offre quelques outils intéressants pour réduire la complexité des algorithmes.

Mais connaissez-vous les generator-expression ? Et les set et dict comprehension ?

Bon, tout n'est pas toujours disponible dans toutes les versions de python. Alors je vous préviens, moi, je parle de Python 3.4, version que j'utilise désormais au quotidien. La plupart des exemples et concepts peuvent être transposés, d'une façon ou d'une autre, en Python 2.7.

Python logging et log level

Par Florian Strzelecki - 23:59 - 03.11.2014

Tags : Python, Documentation, Programmation, logging

Dans le précédent article, je parlais de l'héritage des loggers, technique très pratique pour mutualiser des comportements ou au contraire de les séparer. Vous avez pu voir comment faire en sorte que deux loggers n'utilisent pas le même niveau de log ou pas les mêmes handlers.

Un point que je n'ai pas abordé (parmi tant d'autres) est la gestion du niveau de log : il existe deux configurations de niveau, puis à l'usage il suffit de choisir la bonne méthode pour le bon niveau de log désiré. Certes, j'ai utilisé le niveau de log des loggers, mais je n'ai pas vraiment utilisé la notion de log level du handler.

Commençons donc par un exemple simple, cette fois-ci avec un seul logger ayant deux handlers :

from logging import getLogger
from logging.config import dictConfig

if __name__ == '__main__':

    dictConfig({
        'version': 1,
        'handlers': {
            'console': {
                'level': 'INFO',
                'class': 'logging.StreamHandler',
            },
            'file': {
                'level': 'INFO',
                'class': 'logging.FileHandler',
                'filename': 'log.txt'
            }
        },
        'loggers': {
            'main': {
                'level': 'INFO',
                'handlers': ['console', 'file'],
            },
        }
    })

    main = getLogger('main')

    main.info('My message.')
    main.error('My error message.')

Ce qui donne comme résultat :

$ python log.py
My message.
My error message.

Et dans le fichier log.txt vous retrouverez exactement les même messages.

Python logging et héritage de loggers

Par Florian Strzelecki - 22:20 - 02.11.2014

Tags : Python, Documentation, Programmation, logging

Prenons un exemple simple, un fichier log.py qui va définir deux loggers : main et main.part. Ces loggers pourraient très bien être définis dans un package main et son sous-module part. Pour l'exemple, nous allons faire beaucoup plus simple :

from logging import getLogger
from logging.config import dictConfig

if __name__ == '__main__':

    dictConfig({
        'version': 1,
        'handlers': {
            'console': {
                'level': 'INFO',
                'class': 'logging.StreamHandler'
            }
        },
        'loggers': {
            'main': {
                'level': 'INFO',
                'handlers': ['console', ],
            }
        }
    })

    main = getLogger('main')
    part = getLogger('main.part')

    main.info('My message on the main logger.')
    part.info('And my second message on the main.part logger.')

Ici, il y a donc :

Si vous faites python log.py c'est ceci qui devrait s'afficher dans votre console :

$ python log.py
My message on the main logger.
And my second message on the main.part logger.

Sauf que si vous retirez la partie dictConfig de ce bout de code, plus rien ne s'affiche. Il y a donc bien quelque chose à regarder de ce côté là.

Configurer pip en fonction du virtualenv

Par Florian Strzelecki - 23:52 - 17.09.2014

Tags : Python, Bonne pratique, virtualenv, ProTips

Si vous développez avec python, alors vous avez sans doute entendu parler de deux outils très pratiques : virtualenv et pip. Le premier permet des environnements python isolés les uns des autres, et le second permet d'installer des paquets python (que ce soit au niveau global ou dans un environnement isolé).

À ce stade de la lecture, si vous ne savez pas ou ne connaissez pas l'un ou l'autre de ces outils, je ne peux que vous conseiller vivement de vous y intéresser.

Personnellement, depuis que j'ai investis un peu de temps à comprendre et à utiliser ces deux outils, je ne peux plus m'en passer. Cela dit, virtualenv, bien que très pratique, est un peu "brut", et je ne l'utilise plus qu'avec un autre outil, virtualenvwrapper, qui est d'ailleurs nécessaire pour la suite de l'article.

Fichier de configuration pour pip

Pour une raison ou une autre, vous pourriez avoir envie de configurer pip, que ce soit une option particulière (suivre les dépendances par exemple, ou toujours mettre à jour), ou d'ajouter un serveur de distribution. Car oui, même si, par défaut, pip utilise le PYthon Package Index, il peut vous arriver de vouloir utiliser un miroir, voire en cas d'utilisation avancée, votre propre serveur/miroir (ce qui peut arriver assez vite quand vous travaillez dans une équipe qui travaille principalement avec des projets en python).

Pour se faire, vous avez deux options :

Comme ajouter la même option à votre ligne de commande est fastidieux (et qu'une erreur est si vite arrivée), vous aurez plus vite fait d'utiliser un fichier de configuration. Pour cela, vous devez créer le créer au bon endroit : par défaut, il s'agit du fichier $HOME/.pip/pip.conf, et pour notre cas il ressemble à ceci :

[global]
index-url = https://pypi.team.my-companie.biz/dev/+simple/

Oui, .biz, je fais ce que je veux, c'est un exemple et il est tard.

Si vous voulez en savoir un peu plus sur ce fichier de configuration, je vous invite à lire la documentation "PIP User Guide: configuration".

Le soucis maintenant, c'est qu'à chaque fois que vous utiliserez pip, vous passerez forcément par ce serveur, et ce n'est pas forcément ce dont vous avez envie. Personnellement, j'ai plusieurs projets sur mon poste de travail, et tous n'utilisent pas la même configuration.

Configurer pip par virtualenv avec virtualenvwrapper

Si vous n'utilisez pas virtualenv wrapper, je ne peux que vous invitez à considérer ou à reconsidérer son usage : c'est un outil pratique, qui permet une grande flexibilité, et vous évite de nombreuses tâches répétitives au quotidien.

Lorsque vous créez un virtualenv (que ce soit directement avec mkvirtualenv ou via un projet avec mkproject), l'environnement installé dispose d'un ensemble de scripts servant de hook à l'activation et à la désactivation de l'environnement. C'est généralement le bon endroit pour ajouter votre touche personnelle.

Celui qui nous intéresse ici est le script postactivate : en le modifiant, vous pourrez exécuter des commandes et configurer des variables d'environnements.

Il se trouve que l'emplacement du fichier de configuration de pip peut être défini par une variable d'environnement : PIP_CONFIG_FILE. Maintenant, si je vous dis qu'à l'activation de votre environnement, une variable VIRTUAL_ENV indique le répertoire où se trouve les fichiers de l'environnement, vous devriez deviner tout seul ce qu'il vous reste à faire... mais voici un exemple concret de fichier postactivate :

#!/bin/bash
# This hook is sourced after this virtualenv is activated.

export PIP_CONFIG_FILE="$VIRTUAL_ENV/.pip/pip.conf"

Une fois le virtualenv activé, vous pouvez connaître l'emplacement du fichier postactivate avec echo $VIRTUAL_ENV/bin/postactivate.

Et tadam, le tour est joué : vous avez maintenant un fichier de configuration pour pip spécifique à votre environnement. Il ne vous reste plus qu'à y indiquer les options spécifiques, et à ne plus vous inquiéter de savoir quelles sont les options à modifier avant de changer de projet.

Revenir à la configuration globale

Lorsque vous désactivez l'environnement, sans action particulière de votre part, vous conservez la variable PIP_CONFIG_FILE. Ce n'est pas un problème en soit si vous ne travaillez que dans des virtualenvs, mais vous avez sans doute envie de faire les choses proprement.

Pour se faire, c'est le script postdeactivate, placé au même endroit que le script postactivate, qui nous intéresse. Il suffit d'y indiquer la valeur par défaut de PIP_CONFIG_FILE:

#!/bin/bash
# This hook is sourced after this virtualenv is deactivated.

export PIP_CONFIG_FILE="$HOME/.pip/pip.conf"

Et de la même façon, vous trouverez ce script en faisant echo $VIRTUAL_ENV/bin/postdeactivate après avoir activé votre virtualenv.

Il ne vous reste plus qu'à travailler sereinement. Même s'il reste encore un tas d'outils pratiques, là dehors. Et que je n'en connais pas autant que ce que j'aimerais. Et de là à les maîtriser... pfiou...

Tester une IntegrityError pour un modèle Django

Par Florian Strzelecki - 11:05 - 10.09.2014

Tags : Django, SQL, Unit Testing, Postgres, IntegrityError

Quoi de plus agaçant qu'avoir du code fonctionnel "en vrai", mais dont les tests ne passent pas ? C'est ce qui m'est arrivé hier, alors que je voulais optimiser l'usage de requêtes SQL avec Postgres (solution finalement trouvée avec l'aide de @twidi, un monsieur qui fait des trucs très cool).

Le problème est assez simple : il s'agit de vérifier que votre code lève une IntegrityError lorsqu'une opération ne respecte pas une contrainte d'intégrité, comme une contrainte de clé étrangère invalide sur un item.save(). Prenons les modèles suivant très simple :

from django.db import models

class Category(models.Model):
    title = models.CharField(max_length=118)

class Item(models.Model):
    title = models.CharField(max_length=118)
    category = models.ForeignKey(Category)

La première chose à faire est d'utiliser transaction.atomic autour de l'appel à la méthode save, dans votre code de l'application (et non pas dans le code de vos tests) :

def some_function(invalid_category_pk):
    """The PK is invalid here. No. Matter. What."""
    item = Item(category_id=invalid_category_pk)

    with transaction.atomic():
        item.save()

Si vous utilisez la commande shell, vous verrez l'exception IntegrityError, et passerez le cœur léger à l'écriture des tests unitaires.

Oui, mais.

Si comme moi vous cherchez une classe de base fournie par Django, vous aurez probablement envie d'utiliser TestCase. Cela tombe bien, c'est une sous-classe de TransactionTestCase. Cela devrait fonctionner. N'est-ce pas ? Hein ?

Non. Oh que non.

Aussi étrange que cela m'est apparu au premier abord, si vous utilisez TestCase, cela ne fonctionnera pas : cette classe étend TransactionTestCase spécifiquement pour retirer la gestion des transactions. Oui, personnellement, je trouve cela perturbant et trompeur, et je laisse les mauvaises langues dire que mon avis est biaisé par le temps perdu à découvrir et comprendre cela.

Donc, pour tester cette fonction, il suffit de faire ceci :

from django.db import IntegrityError
from django.test.testcases import TransactionTestCase

class TestFunctions(TransactionTestCase):

    def test_some_function_raise(self):
        """Assert some_function raise when category does not exist"""
        with self.assertRaises(IntegrityError):
            some_function(7814567)

Et voilà ! C'était facile. J'ai perdu des heures pour découvrir ça. Fuck my life.

Au passage, voici la docstring de TestCase :

Does basically the same as TransactionTestCase, but surrounds every test
with a transaction, monkey-patches the real transaction management routines
to do nothing, and rollsback the test transaction at the end of the test.
You have to use TransactionTestCase, if you need transaction management
inside a test.

Bordeciel

Par Florian Strzelecki - 23:41 - 13.08.2014

Tags : Ludique, The Elder Scrolls, Skyrim, RPG

Lorsque @iamleyeti publia un article en 2012 sur Skyrim Le jeu de l’année 1997 il l’introduisit avec une question des plus prophétiques à mes yeux :

Peut-on passer cent heures (et plus) sur un mauvais jeu ?

Je viens de passer 84h à jouer à The Elder Scrolls V: Skyrim, et cette question résume parfaitement mon expérience. Je ne peux d’ailleurs que vous recommander la lecture des excellents articles sur Merlanfrit à propos de Skyrim si le sujet vous intéresse.

Avant de continuer la lecture de cet article, un passage par XKCD s’impose.

Doyenne

Par Florian Strzelecki - 18:25 - 04.08.2014

Tags : Société, Femme, Singularité, 4 Août

Quand j'étais petit, tout le monde connaissait Jeanne Calment, née Jeanne Louise Calment. Globalement, c'était une vieille dame, et je doute que grand monde s'en souvienne encore longtemps.

Il est surprenant de constater à quel point la popularité de quelqu'un ne dépend pas spécialement de ses capacités ou de son travail, mais plutôt d'un critère arbitraire qu'ils ne peuvent réellement contrôler - tout au plus tenter de maintenir - comme la beauté, la filiation, ou ici, l'âge.

Il y a un goût de supercherie dans le système...

Elle est morte il y a 17 ans maintenant, ce qui veut dire que la moindre de ses oeuvres devrait être encore protégée pendant 58 ans, c'est à dire, après mes 86 ans. S'il est encore possible de le trouver, j'aimerais bien écouter son "album".

Ce sera le 4 Août 2072.

HTTP Accept header

Par Florian Strzelecki - 21:45 - 25.04.2014

Tags : Web, HTTP, Hypermedia, Développement, HATEOAS, REST, Technique, Programmation, Bonne pratique, API

La RFC 2616 de l'IETF est une spécification du protocole HTTP/1.1. Ce document décrit notamment les headers des requêtes HTTP à disposition des clients, et en particulier le header "Accept", qui sera le sujet de cet article.

À quoi sert-il ?

En résumé : il permet la négociation de contenu dirigée par le client.

En utilisant Accept, le client indique au serveur HTTP quels types de contenus il est capable de gérer, éventuellement avec un ordre de préférence. Le serveur est alors invité à fournir le type de contenu le plus adapté pour répondre à la demande du client (mais ce n'est pas obligatoire).

Ce paramètre peut être utilisé avec d'autres headers, comme Accept-Language ou Accept-Encoding ; par exemple, le serveur Apache (qui documente sa méthode de négociation de contenu) utilise plusieurs headers pour déterminer la meilleure réponse possible.

Comment ça marche ?

Prenons un exemple avec une requête envoyée par Firefox (28.0), qui utilise ce header par défaut :

Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

Ceci indique au serveur que le client attend, par ordre de préférence :

Pour le cas d'une balise img, le header sera différent, et contiendra plutôt image/png (j'ai d'ailleurs remarqué que l'URL n'est absolument pas prise en compte par le navigateur pour générer son header).

Les versions et les API (partie 2)

Par Florian Strzelecki - 17:23 - 16.01.2014

Tags : Web, Bonne pratique, Technique, API, Version, HTTP, Hypermedia

Si vous ne l'avez pas déjà fait, je vous invite à lire la partie 1, qui parle d'applications installées plusieurs fois, dans des environnements clients spécifiques à chacun, et où, finalement, le numéro de version dans l'URL n'a pas beaucoup de sens.

Sommaire

  1. Partie 1 : L'application fournie un service via une interface (ou plusieurs)
  2. Partie 2 : Le service est fourni par une (ou plusieurs) applications

Le service est fourni par une (ou plusieurs) applications

Dans la partie 1 donc, j'ai présenté un exemple de SIGB, qu'il faut installer dans un environnement client. Cette fois, je vais prendre le même SIGB, mais avec un seul serveur centralisé : vous êtes alors en position de fournisseur de service, comme peut l'être Facebook, Twitter, Google, et bien d'autres.

Dans ce contexte, il existe des différences fondamentales avec le contexte précédent :

  1. Chaque version de l'application n'est installée qu'une seule fois (par vous)
  2. Les mises à jour sont indépendantes de la volonté des clients
  3. Chaque client décide à quel moment il exploite un changement rétro-compatible
  4. Chaque changement non rétro-compatible impose une adaptation du client (immédiate ou différée)

Les contraintes et les attentes n'étant pas les mêmes, la notion de version peut être traitée différemment.

Les versions et les API (partie 1)

Par Florian Strzelecki - 00:00 - 16.01.2014

Tags : Programmation, Bonne pratique, Technique, API, Version

Vaste sujet que la gestion des versions dans le domaine des API, et cette fois encore ce billet fait suite à une discussion que j'ai eu sur Twitter. Tout a commencé par un tweet d'@edasfr (compte protégé), dont vous pouvez trouver le blog dont je conseille la lecture à cette adresse : n.survol.fr.

Comme les tweets ne suffisent pas, voici un billet qui, je l'espère, vous éclairera sur la gestion des versions et des URLs pour vos (futures) API.

Je ne prétends ni avoir la réponse, ni détenir de vérité absolue : par mon partage d'expérience j'espère seulement vous donner les bonnes informations pour que vous répondiez vous-même à la question.

Sommaire

  1. Partie 1 : L'application fournie un service via une interface (ou plusieurs)
  2. Partie 2 : Le service est fourni par une (ou plusieurs) applications

Mais de quoi est-il question ?

Une API, ce n'est jamais qu'une interface, et dans notre cas, une interface passant par HTTP et ses bonnes vieilles méthodes (GET, POST, PUT, etc.). De facto, il peut exister moins, autant, ou plus d'interfaces que d'applications.

Mais de quelles applications parlons-nous ? C'est là, à mon avis, que se trouve le premier élément de réponse. Vous ne pouvez pas aborder le problème des versions sans savoir dans quel contexte vous vous situez.

Ensuite, qu'est-ce qui est versionné ? Il y a l'application, l'interface, les formats de données, et les données elle-même.

Si vous croisez les types d'applications avec les types de versions, cela donne un large panel de cas à traiter, ce qui n'est pas une mince affaire. J'ai remarqué que la tentation est forte de résoudre un problème par la mauvaise solution, en se trompant simplement sur "qu'est-ce qui doit être versionné", et "de quoi dépendent les versions".

Dans cette première partie, j'aborde le cas d'une application versionnée et distribuée à des clients qui installent chacun leur propre instance, et peuvent utiliser chacun une version différente.

Le jeu de la vie

Par Florian Strzelecki - 12:22 - 05.01.2014

Tags : Python, Programmation, Bonne pratique, Technique, OOP, FP

J'aime la programmation orientée objets (OOP), tout autant que la programmation fonctionnelle (FP), chacune pour des raisons différentes. Je suis toujours un peu triste lorsqu'un développeur critique l'un en disant que l'autre est mieux parce que [inséré ici un argument basé sur une différence de fonctionnement]. L'un n'est pas mieux que l'autre, car les deux impliquent des orientations et des compromis différents.

Dans son article "Start Writing More Classes", par Armin Ronacher, j'aime beaucoup cette note de fin d'article :

Something else I want to mention: what's written above will most likely result in some sort of warmed up discussion in regards to object oriented programming versus something else. Or inheritance versus strategies. Or virtual methods versus method passing. Or whatever else hackernews finds worthy of a discussion this time around.

All of that is entirely irrelevant to the point I'm making which is that monolithic pieces of code are a bad idea.

Pourquoi ? Parce que la plupart du temps, les comparaisons que je peux lire se base sur les erreurs et toutes ces choses parfaitement stupides que les développeurs peuvent faire - et si vous faites ou avez fait de la maintenance sur de vieilles applications, vous avez déjà forcément rencontré ce genres d'abominations. Pour Armin, ce sont les fonctions monolithiques, pour d'autres ce sera autre chose.

TL; DR: Trouver une solution n'est déjà pas facile, faire en sorte qu'elle soit à la fois simple et élégante l'est donc encore moins. Le bon sens n'étant pas la norme, aucun paradigme ne vous mettra à l’abri des singes de l'Enfer qui codent avec les pieds et font absolument n'importe quoi avec des concepts qu'ils ne maîtrisent pas totalement.

Par contre, il est bel et bien nécessaire d'avoir du recul sur les outils que nous utilisons, et de comprendre pourquoi, quand, et comment nous devrions les utiliser.