The Eigenbase Project

Software Inventory Process

As new software components and features are contributed to the project and existing ones evolve, it is important to maintain an up-to-date inventory of components and their states. This document defines our inventory process.

Goals

By maintaining an inventory, here is what we hope to achieve:
  • Publish useful annotation and indexing on top of the automatically-generated component lists produced by tools such as Javadoc and Doxygen. This benefits developers trying to navigate such a large project. Where possible, we would like to devise inline annotation mechanisms so that the inventory report can be generated automatically by running tools which process the codebase as input.
  • Raise developer awareness of what already exists to encourage controlled reuse. By controlled, we mean that some components may not have been designed for reuse, or may have immature public interfaces; by tracking component states, we can encourage reuse where appropriate and discourage it where it will lead to unmaintainable dependencies. In many cases, design pattern reuse may be possible even where component reuse is not a good idea.
  • Provide candidates for code inspection and design review processes. Component states can be used as prioritization factors.
  • Detect opportunities for standardization, refactoring, and stabilization.
  • Guide component owners in following best practices and managing the code lifecycle.

Component Categories

Components come in a variety of shapes and sizes; they may be individual classes, Java packages, C++ modules, or larger combinations of these. In our current codebase, a number of abstract categories can be imposed to explain the purpose of various components. Compound components (e.g. many Java packages) may fall into more than one category simultaneously. (GUI categories are left out since we don't have any yet.)

Category Description Examples
API An application program interface is a means of telling a system what to do. API's come in many forms, including Java interfaces and their C++ equivalents, command-line interpreters, and parsable languages. Some API's such as JDBC are defined by official third-party specifications; others are public specifications defined by Eigenbase; still others are internal to Eigenbase, meaning the "application" can be a higher-level component in the system. package java.sql; package net.sf.farrago.session
SPI A service provider interface is a well-defined extension point in the system. An SPI defines the interface and contract a plugin must implement in order to be usable by the system. Optionally, an SPI may include a set of abstract base classes which plugin implementations are required or encouraged to subclass. The difference between an API and an SPI is that an API is designed to be called from any component in an unconstrained fashion, whereas an SPI is designed to be called only from specific components being extended. package java.nio.charset.spi; package net.sf.farrago.namespace
class library A class library is a collaborating collection of components designed for use in implementing larger systems. Some class libraries are also designed for extensibility. Whereas an API focuses on action, a class library merely provides a set of building blocks. However, an API may be defined on top of a class library (for example, the class library may provide the "nouns" referenced by the API's method "verbs"). A special case of a class library is an object model: a structural representation for some complex object, such as the parse tree representing an SQL query. STL; package org.eigenbase.sql
functional implementation A functional implementation is an implementation of a specification or interface (typically either an API or SPI) intended for production use. A functional implementation is often self-contained and private except for a factory method which instantiates it. Optionally, a functional implementation may be designed for reuse (e.g. via parameters, configuration files, or subclassing) and/or extensibility (e.g. via SPI's). package net.sf.farrago.jdbc.engine; package net.sf.farrago.db
reference implementation A reference implementation is similar to a functional implementation, but is intended mainly as an example of how to implement the interface, rather than for production use. In some cases, a reference implementation may be the embodiment of the specification ("as long as your behavior conforms to the reference implementation, your implementation is correct"). package net.sf.farrago.rng
mock implementation A mock implementation is similar to a functional implementation, but is intended for use in constructing test cases which need to exercise components in isolation, where those components depend on the interface being mocked. For example, to test an optimizer without coupling it to the behavior of any ruleset's functional implementation, we create a mock ruleset. Besides simple dummying, mocks can also be parameterized and designed to record events, allowing them to be used to probe the functioning and error paths of the component being tested. class org.eigenbase.test.MockCatalogReader
utility A utility provides useful methods for manipulating objects or defines isolated classes (as opposed to a class library, which defines a collection of classes intended to be combined). A typical pattern in the Eigenbase codebase is to define a non-instantiable class with static methods all related to a particular purpose such as manipulation of JMI objects. Besides classes and methods, utilities may also be small programs invoked as part of the build or runtime. package org.eigenbase.util; class net.sf.farrago.query.FennelRelUtil; program class net.sf.farrago.catalog.codegen.ProxyGen
test harness A test harness is a component designed to assist in testing other components. class net.sf.farrago.test.concurrent.FarragoTestConcurrentCommandGenerator
test A test case is a component which tests one or more other components; a test suite is a collection of test cases. class net.sf.farrago.test.FarragoServerTest

Component Maturity

Regardless of categorization, components grow, evolve, merge, split, and die. To assist in tracking this lifecycle and managing its impact on related components, we define the following maturity stages:
  • vapor: The component exists only in a design doc or someone's head.
  • experimental: The component is still under development and not ready for general use. Experimental components are typically meaningful only to their creators, and so will usually not have any dependent components (except possibly other experimental components).
  • fluid: The component is working and may be part of a larger system. However, it is either still undergoing rapid development or has not been "cleaned up" enough to be able to predict what kind of changes it will undergo in the future. If the component has a public interface or extensibility points, they may not be consistently or clearly defined. Dependencies from other components are possible though discouraged, and are always on an "at your own risk" basis, meaning changes to the fluid component will often involve changes to dependent components as well, and these changes may take place without consultation. Extreme programming methodology is appropriate in this phase.
  • congealing: The owner of the component has done enough preparatory work that it is worth review by others, typically with the intention of reaching the solid state. However, it is rare for components to go from congealing to solid without at least minor refactoring.
  • refactorable: The component has been reviewed and the action items from the review involved refactoring. Refactoring may involve splitting the component up, refining or generalizing its interface, merging with another component, internals cleanup, deprecation, etc, and may involve several review/refactor iterations.
  • solid: The component has been reviewed and refactored as necessary, and all parties involved in the review have verified that the results are worthy of being designated solid. If a solid component is designed for reuse and/or extensibility, then creating dependencies on it is encouraged. All dependency-breaking changes to a solid component require consultation and review before proceeding. The parties involved may also agree that after a particular release, a solid component will revert to fluid status to allow major changes to be made.
  • frozen: The component's interface has been frozen and cannot change except under exceptional circumstances. This status will usually only be reached for interfaces which have become part of an official specification. In some cases, implementations may be frozen or near-frozen as well, at least temporarily, to assist stabilization of other components.
  • deprecated: The component is being phased out (ha ha) and may be deleted in a future release. Dependencies should be removed as soon as possible.

Documentation

Another aspect tracked in the inventory is a characterization of the documentation available and what remains to be documented. Documentation may be at the system-level, feature-level, or component-level, so a number of different kinds of documentation may be applicable to a particular component:
  • code-level: Javadoc, inline comments, etc. (TODO: link to style guide)
  • functional specification: where appropriate, this should be by reference to an official specification or standard, with the focus on Eigenbase-specific conformance levels, implementation-defined choices, etc. For features which are unique to Eigenbase, a comprehensive functional spec of quality equivalent to the output of a JSR should be the goal. (TODO: template)
  • design: an explanation of a component's implementation, with reference to a functional spec if one exists. (TODO: template)
  • end-user: documentation on how to use a system or feature, understandable by an external user. This is not currently a primary focus for Eigenbase. However, a HOWTO, man page, or syntax guide is worth the effort.
  • developer: documentation on how to integrate, reuse or extend a component. A HOWTO which walks through a self-contained example can provide much more bang-per-buck than trying to write a comprehensive SDK guide.

Component Quality

The inventory also tracks the tests associated with each component or feature. Test categories are listed below (TODO: move to a separate test process doc and link from here).
  • Unit testing (component-level and feature-level)
    • positive conformance
    • negative conformance
    • error path
    • coverage and boundary value
    • concurrency
  • Integration testing
    • checkin acceptance
    • specific component combinations to be certified for release
    • matrix (exhaustive)
    • matrix (randomized)
  • Extensibility testing
    • mock SPI provider implementation for testing consumers
    • harness for testing SPI providers in isolation
  • Higher-order testing
    • security
    • recovery
    • regression
    • compatibility
    • system
    • application
    • performance
    • stress

Example

Putting it all together, here's an example of the kind of raw data we'd like to see in the inventory (a variety of views and reports can be generated from this):

Subproject Component Description Categorization Maturity Documentation Tests
Farrago package net.sf.farrago.session Defines the Farrago session management interfaces. API (sessions), SPI (personalities) fluid javadoc, HOWTO for SPI; needs functional spec and developer docs integration only; needs API mocks, SPI mocks, API unit tests, SPI harness
Fennel disruptivetech/calc Implements an SQL expression calculator API, SPI (extended instructions), functional implementation, class library congealing functional spec, HOWTO for writing extended instructions; need to update functional spec, write design doc, add more developer docs unit, some integration; need SPI mocks and harness
... ... ... ... ... ... ...


Copyright (C) 2005-2005 The Eigenbase Project. All rights reserved. Contact: info@eigenbase.org

SourceForge Logo