homeproducten visual studio team system
Hoe om te gaan met Generieke Libraries in Team Foundation Server
Door René - .NET ontwikkelaar bij Delta-N
Een belangrijk onderdeel van Visual Studio Team System is het uitgebreide Source Control Systeem. In tegenstelling tot wat veel mensen nog steeds denken, is dit geen opvolger of nieuwe versie van MS Visual SourceSafe (VSS). Het TFS Versie Controle Systeem (TFS VCS) is volledig gebaseerd op SQL Server en heeft vele nieuwe features t.o.v. het "oude" SourceSafe.
Een vraag die ik vaak krijg bij Team System implementaties of analyses is: "Hoe ga ik om met generieke libraries in TFS VCS?". In dit artikel wil ik deze vraag beantwoorden. De vraag komt veelal voort uit het feit dat er naast de vele features die er zijn bijgekomen, ook twee features verdwenen uit TFS VCS. Dit zijn Sharing en Pinning. In de volgende paragraaf zal ik deze twee begrippen even kort toelichten.
Sharing & Pinning
Binnen VSS biedt Sharing de mogelijkheid om bestanden te delen over verschillende projecten heen. Bijvoorbeeld een generieke class library wordt gedeeld naar meerdere projecten. Alle wijzigingen vinden plaats op dezelfde bestanden, terwijl het in de structuur onderdeel "lijkt" te zijn van een project.
Sharing is in TFS VCS komen te vervallen. De filosofie hierachter is dat het potentieel "gevaarlijk" is om gebruik te maken van dezelfde bestanden in verschillende projecten of teams. Als een ontwikkelaar code verandert in Project A kan er onverwacht wat omvallen in Project B. In TFS VCS zijn andere mogelijkheden bedacht, zoals workspaces en Branching en Merging. Deze begrippen worden verderop in het artikel nader toegelicht.
Door middel van Pinning kun je een versie van een bestand vast zetten ("pinnen") in VSS. Alle gebruikers die dit bestand uitchecken, krijgen dan niet de laatste versie maar de versie die is "gepinned". Dit wordt nogal eens gebruikt om de "work in progress" versie wel onder Source Control te houden, maar niet uit te leveren aan collega's.
Pinning is in TFS VCS ook niet meer beschikbaar. Hiervoor zijn o.a. branching en merging en shelving in de plaats gekomen. Shelving geeft de gebruiker de mogelijkheid om bestanden op de server op te slaan op een aparte locatie. Andere gebruikers krijgen deze versie dus niet als zij de laatste versie ophalen. Met branching en merging is het mogelijk om een heel project of feature geïsoleerd te houden van de rest van de code.
Workspaces, Branching en Merging
Voordat ik begin aan het uitleggen van de verschillende methoden om generieke libraries binnen het project beschikbaar te maken, wil ik eerst een aantal termen toelichten die zeer belangrijk zijn in de te bespreken methoden.
Workspaces
In TFS VCS wordt een workfolder gebruikt om een mapping aan te leggen tussen de server locatie (vaak aangeduid met $locatie) en de fysieke locatie op de PC van de gebruiker. Deze mapping staat opgeslagen op de server. Doordat deze mapping bestaat, weet de server of de file die lokaal staat nieuwer of ouder is dan de file die in Source Control staat.
Een workspace in TFS VSC is bijna hetzelfde als een workfolder in Visual SourceSafe, er zijn echter een aantal verschillen
- Een workspace kan meerdere lokale folders bevatten
- Er kunnen meerdere workspaces zijn die naar de zelfde server folder wijzen
Schematisch kan dit als volgt worden weergegeven;

Figuur 1: Schematische weergave workspaces
In VSTS ziet deze mapping er als volgt uit:

Figuur 2: Mapping workspaces in VSTS
In Figuur 2 wordt een workspace gedefinieerd. Een workspace wordt uniek gekenmerkt door een naam, een eigenaar, een server en een computernaam.
In de tabel "Working folders" worden mappings gemaakt tussen server locaties en lokale folders. Veelal zal dit een eenvoudige mapping zijn ($ProjectX -> c:\Work\ProjectX), maar dit kunnen ook gecombineerde mappings zijn. In Figuur 2 is een lokale workfolder opgebouwd door mappings te maken naar verschillende locaties in TFS VCS. Lokaal staat alles netjes gegroepeerd, maar op de server staat het geïsoleerd in het eigen Team Project.
Het is ook mogelijk verschillende lokale folders te laten wijzen naar een zelfde server locatie.
Workspaces blijven altijd geïsoleerd. Wijzigingen die worden aangebracht in de ene workspace kunnen alleen door middel van "Get Latest Version" worden overgehaald naar een andere workspace. In Figuur 3 staat een schermprint van het Pending Changes Window. Alle bestanden die binnen een workspace worden gewijzigd, toegevoegd of verwijderd, worden als Pending Change gekenmerkt.

