Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Livraison continue, simplement

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.
Avatar for martinsson martinsson
January 14, 2020

Livraison continue, simplement

Les astuces pour faire du trunk based development et de la livraison continue simplement.

Présenté à Human Talks Grenoble jan 2020

Avatar for martinsson

martinsson

January 14, 2020
Tweet

More Decks by martinsson

Other Decks in Programming

Transcript

  1. Intégration continue : trunk based dev Livraison continue : déploiement

    auto Dev/QA Déploiement continue : déploiement en Prod
  2. 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
  3. 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
  4. 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... } }
  5. Feature flag Mais si on a 10 ifs pour une

    feature? => Branch by abstraction
  6. 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) { }
  7. 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
  8. 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
  9. 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