Kenmerkvlaggen met veer

1. Overzicht

In dit artikel zullen we kort functievlaggen definiëren en een eigenzinnige en pragmatische aanpak voorstellen om ze in Spring Boot-applicaties te implementeren. Vervolgens gaan we in op meer geavanceerde iteraties die profiteren van verschillende Spring Boot-functies.

We bespreken verschillende scenario's waarvoor het markeren van functies mogelijk vereist is en bespreken mogelijke oplossingen. We zullen dit doen met behulp van een voorbeeldtoepassing van Bitcoin Miner.

2. Functievlaggen

Functievlaggen - ook wel functietoetsen genoemd - zijn een mechanisme waarmee we specifieke functionaliteit van onze applicatie kunnen in- of uitschakelen zonder de code te hoeven wijzigen of, idealiter, onze app opnieuw te implementeren.

Afhankelijk van de dynamiek die een bepaalde functievlag vereist, moeten we deze mogelijk globaal configureren, per app-instantie of gedetailleerder - misschien per gebruiker of verzoek.

Zoals met veel situaties in Software Engineering, is het belangrijk om te proberen de meest eenvoudige benadering te gebruiken die het probleem aanpakt zonder onnodige complexiteit toe te voegen.

Feature vlaggen zijn een krachtig hulpmiddel dat, mits verstandig gebruikt, betrouwbaarheid en stabiliteit aan ons systeem kan brengen. Wanneer ze echter worden misbruikt of onvoldoende worden onderhouden, kunnen ze snel bronnen van complexiteit en hoofdpijn worden.

Er zijn veel scenario's waarin functievlaggen van pas kunnen komen:

Trunk-gebaseerde ontwikkeling en niet-triviale functies

Bij trunk-gebaseerde ontwikkeling, vooral wanneer we regelmatig willen blijven integreren, is het mogelijk dat we niet klaar zijn om een ​​bepaald stuk functionaliteit vrij te geven. Feature vlaggen kunnen van pas komen om ons in staat te stellen te blijven uitgeven zonder onze wijzigingen beschikbaar te maken totdat ze voltooid zijn.

Omgevingsspecifieke configuratie

Mogelijk hebben we bepaalde functionaliteit nodig om onze database opnieuw in te stellen voor een E2E-testomgeving.

Als alternatief moeten we mogelijk een andere beveiligingsconfiguratie gebruiken voor niet-productieomgevingen dan die wordt gebruikt in de productieomgeving.

Daarom kunnen we profiteren van functievlaggen om de juiste instellingen in de juiste omgeving om te schakelen.

A / B-testen

Meerdere oplossingen voor hetzelfde probleem vrijgeven en de impact meten, is een overtuigende techniek die we zouden kunnen implementeren met behulp van kenmerkvlaggen.

Kanarie vrijgeven

Bij het implementeren van nieuwe functies kunnen we besluiten om dit geleidelijk te doen, te beginnen met een kleine groep gebruikers, en de acceptatie ervan uit te breiden naarmate we de juistheid van het gedrag ervan valideren. Feature vlaggen stellen ons in staat dit te bereiken.

In de volgende secties proberen we een praktische benadering te bieden om de bovengenoemde scenario's aan te pakken.

Laten we verschillende strategieën voor het markeren van functies opsplitsen, te beginnen met het eenvoudigste scenario om vervolgens over te gaan naar een meer gedetailleerde en complexere configuratie.

3. Functievlaggen op toepassingsniveau

Als we een van de eerste twee use-cases moeten aanpakken, zijn functievlaggen op toepassingsniveau een eenvoudige manier om dingen werkend te krijgen.

Een eenvoudige kenmerkvlag zou doorgaans betrekking hebben op een eigenschap en een bepaalde configuratie op basis van de waarde van die eigenschap.

3.1. Functievlaggen met behulp van veerprofielen

In het voorjaar kunnen we profiteren van profielen. Handig is dat profielen ons in staat stellen om bepaalde bonen selectief te configureren. Met een paar constructies eromheen kunnen we snel een eenvoudige en elegante oplossing creëren voor functievlaggen op toepassingsniveau.

Laten we doen alsof we een BitCoin-mijnsysteem bouwen. Onze software is al in productie en we hebben de taak om een ​​experimenteel, verbeterd mijnalgoritme te maken.

In onze JavaConfig we konden onze componenten profileren:

@Configuration openbare klasse ProfiledMiningConfig {@Bean @Profile ("! Experimentele-mijnwerker") openbare BitcoinMiner defaultMiner () {retourneer nieuwe DefaultBitcoinMiner (); } @Bean @Profile ("experimentele-mijnwerker") openbare BitcoinMiner experimentalMiner () {retourneer nieuwe ExperimentalBitcoinMiner (); }}

