GitHub Flow

Problemer med git-flow

Jeg rejser rundt overalt for at undervise folk i Git, og næsten alle de kurser og workshops, jeg har afholdt for nylig, har spurgt mig, hvad jeg synes om git-flow. Jeg svarer altid, at jeg synes, at det er fantastisk – det har taget et system (Git), der har en million mulige arbejdsgange, og dokumenteret en velafprøvet, fleksibel arbejdsgang, der fungerer for mange udviklere på en forholdsvis ligetil måde. Det er blevet noget af en standard, så udviklere kan flytte mellem projekter eller virksomheder og være bekendt med denne standardiserede arbejdsgang.

Det har dog også sine problemer. Jeg har hørt en række udtalelser fra folk i retning af, at de ikke bryder sig om, at nye funktionsgrene startes fra develop i stedet for master, eller den måde, den håndterer hotfixes på, men det er ret småting.

Et af de større problemer for mig er, at den er mere kompliceret, end jeg tror, at de fleste udviklere og udviklingsteams faktisk har brug for. Det er kompliceret nok til, at der blev udviklet et stort hjælpescript til at hjælpe med at håndhæve flowet. Selv om det er fedt, er problemet, at det ikke kan håndhæves i en Git GUI, kun på kommandolinjen, så de eneste mennesker, der skal lære det komplekse arbejdsflow rigtig godt, fordi de skal udføre alle trinene manuelt, er de samme mennesker, der ikke er trygge ved systemet nok til at bruge det fra kommandolinjen. Dette kan være et stort problem.

Både disse problemer kan nemt løses ved blot at have en meget mere forenklet proces. På GitHub bruger vi ikke git-flow. Vi bruger, og har altid brugt, en meget enklere Git-arbejdsgang.

Denne enkelhed giver den en række fordele. Den ene er, at det er let for folk at forstå, hvilket betyder, at de hurtigt kan samle det op, og at de sjældent eller aldrig laver rod i det eller skal fortryde trin, de har gjort forkert. En anden er, at vi ikke har brug for et wrapper-script til at hjælpe med at håndhæve det eller følge det, så brug af GUI’er og lignende er ikke et problem.

GitHub Flow

Så, hvorfor bruger vi ikke git-flow på GitHub? Tja, hovedproblemet er, at vi implementerer hele tiden. Git-flow-processen er i høj grad designet omkring “release”. Vi har ikke rigtig “releases”, fordi vi udruller til produktion hver dag – ofte flere gange om dagen. Det kan vi gøre via vores chatroom-robot, som er det samme sted, hvor vores CI-resultater vises. Vi forsøger at gøre processen med at teste og sende så enkel som muligt, så alle medarbejdere føler sig trygge ved at gøre det.

Der er en række fordele ved at udrulle så regelmæssigt. Hvis du udsender med få timers mellemrum, er det næsten umuligt at indføre et stort antal store fejl. Der kan indføres små problemer, men så kan de løses og genudrulles meget hurtigt. Normalt ville man være nødt til at lave et “hotfix” eller noget uden for den normale proces, men det er simpelthen en del af vores normale proces – der er ingen forskel i GitHub-flowet mellem et hotfix og en meget lille funktion.

En anden fordel ved at udrulle hele tiden er muligheden for hurtigt at løse problemer af alle slags. Vi kan reagere på sikkerhedsproblemer, som vi bliver gjort opmærksom på, eller implementere små, men interessante funktionsanmodninger utroligt hurtigt, og alligevel kan vi bruge nøjagtig den samme proces til at behandle disse ændringer, som vi gør til at håndtere normal eller endog stor funktionsudvikling. Det er den samme proces, og det hele er meget enkelt.

Sådan gør vi det

