Slide 1

Slide 1 text

Livraison continue, simplement Johan Martinsson www.changit.fr @johan_alps

Slide 2

Slide 2 text

Pourquoi donc?

Slide 3

Slide 3 text

Livraison continue Satisfaction des salariés Augmentation de valorisation Entreprise

Slide 4

Slide 4 text

Permet Lean Product Development Ou Agile pour de vrai 
 :)

Slide 5

Slide 5 text

Intégration continue : trunk based dev Livraison continue : déploiement auto Dev/QA Déploiement continue : déploiement en Prod

Slide 6

Slide 6 text

Les parties faciles mais souvent negligées • Trunk based development • Parallell change - supporter deux versions • Feature Flags & Dark Launch • Branch by abstraction • Déploiement indépendant sans coordination

Slide 7

Slide 7 text

Parallell change - supporter deux versions export function doSomething(arg1, arg2) { } // new method with slightly modified behavior // no need for synchronising with changes to existing usages export function doSomethingV2(arg1, arg2, arg3) { // duplicated logic and tests (or not) } app.get('/doSomething', doSomething) // publish a new route with a new name app.get('/doSomethingV2', doSomethingV2) Modification non compatible? 
 => créer nouvelle méthode temporairement Et pourquoi pas au niveau public ici REST Quand plus personne utilise l’ancienne version, supprimer

Slide 8

Slide 8 text

Feature flag class SomeService { // inject the flag on boot constructor(private newFeature) {} youNameIt(arg1) { // logic... if (this.newFeature) { // new code } else { // possibly there's some existing behavior } // logic... } }

Slide 9

Slide 9 text

Feature flag Mais si on a 10 ifs pour une feature? => Branch by abstraction

Slide 10

Slide 10 text

Branch By Abstraction Ex changement de fournisseur d’envoi email // usage class WelcomeEmailer { sendWelcomeEmail(email, name) { checkEmailNotSentAlreadyTo(email) // specific to current lib let emailContent = "to: " + email + "\n\n" + "Hi " + name + "\n\n" + "welcome to our new platform!" // specific to current lib send(emailContent) logger.log("email sent to " + email) } } // existing lib export function send(emailContent: string) { } Déplacer le code spécifique vers un nouvel objet EmailAdapter // new lib export function sendEmail(to, from, content: string) { }

Slide 11

Slide 11 text

Branch By Abstraction class WelcomeEmailerUsingAbstraction { constructor(private emailAdapter) { } sendWelcomeEmail(email) { checkEmailNotSentAlreadyTo(email) let content = "Hi " + name + "\n" + "\n" + "welcome to our new platform!" this.emailAdapter.send(email, content) logger.log("email sent to " + email) } } Injecter la strategie d’EmailAdapter voulue

Slide 12

Slide 12 text

Feature flags with many ifs // basic approach requiring many ifs class SomethingMoreComplex { constructor(private newFeature) {} youNameIt(arg1) { if (this.newFeature) { doNewThing_1() } // logic ... if (this.newFeature) { doNewThing_2() } // logic... } } // approach using branch by abstraction class SomethingMoreComplexInjection { // instead of injecting the feature flag, inject the strategy/command object constructor(private strategy: ICoolFeature) {} youNameIt(arg1) { this.strategy.doNewThing_1() // logic ... this.strategy.doNewThing_2() // logic... } } // booting the application : only one if necessary let coolFeature = newFeature? new CoolFeature() : new OldFeature() const toto = new SomethingMoreComplexInjection(coolFeature) Use branch by abstraction

Slide 13

Slide 13 text

Database change 1. Script de création de colonne + remplissage non bloquant 2. Nouveau code gérant les deux modes avec le feature flag OFF 3. Script de remplissage du delta bloquant + feature flag ON 4. Suppression ancien code + feature flag (+ colonne / table?) En plusieurs étapes …. Si vraiment on a besoin d’une haute dispo

Slide 14

Slide 14 text

En savoir plus • trunkbaseddevelopment.com