Skip to content

Blogs de Développeurs: Aggrégateur de Blogs d'Informatique sur .NET, Java, PHP, Ruby, Agile, Gestion de Projet

Forum Logiciel

Forum Logiciel : diffusion de connaissance et d’informations sur toutes les activités liées au développement d’applications informatiques en entreprise.

Blog Société

How to pass the JIRA Administration certification exam

Le blog de Valiantys - jeu, 07/20/2017 - 14:00

Not sure what to expect for how to pass the ACP-JA JIRA Administration certification? As I recently passed the exam, I’d like to share with you a few key tips to help you prepare for the exam. Am I a good candidate? Atlassian recommends having at least two to three years of experience as a JIRA ...

The post How to pass the JIRA Administration certification exam appeared first on Valiantys - Atlassian Platinum Partner.

Catégories: Blog Société

La démarche RSE, phénomène de mode ou engagement réel ?

L'actualité de Synbioz - mer, 07/19/2017 - 23:00

Comme beaucoup de personnes, vous avez déjà dû entendre parler de la démarche RSE, peut-être la pratiquez-vous déjà… et si cela ne vous dit rien, alors nous allons pouvoir vous aider.

Lire la suite...

Catégories: Blog Société

Spring Framework 5 : tour d’horizon des nouveautés

Spring

 

Spring a sorti la première release candidate de la version 5 de son framework il y a un peu plus d’un mois.

A l’heure ou nous Ă©crivons cet article, la version 5 de Spring est disponible en RC2. Une RC3 est prĂ©vue pour courant juillet, peu de temps avant la version finale.

Nous vous proposons par conséquent de découvrir ensemble les nouveautés et améliorations apportées à Spring Framework 5.

Passage Ă  Java 8

Dans sa version 4, Spring était compatible avec Java 6. Avec la version 5, le code du framework a été réécrit en Java 8.

Les développeurs en ont donc profité pour :

  • AmĂ©liorer l’accès aux paramètres d’une mĂ©thode en utilisant les amĂ©liorations de la rĂ©flexivitĂ© de Java 8
  • Utiliser les mĂ©thodes par dĂ©faut des interfaces de Spring
  • Utiliser les classes Charset et StandardCharsets (disponible depuis le JDK 7)

Et dans une optique de compatibilité avec Java 9, l’instanciation des classes se fera désormais par Constructor#newInstance au lieu de Class#newInstance qui va devenir obsolète.

Spring WebFlux

Spring se met à la programmation réactive avec WebFlux dans sa version 5.

Concrètement, il s’agit de pouvoir implĂ©menter une application web (contrĂ´leur REST) ou des clients HTTP de manière rĂ©active. Pour ce faire, Spring 5 intègre dĂ©sormais Reactor et permet de manipuler les objets Mono (qui contient un objet) et Flux (qui peut contenir N objet(s)).

PlutĂ´t qu’un long discours, un peu de code :

ImplĂ©mentation d’un contrĂ´leur rĂ©active

Voici un exemple d’un contrĂ´leur qui expose une API rĂ©active.

@RestController
public class PersonController {

 private final PersonService service;

 public PersonController(PersonService service) {
  this.service = service;
 }

 @PostMapping("/person")
 Mono<Void> create(@RequestBody Publisher<Person> personStream) {
  return this.service.save(personStream).then();
 }

 @GetMapping("/person", produces = "text/event-stream")
 Flux<Person> list() {
  return this.service.findAll();
 }

 @GetMapping("/person/{id}")
 Mono<Person> findById(@PathVariable String id) {
  return this.service.findOne(id);
 }

Notez que la méthode list envoie des événements au consommateur du service. Ce paradigme est connu sous le nom server-sent events (SSE). Ces événements lancés par le serveur peuvent êtres consommés par des clients de différentes natures: client frontal en JavaScript, ou WebClient de Spring en Java.

Ci-dessous, l’implĂ©mentation d’un contrĂ´leur AngularJS permettant l’utilisation d’un service web utilisant SSE grâce Ă  l’interface EventSource :

var personModule = angular.module('persons', []);
personModule.controller('PersonsCtrl', function($scope, Persons) {
   
  $scope.init = function() {
    var source = new EventSource('/persons');
    source.onmessage = function(event) {
      $scope.$apply(function () {
        $scope.entries = JSON.parse(event.data)
      });
    };
  };
 
});

Consommer un service réactive avec Spring

Avec Spring 5, nous pouvons maintenant utiliser WebClient, une alternative non bloquante Ă  RestTemplate qui nous permet de consommer une API REST :

WebClient client = WebClient.create("http://localhost:8080");
Mono<Account> account = client.get()
  .url("/persons/{id}", 1L)
  .accept(APPLICATION_JSON)
  .exchange(request)
  .then(response -> response.bodyToMono(Account.class));

Et pour les tests d’intĂ©gration, grâce au module spring-test, il nous est possible de facilement tester nos API grâce au WebTestClient. Une caractĂ©ristique importante de la classe WebTestClient est qu’on peut tester les points d’accès exposĂ©s par un contrĂ´leur sans dĂ©marrer un serveur de servlets (Tomcat, Netty ou autre). Cette classe propose quelques mĂ©thodes permettant de lancer des assertions sur la rĂ©ponse reçue du serveur. Voici un exemple de code :

WebTestClient client = WebTestClient
        .bindToController(new PersonController())
        .build();

client.get().uri("/persons/42")
        .exchange()
        .expectStatus().isOk()
        .expectHeader().contentType(MediaType.APPLICATION_JSON_UTF8)
        .expectBody(Person.class).value().isEqualTo(new Person("John"));

Bien entendu, on peut aussi se connecter une URI comme d’habitude :

WebTestClient client = WebTestClient
        .bindToServer().baseUrl("http://localhost:8080")
        .build();

client.get()...

Support avec Kotlin 1.1+

Kotlin a le vent en poupe. Et avec Spring 5, il sera possible d’utiliser le framework avec le langage de JetBrains.

Ainsi, voici comment implémenter un contrôleur REST avec Kotlin :

{
    ("/blog" and accept(TEXT_HTML)).nest {
        GET("/", fooHandler::findAllView)
        GET("/{slug}", fooHandler::findOneView)
    }
    ("/api/blog" and accept(APPLICATION_JSON)).nest {
        GET("/", this@barHandler::findAll)
        GET("/{id}", this@barHandler::findOne)
    }
}

Améliorations pour les tests

Dans sa version 5, Spring sera entièrement compatible avec les extensions de JUnit 5, en proposant SpringExtension, qui reprendra les mêmes fonctionnalités du Spring TestContext Framework.

Nous aurons aussi le droit Ă  plusieurs nouvelles annotations :

  • @SpringJUnitConfig : Ă©quivalent Ă  @ExtendWith(SpringExtension.class) (de JUnit 5) et @ContextConfiguration
  • @SpringJUnitWebConfig : tout comme @SpringJUnitConfig, avec en plus @WebAppConfiguration

Pour davantage d’informations, n’hĂ©sitez pas Ă  consulter la release notes ainsi que la documentation de Spring Framework 5.

Des exemples d’utilisation de Spring 5 sont disponibles sur GitHub, que ce soit en Java et en Kotlin.

Catégories: Blog Société

ITSM basics: Problem or incident?

Le blog de Valiantys - jeu, 07/13/2017 - 16:32

Amongst the ITIL processes (incident, problem, service request, change), two of these processes collide: incident and problem. In the English dictionary the two words are technically synonyms, however in the IT world the confusion between the two concepts goes beyond mere semantics. Being able to properly determine the difference between an incident and a problem means ...

The post ITSM basics: Problem or incident? appeared first on Valiantys - Atlassian Platinum Partner.

Catégories: Blog Société

Revue de Presse Xebia

revue de presse Xebia
La revue de presse hebdomadaire des technologies Big Data, DevOps et Web, architectures Java et mobilité dans des environnements agiles, proposée par Xebia.

MobilitĂ© Vision Framework – Exemples & APIs http://www.gravatar.com/avatar/3f309c992e2b1a5c3c014e63810a2f68http://blog.xebia.fr/author/scivettahttp://twitter.com/viteinfinitehttp://github.com/viteinfinitePar Simone Civetta

Depuis l’annonce de Vision Framework lors de la dernière WWDC en juin, GitHub et Medium abondent d’exemples de mises en pratique. Si vous ne savez pas lequel choisir, ou si tout simplement vous voulez commencer avec une implĂ©mentation la plus minimaliste possible, voici un article et un repository qui, Ă  l’aide de quelque snippets, fournissent une vision d’ensemble efficace des principales fonctionnalitĂ©s et des APIs du framework. En particulier :

  • reconnaissance de QR Code
  • reconnaissance de visages
  • reconnaissance des « landmarks » d’un visage (bouche, yeux, nez,…)
  • reconnaissance de textes
  • suivi (tracking) d’objet
  • Vision + CoreML
Back Kafka propose de l’exactly-once-delivery ! http://www.gravatar.com/avatar/00ebd4762e0c123dd62894728ab6c94bhttp://blog.xebia.fr/author/achotardhttp://twitter.com/horgixhttp://github.com/horgixPar Alexis Horgix Chotard

Article en cours d’Ă©criture

Confluent vient d’annoncer le support du presque mythique « exactly-once-delivery » par Apache Kafka.

La problĂ©matique est simple Ă  prĂ©senter : garantir qu’un message sera dĂ©livrĂ© et traitĂ© une seule fois; pas 0, pas 2, pas 5; une… et ce mĂŞme en cas de problème (partition rĂ©seau, crash d’un composant).

En revanche, elle est bien moins simple Ă  rĂ©soudre et encore moins de manière performante ! C’est

  • Idempotence: Exactly-once in order semantics per partition. The producer send operation is now idempotent… written once on broker
  • Transactions: Atomic writes across multiple partitions

De manière plus gĂ©nĂ©rale, cet article est un excellent rĂ©sumĂ© des problĂ©matiques de livraison de messages, et a mĂŞme Ă©tĂ© applaudi par Wernel Vogels, le CTO d’Amazon.

Data Spark 2.2 : une petit pas pour le streaming, un grand pas pour le développeur http://blog.xebia.fr/author/slequeuxhttp://twitter.com/slequeuxPar Sylvain Lequeux

Ce mardi 11 juillet, Apache Spark 2.2 a été annoncé en release. Outre les ajouts de nouveaux algorithmes distribués (cette fois-ci majoritairement sur GraphX, MLLib et SparkR), une annonce forte est faite dans la release note.

Spark Structured Streaming est maintenant considĂ©rĂ© comme « production ready ». Ceci signifie que le tag « experimental » n’est maintenant plus prĂ©cisĂ© et que les Ă©quipes de dĂ©veloppement du framework considèrent le tout comme suffisamment mature pour ĂŞtre exploitĂ© par tous.

Catégories: Blog Société

Le choix d'un Ă©diteur wysiwyg

L'actualité de Synbioz - mer, 07/12/2017 - 23:00

Tout d’abord, une petite explication s’impose : Qu’est-ce qu’un éditeur wysiwyg ? Il faut savoir que wysiwyg est l’acronyme de what you see is what you get (Ce que vous voyez c’est ce que vous obtenez). Pour être plus clair, lorsque vous formaterez du texte dans cet éditeur (mise en gras par exemple), vous verrez directement le texte en gras, et non pas avec les balises qui le formatent.

Ici, je ne vais pas écrire un classement, pour la simple raison que comme beaucoup de choses tout dépend surtout de l’utilisation que vous aurez de l’outil. Ce n’est pas non plus (loin de là) une liste exhaustive des éditeurs existants. Il faut plutôt voir cet article comme un pense-bête de manière à pouvoir choisir rapidement, sans vous perdre dans la masse de l’existant pour trouver celui qui correspond le mieux à vos besoins.

Lire la suite...

Catégories: Blog Société

Sous quelles conditions utiliser les microservices ?

Les microservices attirent de plus en plus l’attention des architectes. Il s’agit d’un pattern architectural semblable au SOA (architecture orientĂ©e services), la diffĂ©rence Ă©tant que les microservices ont tendance Ă  valoriser des services plus restreints. Dans cet article, nous allons dĂ©couvrir ce qui se cache derrière ce terme. Introduction L’idĂ©e principale est de dĂ©couper une […]
Catégories: Blog Société

Kafka 0.11.0 == ♥

Blog d’Ippon Technologies - mar, 07/11/2017 - 09:55

Le 22 juin dernier Kafka sortait sa dernière version : la 0.11. Je vais revenir sur les nouveautés de cette version, car elle introduit des concepts très intéressants voire innovants pour un outil distribué comme Kafka !

PS : pour ceux n’ayant pas de background avec Kafka je vous conseille de regarder la vidéo suivante qui constitue une bonne introduction, quelques modifications ont néanmoins eu lieu avec la nouvelle version ! #AutoPub

Headers de message

Allez, on commence doucement avec cette première fonctionnalité. La possibilité d’ajouter des headers à un message Kafka. La spec provient de la KIP-82. Comment ça marche ? L’ajout de headers se fait via une modification du protocole d’envoi vers le broker Kafka ainsi que d’ajouts d’interfaces de manipulation de ces headers pour les clients (producer / consumer). On a donc maintenant un champ spécifique comme décrit ci-dessous :

Message =>

       Length => varint

       Attributes => int8

       TimestampDelta => varlong