Dan, met de vorige configuratie hoeven we alleen ons profiel op te nemen om in te schrijven voor onze nieuwe functionaliteit. Er zijn talloze manieren om onze app in het algemeen te configureren en profielen in het bijzonder in te schakelen. Evenzo zijn er testprogramma's om ons leven gemakkelijker te maken.

Zolang ons systeem eenvoudig genoeg is, we zouden dan een omgevingsgebaseerde configuratie kunnen maken om te bepalen welke kenmerkvlaggen moeten worden toegepast en welke moeten worden genegeerd.

Laten we ons voorstellen dat we een nieuwe gebruikersinterface hebben op basis van kaarten in plaats van tabellen, samen met de vorige experimentele mijnwerker.

We willen beide functies inschakelen in onze acceptatieomgeving (UAT). We zouden een application-uat.yml het dossier:

spring: profiles: include: experimental-miner, ui-cards # Meer configuratie hier

Met het vorige bestand op zijn plaats, hoeven we alleen het UAT-profiel in de UAT-omgeving in te schakelen om de gewenste set functies te krijgen.

Het is ook belangrijk om te begrijpen hoe u hiervan kunt profiteren spring.profiles.include. In vergelijking tot spring.profiles.active, het eerste stelt ons in staat om profielen op een additieve manier op te nemen.

In ons geval willen we de uat profiel om ook experimentele-mijnwerker en ui-kaarten op te nemen.

3.2. Functievlaggen die aangepaste eigenschappen gebruiken

Profielen zijn een geweldige en eenvoudige manier om de klus te klaren. Het is echter mogelijk dat we profielen nodig hebben voor andere doeleinden. Of misschien willen we een meer gestructureerde vlaginfrastructuur bouwen.

Voor deze scenario's kunnen aangepaste eigenschappen een wenselijke optie zijn.

Laten we ons vorige voorbeeld herschrijven door gebruik te maken van @ConditionalOnProperty en onze naamruimte:

@Configuration openbare klasse CustomPropsMiningConfig {@Bean @ConditionalOnProperty (name = "features.miner.experimental", matchIfMissing = true) openbare BitcoinMiner defaultMiner () {retourneer nieuwe DefaultBitcoinMiner (); } @Bean @ConditionalOnProperty (name = "features.miner.experimental") openbare BitcoinMiner experimentalMiner () {retourneer nieuwe ExperimentalBitcoinMiner (); }}

Het vorige voorbeeld bouwt voort op de voorwaardelijke configuratie van Spring Boot en configureert de ene of de andere component, afhankelijk van of de eigenschap is ingesteld op waar of false (of helemaal weggelaten).

Het resultaat lijkt erg op dat in 3.1, maar nu hebben we onze naamruimte. Met onze naamruimte kunnen we betekenisvolle YAML / eigenschappen-bestanden maken:

# [...] Enkele Spring config features: miner: experimenteel: true ui: cards: true # [...] Andere feature vlaggen

Deze nieuwe setup stelt ons ook in staat om onze functievlaggen te prefixen - in ons geval met behulp van de Kenmerken voorvoegsel.

Het lijkt misschien een klein detail, maar naarmate onze applicatie groeit en de complexiteit toeneemt, zal deze eenvoudige iteratie ons helpen onze functievlaggen onder controle te houden.

Laten we het hebben over andere voordelen van deze aanpak.

3.3. Gebruik makend van @Configuratie-eigenschappen

Zodra we een vooraf ingestelde set eigenschappen krijgen, kunnen we een POJO maken die is versierd met @ConfigurationProperties om een ​​programmatische handle in onze code te krijgen.

Ons doorlopende voorbeeld volgen:

