Maintaining Design Integrity Over Time

Joseph Crick
5 min readJan 8, 2018

--

Software systems change. Ideally, they evolve. “Evolve”, as it’s used here, means they improve. They become simpler — and more powerful, useful, and enjoyable to use. This is true from both the user’s and the developer’s perspectives.

Adding new features, or tweaking performance doesn’t necessarily correlate with evolution. Nor does a refactor. Either can come with the cost of bugs, or increased complexity.

In fact, the more changes introduced into a software system, the more susceptible it is to entropy. “Entropy”, here, refers to a progressive decline. More complexity, more defects, worse performance, &c.

Well-designed systems provide paradigms, patterns, and protections that help them evolve while limiting entropy. The extent to which a system continues to adhere to its design principles is its Design Integrity.

Maintaining design integrity as a product evolves can be challenging. Developers come and go. Documentation, if there is any, ages and becomes less useful. Time passes, and people forget things.

What can you do to help maintain your system’s design integrity, over time? A lot, actually. We’ll look at three options:

  1. Design-first approach to development
  2. Verification systems
  3. Workflow practices

Design-first Development

…the pendulum has swung from “design everything” to “design nothing.” But the alternative to [Big Design Up Front] isn’t no design up front, it’s a Little Design Up Front (LDUF) or Enough Design Up Front — ENUF.
— Steve McConnell

There’s an old adage in woodworking: “Measure twice, cut once.” If you spend the time to prepare what you’re doing — before you do it — you’re less likely to run into problems. In software, this means doing some design up front — ideally, “Enough Design Up Front”. (1)

McConnell defines ENUF as follows:

How do you tell how much is enough? That’s a judgment call, and no one can make that call perfectly. But while you can’t know the exact right amount of design with any confidence, two amounts of design are guaranteed to be wrong every time: designing every last detail and not designing anything at all.

Doing ENUF has several benefits:

  • It helps you understand the big picture.
    The majority of the complexity in an application isn’t in its components. It lies in the relationships between components. Understanding the big picture helps you see — and, thus, create — conceptual integrity. An ideal application will have concepts that work well together, and “make sense”. For example, it can have its own language for accomplishing tasks.
  • It gets you iterating right away.
    Yes, refactoring is important. Yes, you will probably refactor your code even if you do some design up front. However, it’s cheaper and faster to iterate on designs, than it is on the code. Too, if you do some design up front, you’re more likely to iterate earlier in the development process — and, therefore, produce better code.
  • It helps you manage complexity.
    The Prime Directive in software development is managing complexity. If you don’t think about design first, it’s a lot easier for complexity to find its way in to your application — as things are added based on the whims of developers. Some design up front helps you identify complexity as it begins to emerge. You see the relationships between parts of the system. You begin to think and talk about those relationships.
  • It reduces defects — by a lot.
    Several studies by Jones and Shull showed that design up front accounted for 85% of defect removal in the projects they studied. (2) In comparison, unit tests only accounted for 50%. (3)

A well-designed application is a joy to work with. It’s simple to understand. It evolves easily. It has fewer defects. It’s fast. It’s something you can be proud of.

Recommendations:

  • Do some design up front —ENUF design.
  • Modeling and prototyping have been found to be very effective ways to flesh out designs (c.f., Jones and Shull).
  • The Pseudocode Programming Process encourages design iteration, and the documentation of design decisions.
  • Iterate your designs. The best designs are usually the result of an iterative process. We learn from the process of making, and testing our designs.

Verification

Software verification has many forms. Most devs these days write unit tests. Some write integration and e2e tests. A few write contracts. Even fewer, significantly at Amazon and Microsoft, use TLA+.

In addition to their other benefits, verification methods help preserve design integrity. They codify your design thinking and decisions functionally. You have constraints, considerations, and caveats running against the code (or specification) you’ve written.

What’s valuable here is feedback. Using verification methods you can get quick feedback if you’ve broken a design constraint.

The various forms of verification:

  • provide an infrastructure that helps maintain the integrity of the original design principles by providing feedback when these principles have been violated.
  • communicate the original design principles to new developers.

Recommendations:

  • Pick and use at least two (if not more) verification methods, as appropriate to your application.
  • While somewhat controversial, TDD has been researched and shown to reduce software defects. Its proponents contend that it helps both improve and document design. In this way, it can be argued that it will also help to preserve design integrity. At the very least, unit and integration tests will help with this.
  • Design by Contract greatly improves design. It is especially good at helping preserve design constraints, because it provides immediate feedback when a design has been violated.
  • TLA+ is a formal specification language. It is intended to “uncover design flaws before system implementation is underway.” I do not have direct experience with TLA+; however, those that do sing its praises.

Workflow Practices

Study after study has shown that code reviews can have a dramatic impact on reducing software defects. When paired with documented design guidelines, these become an incredibly powerful tool to help reduce defects, and preserve design integrity.

Requiring more than one code reviewer is even more beneficial. “Studies at NASA’s Software Engineering Laboratory, Boeing, and other companies have reported that different people tend to find different defects.” Only about 20 percent of the errors found by multiple inspectors were duplicates. (4)

Interestingly, research conducted by Caspar Jones found that pair programming was not as effective as programmers working individually. This applied both to the number of defects and the development cost:

Individual programmers who use static analysis and inspections have better quality at about half the cost and 75% of the schedule of a pair.
— Caspar Jones (5)

Recommendations:

  • Have a formal code review process.
  • Don’t let any code into production that has not been reviewed and approved by at least two people.
  • Ensure your designs are well documented, and understood by the whole team.

Conclusion

All software systems change over time. As a result, all software applications experience entropy. A good, coherent design can help protect your application from entropy. A good design, however, is not enough. You must also protect the application’s design integrity over time. Using a design first approach, along with well-known, industry standard verification and workflow methods can significantly influence the quality of your application now and in the future.

(1) Even the XP community, which is famous for criticizing design up front, doesn’t recommend no design: https://www.martinfowler.com/articles/designDead.html

(2) Jones and Shull specifically note the value of modeling and prototyping as design tools that help with defect removal.

(3) Code Complete, table 20–2.

(4) ibid, §20.3.

(5) Software Defect Origins and Removal Methods, Jones, 2012, pg 6.

--

--