       OffsetDelta => varint

       KeyLen => varint

       Key => data

       ValueLen => varint

       Value => data

       Headers => [Header] <------------ NEW Added Array of headers

Header =>

       Key => string (utf8) <- NEW UTF8 encoded string (uses varint length)

       Value => bytes  <- NEW header value as data (uses varint length)

C’est assez pratique pour passer des informations en plus de votre contenu pour une utilisation par le consommateur par exemple. Ce n’est pas très révolutionnaire car commun pour les queues de messaging. Mais c’est un ajout sympathique. Je suis personnellement intéressé par cette feature à des fins de tracing de requêtes asynchrones… Je dérive &#x1f642;

Exactly once

Bon maintenant, on rentre dans le vif du sujet. Cet article est un peu une excuse pour vous parler de la fonctionnalité phare de cette version : le support de la garantie de délivrance d’un message une fois exactement (ça rend pas bien en français donc je parlerai de “exactly once”).

Avant, il y a bien 1 semaine, “exactly once” ça semblait être de la fiction. De nombreuses personnes considéraient la fonctionnalité impossible dans un environnement distribué. En effet, c’est un des problèmes très complexes de garantir l’envoi unique d’un message dans un outil distribué. Illustrons cela :

J’envoie un message Ă  Kafka, le broker le reçoit, mais met trop de temps Ă  rĂ©pondre. Typiquement c’est le genre de panne que l’on peut attendre du rĂ©seau (CF “Fallacies_of_distributed_computing” ). Kafka prĂ©voyait le coup et permettait (via des options) d’envoyer des messages de retry lorsque l’ACK n’arrivait pas. Ainsi on pouvait choisir entre le fait d’avoir “at most once” si on ne rĂ©essaie  pas et “at least once” si on rĂ©essaie jusqu’à la rĂ©ussite. Pourquoi “at most once” me direz vous ? Et bien si on reçoit un timeout lors de l’ACK, simplement Ă  cause d’une lenteur rĂ©seau et que l’on a rĂ©ellement Ă©crit le message dans le commit log, le producer renvoie et Badaboum, un doublon.

Alors comment contournent-ils cela ?

Idempotence

Ça veut dire que si on envoie deux fois la mĂŞme chose via un producer, on a tout le temps le mĂŞme rĂ©sultat. Le producer va avoir un petit ID dans ses batchs de message permettant de les dĂ©doublonner. Tant que l’on n’a pas de rĂ©ponse de Kafka on renvoie et si Kafka reçoit des doublons il gère ! Pour l’activer rien de plus simple, il suffit de mettre le paramètre enable.idempotence=true. En plus de tout ça, les fameux IDs sont Ă©crits dans un log, du coup c’est rĂ©silient en cas de panne.

C’est beau.

Support des transactions

Ça y est, on rentre dans des choses intéressantes. Kafka, c’est un log distribué : OK ! On peut produire et lire des messages dans un topic donné (streaming ou non). Avant on produisait un message et on continuait nos traitements sans avoir de garantie, en cas de précédence entre nos messages, que tous seraient envoyés. Avec le support des transactions, on a maintenant une syntaxe proche des transactions de nos bases de données :

producer.initTransactions();
try {
  producer.beginTransaction();
  producer.send(record1);
  producer.send(record2);
  producer.commitTransaction();
} catch(ProducerFencedException e) {
  producer.close();
} catch(KafkaException e) {
  producer.abortTransaction();
}
kafka-transactions.java affichage brut

On a donc la possibilitĂ© d’effectuer des Ă©critures atomiques sur diffĂ©rentes partitions d’un topic donnĂ©. C’est plutĂ´t cool et cela apporte de nouveaux use-cases pour Kafka. Vous savez Kafka Streams fait typiquement du “read => transform => send”. Tu fais ça dans une transaction et tu as du streaming en exactly once ! Encore une fois une simple propriĂ©tĂ© permet d’assurer cette garantie : processing.guarantee=exactly-once.

Performances ?

Le support de la fonctionnalité n’est pas automatique. Du coup si vous ne l’utilisez pas, même avec l’ajout des headers, vous aurez de meilleures performances. Un travail a été effectué de ce côté-là notamment au niveau de la compression des messages Kafka sur le réseau et sur disque. Du coup, on parle quand même de 50% d’augmentation en termes de débit !

Et là vous vous demandez, “et pour le exactly once ?”. En fait il y a bien un overhead sur l’utilisation de la fonctionnalité. On parle d’environ 3% ou 20% pour l’utilisation du ”exactly once” (en comparaison de “at-least“ ou “at-most” respectivement). Mais pas besoin d’être triste, en fait le but avoué est de faire du “exactly once” la norme et donc de l’activer par défaut. C’est possible grâce au gain de performance des autres parties du protocole décrit plus haut ! Alors mangez-en !

Pour conclure : Idempotence + Transactions multi partitions = Exactly once pour tous les usages de Kafka = ♥

Pour une description complète des nouvelles features, des améliorations ou des bug corrigés, les releases-notes sont disponibles ici : http://www.mirrorservice.org/sites/ftp.apache.org/kafka/0.11.0.0/RELEASE_NOTES.html. Les articles de Confluent, dont je me suis grandement inspiré, rentrent bien plus dans le détail que moi concernant le exactly-once et peuvent être trouvés dans la KIP-98 (attention c’est long), dans un article de lancement de @nehanarkhede ou dans un blog de @jaykreps pour les septiques.

L’article Kafka 0.11.0 == ♥ est apparu en premier sur Le Blog d'Ippon Technologies.

Catégories: Blog Société

DĂ©veloppement Agile et Craft

développement agile

Vous ĂŞtes dĂ©veloppeur mobile pour un journal d’information bien connu. Les articles paraissent sur un site web et l’application Android correspondante est utilisĂ©e par des centaines de milliers d’utilisateurs. Elle permet de rĂ©agir sur les articles, et les utilisateurs ne s’en privent pas : c’est le théâtre de dĂ©bats d’un haut niveau Ă©motionnel, si ce n’est orthographique.

Mais un matin, aucune rĂ©action postĂ©e depuis l’application ne paraĂ®t plus sur le site. Les avis dĂ©favorables s’accumulent sur le Play Store :

Impossible de rĂ©agir depuis l’application. Aucune rĂ©action envoyĂ©e depuis l’application ne parvient aux modĂ©rateurs, alors que les rĂ©actions envoyĂ©es depuis le site passent sans problème. –RaphaĂ«l H.

Je suis abonnĂ© mais aucun des messages que je dĂ©pose avec mon smartphone ne s’affiche lorsque je commente l’actualitĂ©… –Michel K.

[…] Censure des commentaires n’allant pas dans le sens des articles, je me suis donc dĂ©sabonnĂ©… […] –Adrien V.

Cette situation demande d’agir vite et bien. C’est l’occasion d’illustrer une approche de dĂ©veloppement qui mĂŞle agilitĂ© et savoir-faire (craftsmanship).

Voici le comportement de l’application en matière de commentaires :

  • L’application permet de poster un commentaire en rĂ©action Ă  l’article lui-mĂŞme (indicateur response à false)
  • Elle permet de rĂ©pondre au commentaire d’un autre utilisateur (indicateur response à true)

Avant de vous distraire de votre tâche en cours, votre leader technique a pris le temps d’analyser l’anomalie : l’application ne poste pas les commentaires correctement. Le mapping entre l’indicateur response et la valeur envoyĂ©e Ă  l’API est incorrect. C’est un « if » qui a Ă©tĂ© codĂ© Ă  l’envers.

Le code en question rĂ©vèle l’erreur de mapping concernant la valeur du paramètre responseFlag attendu par l’API.

sendReaction(message, item, response);
public void sendReaction(String message, String answeredItem, boolean response) {
 
    int responseFlag;
    if (response) responseFlag = 0;
    else responseFlag = 1;
                    
    api.postReaction(message, answeredItem, responseFlag);
}
Agilité

Compte tenu de l’importance du bug, vous mettez de cĂ´tĂ© vos dĂ©veloppements en cours. Vous committez vos changements et vous passez sur une nouvelle branche issue de la version de production.

Votre premier rĂ©flexe est d’ouvrir le test unitaire (TU) du code en question. Qui n’existe pas. Damned. Le bug aurait pu ĂŞtre repĂ©rĂ© si un test avait Ă©tĂ© rĂ©digĂ© lors du dĂ©veloppement initial.

Le code est dans une Activity Android. Vous Ă©valuez l’investissement que constitue la rĂ©daction d’un nouveau test unitaire dans cette situation, et vous prĂ©fĂ©rez laisser le TU de cĂ´tĂ© le temps de produire un correctif dĂ©montrable afin de rassurer votre client.

Vous appliquez le correctif qui consiste Ă  inverser les branches du if. Dans l’immĂ©diat, vous privilĂ©giez la correction du bug par rapport au refactoring.

public void sendReaction(String message, String answeredItem, boolean response) {
 
    int responseFlag;
    if (response) responseFlag = 1;
    else responseFlag = 0;
                    
    api.postReaction(message, answeredItem, responseFlag);
}

Vous testez le correctif sur votre poste de dĂ©veloppement, puis vous publiez le code sur une branche du dĂ©pĂ´t de votre organisation. Le leader technique valide vos changements, vous faites le merge dans la branche d’intĂ©gration.

Votre serveur d’intĂ©gration continue met automatiquement Ă  disposition du reste de l’Ă©quipe une version de recette. Votre Ă©quipe pratique le dogfooding : c’est quand les membres de l’Ă©quipe sont les premiers utilisateurs de leur propre application. Cette pratique permet de confirmer l’absence de rĂ©gression dans des conditions rĂ©elles d’utilisation. Vous montrez le correctif Ă  votre client qui confirme la correction.

Craftsmanship

Et maintenant ? Le processus minimal est respectĂ©, du point de vue de votre client, vous pourriez donc livrer une nouvelle version hotfix de votre application et passer au ticket suivant, mais votre travail n’est pas terminĂ©. Dans la mĂ©thode sendReaction, au moins un refactoring vous dĂ©mange. Et votre beau correctif n’est toujours pas testĂ© unitairement.

Compte tenu de la simplicité du correctif, vous pouvez tout reprendre du début.

Vous commencez par écrire un test automatisé. Ici, le plus approprié est un test unitaire qui vérifie la logique du if.

Mais avant de pouvoir écrire ce test unitaire, le code doit être repensé. La classe impactée par le correctif est fortement couplée avec le framework Android qui est difficile à simuler dans un test unitaire. Vous avez deux solutions :

  • Mettre en place le framework Robolectric pour simuler le framework Android dans votre TU
  • Appliquer le pattern Passive View pour sortir la logique du if dans une classe sĂ©parĂ©e, qui serait testable facilement car non dĂ©pendante du framework

Vous choisissez la Passive View car vous pensez que Robolectric ralentirait l’exĂ©cution de vos tests, et Ă  cause du dĂ©lai des mises Ă  jour de Robolectric suite Ă  celles du SDK Android.

Dès que le test passe, vous en profitez pour transformer le paramètre response en enum, petit refactoring qui vous démangeait :

public void sendReaction(String message, String answeredItem, CommentType commentType) {
 
    api.postReaction(message, answeredItem, commentType.getApiFlag());
} 
enum CommentType {

    INITIAL("0"), ANSWER("1");

    private String mApiFlag;

    CommentType(final String apiFlag) {
        mApiFlag = apiFlag;
    }

    public String getApiFlag() {
        return mApiFlag;
    }
}

Vous adaptez votre test unitaire au changement de signature de la méthode sendReaction().

Ensuite vous faites en sorte que votre développement puisse être repris et maintenu facilement par un autre développeur, grâce à une séance de revue de code. En support écrit, vous préparez une pull request détaillée sur GitHub dont la description rappelle :

Le besoin fonctionnel Correction d’un bug au niveau des rĂ©actions La spĂ©cification technique
  • Extraction de la logique dans une classe dĂ©couplĂ©e
  • Inversion du sens du if
Le scénario de test
  1. Brancher l’application sur un environnement de recette
  2. Poster un commentaire sur un article
  3. Poster une réaction à un autre commentaire
  4. Observer qu’ils sont bien reçus par le backend et que leur type est correct

Ce niveau de détail par écrit permet de garder une documentation écrite et indexée au sujet du correctif au cas où un autre besoin impacterait ce périmètre de code.

NB : l’idĂ©al serait d’intĂ©grer la documentation au code afin de la rendre plus « vivante » et pour qu’elle puisse Ă©voluer avec le code. Mais ce type d’approche est coĂ»teux Ă  mettre en place sur du code existant ; vous laissez donc de cĂ´tĂ© ce chantier pour l’instant.

Quelques heures plus tard, la version corrigĂ©e est publiĂ©e sur le Play Store, certains utilisateurs l’ont installĂ©e, et les premières rĂ©actions des utilisateurs arrivent sur les articles. Vous retrouvez enfin leur douce prose, les trolls, la crĂ©ativitĂ© orthographique.

Conclusion

Ă€ partir d’une situation vĂ©cue, dans le contexte du dĂ©veloppement mobile, cet article illustre l’application des valeurs agiles et craft.

Dans le contexte d’un bug sensible, cette approche s’appuie sur les valeurs agiles pour prendre en compte les attentes du client en termes de rapiditĂ© de mise au point d’un correctif.

Elle s’appuie aussi sur les valeurs craft pour permettre Ă  la base de code de rester saine et comprĂ©hensible par les autres dĂ©veloppeurs, prĂ©sents et futurs.

Références
Catégories: Blog Société

[Tribune] Money2020 Europe, ou le réveil des banques européennes

La conférence Money2020 Europe s’est tenue à Copenhague fin juin. Cet événement, arrivé l’année dernière des Etats-Unis, se présente comme le plus grand rendez-vous européen de l’innovation dans les services financiers. La keynote d’ouverture a donné la parole aux CEOs de trois grands acteurs bancaires européens qui ont détaillé leur vision des transformations en cours dans ce secteur. Compte rendu de ces discours qui pourraient inspirer les dirigeants français.

Retrouvez la suite de notre tribune sur Maddyness.com >

Articles suggested :