@Component @ConfigurationProperties (prefix = "features") publieke klasse ConfigProperties {privé MinerProperties mijnwerker; privé UIProperties ui; // standaard getters en setters openbare statische klasse MinerProperties {private boolean experimenteel; // standaard getters en setters} openbare statische klasse UIProperties {privé booleaanse kaarten; // standaard getters en setters}}

Door de status van onze functievlaggen in een samenhangende eenheid te plaatsen, openen we nieuwe mogelijkheden, waardoor we die informatie gemakkelijk kunnen tonen aan andere delen van ons systeem, zoals de gebruikersinterface, of aan stroomafwaartse systemen.

3.4. Functieconfiguratie blootleggen

Ons Bitcoin-mijnsysteem heeft een UI-upgrade gekregen die nog niet helemaal klaar is. Om die reden hebben we besloten om het te markeren. We hebben mogelijk een app met één pagina die React, Angular of Vue gebruikt.

Ongeacht de technologie, we moeten weten welke functies zijn ingeschakeld, zodat we onze pagina dienovereenkomstig kunnen weergeven.

Laten we een eenvoudig eindpunt maken voor onze configuratie, zodat onze gebruikersinterface de backend kan opvragen wanneer dat nodig is:

@RestController openbare klasse FeaturesConfigController {privé ConfigProperties-eigenschappen; // constructor @GetMapping ("/ feature-flags") openbare ConfigProperties getProperties () {retourneer eigenschappen; }}

Er zijn mogelijk meer geavanceerde manieren om deze informatie te verstrekken, zoals het maken van aangepaste actuator-eindpunten. Maar omwille van deze handleiding voelt een controller-eindpunt als een goede oplossing.

3.5. Het kamp schoon houden

Hoewel het misschien voor de hand liggend klinkt, is het, nadat we onze functievlaggen zorgvuldig hebben geïmplementeerd, net zo belangrijk om gedisciplineerd te blijven bij het verwijderen ervan als ze niet langer nodig zijn.

Feature vlaggen voor de eerste use case - trunk-gebaseerde ontwikkeling en niet-triviale features - zijn doorgaans van korte duur. Dit betekent dat we ervoor moeten zorgen dat onze ConfigProperties, onze Java-configuratie en onze YAML bestanden blijven schoon en up-to-date.

4. Meer gedetailleerde functievlaggen

Soms bevinden we ons in complexere scenario's. Voor A / B-testen of kanarie-releases is onze eerdere aanpak gewoon niet voldoende.

Om functievlaggen op een gedetailleerder niveau te krijgen, moeten we mogelijk onze oplossing maken. Dit kan het aanpassen van onze gebruikersentiteit betekenen om feature-specifieke informatie op te nemen, of misschien om ons webframework uit te breiden.

Het vervuilen van onze gebruikers met functievlaggen is misschien niet voor iedereen een aantrekkelijk idee, en er zijn andere oplossingen.

Als alternatief zouden we kunnen profiteren van enkele ingebouwde tools zoals Togglz. Deze tool voegt wat complexiteit toe, maar biedt een mooie kant-en-klare oplossing en biedt eersteklas integratie met Spring Boot.

Togglz ondersteunt verschillende activeringsstrategieën:

  1. Gebruikersnaam: Vlaggen die aan specifieke gebruikers zijn gekoppeld
  2. Geleidelijke uitrol: Vlaggen ingeschakeld voor een percentage van het gebruikersbestand. Dit is handig voor releases op de Canarische Eilanden, bijvoorbeeld wanneer we het gedrag van onze functies willen valideren
  3. Publicatiedatum: We zouden kunnen plannen dat vlaggen op een bepaalde datum en tijd worden ingeschakeld. Dit kan handig zijn voor een productlancering, een gecoördineerde release of aanbiedingen en kortingen
  4. Cliënt IP: Gemarkeerde functies op basis van IP-adressen van klanten. Deze kunnen van pas komen bij het toepassen van de specifieke configuratie op specifieke klanten, aangezien deze statische IP-adressen hebben
  5. Server IP: In dit geval wordt het IP-adres van de server gebruikt om te bepalen of een functie moet worden ingeschakeld of niet. Dit kan ook handig zijn voor kanarie-releases, met een iets andere aanpak dan de geleidelijke uitrol, zoals wanneer we de prestatie-impact in onze instanties willen beoordelen
  6. Script Engine: We kunnen kenmerkvlaggen inschakelen op basis van willekeurige scripts. Dit is misschien wel de meest flexibele optie
  7. Systeem eigenschappen: We zouden bepaalde systeemeigenschappen kunnen instellen om de status van een kenmerkvlag te bepalen. Dit zou vergelijkbaar zijn met wat we hebben bereikt met onze meest eenvoudige aanpak

5. Samenvatting

In dit artikel hebben we de kans gehad om over functievlaggen te praten. Daarnaast hebben we besproken hoe Spring ons kan helpen om een ​​deel van deze functionaliteit te realiseren zonder nieuwe bibliotheken toe te voegen.

We zijn begonnen met te definiëren hoe dit patroon ons kan helpen met een paar veelvoorkomende gebruiksscenario's.

Vervolgens hebben we een paar eenvoudige oplossingen gebouwd met behulp van Spring en Spring Boot kant-en-klare tools. Daarmee kwamen we met een eenvoudige maar krachtige functie-markeringsconstructie.

Hieronder hebben we een aantal alternatieven vergeleken. Overstappen van de eenvoudigere en minder flexibele oplossing naar een meer geavanceerd, hoewel ingewikkelder patroon.

Ten slotte hebben we in het kort enkele richtlijnen gegeven om robuustere oplossingen te bouwen. Dit is handig wanneer we een hogere mate van granulariteit nodig hebben.