”A designer knows he has achieved perfection not when there is nothing left to add, but when there is nothing left to take away” ~Antoine de Saint-Exupery

How To Design A Good API and Why it Matters

Posted: February 27th, 2009 | Author: maudrit | Filed under: Design Principles | No Comments »

Great talk by Joshua Bloch that complements the Good Code topic (see post below).

Copyright: All rights reserved by creator.


Good Code

Posted: February 25th, 2009 | Author: maudrit | Filed under: Architecture, Design Principles | No Comments »

Good code is key for the long term success of any software based system. We all strive to produce it; we all believe we do our best when crafting it; we all like it; … but,

What is good code?

The fact that it compiles and actually works (and is in production) does not made any code good code. A good code based displays the following characteristics:

  • It’s clean - the code is predictable; each line of code is what you expect it to be. You never find yourself asking what does this line means?, why is here?.
  • It’s simple - it doesn’t matter the domain; it doesn’t matter how complex are the use cases; good code should be simple. There are always simple and elegant solutions for complex problems if the right design principles are applied.
  • It focus on the problem it solves.
  • It’s DRY (don’t repeat yourself).
  • It has Interface Segregation - the code based contains well defined fine grained interfaces that are client specific.
  • It demonstrates good separation of concern.
  • It’s been implemented using a pattern-based design.
  • It contains well defined conceptual boundaries.
  • It demonstrates appropriate dependencies management.
  • It’s documented.
  • It has decent test coverage - code that is not backed by an appropriate number of automated tests is not considered good code.

Most of the things described above are well known by most people, achieving them it is a total different story. So, what is involve on crafting good code?

Achieving good code

The following design principles, architectural qualities and general guidelines provide you with a start point for achieving good code.

Design principles

(Robert C. Martin; Agile Software Development: Principles, Patterns and Practices)

Class Design:

  • Single responsibility - a class should have one, and only one, reason to change.
  • Open closed - you should be able to extend a classes behavior, without modifying it.
  • Liskov substitution - derived classes must be substitutable for their base classes.
  • Interface segregation - make fine grained interfaces that are client specific.
  • Dependency inversion - depend on abstractions, not on concretions.

Packages Cohesion:

  • Release reuse equivalency - the granule of reuse is the granule of release.
  • Common closure - classes that change together are packaged together.
  • Common reuse - classes that are used together are packaged together.

Couplings between Packages:

  • Acyclic dependencies - the dependency graph of packages must have no cycles.
  • Stable dependencies - depend in the direction of stability.
  • Stable abstractions - abstractness increases with stability.

Architectural quality

(Inspired in some of the talks by Juergen Hoeller)

Conceptual Boundaries:

  • A module is a collection of specific packages combined into a logical conceptual unit. Modules are characterized by low coupling and high cohesion.
  • Modules are a conceptual unit.
  • A module is a unit of comprehension.
  • Separation of physical tiers is not a conceptual modules separation.
  • Typically modules are driven by deployment needs (usage scenarios) but that does not fit under the conceptual module definition.
  • Module should be driven by domain boundaries (conceptual boundaries related to a specific problem).

Layers:

  • Layers allow the organization of different modules into a hierarchical structure.
  • Layers are driven by visualization intent.
  • Modules are vertical slice; layers are horizontal structures; in many scenarios there are orthogonal to each other.
  • Layers must keep unidirectional dependency.

General Guidelines

  • Document conceptual boundaries in architecture documents. Establish well defined naming conventions.
  • The core structures needs to be in the proper conventions upfront.
  • Trade-off happened during the evolution of the code base, you want the code to evolve without deterioration, try not to compromise the architectural quality of the system/modules.
  • Enforce very strict architectural guidelines. Try to keep Backward compatibility, but re-evaluate if it’s compromise architectural quality.
  • Embrace unit testing and contentious integration. Automatic testing is a must have.
  • Shot for the higher test coverage possible (anything between 80% and 90% is good). 100% is unreal, most coverage tools won’t give you that number.
  • Adopt and use code metric and analysis tools. Plan refactories based on the results obtained from the metric and analysis tools.

If up to this point you still believe you have been producing good code, look at the mirror and tell me if the following symptoms sounds familiar; Is your system suffering from any of these?

  • Rigidity - tendency for software to be difficult to change.
  • Fragility - tendency of the software to break in many places every time it is changed.
  • Immobility - inability to reuse software from other projects or from parts of the same project.
  • Viscosity - comes in two forms: viscosity of the design (it’s easier to implement a hacks than mentioning the spirit of the design/architecture), and viscosity of the environment (development environment is slow and inefficient so people prefer to take shortcuts or move away from the process).

Thoughts?