  1. Suivez la série OCTO Technology « La banque de demain »: quelles évolutions pour le modèle bancaire ?
  2. FinTech – Hugues Le Bret : « Nous sommes une entreprise technologique qui offre Ă  tous les moyens de se faire payer et de payer en temps rĂ©el »
  3. Faut-il être schizophrène pour Innover dans un grand groupe et en particulier dans les banques ?

Catégories: Blog Société

[Tribune] Money2020 Europe, ou le réveil des banques européennes

La conférence Money2020 Europe s’est tenue à Copenhague fin juin. Cet événement, arrivé l’année dernière des Etats-Unis, se présente comme le plus grand rendez-vous européen de l’innovation dans les services financiers. La keynote d’ouverture a donné la parole aux CEOs de trois grands acteurs bancaires européens qui ont détaillé leur vision des transformations en cours dans ce secteur. Compte rendu de ces discours qui pourraient inspirer les dirigeants français.

Retrouvez la suite de notre tribune sur Maddyness.com >

Articles suggested :

  1. Suivez la série OCTO Technology « La banque de demain »: quelles évolutions pour le modèle bancaire ?
  2. FinTech – Hugues Le Bret : « Nous sommes une entreprise technologique qui offre Ă  tous les moyens de se faire payer et de payer en temps rĂ©el »
  3. Faut-il être schizophrène pour Innover dans un grand groupe et en particulier dans les banques ?

Catégories: Blog Société

Valiantys’ ShipIt 2017: a day of innovation

Le blog de Valiantys - jeu, 07/06/2017 - 14:00

At some point you probably have wanted to take the time to develop a project which you are passionate about. You may even have a really good idea that you think will be interesting for your company and has a direct link to making you better at your current role. Once a year at Valiantys, ...

The post Valiantys’ ShipIt 2017: a day of innovation appeared first on Valiantys - Atlassian Platinum Partner.

Catégories: Blog Société

Versionning d’API, Zero Downtime Deployment et migration SQL : théorie et cas pratique

Pour démythifier le Zero Downtime Deployment

Dans les patterns associés aux géants du web, le Zero Downtime Deployment (ZDD) partage une caractéristique avec l’auto-scaling : on en parle d’autant plus qu’ils sont peu mis en œuvre.

Le ZDD est victime d’un cercle vicieux : il a l’air très complexe car il est peu pratiqué, et comme il est peu pratiqué, on pense qu’il est très complexe.

Pour sortir de cette impasse, cet article présente un cas pratique, code à l’appui.

L’objectif n’est pas que tout le monde fasse du ZDD, car on verra qu’il ajoute du travail et de la complexité, mais que vous vous en ayez une vision claire. Cela vous permettra de pouvoir décider d’en faire ou pas en connaissance de cause.

Notre cas d’exemple

Notre exemple s’approche de celui décrit dans le premier article.

Soit une application exposée via une API REST.

Au départ cette application gère des personnes, chaque personne ayant zéro ou une adresse. Cette version est exposée sur le préfixe /v1 à l’aide d’un unique type de ressource /person.

La version suivante de l’application permettra d’associer plusieurs adresses à une personne, et sera exposée sur le préfixe /v2 en utilisant deux ressources, /person et /address.

Les deux versions

Cette application met en avant sa haute disponibilité, il est donc primordial que toutes les opérations soient effectuées sans interruption de service.

L’API est publique, nous ne maîtrisons donc pas l’utilisation qui en est faite. Il est donc impossible de faire une bascule de /v1 à /v2 en une seule fois. Les deux versions devront donc fonctionner ensemble le temps de permettre la migration de tous les utilisateurs. Cette période pouvant être très longue suivant les cas.

Les clients consomment les API via des intermédiaires, il est donc possible que pendant cette période ils utilisent à la fois les versions /v1 et /v2.

Dans la suite de l’article, /v1 et /v2 correspondent aux versions des services, et v1 et v2 correspondent aux deux manières dont les données seront stockées en base.

La stratégie Comment gérer la migration ?

Il est possible de se passer complètement de script de migration au sens traditionnel : vous exposez les services en /v2, et, lorsqu’ils sont appelés pour une donnée encore au format /v1, vous migrez la donnée à la volée. Cela permet de migrer les données petit à petit au fur et à mesure que les utilisateurs des services appelleront les APIs /v2. C’est l’approche qui est souvent prise avec les bases de données NoSQL.

Malheureusement, en procédant ainsi, il est possible que la migration ne se termine jamais, ou alors seulement dans très longtemps (si vous purgez les données trop anciennes). Pendant ce temps, vous devez maintenir le code supplémentaire permettant de prendre en charge ce cas.

L’autre approche est d’utiliser un script. Cela permet de faire en sorte que la migration se fasse rapidement. C’est le même type de script que vous utilisez pour vos migrations habituelles, sauf qu’il doit prendre en compte le fait qu’il s’exécute en même temps que le code. Ainsi toutes les opérations qui créent des verrous pendant plus de quelques millisecondes ne doivent pas être utilisées, et il faut s’assurer de ne pas créer d’interblocage.

La migration doit se faire de manière transactionnelle, pour éviter d’introduire des incohérences dans les données. En cas de problème, le script doit également pouvoir être interrompu et relancé sans que cela ne perturbe l’exécution du programme.

Dans l’article c’est cette approche que nous allons utiliser.

Quand migrer ?

Sans ZDD, le process de migration est classiquement le suivant :

  1. Fermer le service
  2. Arrêter l’application
  3. Faire un backup des données, pour pouvoir les restaurer en cas de problème
  4. Migrer les données
  5. Déployer la nouvelle version de l’application
  6. Démarrer la nouvelle version de l’application
  7. VĂ©rifier que tout fonctionne bien
  8. Ouvrir le service

En ZDD, plus question de fermer le service : la migration de données a lieu « à chaud ».

Il y a deux manières possibles de s’y prendre, suivant que la migration a lieu avant ou après l’ouverture des services /v2 :

Les deux manières de migrer

Numéro de l’étape Version du code Migration après l’ouverture de /v2 Migration avant l’ouverture de /v2

1

1

Application servant /v1 avec un modèle de de donnée v1

2

1

Modifier le schéma de BDD pour permettre de stocker les données nécessaires à /v2

3

2

Déployer une nouvelle version de l’application exposant /v1 et /v2 sur les modèles de donnée v1 ou v2

Déployer une nouvelle version de l’application exposant /v1 sur les modèles de donnée v1 ou v2

4

2

Migrer les données au modèle v2 sans interruption de service, en tenant compte du code /v1 et /v2

Migrer les données au modèle v2 sans interruption de service, en tenant compte du code qui sert /v1

5

3

Déployer une nouvelle version de l’application exposant /v1 et /v2 sur le modèle de donnée v2

6

3

Nettoyer le schéma de BDD des artefacts v1

7

4

Déployer une nouvelle version de l’application gérant /v2 sur le modèle de donnée v2

La première approche permet d’ouvrir les services /v2 plus rapidement car il n’y a pas besoin d’attendre la migration des données.

La seconde approche est plus simple :

  • la version exposant de l’application fonctionnant avec les modèles de donnĂ©es v1 et v2 n’expose que les services /v1, vous faites ainsi l’économie du cas oĂą un appel de service /v2 accède Ă  des donnĂ©es v1 ;
  • pendant la migration de donnĂ©es, les services /v2 ne sont pas encore exposĂ©s, cela veut dire moins de patterns d’accès aux donnĂ©es Ă  prendre en compte pour dĂ©signer une migration Ă©vitant les incohĂ©rences de donnĂ©es et les interblocages.

Sauf si votre process de migration est extrêmement long, la seconde approche est à privilégier, et c’est celle qui sera utilisée dans la suite de l’article.

/v1 et /v2 sont dans un bateau …

Les migrations d’APIs ouvertes posent deux problèmes métier et un problème technique.

Comment migrer les données ?

Le premier problème, valable aussi pour les API fermées, est de savoir comment migrer les données de /v1 à /v2. Je ne parle pas d’un point de vue technique mais bien d’un point de vue métier : la sémantique change entre les deux versions, il faut donc déterminer comment transformer les données de /v1 en /v2 d’une manière qui soit logique et qui ne surprenne pas les utilisateur·rice·s de l’API.

Dans notre cas la solution est immédiate : /v1 a au plus une seule adresse, et /v2 peut en avoir plusieurs, l’adresse de /v1 devient donc une des adresses de /v2.

Comment gérer la rétro-compatibilité ?

L’autre problème est de savoir comment interpréter en /v1 des données /v2. En effet si l’API est ouverte, vos utilisateur·rice·s peuvent appeler vos services /v1 alors que les données sont déjà au modèle /v2.

Il est souvent plus compliqué que le premier car au fur et à mesure des évolutions, les API ont tendance à devenir plus riches. Accéder à des données plus riches de la /v2 au travers du prisme plus étroit de l’API /v1 peut être un vrai casse-tête.

Si c’est le seul moyen que cette transition se passe bien, il est parfois nécessaire d’adapter le design de l’API /v2.

C’est un équilibre à trouver entre la facilité de transition, des restrictions possibles à ajouter pour les appelants de l’API, et le temps à investir.

Comment répondre vite et bien ?

Le problème technique est de parvenir à rendre les différents services, y compris la compatibilité, tout en s’assurant de toujours avoir des données cohérentes et sans (trop) pénaliser les performances. Si, entre les deux versions, les données ne sont plus structurées de la même manière, la gestion de la compatibilité peut demander de croiser les données de plusieurs tables.

Ainsi dans notre exemple, en v1 les adresses sont stockées dans la table person alors qu’en v2 elles sont dans une table address séparée. Pendant la période de compatibilité, il faut que les appels à v1 qui mettent à jour le nom de la personne et son adresse modifient les deux tables de manière transactionnelle pour éviter qu’une lecture v1 qui se produirait au même moment ne renvoie des données incohérentes. De plus, il faut parvenir à le faire sans avoir à poser trop de verrous en base de données, car cela raletit les accès.

La meilleure stratégie est de privilégier une approche que vous maîtrisez bien et qui donne des résultats acceptables plutôt qu’une solution plus efficace ou plus rapide mais plus complexe.

Dans tous les cas, des tests sont absolument essentiels.

Pour servir les deux versions de l’API, vous pouvez utiliser une application unique ou choisir de séparer votre code en deux applications, une par version de services. Cette question n’étant pas structurante pour la question du ZDD, nous choisissons de ne pas la traiter ici. Dans notre exemple, nous avons choisi de n’avoir qu’une seule application.

… et ZDD les rejoint à bord

Sans ZDD la situation est claire : on arrête l’application, les données sont migrées, et on redémarre l’application dans la nouvelle version. Il y a donc un avant et un après.

Avec ZDD la migration s’effectue à chaud pendant que les services sont disponibles, s’ajoute une situation intermédiaire.

Pendant cette période, les données peuvent donc être encore stockées au format /v1 ou migrées au format /v2.

Il faut alors parvenir à déterminer dans quel état sont les données : pour savoir quel code doit être appelé il faut savoir si la donnée a été migrée ou pas. De plus, le morceau de code en charge de cela va être exécuté très souvent, il doit donc être très efficace.

En cas de difficulté, la solution qui devrait fonctionner dans tous les cas est d’ajouter dans les tables impliquées un numéro indiquant la « version de schéma » de la donnée correspondante, et qui sera incrémenté lors de la migration de la donnée. Dans ce cas l’opération de vérification est très simple et rapide. L’opération d’ajout de colonne est alors à faire en avance de phase, ce qui augmente le travail nécessaire à la migration.

Si vous choisissez de faire la migration de données après l’ouverture de /v2, s’ajoute le cas où on appelle une api /v2 alors que la donnée est encore stockée au format v1. Il faut alors migrer la donnée à chaud, de manière transactionnelle en limitant les ralentissements induits.

Pour résumer, il y a quatre situations :

Appel /v1 Appel /v2

Données stockées au format v1

RĂ©pondre comme auparavant

(Seulement si migration après ouverture de /v2) Migrer les données à chaud

Données stockées au format v2

Compatibilité v1

Répondre avec la nouvelle sémantique

Bien ouvrir /v2, et bien décomissionner /v1

Lorsque vous ouvrez /v2 pour la première fois, faites-attention à la manière dont la bascule vers la nouvelle version est faite.

Avant de rendre les nouveaux endpoints accessibles, assurez-vous que tous les serveurs utilisent la dernière version de l’application. Dans le cas contraire, si vous appelez un /v1 alors que la donnée correspondante a été migrée en v2 le code ne saura pas la lire correctement et risque de planter ou de renvoyer une information fausse.

Un autre problème se pose suivant la manière dont vous avez implémenté les modifications de donnée lorsque vous appelez une API /v1.

Le premier cas consiste à sauvegarder la donnée au format v2, mais cela veut dire qu’à nouveau, les versions précédentes de l’application ne pourront pas la lire. La solution la plus simple est alors d’utiliser le feature flipping pour faire basculer le code.

Dans le cas contraire, votre code doit détecter sous quel format la donnée est stockée, et la resauvegarder sous ce même format : une donnée v1 reste en v1, et une donnée v2 reste en v2.
On Ă©vite le feature flipping, mais en Ă©change le code est plus complexe.

Pour décomissionner /v1 il suffit de rendre les endpoints inaccessibles, la suppression du code peut se faire plus tard.

À propos des verrous et des modifications de schémas

Comme on vient de le voir, le ZDD s’appuie beaucoup sur l’utilisation de la base de données, et notamment ses fonctionnalités d’accès concurrent. Si vos comportements métiers sont simples, que vous utilisez un ORM, et que vous avez des tests de performances automatisés, il s’agit d’un domaine auquel vous n’avez pas souvent à vous intéresser. Si vous vous y prenez mal, il est facile de bloquer la base, renvoyer des erreurs (en cas d’interblocage), ou des résultats incohérents.

Notre conseil est de bien vous documenter en amont voire de faire des POC pour éviter d’avoir à refaire un design parce que votre base de données ne fonctionne pas comme vous l’imaginiez. Ne faites pas confiance à des souvenirs ou à des rumeurs : lisez en détail la documentation correspondant à la version de l’outil que vous utilisez, et surtout testez !

Si vous n’avez jamais creusé ces sujets ou que vous êtes rouillé·e, la première migration vous demandera sûrement pas mal de travail, et vous donnera quelques sueurs froides lorsque vous l’exécuterez. Mais dites-vous que toutes les opérations suivantes manipuleront les mêmes concepts, et se passeront donc beaucoup mieux.

Il n’y a pas que le REST dans la vie

REST possède deux caractéristiques qui en font un candidat idéal pour le ZDD :