Figuur 3: Pending changes binnen een workspace
Aan te raden is om voor ieder project een aparte workspace aan te maken. Zo voorkom je dat wijzigingen van verschillende projecten door elkaar heen gaan lopen.
Branching & Merging
Branching betekent letterlijk aftakken. In het geval van Source Control betekent het dit ook, het aftakken van een folder. Er zijn verschillende strategieën voor het branchen. Het meest gangbare scenario is de "Branching for Release" strategie.
Hierbij wordt een aftakking gemaakt van de stabiele versie zodra deze klaar is voor release. Ontwikkelaars werken door op de development branch. Eventuele hotfixes kunnen op de release branch worden gemaakt. Zodra de wijzigingen zijn doorgevoerd kunnen de wijzigingen van de release branch worden teruggezet naar de main en development branch (als dit nodig is). Dit terugbrengen naar de oorspronkelijke branch heet mergen.

Figuur 4: Branche for Release strategie
In de meest simpele vorm is branchen het "slim" kopiëren van code naar een andere locatie. De relatie naar het origineel blijft altijd behouden en wijzigingen kunnen dus relatief eenvoudig heen en weer worden gebracht.
Voor meer informatie over Branching en Merging en alle verschillende strategieën kijk hier:
- http://www.codeplex.com/BranchingGuidance
- http://www.codeplex.com/TFSBranchingGuideII/Wiki/View.aspx?title=Home
Drie mogelijke oplossingen
Om terug te komen op de hoofdvraag van het artikel, "Hoe ga ik om met generieke libraries in TFS VCS?", zijn er 3 methoden toepasbaar. De keuze voor een methode is een keuze die, naast persoonlijke voorkeur, afhangt van een aantal belangrijke criteria;
- Vinden er veel veranderingen plaats in de generieke libraries gedurende de ontwikkeling van een project?
- Worden de wijzigingen door een vast persoon of een aantal vaste personen gedaan?
- Zijn er vaste release data voor de generieke libraries? Worden de generieke libraries ook gereleased en krijgen deze een versienummer?
- Moeten wijzigingen direct voor iedereen (ook op andere projecten) beschikbaar zijn?
- Werken projecten op een vaste versie van de Generieke Libraries?
- Is het belangrijk dat de Branch/Merge werkzaamheden tot een minimum worden beperkt?
- Moet het mogelijk zijn dat projectleden zelf bepalen wanneer de generieke Libraries worden geupdate.
- Is build snelheid belangrijk tijdens ontwikkelen?
- Is de structuur ingeregeld op server?
Na beoordeling van bovenstaande criteria kan er één van de onderstaande oplossingrichtingen gekozen worden;.
- Generieke libraries als gecompileerde assemblies
- Generieke libraries als branch in je project
- Generieke libraries in je project d.m.v. een workspace template
Generieke libraries als gecompileerde assemblies
Dit alternatief is niet uniek voor TFS VCS. Dit kan ook met de oude SourceSafe. Hoe voor de hand liggend het ook lijkt, generieke libraries in een assembly compileren en deze gebruiken in projecten is voor vrij statische libraries een zeer goede methode.
Werkwijze
Op de TFS is er een Team Project aangemaakt speciaal bedoeld voor de generieke libraries. Dit project heeft zijn eigen eigenaar en zijn eigen release strategie. Een voorbeeld hoe de structuur in TFS VC eruit zou kunnen zien volgt hieronder:

Figuur 5: Structuur TFS VCS
Omdat er een apart Team Project is aangemaakt, kunnen nieuwe wensen, bugs etc. op dit Team Project als Work Item worden aangemaakt. De eigenaar van de generieke libraries zorgt voor een verdeling van het werk onder de "Generieke Library Ontwikkelaars".
De "Generieke Library Ontwikkelaars" ontwikkelen op de DEV Branch. Als zij wijzigingen doorvoeren komen deze via een Merge actie in de STABLE Branch. Als alle wijzigingen zijn getest wordt de STABLE Branch gemerged met de RELEASE Branch.

