GitHub Flow

Problem med git-flow

Jag reser runt överallt för att lära ut Git till folk och nästan varje klass och workshop jag har hållit nyligen har frågat mig vad jag tycker om git-flow. Jag svarar alltid att jag tycker att det är bra – det har tagit ett system (Git) som har en miljon möjliga arbetsflöden och dokumenterat ett väl testat, flexibelt arbetsflöde som fungerar för många utvecklare på ett ganska okomplicerat sätt. Det har blivit något av en standard så att utvecklare kan flytta mellan projekt eller företag och vara bekanta med detta standardiserade arbetsflöde.

Hursomhelst har det sina problem. Jag har hört ett antal åsikter från folk i stil med att de inte gillar att nya funktionsgrenar startas från develop istället för master, eller hur den hanterar hotfixes, men de är ganska små.

Ett av de större problemen för mig är att den är mer komplicerad än vad jag tror att de flesta utvecklare och utvecklingsteam faktiskt behöver. Det är tillräckligt komplicerat för att ett stort hjälpscript utvecklades för att hjälpa till att upprätthålla flödet. Även om detta är coolt är problemet att det inte kan verkställas i ett Git GUI, bara på kommandoraden, så de enda personer som måste lära sig det komplexa arbetsflödet riktigt bra, eftersom de måste göra alla steg manuellt, är samma personer som inte är tillräckligt bekväma med systemet för att använda det från kommandoraden. Detta kan vara ett stort problem.

Båda dessa problem kan lätt lösas bara genom att ha en mycket enklare process. På GitHub använder vi inte git-flow. Vi använder, och har alltid använt, ett mycket enklare Git-arbetsflöde.

Dess enkelhet ger det ett antal fördelar. En är att det är lätt för människor att förstå, vilket innebär att de kan ta till sig det snabbt och att de sällan eller aldrig klantar till det eller måste ångra steg de gjort fel. En annan är att vi inte behöver ett omslagsskript för att hjälpa till att genomdriva den eller följa den, så det är inget problem att använda grafiska gränssnitt och liknande.

GitHub Flow

Så, varför använder vi inte git-flow på GitHub? Tja, huvudproblemet är att vi distribuerar hela tiden. Git-flow-processen är till stor del utformad kring ”release”. Vi har egentligen inga ”releaser” eftersom vi distribuerar till produktion varje dag – ofta flera gånger om dagen. Vi kan göra det genom vår chattrumsrobot, som är samma ställe där våra CI-resultat visas. Vi försöker göra processen för testning och leverans så enkel som möjligt så att varje anställd känner sig bekväm med att göra det.

Det finns ett antal fördelar med att distribuera så regelbundet. Om du distribuerar med några timmars mellanrum är det nästan omöjligt att införa ett stort antal stora buggar. Små problem kan introduceras, men då kan de åtgärdas och distribueras på nytt mycket snabbt. Normalt skulle man behöva göra en ”hotfix” eller något utanför den normala processen, men det är helt enkelt en del av vår normala process – det finns ingen skillnad i GitHub-flödet mellan en hotfix och en mycket liten funktion.

En annan fördel med att distribuera hela tiden är möjligheten att snabbt åtgärda problem av alla slag. Vi kan reagera på säkerhetsproblem som vi uppmärksammas på eller implementera små men intressanta funktionsförfrågningar otroligt snabbt, samtidigt som vi kan använda exakt samma process för att hantera dessa ändringar som vi gör för att hantera normal eller till och med stor funktionsutveckling. Det är samma process och den är väldigt enkel.

Hur vi gör det

Så, vad är GitHub Flow?

  • Allt i master-grenen kan distribueras
  • För att arbeta med något nytt skapar du en gren med ett beskrivande namn från master (t.ex: new-oauth2-scopes)
  • Lägg till den grenen lokalt och skjut regelbundet ditt arbete till samma namngivna gren på servern
  • När du behöver feedback eller hjälp, eller när du tror att grenen är redo för sammanslagning, öppnar du en pull request
  • När någon annan har granskat och godkänt funktionen kan du slå ihop den till master
  • När den har slagits ihop och lagts till ”master” kan och bör du distribuera den omedelbart

Det är hela flödet. Det är mycket enkelt, mycket effektivt och fungerar för ganska stora team – GitHub har 35 anställda nu, varav kanske 15-20 arbetar på samma projekt (github.com) samtidigt. Jag tror att de flesta utvecklingsteam – grupper som arbetar med samma logiska kod samtidigt vilket skulle kunna ge upphov till konflikter – är ungefär lika stora eller mindre. Särskilt de som är tillräckligt progressiva för att göra snabba och konsekventa driftsättningar.

Så, låt oss titta på vart och ett av dessa steg i tur och ordning.

#1 – allt i mastergrenen är driftsättningsbart

