Modularisation of Software Configuration Management Henrik Bærbak Christensen Centre for Experimental Computer Science University of Aarhus DK-8200 ˚ Arhus N, Denmark Tel.: +45 89 42 31 88 Email:
[email protected]
Abstract. The principle of modularisation is one of the main techniques that software designers use to tame the complexity of programming. A software project, however, is complex in many other areas than just programming. In this paper, we focus on one of these complex areas, namely software configuration management, and outline how modularisation is natural and powerful also in this context. The analysis is partly based on experiences from case studies where small- to medium-sized development projects are using a prototype tool that supports modular software configuration management.
1
Introduction
The importance of software configuration management is widely accepted as evident from the prominent role it plays in the Capability Maturity Model [18]. It can also quickly lead to complexity and confusion as developers have to overview not only a high number of software entities but also a high number of versions of them. When software entities becomes versioned the configuration problem arises: Which versions of which entities must I combine in order to get a consistent and meaningful system? Tichy termed this the selection problem [23], and thereby hinted at the solution adopted by most configuration management tools: Every software entity version has attributes like author, creation time stamp, status, etc. A query can then be run over all versions of all software entities and a subset of versions is selected where the attributes of each version meet the criteria of the query. To uniquely specify a certain bound configuration, e.g. a customer release, the adopted solution is to use labels or tags; symbolic names that are associated a specific entity version. Thus entity versions that are part of, say, customer release “5.4.1” are labelled with an appropriately named label like “Release5.4.1”. Examples of tools based on this approach are ClearCase [13] and CVS [4]. Seen from the viewpoint of modular programming languages, the relation between a configuration and the entity versions that it is composed of, is an odd relation. In modular programming languages like Java [1], Oberon [19], BETA [16], etc., the relation between the client and the provider of functionality
is stated in the client as a use statement: Module A uses module B, because A requires the functionality of B to be functionally complete. In label-based software configuration management, the inverse relation exists: Entity versions state that they are-used-in a specific configuration. The drawback of this approach is that the configuration itself is not explicitly represented. There is no manifest object in the database of versions (the repository) that represents the configuration and therefore we cannot easily identify, query, or manipulate it nor reuse it in a new context—it is not modular. This is in contrast to the perception of the developers (and certainly the customers) who have a strong notion of what, say “Release 5.4.1”, is and represents. In this paper, we describe the Ragnarok architectural software configuration management model that allows software configuration management to be handled in a modular way. The model has been presented elsewhere (see [8, 9, 2]); the main focus of this paper is to discuss the modularisation aspect of this model in greater detail and highlight how the concept of modules brings many of the benefits known from programming language modules into the configuration management domain. It does so primarily through examples, some taken from case studies of using “Ragnarok”, a prototype tool that handles software configuration management in a modular way.
2
Ragnarok Software Configuration Management Model
The Ragnarok software configuration management model is characterised by: – A component concept that allows source code fragments and related data of an abstraction to be treated as a cohesive whole under version control. 1 – Relations that state relationships between specific states (versions) of components. – Transitive check-in and check-out algorithms that ensures that the repository stores rooted configurations of component versions. The logical design of a software system can be viewed as a directed graph: Nodes are software entities like e.g. classes or modules and edges are relations between entities like e.g. ‘part/whole’, or ‘depends-on’. In the Ragnarok model, nodes are termed components and model software abstractions at any granularity: Class, module, subsystem, system. Components contain the underlying data defining the abstraction such as source code files, documentation, etc. As an example, a component representing class foo in a C++ project will typically contain the source files foo.h and foo.c++. Relations between components model the underlying logical relations: Composition, functional dependency, inheritance, etc. Like relations between programming language modules they are uni-directional “use”-relations: Stated in the client component, specifying the supplier. 1
Note that a Ragnarok component does not form a unit of separate deployment and should not be confused with ‘component’ as defined in e.g. COM or CORBA.
Components are defined within a named project, i.e. a namespace. Components are named and are uniquely identified through their hierarchical position in the project as defined by the composition relations, similar to naming in hierarchical file systems. A client component in one project may state a relation to a supplier component in another project, as the (project name, hierarchical component name) pair uniquely identifies the supplier component2 . Components are versioned. A component version is a “snapshot” of a component at a specific point in time. Relations are always between specific component versions: In Fig. 1 component City version 5 depends specifically on component Unit version 9, not on Unit in general (the figure is explained in detail below in section 2.1). Like traditional configuration management tools, Ragnarok maintains a single version database (the repository) and many, local, workspaces where developers modify the software components. A check-in operation copies components from a workspace to the repository thereby publishing new versions of the components. The opposite operation is a check-out that copies component versions to a local workspace for viewing and editing. To ensure consistency of the specific relations between component versions, both check-in and check-out operate in the transitive closure of relations. This is similar to object graph persistence offered by some programming languages, like for instance Java object serialisation [1] and persistent store in BETA [5]: When an object is copied to a persistent storage not only the object itself but also all objects in the transitive closure of object references are stored. In Ragnarok a check-in of a component not only creates a new component version, but new versions of changed components in the transitive closure of component relations. New component versions are only made if necessary, that is, if the component contents has changed (source code and/or relations). The Ragnarok model does presently not allow two different versions of the same component to coexist in a single configuration. 2.1
An Example
Consider a fictitious software team that is developing a strategy game. In the game, a player controls a region of land, comprising various cities, by means of military units. The game domain is implemented in a module, Model, that is composed of classes Terrain, City, and Unit, modelling fundamental game concepts. Figure 1a) shows a milestone, Model version 11. In the figure, the version group for a component is depicted as a box with rounded corners containing the component versions (small squares with the version numbers inside) organised in a version graph. Solid line arrows going out of a component version represent composition relations, dashed line arrows represent functional dependencies. So, Model version 11 is composed of Terrain version 7, City version 5, and Unit version 9—and City version 5 depends on (Unit, 9) and (Terrain, 7), and so on. 2
However, the present Ragnarok tool does not support relations across project boundaries for technical reasons.
Model
a) Terrain
Model
b)
11
Unit 7 9
11
12
Terrain
Unit 7 9
10
Weapon 5 5
City
6
1
City
Fig. 1. The evolution of a game domain module. Component versions are numbered squares, organised as a version history. The rounded box is the version group containing all versions of a given component. Relations between component versions are shown as arrows.
To illustrate a check-in, consider a situation where an inner class, Weapon, is added to class Unit. After implementation and testing, a new version of Model is created, see Fig. 1b), as follows: The transitive check-in is propagated to component Weapon, its contents stored and a new version identity, 1, established. A relation to (Weapon, 1) is added to Unit, and a new version 10 created. City lies on a path to a modified component and is thus checked-in with a relation to (Unit, 10) and finally Model is checked-in. A new version of Terrain is not necessary, as it does not lie on a path to Weapon. The check-out trivially reverses the check-in process: The root component version is checked out, then the check-out is propagated transitively to all related component versions. Thus, checking out (Model, 11) will reestablish the old configuration without the Weapon component. 2.2
Modular Configuration Management
In our example, the Model component represents the game domain. Let us assume that another team is involved in developing the graphical user interface for the game, represented by a GUI component. The user interface developers can work independently of the game domain changes as they can use a stable version, (Model, 11), as illustrated in Fig. 2a). Later the game domain developers announce a new internal release to be integrated with the user interface: (Model, 13). This identity, (Model, 13) in Fig. 2b), is all the interface developers need to retrieve the full game domain configuration to their workspaces (classes City, Unit, etc.); they do not have to worry about what classes it contains or depends on, in which versions, and how they are related. This is an essential modular benefit of the Ragnarok model: The complex, internal, structure of the game domain module is abstracted away; what remains is a very simple “interface” namely the version- and component identity—enough to correctly recreate the module in that particular configuration.
a) Model 11
GUI 12
13
7
8
9
b) Model 11
GUI 12
13
7
8
9
10
Fig. 2. An integration scenario. The sub-structure of the two components is not shown.
2.3
Unification of Version and Bound Configuration
One consequence of the Ragnarok model is that the concepts of “version” and “bound configuration” are unified. E.g. (Model, 11) is a version of the game domain module, but it also defines the bound configuration of all (versions of) classes embodied in it. How does this compare to labelling/tagging in CVS, ClearCase, or similar tools? A label is simply an attribute of a given file version. Though it is possible to label all file versions of the internal release of the game domain module with some appropriate label, there is still no identifiable object in the repository that embodies “Model version 11”. As it is at this level developers discuss and understand their system, this is an unfortunate impedance mismatch between the design domain and the configuration management domain. In the Ragnarok model, (Model, 11) is a manifest object in the repository that can be queried and manipulated. 2.4
Dynamic Relations
Relations stated between components in the configuration domain are not restricted to mirror the static relations between logical design entities in the design domain. A user may also state e.g. dynamic dependencies between entities. An example is an entity that calls another using socket communication instead of using procedure calls—in the former case the relation may not be inferred from the import clauses in the source code. The Ragnarok transitive check-in will traverse any relation stated in a project and ensure that a configuration is version controlled as a unit. We acknowledge that it is difficult to make a comprehensive check of the correspondence between the actual dynamic dependencies and the relations in the corresponding Ragnarok project. In practice, however, missing relations are quickly found by “debugging”; releases are built in a special workspace and if a relation is missing, the checked out configuration behaves differently from the configuration the developer (thought he) checked in.
3
Case Study Scenarios
The Ragnarok prototype tool is presently used in three small- to medium-sized projects with the following main characteristics:
ConSys BETA Compiler Ragnarok Used since Mar. 96 Feb. 97 Feb. 96 Data C++, SQL, binary BETA, C, html BETA NT Unix, NT Unix, NT Platform No. developers 3 4 1 160 40 33 No. components No. files 1340 290 160 No. lines (KLOC) 240 + binary 120 45 General experiences have been reported in [7, 2]. Here we will outline some scenarios that have arisen in the case studies. The scenarios will be contrasted with CVS as representative of label-based models. 3.1
Application Version Identification
The Mjølner BETA compiler is a compiler for the object-oriented, statically typed programming language BETA [16]. The source code and technical documentation are managed by Ragnarok. One feature of Ragnarok is the ability to associate script-invocations on actions like check-in and check-out. In the compiler group, the scripts associated with the root component (that is, the component that represent the compiler as a whole) generate a small BETA source code fragment containing a string identifier that is assigned a value equal to the Ragnarok generated version identity of the component. This source fragment is compiled into the compiler executable and the string is printed when the compiler is called: Mjølner BETA Compiler version 5.4.5 (711) for WinNT/Win95 (i386) (MS)
where “(711)” is the version identity assigned by Ragnarok. Thus developers who use compiler pre-releases can report the exact version identity of the compiler in an error description to the development team. As this identity is automatically generated it is trustworthy compared to the manually maintained “5.4.5” identity. The main point is not that such a script is a “nice feature” but that there indeed exists a single component version, here with identity “711”, that uniquely embodies the exact configuration of the versions of all 290+ source files of the compiler. In CVS the only way to uniquely define such a bound configuration is to label all file versions with some symbolic name. This is a manual and, for large systems, time-consuming process and therefore often not done in practice. For instance, Asklund reports that the labelling process in a large industrial case study takes more than 90 minutes [3]. In contrast, a Ragnarok check-in is proportional to the size of the change, not to the size of the system. A more severe problem is that the set of labels is unstructured; it is simply a set of text strings. The underlying configurations that the labels identify, however, are structured in the time dimension; thus developers are forced to track the evolution of configurations manually (contrary to the idea of tool support). Ragnarok tracks the evolution of versions in a version tree, and as a component
version also identify the configuration rooted in it, the evolution of configurations is naturally organised. Below the version graph command (vg) has been run on the BETA compiler component to display its versions, thus outlining the evolution of compiler configurations. The command automatically contracts less interesting versions (symbolised as [...]). The Ragnarok tool allows a version identity to be associated with a symbolic name, for instance the name “r5 0a” can be used as synonym for (compiler, 700) in the tool. 1:compiler/(v608#)% vg Version graph of component compiler [...] (v603‘r4_2_before_COM’) [...] (v700‘r5_0a’) [...] (v708‘r5_0b’) [...] (v711‘r5_0cde’)
3.2
Subsystem Identification
A mail received from an inexperienced developer on the BETA compiler project is an example of another often seen request: How do I find out which version of the SYNTHESIZER module was used in release 695 of the compiler? As Ragnarok maintains the versioned relations between components, the answer is found using a list version command (vr) of version 695 of the compiler. The list version command enumerates all component versions in the transitive closure of relations rooted in a given component version: 1:compiler/(v608#)% vr 695 List version (v695) of component compiler 1:compiler (v695) 2:CHECKER (v133) 3:SYNTHESIZER (v234) 4:CONTROL (v102) 5:GENERATOR (v391) 8:COFF (v16) 39:TEST (v3) 9:ELF (v37) 35:TEST (v12) (and so on...)
Ragnarok outputs the component versions transitively related to version 695 of the compiler component; all component names are prefixed with an integer component ID, and the version is listed in parentheses. So the answer to the posed question is SYNTHESIZER version 234. In CVS the only way to identify a configuration and thereby a sub-configuration is through the labelling technique. As all files bear the same label it is generally difficult to estimate the exact set of files that goes into a given
subconfiguration; the CVS repository has only information about the full set of files, not how they are related. 3.3
Evolving Architectures
Modern software development methods favour iterative development where designs and code are refined and expanded gradually. One of the case studies, the ConSys project, more than tripled its size in terms of lines of code and number of modules over a two year period. Ragnarok allows modules to be added and removed, their contents changed, and relations between modules to be altered. As any check-in will snapshot the transitive closure of modules and the specific relations interrelating them, all such architectural changes are stored and reproducible. Ragnarok essentially version controls architectural evolution just as much as the underlying source code evolution. The next section shows an example of this. If a given file version does not have a specific label, L, in CVS, there is no simple way to interpret it: Is it because the file was deleted before configuration L was created? Is it because it was added after? Or, did the developers forget to label the file version? Likewise, if the dependency structure between classes changes during the evolution of a module, there is no way to tell this in CVS. You have to check-out the various configurations and inspect the import-relations in the source code to guess at these changes. 3.4
Architectural Difference
Often it is convenient to view the differences between two configurations, say two releases of a system, in terms of changed, added or deleted modules; i.e. at the logical, architectural level. These are easily missed if provision is only given for calculating ’diffs’ on the level of file contents as e.g. in CVS diff. Below is shown the output of an architectural difference calculation command (dr) between version 9 and 30 of component GeoSpace from the Ragnarok project: 15:Ragnarok/DataModel/GeoSpace/(v71#)% dr 9 30 Difference between version 9 vrs 30 15:GeoSpace (v9) (v30) Name change: PhysicalBackbone -> GeoSpace Parts: AbstractionLayer: Deleted ALRegister: Deleted Landscape(v21) : Added 16:Landmark (v12) (v32) 25:Utility (v3) (v5) 31:Decoration (v2) (v5)
Ragnarok lists component IDs and names, version identities for the two configurations being compared, and architectural changes. We note that the GeoSpace component has changed name. Its sub-architecture has changed as two components have been deleted, AbstractionLayer and ALRegister, while component
Landscape has been added. We also note that Utility and Decoration have remained relatively stable, while the Landmark component has been modified a lot (few/many versions of the components). The corresponding normal ’diff’ that lists file contents differences is 532 lines long. It should be noted that architectural difference calculations can also be used to minimise deployment packages. Consider a software system targeted for upgrading; if the version identity of the configuration presently installed is known, an architectural difference calculation between the installed and the new configuration will list exactly what modules need to be upgraded and what modules have remained stable. This list can be used to produce a minimal upgrading package that only contains changed modules. CVS can only report file contents changes as it does not keep track of relations between modules. 3.5
Branching and Merging
Check-in and check-out of a component affect all components in the transitive closure. This transitive behaviour also applies when a branch is created during development. A branch is often used to handle parallel work, for instance to keep control of bug-fixes for a released system while development of new features is going on at the same time. Then bug-fixes are made in a maintenance branch while new features are added on the development branch. The Ragnarok model makes branching sub-configurations easy as there is no problem of finding out what components to branch: Through their relations the components themselves already know. The branching operation simply propagates throughout the transitive closure. The same applies when merging a branch back again. This is a relief at the technical level but even more so at the conceptual level. Conceptually, the Ragnarok model allows you to express the obvious: “I need to branch this version of this module”, as there is a manifest component version to perform this action on. Ragnarok will then take care of the tedious details of doing the actual branching of relevant source code files, components, and update their relations. Buffenbager et al. describe three different branching strategies for label-based systems [6]. It is interesting to note that Buffenbager et al. describe their recommended “merge-at-a-label” strategy as “a low-level integration mechanism, but well-suited to today’s SCM systems” and argue in favour of a tool layer to implement their strategy. Though Ragnarok does not implement their recommended strategy presently, its understanding of software as interrelated abstractions and its track-keeping of changed components provide a strong basis for such a future extension. 3.6
Software Reuse
Software reuse is perhaps the most important technique to reduce the production cost of software. While systematic software reuse is a complex matter both or-
ganisationally and technically, a prerequisite for a systematic approach is proper identification of the assets that are reused in order to track dependencies, updates, etc. Again, the modular approach of the Ragnarok model guarantees unique identification of assets. Moreover, because any component version maintains relations to all other component versions depended upon, getting access to a reusable asset is eased as only the asset itself needs to be known: Other assets depended upon will automatically be copied to a workspace upon check-out.
4
Discussion
The Ragnarok model guarantees consistency in a technical sense: It will reproduce configurations at check-out exactly as they were at check-in. However, to what extent checked-in configurations are also logically consistent in the sense that they will compile, link, and represent “sound” designs, must be defined by team policies. Both the Consys- and BETA compiler teams have adopted an “integrate early” development process that force them to be rather strict concerning when components can be checked in: The underlying source code must be “well-behaved” before a check-in is acceptable. Other policies are possible if teams work independently and announce explicit version identities for integration as exemplified in section 2.2. With such a “integrate late” policy, versions can be made more often; as convenient code snapshots during experimentation, etc. Ragnarok treats any change as having impact on all components that directly or indirectly are related to the changed component. This is a pessimistic view: Standard wisdom is that it should be possible to change a module’s implementation without affecting other modules as they only rely on its interface. However, a main objective of configuration management is traceability—we need to track all changes. Often developers inadvertently break the contract of a module even though they only change its implementation. Such an erroneous change will cause problems for depending modules and here Ragnarok’s conservative approach may help in tracing the source of the problem. On first sight, one may be concerned about the amount of component versions created during manipulations like check-in, branching, etc., and fear that the model leads to a combinatorial explosion of versions in the repository. Our case studies show that this is not the case: An analysis of the component versions in the repositories of the case study projects shows that the number of component versions is proportional to the number of check-ins and to the number of changes; thus combinatorial explosion does not occur. Detailed data is provided in [2].
5
Related Work
A number of other software configuration management models, tools, and languages also support or can simulate a modular view of software configuration management to some extent, notably systems like Adele [11], PCL [25], NUCM [26] COOP/Orm [17], and POEM [14, 15]. Adele, PCL, and NUCM are
systems that define broader versioning models than that of Ragnarok and they can simulate the Ragnarok model more or less naturally. Adele is commercially available and NUCM is available in the public domain. However, no data has been published on actual usage of these models from the viewpoint of modular configuration management as presented here. COOP/Orm and POEM have versioning models very similar to Ragnarok but both are research prototypes that have not yet been tried in a realistic setting. Modern component technologies must also address the configuration problem. CORBA/OMA involves a change management service to handle compatibility of interface versions, but this area has not yet been standardised [22, §13.3.2]. The Microsoft COM technology prescribes a convention where a globally unique integer value, a GUID, must be assigned every published component interface [20], thus providing a simple but strict version control. The Ragnarok model similarly assigns a unique identity to a component version, and the ID is unique in a deep sense: The ID uniquely identifies the specific versions of all components depended upon. As classes in COM must only depend on COM interfaces and as a new GUID must be assigned if a COM interface changes (even if only semantically), a GUID is theoretically also unique in the deep sense. In practice, however, programmers often fail to follow the conventions, leading to broken configurations. In Ragnarok the creation of the ID is a by-product of a check-in, not a prescribed action that must rely on developer discipline or support from available tools. Thus we find the credibility of Ragnarok’s IDs stronger. We of course acknowledge that the scope of Ragnarok is much more limited: A Ragnarok ID can only be understood in the context of the relevant project, not as an entity with its own global semantics as is the case with a COM GUID.
6
Conclusion
Software development is a complex task in every way. The principles of abstraction and modularisation are vital techniques to tame complexity in the domain of software design and implementation. In this paper, we have discussed how these principles can also bring benefits in another important and complex domain, namely software configuration management. Our presentation has motivated these benefits by outlining examples from our case studies, representative of problems encountered in practical software development and configuration management. We have outlined important characteristics of the Ragnarok software configuration management model and described how this model is well suited to handle configuration management in a modular way. It is not the intent, however, to emphasise the Ragnarok model as the only feasible model. The main message is that modular configuration management is indeed viable and beneficial and we encourage configuration management tool producers to provide more and better support for a modular approach in commercially available systems. The Ragnarok prototype tool is provided free of charge and can be downloaded from the Ragnarok home page: http://www.daimi.au.dk/~hbc/Ragnarok/
References 1. K. Arnold and J. Gosling. The Java Programming Language, Second Edition. Addison-Wesley, 1998. 2. U. Asklund, L. Bendix, H. B. Christensen, and B. Magnusson. The Unified Extensional Versioning Model. In System Configuration Management [21], pages 100–122. 3. U. Asklund and B. Magnusson. A Case-Study of Configuration Management with ClearCase in an Industrial Environment. In R. Conradi, editor, Software Configuration Management, Lecture Notes in Computer Science 1235, pages 201–221. ICSE’97 SCM-7 Workshop, Springer Verlag, 1997. 4. B. Berliner. CVS II: Parallelizing Software Development. In USENIX, Washington D.C., 1990. 5. S. Brandt. Towards Orthogonal Persistence as a Basic Technology. PhD thesis, Department of Computer Science, University of Aarhus, Feb. 1997. 6. J. Buffenbarger and K. Gruell. A Branching/Merging Strategy for Parallel Software Development. In System Configuration Management [21], pages 86–99. 7. H. B. Christensen. Experiences with Architectural Software Configuration Management in Ragnarok. In B. Magnusson, editor, System Configuration Management, Lecture Notes in Computer Science 1439, pages 67–74. SCM-8 Symposium, Springer Verlag, 1998. 8. H. B. Christensen. Ragnarok: An Architecture Based Software Development Environment. PhD thesis, Department of Computer Science, University of Aarhus, June 1999. 9. H. B. Christensen. The Ragnarok Architectural Software Configuration Management Model. In R. H. Sprague, Jr., editor, Proceedings of the 32nd Annual Hawaii International Conference on System Sciences, Maui, Hawaii, Jan. 1999. IEEE Computer Society. 10. J. Estublier, editor. Software Configuration Management, Lecture Notes in Computer Science 1005. ICSE SCM-4 and SCM-5 Workshops, Springer Verlag, 1995. 11. J. Estublier and R. Casallas. The Adele Configuration Management. In Tichy [24], chapter 4. 12. IEEE Computer Society Press. Proceedings of the 18th International Conference on Software Engineering, 1996. 13. D. B. Leblang. The CM Challenge: Configuration Management that Works. In Tichy [24], chapter 1. 14. Y.-J. Lin and S. P. Reiss. Configuration Management in Terms of Modules. In Estublier [10]. 15. Y.-J. Lin and S. P. Reiss. Configuration Management with Logical Structures. In Proceedings of the 18th International Conference on Software Engineering [12], pages 298–307. 16. O. L. Madsen, B. Møller-Pedersen, and K. Nygaard. Object-Oriented Programming in the BETA Programming Language. Addison Wesley, 1993. 17. B. Magnusson and U. Asklund. Fine Grained Version Control of Configurations in COOP/Orm. In I. Sommerville, editor, Software Configuration Management, Lecture Notes in Computer Science 1167, pages 31–48. ICSE’96 SCM-6 Workshop, Springer Verlag, 1996. 18. M. C. Paulk, C. V. Weber, B. Curtis, and M. B. Chrissis. The Capability Maturity Model—Guidelines for Improving the Software Process. Addison-Wesley, 1997. 19. M. Reiser and N. Wirth. Programming in Oberon. Addison Wesley, 1992.
20. D. Rogerson. Inside COM. Microsoft Press, 1997. 21. SCM-9 Symposium. System Configuration Management, Lecture Notes in Computer Science 1675. Springer Verlag, 1999. 22. C. Szyperski. Component Software. Addison Wesley Longman Higher Education, 1998. 23. W. F. Tichy. Tools for Software Configuration Management. In Jurgen F. H. Winkler, editor, Proceedings of the International Workshop on Software Version and Configuration Control, Grassau, West Germany, Jan. 1988. B. G. Teubner, Stuttgart. 24. W. F. Tichy, editor. Trends in Software: Configuration Management. John Wiley & Sons, 1994. 25. E. Tryggeseth, B. Gulla, and R. Conradi. Modelling Systems with Variability using the PROTEUS Configuration Language. In Estublier [10]. 26. A. van der Hoek, D. Heimbigner, and A. L. Wolf. A Generic, Peer-to-peer Repository for Distributed Configuration Management. In Proceedings of the 18th International Conference on Software Engineering [12], pages 308–317.