Så, hvad er GitHub Flow?

  • Alt i master-grenen kan implementeres
  • For at arbejde på noget nyt skal du oprette en beskrivende navngiven gren ud af master (dvs: new-oauth2-scopes)
  • Kommiter til denne gren lokalt og push regelmæssigt dit arbejde til den samme navngivne gren på serveren
  • Når du har brug for feedback eller hjælp, eller hvis du mener, at grenen er klar til at blive slået sammen, åbner du en pull request
  • Når en anden person har gennemgået og underskrevet funktionen, kan du flette den til master
  • Når den er flettet og skubbet til “master”, kan og bør du implementere den straks

Det er hele flowet. Det er meget simpelt, meget effektivt og fungerer for ret store teams – GitHub er nu 35 ansatte, hvoraf måske 15-20 arbejder på det samme projekt (github.com) på samme tid. Jeg tror, at de fleste udviklingsteams – grupper, der arbejder på den samme logiske kode på samme tid, hvilket kan give konflikter – er omkring denne størrelse eller mindre. Især dem, der er progressive nok til at lave hurtige og konsekvente udrulninger.

Så lad os se på hvert af disse trin efter tur.

#1 – alt i mastergrenen kan udrulles

Dette er i princippet den eneste hårde regel i systemet. Der er kun én gren, der har nogen specifik og konsistent betydning, og vi kaldte den master. For os betyder det, at den er blevet deployet eller i værste fald vil blive deployet inden for få timer. Det er utroligt sjældent, at den bliver rullet tilbage (grenen flyttes tilbage til et ældre commit for at tilbageføre arbejdet) – hvis der er et problem, vil commits blive tilbageført eller der vil blive indført nye commits, der løser problemet, men selve grenen bliver næsten aldrig rullet tilbage.

master-grenen er stabil, og det er altid, altid sikkert at implementere fra den eller oprette nye grene ud fra den. Hvis du skubber noget til master, som ikke er testet, eller som ødelægger buildet, bryder du udviklingsteamets sociale kontrakt, og du har det normalt ret dårligt med det. Hver gren vi skubber har tests kørt på den og rapporteret i chatrummet, så hvis du ikke har kørt dem lokalt, kan du blot skubbe til en emnegren (selv en gren med et enkelt commit) på serveren og vente på, at Jenkins fortæller dig, om den består alt.

Du kunne have en deployed gren, der kun opdateres, når du deployerer, men det gør vi ikke. Vi eksponerer simpelthen den aktuelt deployede SHA gennem selve webappen og curl den, hvis vi har brug for at få foretaget en sammenligning.

#2 – opret beskrivende grene fra master

Når du vil begynde at arbejde på noget, opretter du en beskrivende navngiven gren fra den stabile master gren. Nogle eksempler i GitHub-kodebasen lige nu ville være user-content-cache-key, submodules-init-task eller redis2-transition. Dette har flere fordele – den ene er, at når du henter, kan du se de emner, som alle andre har arbejdet på. En anden er, at hvis du forlader en gren i et stykke tid og går tilbage til den senere, er det forholdsvis nemt at huske, hvad det var.

Det er rart, for når vi går til GitHubs grenliste-side, kan vi nemt se, hvilke grene der er blevet arbejdet på for nylig og nogenlunde hvor meget arbejde de har på dem.

github branch list

Det er næsten som en liste over kommende funktioner med den nuværende grove status. Denne side er fantastisk, hvis du ikke bruger den – den viser dig kun de grene, der har unikt arbejde på dem i forhold til din aktuelt valgte gren, og den sorterer dem, så de grene, der senest er blevet arbejdet på, står øverst. Hvis jeg bliver rigtig nysgerrig, kan jeg klikke på knappen ‘Compare’ (sammenlign) for at se, hvad den faktiske forenede diff- og commit-liste er, som er unik for den pågældende gren.

Så i skrivende stund har vi 44 grene i vores repository med unmerged arbejde i dem, men jeg kan også se, at kun omkring 9 eller 10 af dem er blevet pushet til i den sidste uge.

#3 – push til navngivne grene konstant