Detta är i princip den enda hårda regeln i systemet. Det finns bara en gren som har någon specifik och konsekvent betydelse och vi gav den namnet master. För oss betyder detta att den har distribuerats eller i värsta fall kommer att distribueras inom några timmar. Det är otroligt sällan som den blir tillbakarullad (grenen flyttas tillbaka till en äldre commit för att återgå till arbete) – om det finns ett problem kommer commits att återgå eller nya commits kommer att introduceras som löser problemet, men själva grenen rullas nästan aldrig tillbaka.

Den master-grenen är stabil och det är alltid, alltid säkert att distribuera från den eller skapa nya grenar från den. Om du skickar något till master som inte är testat eller som bryter byggandet bryter du mot utvecklingsteamets sociala kontrakt och du känner dig normalt ganska illa till mods för det. Varje gren som vi pushar har tester som körs på den och rapporteras i chattrummet, så om du inte har kört dem lokalt kan du helt enkelt pusha till en ämnesgren (även en gren med en enda commit) på servern och vänta på att Jenkins talar om för dig om den klarar allting.

Du skulle kunna ha en deployed-gren som uppdateras endast när du deployerar, men det gör vi inte. Vi exponerar helt enkelt den aktuella distribuerade SHA via själva webbappen och curl den om vi behöver göra en jämförelse.

#2 – skapa beskrivande grenar utanför master

När du vill börja arbeta med något skapar du en beskrivande namngiven gren utanför den stabila master grenen. Några exempel i GitHub-kodbasen just nu skulle vara user-content-cache-key, submodules-init-task eller redis2-transition. Detta har flera fördelar – en är att när du hämtar kan du se de ämnen som alla andra har arbetat med. En annan är att om du överger en gren ett tag och går tillbaka till den senare är det ganska lätt att komma ihåg vad det var.

Det här är bra eftersom vi när vi går till GitHubs sida med grenlistor lätt kan se vilka grenar som det har arbetats med nyligen och ungefär hur mycket arbete de har på dem.

github branch list

Det är nästan som en lista över kommande funktioner med aktuell grov status. Den här sidan är fantastisk om du inte använder den – den visar bara grenar som har unikt arbete på sig i förhållande till din för tillfället valda gren och den sorterar dem så att de senast arbetade grenarna ligger överst. Om jag blir riktigt nyfiken kan jag klicka på knappen ”Compare” (jämför) för att se vad den faktiska enhetliga diff- och commit-listan är som är unik för den grenen.

Så, i skrivande stund har vi 44 grenar i vårt repository med unmerged arbete i dem, men jag kan också se att bara cirka 9 eller 10 av dem har pushats till under den senaste veckan.

#3 – push till namngivna grenar konstant

En annan stor skillnad mot git-flow är att vi pushar till namngivna grenar på servern konstant. Eftersom det enda vi egentligen behöver oroa oss för är master ur ett utplaceringsperspektiv, så ställer inte pushning till servern till det för någon eller förvirrar saker och ting – allt som inte är master är helt enkelt något som arbetas med.

Det ser också till att vårt arbete alltid är säkerhetskopierat ifall en bärbar dator skulle gå förlorad eller om hårddisken skulle gå sönder. Ännu viktigare är att det gör att alla har ständig kommunikation. En enkel ”git fetch” ger dig i princip en TODO-lista över vad alla för närvarande arbetar med.

$ 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 låter också alla se, genom att titta på sidan GitHub Branch List, vad alla andra arbetar med så att de kan inspektera dem och se om de vill hjälpa till med något.

#4 – öppna en pull request när som helst

GitHub har ett fantastiskt system för kodgranskning som kallas för Pull Requests och som jag är rädd att för få människor känner till. Många använder det för arbete med öppen källkod – gaffel ett projekt, uppdatera projektet, skicka en pull request till den som upprätthåller projektet. Men det kan också lätt användas som ett internt kodgranskningssystem, vilket är vad vi gör.

Förresten använder vi det mer som en grenkonversationsvy än som en pull request. Du kan skicka pull requests från en gren till en annan i ett enskilt projekt (offentligt eller privat) i GitHub, så du kan använda dem för att säga ”Jag behöver hjälp eller granskning av det här” utöver ”Vänligen sammanfoga det här”.

Främre pr-meddelande

Här kan du se att Josh skickar en cc till Brian för granskning och att Brian kommer in med några råd om en av kodraderna. Längre ner kan vi se att Josh bekräftar Brians oro och skickar mer kod för att åtgärda dem.

Mer diskussion

Slutligt kan du se att vi fortfarande befinner oss i försöksfasen – det här är inte en driftsättningsklar gren ännu, vi använder Pull Requests för att granska koden långt innan vi faktiskt vill slå ihop den i master för driftsättning.

