PageSourceSearch

https://bd90.pl/component---src-pages-mdx-frontmatter-slug-tsx-con…e-toggles-index-mdx-5c6929707330ab18f914.js

js bd90.pl collected 2026-10-07 02:47:42 UTC 21,761 bytes, 2 lines download raw bytes

1"use strict";(self.webpackChunkbd90=self.webpackChunkbd90||[]).push([[6011],{3139:function(e,a,n){n.r(a),n.d(a,{Head:function(){return E},default:function(){return x}});var t=n(1151),i=n(7294);function o(e){const a=Object.assign({p:"p",code:"code",h2:"h2",div:"div",pre:"pre",h3:"h3"},(0,t.ah)(),e.components);return i.createElement(i.Fragment,null,i.createElement(a.p,null,"Pierwszy raz kiedy spotkałem się z 'flagami funkcjonalności'... Powiem szczerze - wyglądało to jak czarna magia. Jednoczesna możliwość dynamicznego zmieniania zachowań systemu napawała mnie obawą o zawodność rozwiązania. Przecież nie wiadomo, co się wydarzy kiedy konfiguracja funkcjonalności nie zostanie zaciągnięta. Istnieje możliwość przypadkowej zmiany flagi, co wpłynie na użyteczność systemu. Jak widać wiele może zawieść.  Warto jednak zaryzykować, ponieważ korzyści jakie dają ",i.createElement(a.code,null,"Feature Toggles")," są ogromne.  Przede wszystkim możliwość rozdzielenia deploy-u od wdrożenia, testy A/B a nawet testy na produkcji dla ograniczonej ilości użytkowników."),"\n",i.createElement(a.p,null,"Ucieszyłem się, kiedy zobaczyłem, że Gitlab udostępnił możliwość zarządzania flagami funkcjonalności dla wszystkich kont (konta free obecnie mają ograniczenia do 50 flag na projekt). Natchniony odkryciem postanowiłem w dzisiejszym artykule przybliżyć koncept ",i.createElement(a.code,null,"Feature Toggle")," , a także pokazać jak w kilku prosty krokach skonfigurować i zintegrować naszą aplikację z API wystawionym przez serwis Gitlab."),"\n",i.createElement(a.h2,null,"Feature Toggles (Feature Flag)"),"\n",i.createElement(a.div,{dangerouslySetInnerHTML:{__html:'<span\n      class="gatsby-resp-image-wrapper"\n      style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 500px; "\n    >\n      <a\n    class="gatsby-resp-image-link"\n    href="/static/6166b20f473da99cc851924f9129e716/0b533/feature_flags.png"\n    style="display: block"\n    target="_blank"\n    rel="noopener"\n  >\n    <span\n    class="gatsby-resp-image-background-image"\n    style="padding-bottom: 40%; position: relative; bottom: 0; left: 0; background-image: url(\'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAICAIAAAB2/0i6AAAACXBIWXMAAAsTAAALEwEAmpwYAAABFUlEQVR42l1O20rDQBTs5yr46I+I3+GbFIvFV8ELKl6earFgbLpJsyG72T27ey7SpJqSYTgMnDNnZiIixB1lRxZpk+goVZAq8FNFJkkkqaNsgziU3Mu7lh6TbjIn4OQpOhH6bOSukHkm0wxPHtzGkQK538rsR161XKz47INN+jPPv/Doyp/ehOMZnD9G6fJ7JB50D4u7XkPyIveXz9X1m56+VLeLBhFDiN5DJ4JpGu7e9V8CgLOG/81NG78Luy7brLBF7WOM3ntjTC+UUofJWuuyLIlob65tXOZ2tWmXuV1v/eEp86i1EFGMcajNzAn3JBIAsNbWdR1CaNs2y7JRslJqSB6BmYkIEZkZEQHgcAsAzrm+0S873MyxL7sYgAAAAABJRU5ErkJggg==\'); background-size: cover; display: block;"\n  ></span>\n  <img\n        class="gatsby-resp-image-image"\n        alt="feature flags"\n        title="feature flags"\n        src="/static/6166b20f473da99cc851924f9129e716/0b533/feature_flags.png"\n        srcset="/static/6166b20f473da99cc851924f9129e716/6b2ea/feature_flags.png 275w,\n/static/6166b20f473da99cc851924f9129e716/0b533/feature_flags.png 500w"\n        sizes="(max-width: 500px) 100v
1w, 500px"\n        style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"\n        loading="lazy"\n        decoding="async"\n      />\n  </a>\n    </span>'}}),"\n",i.createElement(a.p,null,i.createElement(a.code,null,"Feature Toggles"),", jak pisze Pete Hodgson w swoim artykule na stronie Martin-a Fowler-a, to zestaw pattern-ów pomagających zespołom IT w dostarczaniu nowych funkcjonalności, w szybki i bezpieczny sposób. Nazewnictwo czasem się różni, napotkałem terminy jak: ",i.createElement(a.code,null,"Feature Flags"),", ",i.createElement(a.code,null,"Feature Bits"),", ",i.createElement(a.code,null,"Feature Flipers"),". W bardzo dużym uproszczeniu jest to po prostu ",i.createElement(a.code,null,"if")," w kodzie, który uruchamia funkcjonalność albo nie. Moglibyśmy to zachowanie zaprezentować w pseudokodzie w sposób następujący:"),"\n",i.createElement(a.pre,null,i.createElement(a.code,{className:"language-csharp"},"if (funcionality.IsOn())\n   serve.NewFuncionality();\nelse\n   serve.OldFuncionality();\n")),"\n",i.createElement(a.p,null,"Oczywiście to tylko najprostszy przypadek użycia. ",i.createElement(a.code,null,"Feature Flags")," możemy podzielić na 4 rodzaje w zależności od strategii użycia."),"\n",i.createElement(a.h3,null,"Release Toggles"),"\n",i.createElement(a.p,null,"Są to przełączniki pozwalające na dogrywanie jeszcze nie dokończonej implementacji do naszego głównego branch-a. Pozwala to nawet na wgranie nie ukończonej implementacji na produkcję. Dopóki ktoś w panelu administracyjnym nie zmieni stanu przełącznika, kod nigdy nie powinien się uruchomić. Taki rodzaj Toggles jest bardzo często spotykany przy tzw. ",i.createElement(a.code,null,"Trunk-based development")," w zespołach, które kładą bardzo duży nacisk na ",i.createElement(a.code,null,"Continuous Delivery"),"."),"\n",i.createElement(a.h3,null,"Experiment Toggles"),"\n",i.createElement(a.p,null,"Ten rodzaj przełączników pozwala na eksperymenty na użytkownikach aplikacji. Jest często wykorzystywany przy okazji testów A/B na prawdziwym użytkownikach. W ten sposób możemy zebrać dane potrzebne do optymalizacji, czy to w procesach, czy też w warstwie UI."),"\n",i.createElement(a.h3,null,"Ops Toggles"),"\n",i.createElement(a.p,null,'Ten rodzaj przełączników jest używany do kontrolowania operacyjnych aspektów oprogramowania. Zazwyczaj jest wprowadzany, kiedy chcemy dodać, do naszego systemu, nową funkcjonalność, ale nie jesteśmy pewni czy ruch produkcyjny nie będzie dla niej za dużym opóźnieniem. Jeżeli zachowamy ten przełącznik, może on robić w naszym systemie jako "Kill Switch" aby w sytuacjach kryzysowych szybko wyłączyć pewne funkcjonalności. Dla przykładu załóżmy, że jesteśmy odpowiedzialni za duży sklep internetowy i nadchodzi nasze największe święto w roku - ',i.createElement(a.code,null,"Black Friday"),". Kiedy zauważymy, że ilość użytkowników jest zbyt duża dla naszych serwerów,  możemy wyłączyć pewne moduły jak np. komentarze na stronie produktu czy też lista rekomendacji, aby przekierować wolne zasoby na procesowanie ważniejszych request-ów."),"\n",i.createElement(a.h3,null,"Permissioning Toggles"),"\n",i.createElement(a.p,null,"Jest to chyba najczęściej używany rodzaj przełączników stosowanych przeze mnie. Pozwala on zdefiniować grono użytkowników, dla których funkcjonalność ma być włączona. W przeciwieństwie do ",i.createElement(a.code,null,"Experiment Toggles")," możemy tutaj dosłownie zdefiniować, że użytkownik o ID 123 będzie miał aktywną funkcjonalność. Pozwala nam to na testowanie nowych rzeczy na produkcji bez wiedzy faktycznych użytkowników. To po prostu mega sposób aby być pewnym, że kiedy będziemy gotowi aby wypuścić funkcjonalność, będzie ona poprawnie przetestowana."),"\n",i.createElement(a.h2,null,"Gitlab Feature Flags"),"\n",i.createElement(a.p,null,"To by było na tyle jeżeli chodzi o teorię, przejdźmy do tego co lubimy najbardziej - dużych frytek z polewą serową... znaczy praktyki. Zacznijmy od zdefiniowania przykładowej flagi funkcjonalności w panelu Gitlab-a. Aby to zrobić musimy posiadać swój projekt, przejść do sekcji ",i.createElement(a.code,null,"Operations")," , a tam do pod sekcji ",i.createElement(a.code,null,"Feature Flags"),". Zostaniemy przeniesieni na listę zdefiniowanych flag. Nad listą będzie przycisk ",i.createElement(a.code,null,"New feature flag"),". Po kliknięciu w niego zostaniemy przekierowani na poniższy formularz."),"\n",i.createElement(a.div,{dangerouslySetInnerHTML:{__html:'<span\n      class="gatsby-resp-image-wrapper"\n      style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1024px; "\n    >\n      <a\n    class="gatsby-resp-image-link"\n    href="/static/159c0cba86eb7c43a7772ebd13fc6255/2bef9/Screenshot-2020-11-29-at-17.05.46-1024x537.png"\n    style="display: block"\n    target="_blank"\n    rel="noopener"\n  >\n    <span\n    class="gatsby-resp-image-background-image"\n    style="padding-bottom: 52.36363636363636%; position: relative; bottom: 0; left: 0; background-image: url(\'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAAsTAAALEwEAmpwYAAABRElEQVR42o1Sy26DMBD0//9Fjz321mvPPfYSKT1UEaEUEozBL7A9W+0Gokqtkq40WGA8O7Nj1bYnMIZh
1IOccAwCIUUpBgRRxbeutUlVVo64bITTGUF3XYGImEwIIEf5NeD6fqO970lrDe085Z4oxopRCuRT4NLNcwqr0BqRU03yBVWmtyVorSCmJlFQKHBNe6pY8bE/VNA0x2C7bPhwOophn6awj7xw3wTRN0mwcR254nemm3PgR4xKguq5D27Y8Qzm0/cyWV2DF9dtfdlPOZHZvEIVVVdEaCvh9miYJxtiAH1bvWk6DhjoeP7F//0DbdpI0K40xSjgpZ+4MDvweqSTHKbPlrjuh1xohBE6YYoicOIL3ZEaLMCdcjtxPWrEq6ywty0LWWzy9PuPh5ZF21R5UCPM8y968LJgv6y/wvvOe+Np9A5zdC+lm0mC/AAAAAElFTkSuQmCC\'); background-size: cover; display: block;"\n  ></span>\n  <img\n        class="gatsby-resp-image-image"\n        alt="Screenshot 2020 11 29 at 17 05 46 1024x537"\n        title="Screenshot 2020 11 29 at 17 05 46 1024x537"\n        src="/static/159c0cba86eb7c43a7772ebd13fc6255/2bef9/Screenshot-2020-11-29-at-17.05.46-1024x537.png"\n        srcset="/static/159c0cba86eb7c43a7772ebd13fc6255/6b2ea/Screenshot-2020-11-29-at-17.05.46-1024x537.png 275w,\n/static/159c0cba86eb7c43a7772ebd13fc6255/dd45a/Screenshot-2020-11-29-at-17.05.46-1024x537.png 550w,\n/static/159c0cba86eb7c43a7772ebd13fc6255/2bef9/Screenshot-2020-11-29-at-17.05.46-1024x537.png 1024w"\n        sizes="(max-width: 1024px) 100vw, 1024px"\n        style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"\n        loading="lazy"\n        decoding="async"\n      />\n  </a>\n    </span>'}}),"\n",i.createElement(a.p,null,"Na start polecam uzupełnienie tylko pola ",i.createElement(a.code,null,"Name"),", ewentualnie ",i.createElement(a.code,null,"Description"),". Jako, że ten artykuł i tak już jest długi, lepiej nie zaczynajmy zabawy strategiami. Po kliknięciu przycisku ",i.createElement(a.code,null,"Create feature flag")," powinniśmy zostać przekierowaniu na listę zdefiniowanych flag."),"\n",i.createElement(a.div,{dangerouslySetInnerHTML:{__html:'<span\n      class="gatsby-resp-image-wrapper"\n      style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1024px; "\n    >\n      <a\n    class="gatsby-resp-image-link"\n    href="/static/1a4711d1379520bca3ed0c0d5d535c23/2bef9/Screenshot-2020-11-29-at-17.03.25-1024x180.png"\n    style="display: block"\n    target="_blank"\n    rel="noopener"\n  >\n    <span\n    class="gatsby-resp-image-background-image"\n    style="padding-bottom: 17.454545454545453%; position: relative; bottom: 0; left: 0; background-image: url(\'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAADCAYAAACTWi8uAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAlElEQVR42m2Ou27DMBAE9f8/lyYIkM6lrdiC+BDJI3mzgezO8JaLncEuKReFkCk5M8ZQKUW9d0nC3dWtqbeKmGcld6faxMak9YYN4x43rvsfmxUWcE76csv04eSciTE+5QBbbKx75R4MG6/tx0xX/f3Rcr6Yji5reQLHcZBSwswE4pEGa+hcd6P1l5D3SFirhO8v/gEn3uoMoX3qxwAAAABJRU5ErkJggg==\'); background-size: cover; display: block;"\n  ></span>\n  <img\n        class="gatsby-resp-image-image"\n        alt="Screenshot 2020 11 29 at 17 03 25 1024x180"\n        title="Screenshot 2020 11 29 at 17 03 25 1024x180"\n        src="/static/1a4711d1379520bca3ed0c0d5d535c23/2bef9/Screenshot-2020-11-29-at-17.03.25-1024x180.png"\n        srcset="/static/1a4711d1379520bca3ed0c0d5d535c23/6b2ea/Screenshot-2020-11-29-at-17.03.25-1024x180.png 275w,\n/static/1a4711d1379520bca3ed0c0d5d535c23/dd45a/Screenshot-2020-11-29-at-17.03.25-1024x180.png 550w,\n/static/1a4711d1379520bca3ed0c0d5d535c23/2bef9/Screenshot-2020-11-29-at-17.03.25-1024x180.png 1024w"\n        sizes="(max-width: 1024px) 100vw, 1024px"\n        style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"\n        loading="lazy"\n        decoding="async"\n      />\n  </a>\n    </span>'}}),"\n",i.createElement(a.p,null,"Z automatu utworzona flaga będzie aktywna (osobiście uważam, że nie powinna, pamiętajcie o przestawieniu jej na początku). Najważniejsze, by na tym ekranie zwrócić uwagę na przycisk ",i.createElement(a.code,null,"Configure"),", pod którym kryje się konfiguracja, którą będziemy musieli wykorzystać później w projekcie."),"\n",i.createElement(a.p,null,i.createElement(a.div,{dangerouslySetInnerHTML:{__html:'<span\n      class="gatsby-resp-image-wrapper"\n      style="position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 1024px; "\n    >\n      <a\n    class="gatsby-resp-image-link"\n    href="/static/7c1f413c81cf464dce89aaa95dbcf58a/2bef9/Screenshot-2020-11-29-at-17.21.44-1024x194.png"\n    style="display: block"\n    target="_blank"\n    rel="noopener"\n  >\n    <span\n    class="gatsby-resp-image-background-image"\n    style="padding-bottom: 18.90909090909091%; position: relative; bottom: 0; left: 0; background-image: url(\'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAIAAAABPYjBAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAWElEQVR42nXOsQoAIQwDUP//PwUXTYuDtI3HDYcgJ74hY5JUSokIW9zdNvOCpLun1hoAVRURAHsC+OpOZqaqKefceycZEc9hXqcng6nWimWM8bvtF2YmIi/Er+iahjlqdAAAAABJRU5ErkJggg==\'); background-size: cover; display: block;"\n  ></span>\n  <img\n        class="gatsby-resp-image-image"\n        alt="Screenshot 2020 11 29 at 17 21 44 1024x194"\n        title="Screenshot 2020 11 29 at 17 21 44 1024x194"\n        src="/static/7c1f413c81cf464dce89aaa95dbcf58a/2bef9/Screenshot-2020-11-29-at-17.21.44-1024x194.png"\n        srcset="/static/7c1f413c81cf464dce89aaa95dbcf58a/6b2ea/Screenshot-2020-11-29-at-17.21.44-1024x194.png 275w,\n/static/7c1f413c81cf464dce89aaa95dbcf58a/dd45a/Screenshot-2020-11-29-at-17.21.44-1024x194.png 550w,\n/static/7c1f413c81cf464dce89aaa95dbcf58a/2bef9/Screenshot-2020-11-29-at-17.21.44-1024x194.png 1024w"\n        sizes="(max-width: 1024px) 100vw, 1024px"\n        style="width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;"\n        loading="lazy"\n        decoding="async"\n      />\n  </a>\n    </span>'}})," Obie wartości są istotne, ale to nie sekrety w KeyVault-cie. Zawsze będziemy mogli tutaj wrócić i je podejrzeć."),"\n",i.createElement(a.h2,null,"Obsługa w aplikacji .NET"),"\n",i.createElement(a.p,null,"Gitlab poprawnie zaimplementował wsparcie ",i.createElement(a.code,null,"Feature Flag"),", ponieważ jest to całkowicie open source-wa implementacja. Serwer, który mamy dostępny do naszej dyspozycji, nazywa się Unleash. Cieszy mnie, że powstał oficjalny klient dostępny jak paczka Nuget - ",i.createElement(a.code,null,"Unleash.Client"),".  Ma to swoje dodatkowe korzyści. Gdybyśmy chcieli odejść od g
1itlaba mamy możliwość samemu postawić sobie serwer Unleash. Wystarczy tylko node.js i baza danych PostgreSql (niestety na chwile obecną to jedyny wspierany typ bazy danych)."),"\n",i.createElement(a.p,null,"Sama podstawowa konfiguracja klienta jest bardzo prosta, wystarczy że do pliku ",i.createElement(a.code,null,"Startup.cs")," w sekcji ",i.createElement(a.code,null,"ConfigureServices")," dodamy poniższy kod:"),"\n",i.createElement(a.pre,null,i.createElement(a.code,{className:"language-csharp"},'public void ConfigureServices(IServiceCollection services)\n{\n    var settings = new UnleashSettings\n    {\n        AppName = "dotnet-test",\n        InstanceTag = "kqNfCeMpAZyq5xE8Ykxz",\n        UnleashApi = new Uri("https://gitlab.com/api/v4/feature_flags/unleash/22769163"),\n    };\n\n    var unleash = new DefaultUnleash(settings);\n    services.AddSingleton<IUnlesh>(unleash);\n}\n')),"\n",i.createElement(a.p,null,"Specjalnie zostawiłem wartości z sekcji Configure abyście wiedzieli dokładnie w które miejsca je wstawić."),"\n",i.createElement(a.p,null,"Taka mała uwaga z mojej strony - powyższy kod działa według mechanizmu ",i.createElement(a.code,null,"cold start")," . Oznacza to, że na starcie aplikacji wszystkie flagi będą ustawione na wartości domyślne. Dopiero po 30 sekundach klient pobierze konfigurację z serwera. Oczywiście istnieje też konfiguracja tzw. ",i.createElement(a.code,null,"hot start")," , gdzie unleash będzie próbowało pobrać dane w momencie uruchamiania się aplikacji, ale to już zapewne opiszę w innym artykule."),"\n",i.createElement(a.p,null,"Przejdźmy do wykorzystania flagi. Dla przykładu pobierzmy, w naszym kontrolerze, instancję obiektu ",i.createElement(a.code,null,"IUnleash")," z naszego kontenera dependency injection. Następnie, za pomocą api tego obiektu, sprawdźmy, czy funkcjonalność o wcześniej zdefiniowanej nazwie ",i.createElement(a.code,null,"my_feature")," jest uruchomiona."),"\n",i.createElement(a.pre,null,i.createElement(a.code,{className:"language-csharp"},'public class MyController : ControllerBase {\n    private readonly IUnleash _unleash;\n    public MyController(IUnleash unleash) {\n        _unleash = unleash;\n    }\n\n    public IActionResult Method(){\n        if (_unleash.IsEnabled("my_feature")) return Ok("feature on!");\n        return Ok("feature off!");\n    }\n}\n')),"\n",i.createElement(a.p,null,"Na koniec ostatnia uwaga: w standardowej konfiguracji klient Unleash odpytuje serwer co 30 sekund, więc na zmiany wprowadzone w panelu Gitlab-a będzięmy czekać maksymalnie pół minuty."),"\n",i.createElement(a.h2,null,"Podsumowanie"),"\n",i.createElement(a.p,null,"Tak oto mamy działające ",i.createElement(a.code,null,"Feature Flags")," z zewnętrznym miejscem do ich konfiguracji. To naprawdę potężne narzędzie, które warto jest mieć w swoim arsenale. Jak zwykle mam nadzieje, że artykuł się podobał, jeżeli chcecie być na bieżąco zapraszam do zapisania się na newsletter lub do obserwowania mnie w social mediach. Do Następnego!"))}var l=function(e){void 0===e&&(e={});const{wrapper:a}=Object.assign({},(0,t.ah)(),e.components);return a?i.createElement(a,e,i.createElement(o,e)):o(e)},r=n(5785),c=n(7604),s=n(4001),m=n(4534),d=n(2809),p=n(3662),u=n(515),y=n(3707),g=n(2110),w=n(6245),z=n(3723),k=n(120),b=n(8926),h=n(9313);const f="https://bd90.pl",A=["stycznia","lutego","marca","kwietnia","maja","czerwca","lipca","sierpnia","września","października","listopada","grudnia"],j=e=>{var a;let{data:n,children:t}=e;const o=(0,z.c)(n.mdx.frontmatter.hero_image),l=n.mdx.frontmatter.categories||[],s=n.mdx.frontmatter.slug,f=new Set(l),j=((null===(a=n.related)||void 0===a?void 0:a.nodes)||[]).filter((e=>e.frontmatter.slug!==s)),E=j.filter((e=>(e.frontmatter.categories||[]).some((e=>f.has(e))))).slice(0,3);for(const i of j){if(E.length>=3)break;E.includes(i)||E.push(i)}return i.createElement(c.Z,{pageTitle:n.mdx.frontmatter.title,displayCover:!1},i.createElement("div",{className:h.Y2},i.createElement("header",{className:h.YM},i.createElement(y.Z,{items:[{name:"Strona główna",to:"/"}].concat((0,r.Z)(l[0]?[{name:l[0],to:(0,k.c)(l[0])}]:[]),[{name:n.mdx.frontmatter.title}])}),i.createElement("h1",{className:h.TN},n.mdx.frontmatter.title),i.createElement("div",{className:h.$y},i.createElement("span",null,(e=>{const a=new Date(e);return isNaN(a.getTime())?e:a.getDate()+" "+A[a.getMonth()]+" "+a.getFullYear()})(n.mdx.frontmatter.date)),i.createElement("span",{className:h.AK,"aria-hidden":"true"},"·"),i.createElement("span",null,"Kamil Kiełbasa"))),o&&i.createElement(z.G,{className:h.rP,image:o,alt:n.mdx.frontmatter.hero_image_alt||n.mdx.frontmatter.title}),i.createElement("article",{className:b.rA+" "+h.yF},t),i.createElement("div",{className:h.M},i.createElement(d.Z,{items:n.mdx.frontmatter.references}),i.createElement(m.Z,{date:n.mdx.frontmatter.date,categories:n.mdx.frontmatter.categories,url:"https://bd90.pl/"+n.mdx.frontmatter.slug,title:n.mdx.frontmatter.title,de
1scription:n.mdx.excerpt}),i.createElement("section",{style:{marginTop:"2.5rem"}},i.createElement(w.Z,null)),E.length>0&&i.createElement("section",{className:h.zY},i.createElement("div",{className:h.kK},i.createElement("span",{className:"bd-eyebrow"},"Czytaj dalej"),i.createElement("h2",{className:h.JR},"Powiązane wpisy")),i.createElement("div",{className:h.aM},E.map((e=>i.createElement(u.Z,{key:e.id,id:e.id,slug:e.frontmatter.slug,title:e.frontmatter.title,excerpt:e.excerpt,date:e.frontmatter.date,hero:{image:e.frontmatter.hero_image,alt:e.frontmatter.title},categories:e.frontmatter.categories,variant:"card"}))))),i.createElement("section",{style:{marginTop:"2.5rem"}},i.createElement(p.Z,null)),i.createElement("section",{style:{marginTop:"2.5rem"}},i.createElement(g.Z,{config:{url:"https://bd90.pl/"+n.mdx.frontmatter.slug,identifier:n.mdx.frontmatter.slug,title:n.mdx.frontmatter.title}})))))},E=e=>{var a,n;let{data:t}=e;const o=(0,z.c)(t.mdx.frontmatter.hero_image),l=null===(a=t.mdx.frontmatter.categories)||void 0===a?void 0:a[0];return i.createElement(s.Z,{title:t.mdx.frontmatter.title,description:t.mdx.excerpt,hero:null==o||null===(n=o.images.fallback)||void 0===n?void 0:n.src,heroType:t.mdx.frontmatter.hero_image_type,slug:t.mdx.frontmatter.slug,publishedTime:t.mdx.frontmatter.date,type:"article",width:null!=o&&o.width?String(o.width):void 0,height:null!=o&&o.height?String(o.height):void 0,breadcrumbs:[{name:"Strona główna",url:f+"/"}].concat((0,r.Z)(l?[{name:l,url:""+f+(0,k.c)(l)+"/"}]:[]),[{name:t.mdx.frontmatter.title}])})};function x(e){return i.createElement(j,e,i.createElement(l,e))}}}]);
2//# sourceMappingURL=component---src-pages-mdx-frontmatter-slug-tsx-content-file-path-workspace-linecode-bd-90-blog-2020-12-07-gitlab-feature-toggles-index-mdx-5c6929707330ab18f914.js.map

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.