Figuur 6: Build Definitie
Op de RELEASE Branch staat een release build gedefinieerd. Deze wordt door de eigenaar handmatig aangezet. Aan het einde van de build kunnen de assemblies op een gedeelde netwerklocatie neergezet worden, of kan er een mail worden rondgestuurd dat er een nieuwe release van de generieke libraries beschikbaar is.
Samenvatting
Het gebruik van generieke libraries als gecompileerde assemblies is een goed alternatief wanneer deze deze door externe partijen en/of andere ontwikkelaars ontwikkeld worden, of wanneer weinig wijzigingen op plaatsvinden. Wanneer er veel wijzigingen zijn zorgt dit voor veel overhead.
Gebruik dit alternatief niet als er veel wijzigingen plaatsvinden op de generieke libraries die worden gestuurd door een project. Dit zorgt voor veel overhead.
Generieke libraries als branch in het project
Een methode die erg veel flexibiliteit biedt is het aanmaken van een speciale branch in het te ontwikkelen project. Deze methode is vooral geschikt als er aan meerdere projecten tegelijk wordt gewerkt (die gebruik maken van generieke libraries) en als de generieke libraries aan veel verandering onderhevig zijn.
Zeker als er wordt begonnen met het opzetten van generieke libraries, wordt er veel code toegevoegd en gewijzigd in de generieke library projecten. Als er wordt gekozen voor gecompileerde assemblies, kan dit vertragend werken binnen een project. Ontwikkelaars werken vaak tegelijk aan de library en aan het project.
Als er meerdere projecten gaande zijn, is het vaak niet wenselijk dat generieke code wordt aangepast ten behoeve van een project. Dit kan namelijk leiden tot fouten en build errors in een ander project. Om toch direct aan de generieke libraries te kunnen werken, zonder het risico dat andere code hierdoor omvalt, kan branching worden ingezet.
Werkwijze
Net zoals het alternatief met de gecompileerde assemblies is er voor de Generieke Libraries een apart Team Project aangemaakt. De wens is nu echter dat ontwikkelaars kunnen werken aan de libraries binnen hun VS solution en zonder direct andere projecten te beïnvloeden. Schematisch kan deze structuur er alst volgt uit;

Figuur 7: Branch/Merge structuur
Er zijn twee projecten op de TFS aangemaakt. Eén voor de "Generic Libraries" en één voor "ProjectX". Deze hebben allebei dezelfde Branching en Merging strategie. Namelijk dat er van een stabiele branch wordt ontwikkeld en gereleased.
Als extra toevoeging wordt de Development Branch van de GenericLibraries nogmaals gebranched naar ProjectX. Dit heeft als doel dat Project X nu de generieke libraries in het project kan gebruiken, maar dat wijzigingen gecontroleerd kunnen worden doorgezet naar de Generieke Libraries repository (door Merging).

Figuur 8: Branch maken
In Source Control (of Windows Verkenner) ziet dit er dan als volgt uit.

Figuur 9: Windows Explorer

Figuur 10: Generic Libraries Team Project

Figuur 11: Project X Team Project
Binnen de Visual Studio Solution van ProjectX wordt nu het gebranchede GenericLibraries project toegevoegd.

Figuur 12: Generieke Libraries als onderdeel van Solution
Wijzigingen die worden gemaakt op de "Generic Libraries" gelden alleen voor de "Generic Libraries" binnen "ProjectX". Omdat het een branch betreft kan er echter altijd worden terug gemerged naar het originele Generic Libraries project.
Om de wijzigingen terug te mergen naar het origineel kun je in Source Control kiezen voor "Merge". Zorg er wel voor dat de actieve workspace degene van de generieke Libraries is. De wijzigingen die worden gemerged, worden namelijk op de lokale bestanden gedaan. Als de actieve workspace niet de merge locatie in zich heeft, wordt een melding gegeven dat er een workspace mapping moet worden gemaakt. De wijzigingen kunnen dan niet worden gemaakt.

Figuur 13: Merge initieren
Als er geen conflicten zijn worden de wijzigingen gemerged. Dit gebeurt in eerste instantie alleen lokaal dus er moet nog worden ingecheckt voordat dit definitief wordt.
Door een vergelijking te doen op het laatste bestand in TFS VCS en de gemergede versie zijn de verschillen duidelijk.

