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.