I want to tell you of a beautiful, elusive dream. It is an idealistic goal I call “The Living Specification.” The Living Specification is the idea that we can continue to update a specification, the description of an application’s behavior, throughout its life. I will detail why this is so challenging, and the steps that Grio can take to make this dream a reality.
The Yawning Crevasse of Doom
In a 2008 talk entitled “The Yawning Crevasse of Doom,” Martin Fowler and Dan North highlighted the vast gap in communication that tends to exist between clients and developers. One of Fowler and North’s main solutions to this problem was establishing and continuing to specify the domain of an application. While Grio is a communication-focused company, it is this continued documentation where we could continue to improve.
Documentation Vs. Specification
Throughout an application’s lifespan, both the documentation and the specification must be kept up to date. Documentation is the technical information that describes what an application does and how it works, and is essential to the success of the development team. The specification, by contrast, is a generally accessible, big-picture view of the application. The specification allows developers to successfully communicate with the client team and ensure that the overarching goals of the project remain intact.
Specification Pain Points
A specification spells out the requirements of an application. As an application progresses through its life, it is common for the specification to fall into disarray. Some of the most common pain points when dealing with specifications include:
- Nonexistent or Incomplete Specification: When a project is first created, the specification is often incomplete. The client may have a general idea of what they want, but the specifics have not been decided. We respond to the client’s requirements, but we rarely record them in a permanent way, leaving the specification to fallible memory.
- Outdated Specification: When a specification is not maintained, it no longer represents what’s actually going on in the application as it progresses. This creates issues when a new developer joins the team, as they won’t have a clear understanding of what the product is supposed to do and may be unable to identify problems that arise. Furthermore, the client often has only a general understanding of the product, and may not know the specific intentions behind any user interaction. An outdated specification can actually be more harmful than no specification at all.
- Forgotten, Lost, or Abandoned Specification: While documentation can help developers keep track of what has been implemented within the application on a technical level, it is not typically accessible to the client. When a specification is forgotten, lost, or abandoned, developers lose the high-level communication with the client that it provides.
The first element that is important in ensuring that a specification will live beyond the release of the product is the adoption of a ubiquitous language. Ubiquitous language, a term introduced by Eric Evans in Domain-Driven Development, is a glossary of terms that are used in the project.
As clients and developers discuss the project, they are constantly evoking the ubiquitous language for the project. However, if it is not documented, the meaning of terms can shift and change, leading to miscommunications, sometimes disastrous.
For example, a recent project revolved around the concept of a “project” and the concept of a “campaign”. In the data schema a campaign was defined as a subtype of project; however in high-level discussions, the two words were used interchangeably. Some developers were confused, resulting in a number of bugs later in the development process.
To bring the specification to life, it needs to correspond to the implementation in an ongoing way. It has to be embedded in the program in some way. I’ve divided these into imperative/declarative solutions and verification-based solutions.
There are several imperative and/or declarative solutions that may be utilized for this purpose:
1. Rules Engine: At its simplest, a rules engine is a set of “if, then” statements. The rules engine chains these statements together and executes them, which changes the state of the system. This process works in a cyclic fashion in which the rules engine identifies that a new state of the system has been introduced, determines which rules need to be executed, and then brings the system into alignment with those rules.
The benefit of a rules engine is that the rules are more or less readable by a general audience. Using a rules engine in the kind of software that Grio typically develops, however, would have multiple drawbacks. First, because of the way that rules are “chained” (sequenced), it is difficult for a human to predict (or engineer) the final outcome of any given operation. This kind of apparent indeterminacy is totally counterproductive. Second, a rules engine will only cover a specific subset of the behavior of a program. The boundaries between business logic and everything else need to be kept immaculate. In a typical application, however, system state changes in multiple locations, making a rules engine difficult to implement.
2. Domain-Specific Language: Martin Fowler (of the aforementioned crevasse) was a great proponent of domain-specific languages. DSLs are specifically tailored to the problem domain of the application. They encapsulate a limited set of operations and features that can be easily understood by the client. However, the implementation of a domain-specific language is complex, and it would be overkill for most projects. DSLs also suffer from the problem with rules engines, outlined above, in that they need to be isolated to a core functionality related to business logic, impractical for many applications.
3. Logic Language: Used in a standard application, a logic language (such as Prolog) would serve the same function as a rules engine. However, a logic language is much less comprehensible to the client, making it less beneficial as a living specification solution.
The second set of solutions that I considered were verification-based solutions. These solutions verify the application rather than defining it. The most prominent verification-based solution is Behavior-Driven Development (introduced by Dan North, also of the crevasse, above).
Behavior-Driven Development has been around for some time. In fact, many of the elements are already utilized at Grio, including:
1. Acceptance Criteria Standard: The acceptance criteria standard is a system that uses “given, when, and then” logic:
- Given: The initial context at the beginning of the scenario, in one or more clauses.
- When: The event that triggers the scenario.
- Then: The expected outcome, in one or more classes.
Acceptance criteria standards are often utilized at Grio. They are usually employed in the context of task descriptions, however, and not captured as a referenceable specification.
2. Functional Tests: We perform large-scale black-box testing, such as integration testing, using the language and tools of functional testing (for example, RSpec). What we lack is a specified behavior — we usually end up testing that the system continues to work as initially implemented, rather than as expected.
However, to create a living specification, there are several features that we still need to incorporate into our projects.
Moving Grio Towards a Living Specification
Grio is already approaching something resembling a living specification. With the tools we are already using, we could produce specifications that stayed up-to-date with applications and allowed clients to track exactly what their application is doing at any given time.
The main features that we still need to implement or improve to achieve a living specification include:
- Maintain a Ubiquitous Language Glossary
- Capture core requirements as acceptance criteria with consistency
- Write functional tests to demonstrate requirements
- Publish functional tests legibly to team members
With these small changes, we have the potential to move Grio away from outdated and inaccurate specifications and into the realm of superior client-developer communications.