Incremental Design with Standards-Based Products

Products supporting standards have an all-or-nothing flavor. After all, who wants a compiler for only half of a programming language? We’ll look at guidelines that let you develop such products incrementally.

Some examples of such systems:

  • Compilers
  • Phone switching systems
  • The World-Wide Web
  • Information retrieval
  • File formats

Guidelines

These guidelines suggest ways to support a standard in smaller bites:

  1. Implement versions and/or subsets
  2. Apply the Strangler Fig pattern
  3. Replace pre-built components
  4. Leverage implementation-defined behavior
  5. Reduce external quality
  6. Subset customers and/or users
  7. Drive from overlapping rich scenarios

1. Implement Versions and/or Subsets

When a standard seems like “just too much”, you may find that the standard’s authors have already addressed this by defining versions, levels, or subsets.

For example, Z39.50 (see References) is a standard for information retrieval (e.g., searching a library card catalog). It’s got three versions; V1 and V2 are essentially the same, and V3 extends them in a way that all versions can interoperate.

Thus, you can incrementally implement a V3 system by first implementing V2, then extending it.

Standards often have core features that all implementations must offer, then optional extensions. Sometimes these extensions can be negotiated at runtime. For example, a web client can propose file formats it can handle, and the server can intersect that list with those it supports, and see if there’s a way to communicate.

Again, focus first on required features, then optional ones.

2. Apply the Strangler Fig Pattern

Martin Fowler identified the Strangler Fig pattern [see References]. The idea is to replace an existing system by introducing a new system, routing interactions to it for things it can handle, and to the original system for things it can’t. Over time, as you extend the new system, less and less goes to the original. When everything is covered, you can delete the original system and the routing code.

Router code checks whether the new system supports a command or query, then sends it to the new or old system accordingly

The original system need not be your legacy system – you might even use a competitor’s system until your new one is ready. After all, you’re delivering a whole system or product; the “new system” is really a component of the whole system.

3. Replace Pre-Built Components

Sometimes, you may be able to find pre-built components that you can use to create an initial implementation. These components may have problems: poor performance, incompleteness, high cost to deploy or maintain.

But, in spite of their flaws, they can help you build something a level deeper than a “walking skeleton”. This lets you create an early release, then incrementally replace or improve unsatisfactory parts.

4. Leverage Implementation-Defined Behavior

Some standards identify implementation-defined behavior – you choose what to do, you just have to document your choices. The C standard is notorious for this.

You can take advantage of this to reduce your work: start with expedient behaviors, then migrate to more sophisticated behavior over time.

Be careful that your early users are comfortable dealing with these transitions.

5. Reduce External Quality

Standards often relegate some areas to “external quality” or “quality of implementation”. These don’t mean “It’s OK to have bugs”, but rather is a way to let you trade off non-functional aspects.

For a compiler, the questions of “How fast does the compiler run?” and “How fast does the generated code run?” are quality-of-implementation questions. There’s often a tradeoff between these, so the standard doesn’t dictate how you balance them.

When a market is new, it will tolerate poor usability or performance, since just having the functional delights users. As the market matures, non-functional qualities become differentiators, and early players have to level up or lose traction.

6. Subset Customers or Users

Your customers and users are rarely homogeneous. Some may need the whole standard supported all at once, but others may be willing and able to take advantage of reduced functionality. You can work with them to ship and get early feedback.

For example, compilers for students may need fast compilation much more than fast execution. Even if you intend to provide both, you may be able to deliver a fast compiler early.

7. Drive from Overlapping Rich Scenarios

If you can’t subset users, you can still build incrementally by using rich scenarios.

A rich scenario goes beyond single-feature user stories or use cases; I think of it as a cluster of mutually supporting features, jointly able to accomplish something the user cares about. By developing a series of increasingly sophisticated scenarios, you can “cover” the whole system. The intermediate versions may not be complete, but they make sense.

I’m taking this approach with the tune similarity program I’m working on. There are many ways to assess similarity, but I’ve started with a Parsons code, which defines a graph showing whether a tune moves up or down.

My input format is abc notation (see References), a moderately complex format with a 100-page description. The Parsons code analysis only needs part of the information: pitches in the tune – not their duration, loudness, etc.

The result is simple, but my user is delighted with what we have so far. (You might think of this as subsetting users, but I don’t think the one-user subset of a one-user tool quite counts:)

My next scenario adds the notion of parts and repeats. When music has repetition, it’s common to describe it like you would the rhyme scheme of a poem, e.g., AABB. The abc notation has several ways to do that. We’ll use this to build up a notion of a tune as more than its sequence of notes.

After that, I want to work on quantizing rhythms. This is to look at the notes when they occur, not just their sequence. For example, a straightforward “Twinkle, Twinkle” has this rhythm:

Quarter notes

A fancy Mozart version might add extra notes between the main notes:

Eighth notes between the quarter notes

But if you look at both versions as if they were quarter notes (dropping the extra notes from the Mozart version), you would see that the “bones” of the tune is the same.

Quantizing rhythms will support other capabilities, on and on, until I’ve justified every required capability into existence.

Conclusion

It’s challenging to support a large standard, but these ideas may make it easier to do so incrementally:

  1. Implement versions and/or subsets
  2. Apply the Strangler Fig pattern
  3. Replace pre-built components
  4. Leverage implementation-defined behavior
  5. Reduce external quality
  6. Subset customers and/or users
  7. Drive from overlapping rich scenarios

References

The abc music standard 2.1 (Dec 2011)“, by Chris Walshaw. https://abcnotation.com/wiki/abc:standard:v2.1, retrieved 2022-10-31.

StranglerFigApplication“, by Martin Fowler. https://martinfowler.com/bliki/StranglerFigApplication.html, retrieved 2022-10-30.

ANSI/NISO Z39.50-2003 – Information Retrieval (Z39.50): Application Service Definition and Protocol Specification“, https://www.loc.gov/z3950/agency/Z39-50-2003.pdf, retrieved 2022-10-31.