  • exposer plusieurs versions de services est une pratique standard ;
  • les appels sont supposĂ©s ĂŞtre sans Ă©tat.

Si vos services sont exposés d’une autre manière, il faudra donc vous intéresser à ces sujets. Les sessions, comme tous les types de cache, peuvent demander une attention particulière si les données qu’elles contiennent font l’objet d’un changement de structure entre versions.

Retour Ă  notre exemple

Nous prenons l’hypothèse où le modèle de données suit directement les ressources à exposer. L’adresse est initialement un champ de la table person, et est migrée dans une table address distincte.

L’évolution du schéma

Nous n’utilisons pas de colonne spécifique pour stocker la « version de schéma » des objet. À la place nous allons vérifier en base la manière dont les données sont stockées : si la table person contient une adresse, c’est qu’elle est en version v1, sinon il faut vérifier l’existence d’une adresse dans la table dédiée. Cela évite d’alourdir le schéma SQL, mais augmente le nombre de requêtes exécutées.

Les Ă©tapes Ă  suivre pour la migration :

  1. Version initiale : l’adresse est dans la colonne address de la table person, le code ne sait fonctionner que de cette manière.
  2. Ajout de la nouvelle table address dans la base de données, à cette étape le code ne connaît pas encore cette table.
  3. Déploiement du code qui fournit l’api /v1 et qui est compatible avec les deux manières de stocker l’adresse.
  4. Exécution du script de migration.
  5. Déploiement du code qui fournit les api /v1 et /v2 et qui est compatible avec la nouvelle manière de stocker l’adresse, la colonne address de la table person n’est plus utilisée par le code.
  6. Suppression de la colonne address de la table person.

Le ZDD a pour conséquence d’ajouter des versions de code et des migrations de schémas intermédiaires. Dans un environnement où les déploiements ne sont pas automatisés, cela signifie une augmentation de la charge de travail et donc du risque d’erreur. Mieux vaut donc s’outiller et disposer d’un pipeline de livraison fiable avant de se lancer.

Analyse détaillée La compatibilité des services

Dans notre exemple le problème de compatibilité est le suivant : une fois une personne migrée, elle peut avoir plusieurs adresses. Que faire quand on récupère cette même personne en passant par l’API /v1 ?

Ici il n’y a pas de réponse évidente : il n’y a pas de notion d’adresse préférée, ou de dernière adresse utilisée qui fournirait une manière de discriminer les différentes possibilités. Comme la réponse influe sur le comportement de l’API, c’est une décision à prendre par les personnes du métier.

La solution choisie ici est de renvoyer une adresse parmi celle dans la liste. Elle n’est pas parfaite, mais elle peut être acceptable suivant l’usage qui en est fait : il revient aux personnes du métier d’en décider.

La transactionnalité

Pour résoudre la question de transactionnalité, nous avons choisi la solution la plus simple : poser un verrou sur les entrées correspondantes de la table person.

Si toutes les opérations suivent le même principe, ce verrou joue le rôle d’une mutex en s’assurant que les appels s’exécutent bien l’un après l’autre : lorsqu’une opération pose un risque, elle commence par demander l’accès à ce verrou, et pour cela elle doit attendre son tour.

Exemple avec un appel à PUT /v1/people/127 alors que la personne correspondante est stockée au format v2 mais n’a pas encore d’adresse.

Exemple sans verrou :

Fil d’exécution 1 Fil d’exécution 2

PUT /v1/people/127/addresses

PUT /v1/people/127/addresses

BEGIN

BEGIN

SELECT * from person where id = 127 pour récupérer la personne, vérifie qu’il n’y a pas d’adresse et que les autres champs ne sont pas à modifier

SELECT * from person where id = 127 pour récupérer la personne, vérifie qu’il n’y a pas d’adresse et que les autres champs ne sont pas à modifier

SELECT * from address where id_person = 127 pour récupérer une adresse à mettre à jour, n’en trouve pas et déduit donc qu’il faut en insérer une

SELECT * from address where id_person = 127 pour récupérer une adresse à mettre à jour, n’en trouve pas et déduit donc qu’il faut en insérer une

INSERT INTO address … pour insérer l’adresse

INSERT INTO address … pour insérer l’adresse

commit

commit

RĂ©sultat : la personne se retrouve avec deux adresses !

Exemple avec verrou :

Fil d’exécution 1 Fil d’exécution 2

PUT /v1/people/127/addresses

PUT /v1/people/127/addresses

BEGIN

BEGIN

SELECT address from person where id = 127 FOR UPDATE pour récupérer la personne, vérifie qu’il n’y a pas d’adresse et que les autres champs ne sont pas à modifier et verrouille la ligne

SELECT * from address where id_person = 127 pour récupérer une adresse à mettre à jour, n’en trouve pas et déduit donc qu’il faut en insérer une

INSERT INTO address … pour insérer l’adresse

commit qui relache le verrou sur person

SELECT address from person where id = 127 FOR UPDATE pour récupérer la personne, vérifie qu’il n’y a pas d’adresse et que les autres champs ne sont pas à modifier et verrouille la ligne, attendait que le verrou sur person soit disponible

SELECT id, address FROM address WHERE id_person = 127 récupère l’adresse

SELECT * from address where id_person = 127 pour récupérer une adresse à mettre à jour, trouve l’adresse insérée par l’autre fil d’exécution

UPDATE address set address = … where id = 4758 met à jour l’adresse

commit qui relâche le verrou sur person

RĂ©sultat : une seule adresse.

Le script de migration SQL

Le script de migration déplace les données par blocs de person à address.

Dans notre exemple, une fois le code basculé à la nouvelle version, toutes les données sont écrites au format v2, qu’il s’agisse des créations ou des modifications.

La migration est donc irréversible, nous savons qu’il suffit de migrer toutes les données une fois pour que le travail soit fait.