En anden stor forskel i forhold til git-flow er, at vi skubber til navngivne grene på serveren konstant. Da det eneste, vi virkelig skal bekymre os om, er master ud fra et udrulningssynspunkt, så forvirrer push til serveren ikke nogen eller forvirrer tingene – alt, der ikke er master, er simpelthen noget, der arbejdes på.

Det sørger også for, at vores arbejde altid er sikkerhedskopieret i tilfælde af tab af bærbar computer eller harddiskfejl. Endnu vigtigere er det, at det sætter alle i konstant kommunikation. En simpel ‘git fetch’ vil dybest set give dig en TODO-liste over, hvad alle arbejder på i øjeblikket.

$ git fetchremote: Counting objects: 3032, done.remote: Compressing objects: 100% (947/947), done.remote: Total 2672 (delta 1993), reused 2328 (delta 1689)Receiving objects: 100% (2672/2672), 16.45 MiB | 1.04 MiB/s, done.Resolving deltas: 100% (1993/1993), completed with 213 local objects.From github.com:github/github * charlock-linguist -> origin/charlock-linguist * enterprise-non-config -> origin/enterprise-non-config * fi-signup -> origin/fi-signup 2647a42..4d6d2c2 git-http-server -> origin/git-http-server * knyle-style-commits -> origin/knyle-style-commits 157d2b0..d33e00d master -> origin/master * menu-behavior-act-i -> origin/menu-behavior-act-i ea1c5e2..dfd315a no-inline-js-config -> origin/no-inline-js-config * svg-tests -> origin/svg-tests 87bb870..9da23f3 view-modes -> origin/view-modes * wild-renaming -> origin/wild-renaming

Det lader også alle se, ved at kigge på GitHub Branch List-siden, hvad alle andre arbejder på, så de kan inspicere dem og se, om de vil hjælpe med noget.

#4 – åbn en pull request til enhver tid

GitHub har et fantastisk kodegennemgangssystem kaldet Pull Requests, som jeg er bange for, at ikke nok mennesker kender til. Mange mennesker bruger det til open source-arbejde – fork et projekt, opdater projektet, send en pull request til maintainer. Det kan dog også sagtens bruges som et internt kodegennemgangssystem, hvilket er det, vi gør.

Vi bruger det faktisk mere som en branch conversation view mere end en pull request. Du kan sende pull requests fra en gren til en anden i et enkelt projekt (offentligt eller privat) i GitHub, så du kan bruge dem til at sige “Jeg har brug for hjælp eller gennemgang af dette” ud over “Please merge this in”.

early pr message

Her kan du se Josh cc’ing Brian til gennemgang og Brian komme ind med nogle råd om en af kodelinjerne. Længere nede kan vi se, at Josh anerkender Brians bekymringer og skubber mere kode for at løse dem.

mere diskussion

Sidst kan du se, at vi stadig er i prøvefasen – dette er ikke en implementeringsklar gren endnu, vi bruger Pull Requests til at gennemgå koden længe før vi faktisk ønsker at flette den ind i master til implementering.

Hvis du sidder fast i udviklingen af din funktion eller gren og har brug for hjælp eller råd, eller hvis du er udvikler og har brug for en designer til at gennemgå dit arbejde (eller omvendt), eller selv hvis du har lidt eller ingen kode, men nogle screenshot comps eller generelle idéer, åbner du en pull request. Du kan cc folk i GitHub-systemet ved at tilføje et @brugernavn, så hvis du vil have anmeldelse eller feedback fra bestemte personer, skal du blot cc dem i PR-meddelelsen (som du så Josh gøre ovenfor).

Dette er fedt, fordi Pull Request-funktionen giver dig mulighed for at kommentere på individuelle linjer i den forenede diff, på enkelte commits eller på selve pull request’en og trækker alt inline til en enkelt konversationsvisning.Det lader dig også fortsætte med at skubbe til grenen, så hvis nogen kommenterer, at du har glemt at gøre noget, eller der er en fejl i koden, kan du rette det og skubbe til grenen, GitHub vil vise de nye commits i konversationsvisningen, og du kan fortsætte med at iterere på en gren på den måde.

