To derive these principles, we need to identify the objects that correspond to the version-control abstractions and which dependencies we wish to minimize and manage. Each version-control object is a container that encapsulates its associated state (data/contents). The key behavior a container exhibits is that of evolution: either the container's contents are allowed to change, while still being the same container, or else the container (but not it's contents) undergoes a change of state as it evolves. Any time a container evolves, there is some set of characteristics (its interface) that it tries to preserve within its own context. The part that is unchanged by the evolution operation is an important part of the context for the object.
- Changes take place within the context of a workspace (sandbox) in which elements are created/revised and then built/tested before the change is considered complete.
- Workspaces (sandboxes) are populated, and then subsequently updated, with the appropriate "base view" of files & versions from the codeline. Some of these are modified or new files created as part of the change that is to be committed (checked-in) to the codeline.
- Configurations of the codeline are created every time a codeline or workspace is updated. Each change is against an existing current configuration and results in a new configuration that, when checked-in, becomes the new current configuration of the codeline.
- Baselined versions are created when we tag/label a checked-in configuration with a meaningful version name that corresponds to the contents of that configuration at that point in time. Once identified, these baselined configurations typically undergo one or more levels of quality assurance before they can be ultimately released.
- Codelines (streams) encapsulate an evolving current configuration of a set of files and other objects that are eventually built and tested together. A codeline represents one flow of changes to an evolving configuration.
The principles of OOD refer almost exclusively to classes or packages, but we will need to determine how they apply to all of the above. Some OOD principles may apply to more than one of these version-control objects, possibly resulting in multiple principles. We must keep this in mind as we derive our principles of version-control. A first pass through the principles would seem to suggest the following:
Single Responsibility - Cohesive Flow
The Single Responsibility Principle for OOD says that, “a class should have one and only one reason to change.” This is a statement of cohesion for a class, that it should encapsulate a single coherent and cohesive responsibility. Restating this in terms of version-control would yield: A container should have one, and only one, reason for its state to change. This is a statement about cohesive flow of evolution. Every evolutionary step should happen for a distinct reason and all the things that changed should have been very closely related. This an extremely generic statement and would probably be a lot easier to understand if we broke it down by the various kinds of containers to which it may apply. We'll do that later but, for now, let's press onward.
Open-Closed - Identity Preservation
The Open-Closed Principle for OOD states that a class should be “open for extension, but closed for modification.” Translating this into version-control terms is a little bit tricky. Extension corresponds to deriving or evolving a new entity from an existing entity. If we add to the existing entity, we create a new one that reuses the essential characteristics of the original. But I shouldn’t have to modify the original entity in order to do it? What does that mean?
The original entity or container corresponded to a particular meaning at a certain point in time. Evolving it into something new shouldn’t destroy the existence or history of what it evolved from. The evolved result may be new, but this is version control, and the thing that evolved must somehow maintain its identity or essence. This is a statement about Identity Preservation: A container should be open for evolution, but closed against redefinition. What does this mean? We'll, say more about that later too. Onward again!
Liskov Substitution - Evolution Integrity