  • Il commence par rĂ©cupĂ©rer l’ id de person le plus Ă©levĂ©. Comme le script est lancĂ© après le dĂ©ploiement de la nouvelle version, toutes les personnes crĂ©Ă©es après ce moment le sont avec une adresse stockĂ©e dans address. Cela signifie que le script peut s’arrĂŞter Ă  cette valeur.
  • Le script itère par groupes de person de 0 Ă  l’ id qu’il vient de rĂ©cupĂ©rer. Le pas de l’itĂ©ration est Ă  dĂ©terminer expĂ©rimentalement : un pas plus grand permet de faire moins de requĂŞtes donc de diminuer le temps total de la migration, au dĂ©triment du temps unitaire de chaque itĂ©ration, et donc du temps oĂą les verrous existent en base.
    • Il dĂ©marre une transaction.
    • Il sĂ©lectionne les id des personnes qui ont une adresse, et les verrouille.
    • Il insère dans address les donnĂ©es correspondantes Ă  l’aide d’un INSERT … SELECT …`.
    • Il vide le champs address de ces entrĂ©es dans la table person.
    • Il valide la transaction, relâchant ainsi les donnĂ©es.

En cas d’arrêt du script, les données déjà migrées ne sont pas perdues, et relancer le script ne pose pas de problèmes, les données migrées n’étant pas retraitées.

Les Ă©tapes Ă  suivre
  1. Version initiale fournissant l’API /v1 et où l’adresse est stockée dans la colonne address de la table person.
  2. Ajout en base de la table address, non encore utilisée par le code. La création d’une table n’a en principe aucun impact sur la base mais il faut le vérifier.
  3. Fournit l’API /v1, stocke l’adresse dans la table address et sait la lire aux deux endroits. Lors d’une lecture en /v1 sur une donnée v1 la donnée n’est pas migrée en v2 pour garder le code plus simple.
  4. Migration des adresses vers la table address.
  5. Fournit les API /v1 et /v2, et ne sait la lire qu’au format v2, suppression de la colonne address de la table person du code, la colonne est alors toujours en base.
  6. Suppression en base de la colonne address de la table person. Dans certaines bases de données, supprimer une colonne déclenche la réécriture de toute la table et ne peut donc se faire en ZDD. On se contente donc d’une suppression logique, par exemple en ajoutant un underscore devant son nom, et en la « recyclant » lorsqu’on a besoin d’une nouvelle colonne.
L’implémentation

L’implémentation se trouve sur GitHub. Le code est en open source sous licence MIT, vous pouvez donc vous en servir.

Chaque Ă©tape de la migration est dans un module Ă  part, cela permet de facilement examiner ce qui se passe sans avoir Ă  manipuler git.

Le code est en Java et utilise la bibliothèque Dropwizard. La base de donnée est PostgreSQL, l’accès se fait via Hibernate, et les migrations SQL utilisent Liquibase.

Quelques éléments saillants :

Pour conclure

Faire du ZDD n’est pas magique : cela demande du travail et de la rigueur. Si vous pouvez faire sans, tant mieux pour vous, mais si vous en avez besoin, vous devriez maintenant avoir une idée un peu plus précise de ce que ça représente. Rappelez-vous que l’exemple développé ici est un cas simple : servez-vous en pour avoir une idée de la démarche à suivre, et pas comme un guide pour mesurer l’effort à fournir.

La première migration sera sûrement un peu un défi, mais les suivantes seront de plus en plus faciles. Dans tous les cas, n’oubliez pas de tester, tester, et encore tester !

Catégories: Blog Société

Versionning d’API, Zero Downtime Deployment et migration SQL : théorie et cas pratique

Pour démythifier le Zero Downtime Deployment

Dans les patterns associés aux géants du web, le Zero Downtime Deployment (ZDD) partage une caractéristique avec l’auto-scaling : on en parle d’autant plus qu’ils sont peu mis en œuvre.

Le ZDD est victime d’un cercle vicieux : il a l’air très complexe car il est peu pratiqué, et comme il est peu pratiqué, on pense qu’il est très complexe.

Pour sortir de cette impasse, cet article présente un cas pratique, code à l’appui.

L’objectif n’est pas que tout le monde fasse du ZDD, car on verra qu’il ajoute du travail et de la complexité, mais que vous vous en ayez une vision claire. Cela vous permettra de pouvoir décider d’en faire ou pas en connaissance de cause.

Notre cas d’exemple

Notre exemple s’approche de celui décrit dans le premier article.

Soit une application exposée via une API REST.

Au départ cette application gère des personnes, chaque personne ayant zéro ou une adresse. Cette version est exposée sur le préfixe /v1 à l’aide d’un unique type de ressource /person.

La version suivante de l’application permettra d’associer plusieurs adresses à une personne, et sera exposée sur le préfixe /v2 en utilisant deux ressources, /person et /address.

Les deux versions

Cette application met en avant sa haute disponibilité, il est donc primordial que toutes les opérations soient effectuées sans interruption de service.

L’API est publique, nous ne maîtrisons donc pas l’utilisation qui en est faite. Il est donc impossible de faire une bascule de /v1 à /v2 en une seule fois. Les deux versions devront donc fonctionner ensemble le temps de permettre la migration de tous les utilisateurs. Cette période pouvant être très longue suivant les cas.

Les clients consomment les API via des intermédiaires, il est donc possible que pendant cette période ils utilisent à la fois les versions /v1 et /v2.

Dans la suite de l’article, /v1 et /v2 correspondent aux versions des services, et v1 et v2 correspondent aux deux manières dont les données seront stockées en base.

La stratégie Comment gérer la migration ?

Il est possible de se passer complètement de script de migration au sens traditionnel : vous exposez les services en /v2, et, lorsqu’ils sont appelés pour une donnée encore au format /v1, vous migrez la donnée à la volée. Cela permet de migrer les données petit à petit au fur et à mesure que les utilisateurs des services appelleront les APIs /v2. C’est l’approche qui est souvent prise avec les bases de données NoSQL.

Malheureusement, en procédant ainsi, il est possible que la migration ne se termine jamais, ou alors seulement dans très longtemps (si vous purgez les données trop anciennes). Pendant ce temps, vous devez maintenir le code supplémentaire permettant de prendre en charge ce cas.

L’autre approche est d’utiliser un script. Cela permet de faire en sorte que la migration se fasse rapidement. C’est le même type de script que vous utilisez pour vos migrations habituelles, sauf qu’il doit prendre en compte le fait qu’il s’exécute en même temps que le code. Ainsi toutes les opérations qui créent des verrous pendant plus de quelques millisecondes ne doivent pas être utilisées, et il faut s’assurer de ne pas créer d’interblocage.

La migration doit se faire de manière transactionnelle, pour éviter d’introduire des incohérences dans les données. En cas de problème, le script doit également pouvoir être interrompu et relancé sans que cela ne perturbe l’exécution du programme.

Dans l’article c’est cette approche que nous allons utiliser.

Quand migrer ?

Sans ZDD, le process de migration est classiquement le suivant :

  1. Fermer le service
  2. Arrêter l’application
  3. Faire un backup des données, pour pouvoir les restaurer en cas de problème
  4. Migrer les données
  5. Déployer la nouvelle version de l’application
  6. Démarrer la nouvelle version de l’application
  7. VĂ©rifier que tout fonctionne bien
  8. Ouvrir le service

En ZDD, plus question de fermer le service : la migration de données a lieu « à chaud ».

Il y a deux manières possibles de s’y prendre, suivant que la migration a lieu avant ou après l’ouverture des services /v2 :

Les deux manières de migrer

Numéro de l’étape Version du code Migration après l’ouverture de /v2 Migration avant l’ouverture de /v2

1

1

Application servant /v1 avec un modèle de de donnée v1

2

1

Modifier le schéma de BDD pour permettre de stocker les données nécessaires à /v2

3

2

Déployer une nouvelle version de l’application exposant /v1 et /v2 sur les modèles de donnée v1 ou v2

Déployer une nouvelle version de l’application exposant /v1 sur les modèles de donnée v1 ou v2

4

2

Migrer les données au modèle v2 sans interruption de service, en tenant compte du code /v1 et /v2

Migrer les données au modèle v2 sans interruption de service, en tenant compte du code qui sert /v1

5

3

Déployer une nouvelle version de l’application exposant /v1 et /v2 sur le modèle de donnée v2

6

3

Nettoyer le schéma de BDD des artefacts v1

7

4

Déployer une nouvelle version de l’application gérant /v2 sur le modèle de donnée v2

La première approche permet d’ouvrir les services /v2 plus rapidement car il n’y a pas besoin d’attendre la migration des données.

La seconde approche est plus simple :

  • la version exposant de l’application fonctionnant avec les modèles de donnĂ©es v1 et v2 n’expose que les services /v1, vous faites ainsi l’économie du cas oĂą un appel de service /v2 accède Ă  des donnĂ©es v1 ;
  • pendant la migration de donnĂ©es, les services /v2 ne sont pas encore exposĂ©s, cela veut dire moins de patterns d’accès aux donnĂ©es Ă  prendre en compte pour dĂ©signer une migration Ă©vitant les incohĂ©rences de donnĂ©es et les interblocages.

Sauf si votre process de migration est extrêmement long, la seconde approche est à privilégier, et c’est celle qui sera utilisée dans la suite de l’article.

/v1 et /v2 sont dans un bateau …

Les migrations d’APIs ouvertes posent deux problèmes métier et un problème technique.

Comment migrer les données ?

Le premier problème, valable aussi pour les API fermées, est de savoir comment migrer les données de /v1 à /v2. Je ne parle pas d’un point de vue technique mais bien d’un point de vue métier : la sémantique change entre les deux versions, il faut donc déterminer comment transformer les données de /v1 en /v2 d’une manière qui soit logique et qui ne surprenne pas les utilisateur·rice·s de l’API.

Dans notre cas la solution est immédiate : /v1 a au plus une seule adresse, et /v2 peut en avoir plusieurs, l’adresse de /v1 devient donc une des adresses de /v2.

Comment gérer la rétro-compatibilité ?

L’autre problème est de savoir comment interpréter en /v1 des données /v2. En effet si l’API est ouverte, vos utilisateur·rice·s peuvent appeler vos services /v1 alors que les données sont déjà au modèle /v2.

Il est souvent plus compliqué que le premier car au fur et à mesure des évolutions, les API ont tendance à devenir plus riches. Accéder à des données plus riches de la /v2 au travers du prisme plus étroit de l’API /v1 peut être un vrai casse-tête.

Si c’est le seul moyen que cette transition se passe bien, il est parfois nécessaire d’adapter le design de l’API /v2.

C’est un équilibre à trouver entre la facilité de transition, des restrictions possibles à ajouter pour les appelants de l’API, et le temps à investir.

Comment répondre vite et bien ?

Le problème technique est de parvenir à rendre les différents services, y compris la compatibilité, tout en s’assurant de toujours avoir des données cohérentes et sans (trop) pénaliser les performances. Si, entre les deux versions, les données ne sont plus structurées de la même manière, la gestion de la compatibilité peut demander de croiser les données de plusieurs tables.

Ainsi dans notre exemple, en v1 les adresses sont stockées dans la table person alors qu’en v2 elles sont dans une table address séparée. Pendant la période de compatibilité, il faut que les appels à v1 qui mettent à jour le nom de la personne et son adresse modifient les deux tables de manière transactionnelle pour éviter qu’une lecture v1 qui se produirait au même moment ne renvoie des données incohérentes. De plus, il faut parvenir à le faire sans avoir à poser trop de verrous en base de données, car cela raletit les accès.

La meilleure stratégie est de privilégier une approche que vous maîtrisez bien et qui donne des résultats acceptables plutôt qu’une solution plus efficace ou plus rapide mais plus complexe.

Dans tous les cas, des tests sont absolument essentiels.

Pour servir les deux versions de l’API, vous pouvez utiliser une application unique ou choisir de séparer votre code en deux applications, une par version de services. Cette question n’étant pas structurante pour la question du ZDD, nous choisissons de ne pas la traiter ici. Dans notre exemple, nous avons choisi de n’avoir qu’une seule application.

… et ZDD les rejoint à bord

Sans ZDD la situation est claire : on arrête l’application, les données sont migrées, et on redémarre l’application dans la nouvelle version. Il y a donc un avant et un après.

Avec ZDD la migration s’effectue à chaud pendant que les services sont disponibles, s’ajoute une situation intermédiaire.

Pendant cette période, les données peuvent donc être encore stockées au format /v1 ou migrées au format /v2.

Il faut alors parvenir à déterminer dans quel état sont les données : pour savoir quel code doit être appelé il faut savoir si la donnée a été migrée ou pas. De plus, le morceau de code en charge de cela va être exécuté très souvent, il doit donc être très efficace.

En cas de difficulté, la solution qui devrait fonctionner dans tous les cas est d’ajouter dans les tables impliquées un numéro indiquant la « version de schéma » de la donnée correspondante, et qui sera incrémenté lors de la migration de la donnée. Dans ce cas l’opération de vérification est très simple et rapide. L’opération d’ajout de colonne est alors à faire en avance de phase, ce qui augmente le travail nécessaire à la migration.

Si vous choisissez de faire la migration de données après l’ouverture de /v2, s’ajoute le cas où on appelle une api /v2 alors que la donnée est encore stockée au format v1. Il faut alors migrer la donnée à chaud, de manière transactionnelle en limitant les ralentissements induits.

Pour résumer, il y a quatre situations :

Appel /v1 Appel /v2

Données stockées au format v1

RĂ©pondre comme auparavant

(Seulement si migration après ouverture de /v2) Migrer les données à chaud

Données stockées au format v2

Compatibilité v1

Répondre avec la nouvelle sémantique

Bien ouvrir /v2, et bien décomissionner /v1

Lorsque vous ouvrez /v2 pour la première fois, faites-attention à la manière dont la bascule vers la nouvelle version est faite.

Avant de rendre les nouveaux endpoints accessibles, assurez-vous que tous les serveurs utilisent la dernière version de l’application. Dans le cas contraire, si vous appelez un /v1 alors que la donnée correspondante a été migrée en v2 le code ne saura pas la lire correctement et risque de planter ou de renvoyer une information fausse.

Un autre problème se pose suivant la manière dont vous avez implémenté les modifications de donnée lorsque vous appelez une API /v1.

Le premier cas consiste à sauvegarder la donnée au format v2, mais cela veut dire qu’à nouveau, les versions précédentes de l’application ne pourront pas la lire. La solution la plus simple est alors d’utiliser le feature flipping pour faire basculer le code.

Dans le cas contraire, votre code doit détecter sous quel format la donnée est stockée, et la resauvegarder sous ce même format : une donnée v1 reste en v1, et une donnée v2 reste en v2.
On Ă©vite le feature flipping, mais en Ă©change le code est plus complexe.

Pour décomissionner /v1 il suffit de rendre les endpoints inaccessibles, la suppression du code peut se faire plus tard.

À propos des verrous et des modifications de schémas

Comme on vient de le voir, le ZDD s’appuie beaucoup sur l’utilisation de la base de données, et notamment ses fonctionnalités d’accès concurrent. Si vos comportements métiers sont simples, que vous utilisez un ORM, et que vous avez des tests de performances automatisés, il s’agit d’un domaine auquel vous n’avez pas souvent à vous intéresser. Si vous vous y prenez mal, il est facile de bloquer la base, renvoyer des erreurs (en cas d’interblocage), ou des résultats incohérents.

Notre conseil est de bien vous documenter en amont voire de faire des POC pour éviter d’avoir à refaire un design parce que votre base de données ne fonctionne pas comme vous l’imaginiez. Ne faites pas confiance à des souvenirs ou à des rumeurs : lisez en détail la documentation correspondant à la version de l’outil que vous utilisez, et surtout testez !

Si vous n’avez jamais creusé ces sujets ou que vous êtes rouillé·e, la première migration vous demandera sûrement pas mal de travail, et vous donnera quelques sueurs froides lorsque vous l’exécuterez. Mais dites-vous que toutes les opérations suivantes manipuleront les mêmes concepts, et se passeront donc beaucoup mieux.

Il n’y a pas que le REST dans la vie

REST possède deux caractéristiques qui en font un candidat idéal pour le ZDD :

  • exposer plusieurs versions de services est une pratique standard ;
  • les appels sont supposĂ©s ĂŞtre sans Ă©tat.

Si vos services sont exposés d’une autre manière, il faudra donc vous intéresser à ces sujets. Les sessions, comme tous les types de cache, peuvent demander une attention particulière si les données qu’elles contiennent font l’objet d’un changement de structure entre versions.

Retour Ă  notre exemple

Nous prenons l’hypothèse où le modèle de données suit directement les ressources à exposer. L’adresse est initialement un champ de la table person, et est migrée dans une table address distincte.

L’évolution du schéma

Nous n’utilisons pas de colonne spécifique pour stocker la « version de schéma » des objet. À la place nous allons vérifier en base la manière dont les données sont stockées : si la table person contient une adresse, c’est qu’elle est en version v1, sinon il faut vérifier l’existence d’une adresse dans la table dédiée. Cela évite d’alourdir le schéma SQL, mais augmente le nombre de requêtes exécutées.

Les Ă©tapes Ă  suivre pour la migration :

  1. Version initiale : l’adresse est dans la colonne address de la table person, le code ne sait fonctionner que de cette manière.
  2. Ajout de la nouvelle table address dans la base de données, à cette étape le code ne connaît pas encore cette table.
  3. Déploiement du code qui fournit l’api /v1 et qui est compatible avec les deux manières de stocker l’adresse.
  4. Exécution du script de migration.
  5. Déploiement du code qui fournit les api /v1 et /v2 et qui est compatible avec la nouvelle manière de stocker l’adresse, la colonne address de la table person n’est plus utilisée par le code.
  6. Suppression de la colonne address de la table person.

Le ZDD a pour conséquence d’ajouter des versions de code et des migrations de schémas intermédiaires. Dans un environnement où les déploiements ne sont pas automatisés, cela signifie une augmentation de la charge de travail et donc du risque d’erreur. Mieux vaut donc s’outiller et disposer d’un pipeline de livraison fiable avant de se lancer.

Analyse détaillée La compatibilité des services

Dans notre exemple le problème de compatibilité est le suivant : une fois une personne migrée, elle peut avoir plusieurs adresses. Que faire quand on récupère cette même personne en passant par l’API /v1 ?

Ici il n’y a pas de réponse évidente : il n’y a pas de notion d’adresse préférée, ou de dernière adresse utilisée qui fournirait une manière de discriminer les différentes possibilités. Comme la réponse influe sur le comportement de l’API, c’est une décision à prendre par les personnes du métier.

La solution choisie ici est de renvoyer une adresse parmi celle dans la liste. Elle n’est pas parfaite, mais elle peut être acceptable suivant l’usage qui en est fait : il revient aux personnes du métier d’en décider.

La transactionnalité

Pour résoudre la question de transactionnalité, nous avons choisi la solution la plus simple : poser un verrou sur les entrées correspondantes de la table person.

Si toutes les opérations suivent le même principe, ce verrou joue le rôle d’une mutex en s’assurant que les appels s’exécutent bien l’un après l’autre : lorsqu’une opération pose un risque, elle commence par demander l’accès à ce verrou, et pour cela elle doit attendre son tour.

Exemple avec un appel à PUT /v1/people/127 alors que la personne correspondante est stockée au format v2 mais n’a pas encore d’adresse.

Exemple sans verrou :

Fil d’exécution 1 Fil d’exécution 2

PUT /v1/people/127/addresses

PUT /v1/people/127/addresses

BEGIN

BEGIN

SELECT * from person where id = 127 pour récupérer la personne, vérifie qu’il n’y a pas d’adresse et que les autres champs ne sont pas à modifier

SELECT * from person where id = 127 pour récupérer la personne, vérifie qu’il n’y a pas d’adresse et que les autres champs ne sont pas à modifier

SELECT * from address where id_person = 127 pour récupérer une adresse à mettre à jour, n’en trouve pas et déduit donc qu’il faut en insérer une

SELECT * from address where id_person = 127 pour récupérer une adresse à mettre à jour, n’en trouve pas et déduit donc qu’il faut en insérer une

INSERT INTO address … pour insérer l’adresse

INSERT INTO address … pour insérer l’adresse

commit

commit

RĂ©sultat : la personne se retrouve avec deux adresses !

Exemple avec verrou :

Fil d’exécution 1 Fil d’exécution 2

PUT /v1/people/127/addresses

PUT /v1/people/127/addresses

BEGIN

BEGIN

SELECT address from person where id = 127 FOR UPDATE pour récupérer la personne, vérifie qu’il n’y a pas d’adresse et que les autres champs ne sont pas à modifier et verrouille la ligne

SELECT * from address where id_person = 127 pour récupérer une adresse à mettre à jour, n’en trouve pas et déduit donc qu’il faut en insérer une

INSERT INTO address … pour insérer l’adresse

commit qui relache le verrou sur person

SELECT address from person where id = 127 FOR UPDATE pour récupérer la personne, vérifie qu’il n’y a pas d’adresse et que les autres champs ne sont pas à modifier et verrouille la ligne, attendait que le verrou sur person soit disponible

SELECT id, address FROM address WHERE id_person = 127 récupère l’adresse

SELECT * from address where id_person = 127 pour récupérer une adresse à mettre à jour, trouve l’adresse insérée par l’autre fil d’exécution

UPDATE address set address = … where id = 4758 met à jour l’adresse

commit qui relâche le verrou sur person

RĂ©sultat : une seule adresse.

Le script de migration SQL

Le script de migration déplace les données par blocs de person à address.

Dans notre exemple, une fois le code basculé à la nouvelle version, toutes les données sont écrites au format v2, qu’il s’agisse des créations ou des modifications.

La migration est donc irréversible, nous savons qu’il suffit de migrer toutes les données une fois pour que le travail soit fait.

  • Il commence par rĂ©cupĂ©rer l’ id de person le plus Ă©levĂ©. Comme le script est lancĂ© après le dĂ©ploiement de la nouvelle version, toutes les personnes crĂ©Ă©es après ce moment le sont avec une adresse stockĂ©e dans address. Cela signifie que le script peut s’arrĂŞter Ă  cette valeur.
  • Le script itère par groupes de person de 0 Ă  l’ id qu’il vient de rĂ©cupĂ©rer. Le pas de l’itĂ©ration est Ă  dĂ©terminer expĂ©rimentalement : un pas plus grand permet de faire moins de requĂŞtes donc de diminuer le temps total de la migration, au dĂ©triment du temps unitaire de chaque itĂ©ration, et donc du temps oĂą les verrous existent en base.
    • Il dĂ©marre une transaction.
    • Il sĂ©lectionne les id des personnes qui ont une adresse, et les verrouille.
    • Il insère dans address les donnĂ©es correspondantes Ă  l’aide d’un INSERT … SELECT …`.
    • Il vide le champs address de ces entrĂ©es dans la table person.
    • Il valide la transaction, relâchant ainsi les donnĂ©es.

En cas d’arrêt du script, les données déjà migrées ne sont pas perdues, et relancer le script ne pose pas de problèmes, les données migrées n’étant pas retraitées.

Les Ă©tapes Ă  suivre
  1. Version initiale fournissant l’API /v1 et où l’adresse est stockée dans la colonne address de la table person.
  2. Ajout en base de la table address, non encore utilisée par le code. La création d’une table n’a en principe aucun impact sur la base mais il faut le vérifier.
  3. Fournit l’API /v1, stocke l’adresse dans la table address et sait la lire aux deux endroits. Lors d’une lecture en /v1 sur une donnée v1 la donnée n’est pas migrée en v2 pour garder le code plus simple.
  4. Migration des adresses vers la table address.
  5. Fournit les API /v1 et /v2, et ne sait la lire qu’au format v2, suppression de la colonne address de la table person du code, la colonne est alors toujours en base.
  6. Suppression en base de la colonne address de la table person. Dans certaines bases de données, supprimer une colonne déclenche la réécriture de toute la table et ne peut donc se faire en ZDD. On se contente donc d’une suppression logique, par exemple en ajoutant un underscore devant son nom, et en la « recyclant » lorsqu’on a besoin d’une nouvelle colonne.
L’implémentation

L’implémentation se trouve sur GitHub. Le code est en open source sous licence MIT, vous pouvez donc vous en servir.

Chaque Ă©tape de la migration est dans un module Ă  part, cela permet de facilement examiner ce qui se passe sans avoir Ă  manipuler git.

Le code est en Java et utilise la bibliothèque Dropwizard. La base de donnée est PostgreSQL, l’accès se fait via Hibernate, et les migrations SQL utilisent Liquibase.

Quelques éléments saillants :

Pour conclure

Faire du ZDD n’est pas magique : cela demande du travail et de la rigueur. Si vous pouvez faire sans, tant mieux pour vous, mais si vous en avez besoin, vous devriez maintenant avoir une idée un peu plus précise de ce que ça représente. Rappelez-vous que l’exemple développé ici est un cas simple : servez-vous en pour avoir une idée de la démarche à suivre, et pas comme un guide pour mesurer l’effort à fournir.

La première migration sera sûrement un peu un défi, mais les suivantes seront de plus en plus faciles. Dans tous les cas, n’oubliez pas de tester, tester, et encore tester !

Catégories: Blog Société

Migration de données avec Rails

L'actualité de Synbioz - mer, 07/05/2017 - 23:00

L’évolution. Sujet passionnant s’il en est ! Un jour ou l’autre, nous devons tous faire face à cette situation : le changement. Nos besoins — ou ceux de nos clients — évoluent, changent avec le temps ; et nous devons donc accompagner ce changement en adaptant le code de nos projets. Nous devons effectuer des migrations.

Nous avons tous une idée de ce qu’est ou ce que doit être une migration ; il s’agit d’adapter la structure de nos données ou nos données elles-mêmes à nos nouveaux besoins. Ainsi nous distinguons déjà deux formes de migration : d’une part les migrations de structure (on parlera de schema en anglais) et d’autre part les migrations de données. Voyons ensemble ce que Rails met à notre disposition pour réaliser nos migrations et comment utiliser ces outils au mieux, en évitant les pièges que nous pourrions rencontrer !

Lire la suite...

Catégories: Blog Société

The road to success: Why scoping is important

Le blog de Valiantys - mar, 07/04/2017 - 14:00

Imagine that your car is not working properly. For some reason when you push on the gas, your RPM’s don’t rev up and you can’t seem to get it going to the minimum speed needed to be riding safely on the highway. You would obviously take your car to the garage to get it fixed ...

The post The road to success: Why scoping is important appeared first on Valiantys - Atlassian Platinum Partner.

Catégories: Blog Société

Retour sur #NCrafts 2017

Blog d’Ippon Technologies - lun, 07/03/2017 - 08:30

La confĂ©rence NCrafts 2017 s’est dĂ©roulĂ©e durant les 18 et 19 mai Ă  Paris. Une confĂ©rence cette annĂ©e sur le thème Humble Programmers.

Cette confĂ©rence, dont la première Ă©dition eu lieu en 2014, cherche Ă  mettre l’accent sur le mouvement du software craftsmanship et le mĂ©tier de dĂ©veloppeur. NCrafts se veut agnostique Ă  la fois vis-Ă -vis des technologies, des pratiques et des langages. Apparaissent alors en guest stars les sujets sur l’agilitĂ©, le domain driven design (DDD), la programmation fonctionnelle et la programmation objet, ainsi que de potentiels nouveaux concepts et des rĂ©flexions sur la vie de dĂ©veloppeur. En terme de langage pour les prĂ©sentations techniques, la majoritĂ© des talks autour du code Ă©tait basĂ©e F#, mais il y avait aussi du C#, Scala, Haskell, Java, Swift, JavaScript, Go et bien d’autres encore.

Je vous propose de vous raconter cette expĂ©rience et de vous partager quelques rĂ©flexions personnelles sur les keynotes de NCrafts 2017, dont j’ai apprĂ©ciĂ© la qualitĂ©.

Talk de bienvenue

Rui Carvalho, le principal organisateur de la confĂ©rence, a commencĂ© par nous parler de concepts de dĂ©veloppement de bon sens et paraissant telle une redite, pour peu qu’on ait une certaine habitude des pratiques agiles. Toutefois, le point de vue prĂ©sentĂ© semblait lĂ©gèrement diffĂ©rent des discours habituels sur ce thème.

Puis Rui sort un manifeste nommé The Humble Programmer Manifesto : un manifeste de plus ?!

Il ne s’agissait aucunement d’un Ă©nième manifeste. Rui indique que tout le discours qu’il vient de prĂ©senter avait dĂ©jĂ  Ă©tĂ© Ă©noncĂ© en 1972 par Edsger W. Dijkstra. Plus exactement, il s’agit du discours qu’il a prononcĂ© lors de la remise de son prix Turing ; discours intitulĂ© The Humble Programmer (EWD 340).

Par cette dĂ©monstration, Rui a tentĂ© de faire comprendre que le monde du dĂ©veloppement a besoin de connaĂ®tre son histoire pour s’amĂ©liorer et s’Ă©panouir davantage. Une partie des problèmes de nos jours peuvent ĂŞtre rĂ©solus grâce Ă  des solutions plus anciennes que nous.

L’informatique est un domaine rĂ©cent et son histoire est bien souvent mal connue surtout par ceux qui le pratiquent. Ces constats sont encore plus creux dans le cadre du dĂ©veloppement logiciel. Mais quel intĂ©rĂŞt aurions-nous Ă  Ă©tudier notre passĂ© ? Nombre de conseils venant de dĂ©veloppeurs d’expĂ©rience aujourd’hui ont dĂ©jĂ  Ă©tĂ© prononcĂ©s dans les annĂ©es 60, 70 par ces pionniers de l’informatique que sont Dijkstra et ses nombreuses correspondances, Knuth et son oeuvre intitulĂ© The Art of Computer Programming (TAOCP) ou Backus et son plaidoyer pour un style de programmation alternatif dĂ©crit dans Can Programming be Liberated from the von Neumann Style?. Et il s’agit bien de conseils, d’autant que ces personnes ont forgĂ© la perception que nous avons actuellement du dĂ©veloppement logiciel. Ces pionniers ont trouvĂ© les mots qui nous manquent parfois pour dĂ©crire les problèmes que nous rencontrons rĂ©gulièrement et ont imaginĂ© des solutions pour les rĂ©soudre. De ces Ă©poques reculĂ©es, nous avons beaucoup Ă  apprendre. Comme le disait le philosophe amĂ©ricain George Santayana : Those who cannot remember the past are condemned to repeat it.

Le talk d’introduction de Rui Ă©tait court, mais c’est celui qui m’a probablement le plus intĂ©ressĂ©, tant le sujet m’a passionnĂ© par le passĂ© et me passionne toujours autant actuellement.

Les slides sont disponibles sur slideshare.

What Ever Happened to Being eXtreme? (Rachel Davies)

Rachel Davies est revenue sur une mĂ©thode plus connue pour ses pratiques de dĂ©veloppement que son approche du management : l’eXtreme Programming ou XP. Car, comme je le rappelle assez souvent autour de moi, il ne faut oublier que l’XP couvre un pĂ©rimètre plus large que Scrum. Rachel rappelle que cette mĂ©thode, nĂ©e dans les annĂ©es 90 avant le manifeste agile, a pour objectif de livrer de la valeur aussi vite que possible en vue d’optimiser le feedback Ă  l’Ă©quipe de dĂ©veloppement. Pour cela, l’Ă©quipe projet doit ĂŞtre capable de prendre des risques et pour cela elle a besoin de courage ; le courage Ă©tant l’une des plus importante valeur de l’XP.

Pour Rachel, l’XP est une mĂ©thode qui doit continuer Ă  Ă©voluer. Aussi, elle nous a donnĂ© une Ă©numĂ©ration de ce qu’est l’XP de nos jours, de part sa propre expĂ©rience. Voici quelques points parmis ceux qu’elle a remontĂ© :