Hvis grenen har været åben for længe, og du føler, at den er ved at blive ude af synkronisering med mastergrenen, kan du flette master til din emnegren og fortsætte. Du kan nemt se i pull request-diskussionen eller commit-listen, hvornår grenen sidst blev opdateret med “master”.

master merge

Når alt virkelig og sandelig er gjort på grenen, og du føler, at den er klar til at distribuere, kan du gå videre til det næste trin.

#5 – merge kun efter gennemgang af pull request

Vi laver ikke bare arbejde direkte på master eller arbejder på en emnegren og merger det ind, når vi mener, at det er færdigt – vi forsøger at få en underskrift fra en anden i firmaet. Det er som regel en +1 eller emoji eller en “:shipit:“-kommentar, men vi forsøger at få en anden til at kigge på det.

shipit comment

Når vi får det, og grenen klarer CI, kan vi flette den ind i master med henblik på implementering, hvilket automatisk lukker Pull Request, når vi skubber den.

#6 – deploy straks efter gennemgang

Endeligt er dit arbejde udført og slået sammen i master-grenen. Det betyder, at selv om du ikke udruller det nu, vil folk basere nyt arbejde på det, og den næste udrulning, som sandsynligvis vil ske om et par timer, vil skubbe det ud. Så da du virkelig ikke ønsker, at en anden skal pushe noget, som du har skrevet, og som ødelægger ting, har folk en tendens til at sikre sig, at det virkelig er stabilt, når det bliver fusioneret, og folk har også en tendens til at pushe deres egne ændringer.

Vores campfire-bot, hubot, kan foretage implementeringer for alle medarbejdere, så en simpel:

hubot depoy github to production

vil implementere koden og nul-downtime genstarte alle de nødvendige processer. Du kan se, hvor almindeligt dette er på GitHub:

vores campfire logs

Du kan se, at 6 forskellige personer (herunder en supportmand og en designer) deployerer omkring 24 gange på en dag.

Jeg har gjort dette for grene med ét commit, der indeholder en ændring på én linje. Processen er enkel, ukompliceret, skalerbar og kraftfuld. Du kan gøre det med funktionsgrene med 50 commits på dem, der tog 2 uger, eller 1 commit, der tog 10 minutter. Det er en så enkel og gnidningsfri proces, at du ikke er irriteret over, at du skal gøre det, selv for 1 commit, hvilket betyder, at folk sjældent forsøger at springe over eller omgå processen, medmindre ændringen er så lille eller ubetydelig, at det bare er ligegyldigt.

Dette er en utrolig enkel og kraftfuld proces. Jeg tror, at de fleste vil være enige i, at GitHub har en meget stabil platform, at problemer løses hurtigt, hvis de overhovedet dukker op, og at nye funktioner introduceres i et hurtigt tempo. Der er ikke gået på kompromis med kvalitet eller stabilitet, så vi kan få mere hastighed eller enkelhed eller mindre proces.

Konklusion

Git i sig selv er ret komplekst at forstå, at gøre den arbejdsgang, som du bruger med det, mere kompleks end nødvendigt, er blot at tilføje mere mentalt overhead til alles dag. Jeg vil altid anbefale at bruge det enkleste mulige system, der vil fungere for dit team, og gøre det, indtil det ikke længere fungerer, og derefter kun tilføje kompleksitet, hvis det er absolut nødvendigt.

For teams, der skal lave formelle udgivelser med et længere tidsinterval (et par uger til et par måneder mellem udgivelserne), og være i stand til at lave hot-fixes og vedligeholdelsesgrene og andre ting, der opstår ved at sende så sjældent, giver git-flow mening, og jeg vil stærkt anbefale brugen af det.

For hold, der har etableret en kultur for forsendelse, som skubber til produktion hver dag, som konstant tester og distribuerer, vil jeg anbefale at vælge noget mere simpelt som GitHub Flow.

Leave a Reply