Om du sitter fast i utvecklingen av din funktion eller gren och behöver hjälp eller råd, eller om du är utvecklare och behöver en designer som granskar ditt arbete (eller vice versa), eller till och med om du har lite eller ingen kod men några skärmdumpar eller allmänna idéer, öppnar du en pull request. Du kan skicka cc till personer i GitHub-systemet genom att lägga till ett @användarnamn, så om du vill ha granskning eller feedback från specifika personer skickar du bara cc till dem i PR-meddelandet (som du såg Josh göra ovan).

Det här är häftigt eftersom Pull Request-funktionen gör det möjligt för dig att kommentera enskilda rader i den enhetliga diff:n, enskilda commits eller själva pull request-meddelandet, och drar allting inline till en enda konversationsvy.Det låter dig också fortsätta att pusha till grenen, så om någon kommenterar att du har glömt att göra något eller om det finns ett fel i koden, kan du rätta till det och pusha till grenen, GitHub kommer att visa de nya commitsen i konversationsvyn och du kan fortsätta att iterera på en sådan gren.

Om grenen har varit öppen för länge och du känner att den börjar bli osynkroniserad med master-grenen, kan du slå samman master med din ämnesgren och fortsätta. Du kan enkelt se i diskussionen om pull request eller i commit-listan när grenen senast uppdaterades med ”master”.

master merge

När allting verkligen är klart på grenen och du känner att den är redo att distribueras kan du gå vidare till nästa steg.

#5 – sammanfoga endast efter granskning av pull request

Vi gör inte bara arbete direkt på master eller arbete på en ämnesgren och sammanfogar det när vi tycker att det är färdigt – vi försöker få signoff från någon annan i företaget. Detta är vanligtvis en +1 eller emoji eller ”:shipit:”-kommentar, men vi försöker få någon annan att titta på det.

shipit comment

När vi har fått det och grenen klarar CI kan vi slå in den i master för utplacering, vilket automatiskt kommer att stänga Pull Request när vi pushar den.

#6 – distribuera omedelbart efter granskning

Äntligen är ditt arbete färdigt och sammanfogat i master-grenen. Detta innebär att även om du inte distribuerar det nu, kommer folk att basera nytt arbete på det och nästa distribution, som troligen kommer att ske om några timmar, kommer att distribuera det. Så eftersom du verkligen inte vill att någon annan ska pusha något som du har skrivit och som bryter saker och ting, tenderar folk att se till att det verkligen är stabilt när det läggs samman och folk tenderar också att pusha sina egna ändringar.

Vår campfire-bot, hubot, kan göra distributioner för alla anställda, så en enkel:

hubot depoy github to production

kommer att distribuera koden och nolltidsstartar om alla nödvändiga processer. Du kan se hur vanligt detta är på GitHub:

Våra lägereldsloggar

Du kan se att 6 olika personer (inklusive en supportkille och en designer) distribuerar cirka 24 gånger på en dag.

Jag har gjort det här för grenar med en commit som innehåller en ändring på en rad. Processen är enkel, okomplicerad, skalbar och kraftfull. Du kan göra det med funktionsgrenar med 50 commits på dem som tog 2 veckor, eller 1 commit som tog 10 minuter. Det är en så enkel och friktionsfri process att du inte blir irriterad över att du måste göra det ens för 1 commit, vilket innebär att folk sällan försöker hoppa över eller kringgå processen om inte ändringen är så liten eller obetydlig att det helt enkelt inte spelar någon roll.

Det här är en otroligt enkel och kraftfull process. Jag tror att de flesta håller med om att GitHub har en mycket stabil plattform, att problem åtgärdas snabbt om de överhuvudtaget dyker upp och att nya funktioner introduceras i snabb takt. Det finns ingen kompromiss av kvalitet eller stabilitet så att vi kan få mer hastighet eller enkelhet eller mindre process.

Slutsats

Git i sig självt är ganska komplext att förstå, att göra arbetsflödet som du använder med det mer komplext än nödvändigt är helt enkelt att lägga till mer mental overhead till allas våra dagar. Jag skulle alltid förespråka att man använder det enklaste möjliga systemet som fungerar för teamet och gör så tills det inte fungerar längre och sedan lägger till komplexitet endast när det är absolut nödvändigt.

För team som måste göra formella utgåvor med ett längre tidsintervall (några veckor till några månader mellan utgåvorna), och kunna göra hot-fixes och underhållsgrenar och andra saker som uppkommer när man skickar ut så sällan, är git-flow vettigt och jag skulle starkt förespråka att man använder det.

För team som har etablerat en kultur av leverans, som pushar till produktion varje dag, som ständigt testar och distribuerar, skulle jag förespråka att man väljer något enklare som GitHub Flow.

Leave a Reply