  • Le mob programming pour remplacer le pair programming. Le pair programming incorpore selon elle encore trop de risque d’effet tunnel. On peut le pallier avec une rotation des pairs, mais elle finit par perdre de son efficacitĂ©. Le mob programming consiste Ă  faire travailler toute l’Ă©quipe de dĂ©veloppement sur la mĂŞme fonctionnalitĂ© autour d’une seule machine de dĂ©veloppement (mais avec un vidĂ©o-projecteur Ă  la place de l’Ă©cran). Cette approche offre plus de flexibilitĂ©. Elle rĂ©duit d’autant plus l’effet tunnel au sein de l’Ă©quipe et favorise le partage de la connaissance sur un projet (collective ownership).
  • Pas de branche mais des tokens de dĂ©ploiement. On voit de plus en plus de projets oĂą GitFlow et ses multiples branches (dont les feature branches) sont rejetĂ©s. Sur ces projets, il n’y a plus qu’une seule branche et la livraison se fait au commit près ou au token près. Cette approche permet d’avoir une mise en production quotidienne. NĂ©anmoins, elle ne fonctionne que si les intervenants sont colocalisĂ©s.
  • Rencontre entre tous les acteurs concernĂ©s par le projet (du dĂ©veloppeur Ă  l’utilisateur) pour un meilleur partage de la vision.
  • Backlog d’innovation cross-product gĂ©rĂ© par les dĂ©veloppeurs afin d’amĂ©liorer la livraison de valeur.
  • Story time : estimation des user stories quand elles sont prĂŞtes, plutĂ´t que des rĂ©unions d’estimation Ă  pĂ©riode fixe. Le story time permet de diminuer le temps passĂ© dans les rĂ©unions d’estimation et s’avère ĂŞtre moins contraignant.
  • Un jour par semaine aux dĂ©veloppeurs pour apprendre une nouvelle technologie (c’est un thème qui est souvent revenu au cours de NCrafts 2017). Cela permet de rester Ă  l’Ă©coute des innovations et d’ĂŞtre prĂŞt Ă  l’intĂ©grer si le besoin est exprimĂ©.
  • Changement d’Ă©quipe pour les dĂ©veloppeurs qui le souhaitent, tous les 3 mois. L’idĂ©e est d’apporter pour les projets un regard nouveau et Ă©viter d’autant plus l’enlisement. Quant au dĂ©veloppeur, cela lui donne la possibilitĂ© de briser la monotonie et de satisfaire son Ă©panouissement.

L’ensemble des pratiques proposĂ©es par Rachel est particulièrement tentant. Elles montrent notamment que l’agilitĂ© reste un sujet ouvert et qui doit sans cesse Ă©voluer pour aller toujours vers plus d’efficacitĂ©. Par contre, je ne peux m’empĂŞcher de penser que les pratiques prĂ©sentĂ©es ne fonctionnent qu’avec des Ă©quipes capables de s’adapter et de s’Ă©panouir dans un environnement ouvert. Ce qui fut effectivement le cas pour Rachel.

Well-rounded architect (Patrick Kua)

Dans cette keynote, Patrick Kua de ThoughtWorks est venu partager avec nous sa vision de la notion d’architecte. D’entrĂ©e de jeu, Patrick indique que pour lui architecte est un rĂ´le. Il peut très bien ne pas exister. Il peut aussi ĂŞtre jouĂ© par un ou plusieurs dĂ©veloppeurs, mais pas n’importe lesquels non plus : il faut une certaine expĂ©rience et appĂ©tence sur ce sujet.

LĂ -dessus, Patrick pose la question de la relation entre architecte et architecture. Pour lui, le but de l’architecture est de rĂ©duire le coĂ»t du changement. L’architecte est responsable ou rĂ©fĂ©rent de l’architecture et de la “nourrir”. Par contre, il n’en est pas le propriĂ©taire. L’architecture appartient Ă  l’Ă©quipe. Et l’architecte ne prend pas non plus seul les dĂ©cisions dessus. Pour cela il doit consulter l’Ă©quipe.

Patrick propose un radar Ă  6 axes sur lesquels Ă©valuer la capacitĂ© d’une personne Ă  endosser le rĂ´le d’architecte :