Figuur 14: Verschillen bekijken
Samenvatting
Zodra meerdere projecten tegelijk gebruik maken van de generieke libraries kan isolatie een vereiste zijn. Wijzigingen die worden gedaan voor het ene project zijn niet altijd (direct) wenselijk voor het andere project. Generieke libraries als branch in een project bieden hiervoor een elegante oplossing.
Het nadeel is dat er overhead ontstaat doordat de generieke libraries bij tijd en wijle ook gemerged moeten worden.
Workspace op de client
Zoals in het begin van het artikel is uitgelegd is een workspace een mapping tussen een map op de Team Foundation Server en een map op de client. Een map op de client kan mappen op meerdere server mappen. Dit wordt grafisch weergegeven in Figuur 1.
Als er regelmatig wijzigingen worden gedaan in de generieke assemblies en als het nodig is dat deze wijzigingen direct zichtbaar zijn in andere projecten, is de workspace op de client een goede methode.
In het kort komt deze methode er op neer, dat er een lokale map wijst naar meerdere server mappen. Bij het ophalen van een laatste versie worden dan uit verschillende Team Projecten de laatste sources opgehaald. Wijzigingen hierop worden ook direct naar het juiste project gestuurd.
Werkwijze
De werkwijze voor deze methode is een beetje vergelijkbaar met de werkwijze voor branching. Er is nog steeds een apart Team Project voor Generieke Libraries en een apart Team Project voor ProjectX. De combinatie van folders die we bij Branching op de Server hebben gedaan door middel van branching gaan we nu toepassen op de client door middel van een Workspace mapping.
Iedere deelnemer aan ProjectX dient deze Workspace mapping te hebben. Onderstaand figuur geeft aan hoe dit eruit komt te zien.

Figuur 15: Workspace Mapping
De folder c:\work\ProjectX wordt opgebouwd uit verschillende folders vanuit verschillende Team Projecten.
Zodra er wijzigingen plaatsvinden in deze workspace worden deze automatisch op het juiste Team Project ingecheckt. Andere gebruikers op andere projecten (met een zelfde soort Workspace mapping) halen door middel van een Get Latest Version alle wijzigingen op.
Het aanmaken van de workspace moet op iedere client gebeuren. Dit kan best een tijdrovend karwei zijn en het is foutgevoelig. Immers, een verkeerde mapping kan leiden tot verwijzigngen die niet kloppen en projecten die niet builden. In de October PowerTools voor VS 2008 (http://www.microsoft.com/DOWNLOADS/details.aspx?familyid=FBD14EEA-781F-45A1-8C46-9F6BA2F68BF0&displaylang=en) is een tool gekomen die dit probleem voor een groot deel oplost. Namelijk Workspace Templates.
Door middel van een Workspace template kan de mapping door een persoon worden vastgesteld. Vervolgens kunnen gebruikers aan de hand van dit template hun lokale mapping doen.
Dit ziet er als volgt uit.
De Project Administrator maakt een nieuw Workspace template aan.

Figuur 16: Nieuw Workspace template
Nadat de Project Administrator de mapping heeft aangemaakt kan een Team lid kiezen voor "Create Workspace"

Figuur 17: Create Workspace

Figuur 18: Create Workspace formulier
De code wordt nu in de structuur van de workspace template opgehaald in de lokale folder.
NB: Er zit een bug in de powertool die er voor zorgt dat de workspace templates niet worden opgeslagen. De workaround wordt beschreven op onderstaande url: http://social.msdn.microsoft.com/Forums/en-US/tfspowertools/thread/de813a8b-f901-4f23-acfd-7aef158d4838/
Samenvatting
Zodra er sprake is van een klein ontwikkelteam dat veel werk verricht op de generieke libraries is de workspace variant een geschikte oplossing. Er is geen overhead voor branching en Merging. Wijzigingen zijn direct beschikbaar zodra er Get Latest Version wordt gedaan. Dit kan een voordeel zijn maar ook een nadeel.
Bijkomend nadeel is dat gebruikers ondanks het bestaan van Workspace Templates handmatig een een eigen workspace mapping moeten maken.
Conclusie
Dit artikel heeft drie methoden belicht om generieke libraries in een project te ontsluiten. Alle methoden hebben hun eigen voor- en nadelen. De mogelijkheden van TFS VCS zijn in ieder geval ruimschoots voldoende om de verschillende methoden toe te passen. De beschreven scenario's zijn slechts een greep uit de mogelijkheden. De scenario's kunnen uiteraard ook gecombineerd worden om zo een optimale combinatie te krijgen.
De beslis criteria van het begin van het artikel zijn hieronder in een matrix gezet. Deze matrix geeft aan welke methode het meest geschikt is. De volgorde van belangrijkheid van de criteria is willekeurig. Iedere organisatie moet zelf beslissen welke criteria het zwaarst wegen in de beslissing.
| Assemblies | Branch | Workspace | |
| Veel verandering in generieke libraries gedurende project | |||
| Wijzigingen generieke libraries door aangewezen persoon | |||
| Generieke Libraries vaste release data | |||
| Wijzigingen voor iedereen direct beschikbaar | |||
| Werken projecten op een vaste versie van de Generieke Libraries | |||
| Weinig Branch/Merge werkzaamheden | |||
| Projectleden bepalen zelf wanneer de generieke Libraries worden geupdate | |||
| Build snelheid | |||
| Structuur ingeregeld op server |
| Methode zeer geschikt | |
| Methode niet geschikt | |
| Methode mogelijk geschikt |