  • il doit ĂŞtre un bon leader technique et ĂŞtre capable d’orienter les participants d’un projet dans la mĂŞme direction,
  • il doit ĂŞtre dĂ©veloppeur, afin de mieux comprendre les difficultĂ©s rencontrĂ©es par les autres dĂ©veloppeurs, ainsi que d’ĂŞtre plus Ă  l’Ă©coute des solutions proposĂ©es par ceux-ci,
  • il doit avoir une bonne perception de l’Ă©cosystème qui entoure l’application issue du projet,
  • il doit au moins penser comme un entrepreneur, se poser des questions sur les bĂ©nĂ©fices que vont engager les dĂ©cisions sur un projet et favoriser une approche fail fast (learn fast ?),
  • il doit mettre au point une stratĂ©gie sur les technologies, en demandant d’essayer de nouveaux produits et en produisant des rapports sur ces expĂ©rimentations (en produisant par exemple l’Ă©quivalent d’un TechRadar Ă  la ThoughtWorks),
  • il doit enfin ĂŞtre un communicant efficace et ĂŞtre capable d’adapter son discours Ă  des personnes techniques et non-techniques.

Patrick a donnĂ© par la suite un ensemble de motifs sur la base de son radar pour montrer des profils types d’architectes qui peuvent poser problème et en mettant en valeur ce qui peut manquer comme compĂ©tence dans chacun des cas.

Ce talk m’a intĂ©ressĂ© parce que j’interviens personnellement en tant qu’architecte sur des missions. Je me pose souvent la question de comment un architecte peut avoir un rĂ´le de facilitateur technique, sans tomber dans l’image du bullshitect (merci Ă  Alexandre Victoor pour le jeu de mot ^^) spĂ©cialiste des slides. Il y avait dans ce talk une vision de ce rĂ´le intĂ©ressante Ă  creuser et Ă  dĂ©velopper.

Readable in a New Language (Laura Savino)

Laura Savino est développeur iOS. Elle nous a proposé dans cette keynote de faire un parallèle entre sa découverte de Swift, le langage développé par Apple, et la découverte de langues étrangères ; Laura ayant enseigné des langues étrangères.

Le langage informatique, tout comme le langage naturel, vient avec un certain nombre de nuances, de subtilitĂ©s et de stĂ©rĂ©otypes. Pour l’un, il y a l’accent et la culture locale qui influent sur la langue de base. Pour l’autre, il y a l’adoption de patterns et de frameworks qui dĂ©pendent de la communautĂ© frĂ©quentĂ©e. La traduction directe peut se faire, mais elle donnera des rĂ©sultats inattendus : la simple traduction mot Ă  mot de “thank you” peut se transformer de manière inattendue en l’Ă©quivalent “Whoaaaaa (gracefully)!” dans d’autres langues. De mĂŞme, dans les langages informatiques, la simple tentative de reproduire les patterns habituels d’un langage dans un autre montrera surtout que vous manquez d’expĂ©rience dans le langage cible.

Suite Ă  ce constat, Laura pose la question de ce qu’est le code lisible ? La notion de lisibilitĂ© dĂ©pend de celui qui lit. Car si une chose est sĂ»re, c’est que nous ne sommes absolument pas d’accord sur ce qu’est un code lisible ; et il est faux de croire que les commentaires aident. Le principe Ă©tant que la lecture du code ne doit pas se transformer en sĂ©ance de dĂ©cryptage, qui implique de rester d’autant plus concentrĂ©, d’avoir suffisamment d’Ă©nergie et de s’isoler. Un code lisible doit ĂŞtre un code que vous ĂŞtes capable de lire rapidement et correctement. Ainsi lorsque vous Ă©crivez du code, vous devez penser Ă  minimiser l’effort que fourniront vos collègues lorsqu’ils vous reliront.

Comment produire un code lisible selon Laura ?

  • Éviter certains idiomes, tout comme vous Ă©viteriez l’argot avec des personnes qui n’ont pas votre culture. Par exemple, tout le monde ne comprend pas cette phrase d’argot : “Il se vissa le bada sur la calebasse et mit les adjas sans moufter…”, qui veut dire “Il mit son chapeau sur sa tĂŞte et partit sans dire un mot…”.
  • N’hĂ©sitez pas Ă  introduire des variables pour clarifier le contexte, par exemple dans le cas de one-lines (i.e. le fait d’Ă©crire un traitement avec un chaĂ®nage de mĂ©thodes). Comparez
Map<String, Integer> occurrences = Files.lines(Paths.get(filename))
    .collect(Stream.collector())
    .flatMap(line -> separators.splitAsStream(line).collect(Stream.collector()))
    .map(String::toLowerCase)
    .groupBy(word -> word)
    .mapValues(Stream::size)

Et

Stream<String> lines =
    Files.lines(Paths.get(filename)).collect(Stream.collector())
Stream<String> words =
    lines.flatMap(line -> separators.splitAsStream(line).collect(Stream.collector())).map(String::toLowerCase)
Map<String, Integer> occurrences =
    words.groupBy(word -> word).mapValues(Stream::size)
  • Introduisez les “rĂ©gionalismes” en douceur.
  • Utiliser la rĂ©pĂ©tition de motifs visuels : a priori, des fragments de code qui se ressemblent ont une signification proche.

Quelques pistes pour tester que votre code est lisible.

  • Squint test (plisser les yeux) : prenez du recul, plissez les yeux et regardez globalement ce que donne votre code, en dĂ©tectant des motifs en terme de forme globale.
  • Mettez en correspondance votre code avec des anti-patterns connus. Par exemple : l’action Ă  distance, yo-yo problem, object orgy… Une liste assez complète se trouve sur WikiWikiWeb.

Le principe est avant tout que vous soyez prévisible lorsque vous écrivez du code.

Optimised for what? (Alberto Brandolini)

Dans ce talk, Alberto revient, non sans humour, sur les problèmes rencontrĂ©s en dĂ©veloppement logiciel et sur les potentiels points d’amĂ©lioration, en analysant les mĂ©thodes Waterfall d’une part, ainsi que la mĂ©thode agile et ses diverses interprĂ©tations.

La mĂ©thode Waterfall part bien entendu d’une bonne intention, mais se transforme en une mĂ©thode optimisĂ©e pour repousser les mauvaises nouvelles. Quant Ă  l’agilitĂ©, tout du moins la perception qui ressort sur beaucoup de projets, elle semble ĂŞtre optimisĂ©e pour la prĂ©dictibilitĂ© et rĂ©duite Ă  cet aspect dans la plupart des projets.

Dans les projets, Alberto fait remarquer que le principal goulet d’Ă©tranglement est l’apprentissage. Ici apprentissage est Ă  prendre au sens large : Alberto parle aussi bien du fait d’acquĂ©rir une nouvelle pratique que de comprendre un projet et son contexte. Malheureusement, l’apprentissage n’est pas livrable, n’est pas visible et est fragile (nous ne sommes pas payĂ©s pour apprendre). Mais l’apprentissage est un investissement et c’est ce qui nous fait avancer. D’une manière ou d’une autre, l’apprentissage est inĂ©vitable. Et le logiciel fonctionnel en est l’effet de bord.

LĂ -dessus Alberto nous fait remarquer les points suivants :

  • L’ennui est le pire ennemi de l’apprentissage. Mais l’inverse est vrai aussi : l’apprentissage est le pire ennemi de l’ennui.
  • L’apprentissage nĂ©cessite de faire des erreurs. Il faut donc mettre en place un environnement oĂą l’Ă©chec est permis pour le pratiquer.
  • Le plus souvent, les nouvelles idĂ©es apparaissent de manière inattendue et en dehors du travail. C’est pourquoi il est important d’avoir un jour spĂ©cial libre et dĂ©diĂ© Ă  l’apprentissage.
  • RĂ©inventer la roue est une nĂ©cessitĂ© (mais pas en prod, bien sĂ»r). Cela permet de mieux comprendre et maĂ®triser les mĂ©canismes de ce qui est mis en place ou de ce qu’on souhaite mettre en place.

Lorsque nous travaillons sur un projet, nous avons besoin de sentir que nous avons bien travaillĂ©. Pour cela, il faut un objectif. Et la meilleure façon d’y arriver est de rencontrer l’utilisateur de l’application que nous construisons. Cela aussi fait parti de l’apprentissage !

Conclusions

NCrafts c’est aussi des talks, des workshops et des open spaces. Il y a aussi une journĂ©e de formation la veille de l’Ă©vĂ©nement et une soirĂ©e ouverte Ă  tous les participants. En Ă -cĂ´tĂ©, on peut apprĂ©cier la qualitĂ© des repas en self-service, loin du sandwich que nous propose certains services ferroviaires.

NCrafts 2017 fut une session oĂą dans l’ensemble je me suis rĂ©galĂ© et oĂą surtout j’ai appris. Je me donne rendez-vous l’annĂ©e prochaine les 17 et 18 mai 2018 pour la nouvelle session (les sessions suivantes sont effectivement annoncĂ©es un an Ă  l’avance). Entre temps, une session NCrafts aura lieu le 7 dĂ©cembre 2017 Ă  Bordeaux.

Les différentes vidéos ainsi que celles des années précédentes sont disponibles sur le site : http://videos.ncrafts.io/.

L’article Retour sur #NCrafts 2017 est apparu en premier sur Le Blog d'Ippon Technologies.

Catégories: Blog Société

CSS : .container, la classe Ă  abattre

Il n’y pas si longtemps, un comparse dĂ©veloppeur m’a interrogĂ© sur l’utilitĂ© d’une classe html/css*1 prĂ©sente dans ma codebase :

<div class="container">
...
</div>

Un bureau d’investigation de haute volĂ©e (composĂ© de moi-mĂŞme) dĂ©buta alors une folle enquĂŞte sur ce mystĂ©rieux .container, son parcours et ses motivations …

.container, qui est-il ? D’oĂą vient-il ?

Quand j’ai commencĂ© Ă  faire de l’intĂ©gration html/css, il y avait cette classe, container, un peu partout. En gĂ©nĂ©ral, elle englobait tout le contenu du site, de cette manière :

<html>
    <head><!-- ... --></head>
    <body>
        <div class="container">
            <!-- Et lĂ , bim, bam, boum, on met tout son contenu !!! -->
        </div>
    </body>
</html>

Pour faire simple, elle centrait le contenu de la page et c’était visiblement une pratique normée dans le monde du css, car tout le monde utilisait cette classe.

Et selon les projets, on pouvait trouver des variantes, comme les classes wrapper, section*2, ou encore l’id content, et parfois, une combinaison de tout ça, en gĂ©nĂ©ral avec un nesting, soyons francs, assez alĂ©atoire, mais ça ressemblait grosso modo Ă  cette satanĂ©e maquette qu’il nous fallait intĂ©grer, alors tout allait bien.

.container, oĂą est-il ? Que fait-il ?

Aujourd’hui, si on google rapidement « css container », on tombe sur diffĂ©rents frameworks css qui intègrent une classe container. Bien que je ne conseille pas l’utilisation des frameworks CSS, Ă  moins de n’avoir aucune maquette Ă  respecter, ils restent de bons moyens pour jauger l’Ă©tat de l’art en CSS. Regardons donc un peu plus en dĂ©tails les implĂ©mentations de .container mises en place dans certains frameworks*3 :

  • Bootstrap : http://getbootstrap.com/css/#overview-container
    .container {
        /* On ajoute une marge sur les cotés de l'écran */
        padding-right: 15px;
        padding-left: 15px;
    
        /* Et on centre */
        margin-right: auto;
        margin-left: auto;
    }
    
    /* Sur les grands Ă©crans, on limite la largeur du contenu */
    @media (min-width: 1200px){
        .container {
            width: 1170px;
        }
    }
    
  • Materialize : http://materializecss.com/grid.html
    .container {
        /* On centre */
        margin: 0 auto;
    
        /* On limite la largeur pour les grands Ă©crans */
        max-width: 1280px;
    
        /* On gère la présence d'une marge */
        width: 90%;
    }
    
    /*
    * Comme la dite marge est exprimée en pourcentage,
    * on l'augmente sur les devices moins larges.
    */
    @media only screen and (min-width: 993px){
        .container {
            width: 85%;
        }
    }
    
  • Skeleton : http://getskeleton.com/
    .container {
        /* On limite la largeur */
        width: 100%;
        max-width: 960px;
    
        /* On centre */
        margin: 0 auto;
    
        /* Et on ajoute une marge (sur les petits Ă©crans seulement) */
        padding: 0 20px;
    
        /*
        * Le "box-sizing: border-box;" permet de ne pas impacter la width avec le padding
        * La largeur max réelle du contenu est donc de 920px =&gt; 960 - (2 x 20)
        */
        box-sizing: border-box;
    }
    
    /* Sur les écrans plus larges, la marge est gérée en pourcentage */
    @media (min-width: 400px) {
        .container {
            width: 85%;
            padding: 0;
        }
    }
    
  • W3.CSS : https://www.w3schools.com/w3css/w3css_containers.asp
    /*
    * W3.CSS se rapproche plus de la charte graphique de w3 school,
    * là où les précédents frameworks se veulent plus agnostiques
    */
    
    /* Ici, le container sert simplement à gérer un padding */
    .w3-container {
        padding: 0.01em 16px;
    }
    

Hormis pour ce dernier exemple, on distingue donc deux utilités à notre classe container, centrer le contenu et y ajouter une marge horizontale.

 .container, pourquoi fait-il ça ?

Pour que votre contenu reste lisible. Explications. Une page web est composĂ©e d’Ă©lĂ©ments qui peuvent ĂŞtre :

  • Textuels (ou images liĂ©es) et/ou interactifs ;
  • DĂ©coratifs (image de background, bandeau de couleur, etc.).

Si pour ces derniers, on peut souhaiter qu’ils s’Ă©tendent sur toute la largeur de l’Ă©cran, pour le contenu texte/interactif, il est important d’avoir :

  • Une marge de confort afin d’Ă©viter que votre contenu ne soit collĂ© aux bords de l’Ă©cran (notamment sur les devices mobiles). Cela permet de conserver une lisibilitĂ© de votre texte. En gĂ©nĂ©ral, cette marge se situe entre 15 et 20 pixels ;
  • Une largeur utile, qui correspond Ă  la largeur d’Ă©cran au delĂ  de laquelle il devient inutile (voir contre-productif) d’Ă©taler son contenu sur toute la largeur disponible, au risque de sortir du champ de vision de l’internaute des Ă©lĂ©ments qu’il est censĂ© voir. Souvent, cette largeur utile est centrĂ©e.

Du coup la classe container répond effectivement à ces deux besoins. Toutefois, elle peut parfaitement être remplacée, et par quelque chose de mieux qui plus est !

.container, pourquoi doit-il disparaître ?

Car il manque d’expressivitĂ©. SĂ©mantiquement parlant, le terme container apporte Ă  peu prĂ©s autant d’informations qu’une classe foobar. Quasiment tous les tags HTML sont destinĂ©s Ă  contenir quelque chose.

Étant donnĂ© qu’on sait maintenant ce que faisait .container, et pourquoi il le faisait, on peut sereinement le remplacer par deux classes :

  • .margin-constraint afin de gĂ©rer notre marge de confort ;
  • .useful-width afin de gĂ©rer notre largeur utile.
Une implémentation CSS ?

Partons sur une largeur utile de 800px et des marges de confort de 20px. Cela nous donne :

<!DOCTYPE html>
<html>
<head></head>
<body>
    <div class="full-width">
    Ici, on aura des éléments purement décoratifs qui peuvent s'étaler sur toute la largeur de l'écran (une image, ou encore un bandeau de
    couleur...)
 
        <div class="margin-constraint">
        Ici on peut par exemple placer des éléments qui doivent s'étaler sur toute la largeur,
        mais ne doivent pas être juste au bord de l'écran (certaines décorations, éventuellement certains éléments de menu...)
             
            <div class="useful-width">
            Notre contenu textuel ira généralement ici. Il restera sur une largeur agréable à la lecture
            et gardera un espace par rapport au bord de l'Ă©cran.
            </div>
        </div>
    </div>
</body>
</html>
.margin-constraint{
    /*On indique tout simplement une marge*/
    margin-left: 20px;
    margin-right: 20px;
}
 
.useful-width{
    /*On règle ensuite la largeur utile puis on centre*/
    max-width: 800px;
    margin-left: auto;
    margin-right: auto;
}
 
.margin-constraint, .useful-width{
    /*Si vous utilisez uniquement des div, cette dernière règle css n'est même pas nécessaire*/
    display: block;
    width: auto;
}

Au final, niveau css, on gagne en simplicité comparé aux implémentations de container précédemment évoquées :

  • Pas de media query;
  • On utilise des propriĂ©tĂ©s qui sont sĂ©mantiquement plus proches de ce que l’on souhaite rĂ©aliser ;
  • Si on travaille avec une maquette, on peut directement utiliser les valeurs dĂ©duites de cette dernière, plutĂ´t que de devoir faire un calcul du type (largeur utile – 2 * marge de confort) ou utiliser un pourcentage un peu approximatif (voir les exemples en dĂ©but d’article).

Le principal dĂ©savantage de cette technique est qu’elle nĂ©cessite l’utilisation de 2 balises html imbriquĂ©es (.margin-constraint > .useful-width). Mais avec une petite règle css en plus, vous pourrez Ă©galement utiliser les deux classes sur une mĂŞme balise. Voyez le rĂ©sultat ici : https://codepen.io/alexistessier/pen/XRLoxv.

Ă€ noter : Une autre approche courante consiste Ă  dĂ©finir une max-width puis Ă  appliquer un padding afin de gĂ©rer la marge. Je ne suis pas fan de cette façon de faire car elle implique de forcer le box-sizing (propriĂ©tĂ© assez obscure et globalement assez mal comprise) de l’Ă©lĂ©ment si on veut s’assurer que le padding soit ou non inclus dans la largeur finale de l’Ă©lĂ©ment. Autant que possible, je prĂ©fère utiliser des propriĂ©tĂ©s simples Ă  apprĂ©hender.

Pour conclure

Au delĂ  de vous exhorter Ă  Ă©liminer mĂ©thodiquement tout .container que vous pourriez trouver dans vos CSS, le but de cet article Ă©tait surtout de revenir Ă  des fondamentaux et d’essayer de se souvenir quelles problĂ©matiques rĂ©solvent les frameworks CSS lorsqu’ils mettent une classe tel que container Ă  notre disposition.

Tchao’

Notes de bas de page

  • *1 : En rĂ©alitĂ©, il s’agissait d’un helper en stylus, un prĂ©processeur css qu’il est bien pour styler des choses.
  • *2 : Mais ça c’Ă©tait avant le tag section de HTML5.
  • *3 : Les css prĂ©sentĂ©es dans cet article sont des versions simplifiĂ©es de ce que vous pourrez trouver si vous allez voir vous mĂŞme dans le code source des frameworks css concernĂ©s.
Catégories: Blog Société

Social Media Day: tips for professionals in IT

Le blog de Valiantys - ven, 06/30/2017 - 14:19

The social media phenomenon is a relatively young movement. LinkedIn was launched in 2003 and Twitter makes its debut in 2006, yet within that time these platforms have revolutionised the way people connect and communicate. But is social media relevant to being a professional in the IT community? Does it provide added-value to your career, or ...

The post Social Media Day: tips for professionals in IT appeared first on Valiantys - Atlassian Platinum Partner.

Catégories: Blog Société

AgileFrance 2017, une pause dans le rythme parisien

Blog d’Ippon Technologies - ven, 06/30/2017 - 10:40

Comme chaque annĂ©e, AgileFrance nous permet de faire une pause dans le rythme effrĂ©nĂ© pour s’interroger sur les valeurs et pratiques Agile. Une introspection indispensable pour rester fidèle aux valeurs que nous prĂ´nons tous les jours.

Nous avons pu participer Ă  l’Ă©vĂ©nement cette annĂ©e et assister Ă  de très belles confĂ©rences.

Demander le programme

Les annĂ©es prĂ©cĂ©dentes, les organisateurs nous avaient habituĂ©s Ă  des confĂ©rences plutĂ´t farfelues intitulĂ©s De l’Ă©tat gazeux Ă  l’état plateforme, RĂ©aliser une bonne recette au concombre ou encore Dessinez une tartine. Cette annĂ©e m’a semblĂ© plus sage, du moins dans sa prĂ©sentation, tout en rĂ©servant d’excellente surprise dans le contenu. Ă€ l’exception bien entendu de la session “Ce que les Papous peuvent nous apprendre sur les transformations agiles”.

Cette année, Ippon animait 3 sessions :

Ça a Ă©tĂ© un plaisir de prĂ©senter ces sujets et d’Ă©changer sourires et sous-rires (monnaie de remerciement locale) en approfondissant avec les participants.

Nous regrettons juste de ne pas pouvoir revoir en vidéo les sessions qui passaient en parallèle.

Forum ForAll

Comme les années précédentes, nous avons pu également participer à un grand forum ouvert, durant lequel chaque participant peut proposer son sujet et faire émerger les discussions.

Le temps nous ayant en plus gâté cette année, nous avons pu improviser des échanges passionnés en profitant du soleil.

Les sujets proposés étaient variés. Nous avons opté pour le totem du papillon en allant flâner à plusieurs endroits, pour regarder et écouter attentivement.

Un vrai moment de recul et d’Ă©changes. C’est apprĂ©ciable !

Ă€ emporter

Nous repartons avec un ensemble de sujets Ă  creuser.

Si nous devions en citer deux :

  • Le coaching systĂ©mique (et sa mise en pratique concrète dans l’accompagnement des Ă©quipes), brillamment prĂ©sentĂ© par Arnaud Gervais. Il a su prĂ©senter simplement son utilisation concrète du coaching systĂ©mique pour reprĂ©senter des problèmes classiques.
  • Le refactoring de spĂ©cifications mis en lumière par Cyrile Martraire qui permet de remonter les pratiques de refactoring au niveau d’une logique mĂ©tier et en identifiant des modèles cohĂ©rents.

Nous repartons Ă©galement avec l’envie de refaire des prĂ©sentations et mettre Ă  dispositions nos atelier en Open Source, en attendant la prochaine fois…

L’article AgileFrance 2017, une pause dans le rythme parisien est apparu en premier sur Le Blog d'Ippon Technologies.

Catégories: Blog Société

Partagez la connaissance

Partagez BlogsdeDeveloppeurs.com sur les réseaux sociaux