Tuesday, May 12, 2009

Simplicity rules

Many architectures are overwhelming. Not that they shine due to their excellent quality. Rather they appear as a big ball of mud. I already mentioned what I am calling design erosion in the context of architecture refactoring.

To keep an architecture small, expressive and simple we can do a couple of things as software architects.

For example:

  • Each and every design decision must be based on an architecturally-relevant requirement. There is no decision without documented reason. Of course, this requires architects to feel responsible for the quality of these architecture requirements, but not for requirements engineering, of course! It is recommended practice to keep track on where in the architecture which requirements are “located”. Requirements traceability is an essential value, especially when assessing, refactoring or evolving a system. I am assuming here that architects already checked for the feasibility and essence of the requirements from a business perspective. Availability of 99.99999% is not reasonable in a project with a budget of 100k bucks. 
  • Introduce symmetry and conceptual integrity. This includes the prescription of specific patterns, the introduction of design and coding guidelines, as well as the introduction of guidelines for (at least the most important) non-functional qualities. This prevents that the same problem is solved in various ways within the same solution space and it makes sure, we are applying best practices from experts instead of reinventing the wheel. War story: A system where designers have introduced dozens of ways for fault handling will be inexpressive and complex.
  • Don’t consider technologies as toys. Many projects play with technologies. They base their decisions on a technology-first approach. This is human and we as software engineers like to experiment with the new hip operating system or SOA middleware. But in practice, we should first create an appropriate architecture meeting the requirements (see first bullet point) before we should start introducing new technologies. The business and the requirements drive the architecture, not technology.
  • Test technology. The previous point does not mean we should not care about technology – only that we first address the requirements. Every technology must be checked for its appropriateness. If your task is to build a high performant messaging backbone, then you better check whether EJB is really the proper platform to use. Don’t trust any vendor’s promises here! They do not know your problem context and system environment. Building throw-away prototypes is the best way to check technologies.
  • Apply a test-driven development approach: this is to introduce a kind of safety net. Emphasize testability in your architecture design, e.g., by proper modularization, interfaces, and service contracts. Test-driven in this sense also includes regular design and architecture assessment where you try to identify architecture smells.
  • Regular architecture refactoring helps address architecture issues early. When a dependency cycle has just been introduced unintentionally, it is not a big issue to detect this kind of design erosion early with architecture reviews or architecture analysis tools. You are then able to get rid of the problem early by architecture refactoring. The more you wait, the more architecture entities will make it difficult to address the problem without heavy impact. Thus, do regular architecture reviews and refactorings.
  • Strategic before tactical design. Don’t try to introduce over-generic solutions with myriads of configuration options. First address all strategic requirements (functional requirements and operational requirements) before dealing with tactical issues such as modifiability. Use the Open/Close principle to open your architecture for variation.
  • Introduce priorities for all requirements/features. The priorities help decide which requirement to address first in architecture design. If security is more important than performance, you’ll end up in a completely different solution architecture, then wheny performance has the higher priority.
  • Follow an approach of piecemeal growth. In all but the most trivial applications a big design upfront approach will be doomed to fail. Thus, assign requirements depending on their priorities to iterations and build your system incrementally.

 

These are only a handful of recommendations. Certainly, there are even more. Any tipps from your side?

1 comment:

Adam Smith said...

Maybe followings added to this list:
+ Try to understand problem domain better. Misunderstanding is enemy of simplicity. Same problem may be solved more elegantly if problem is better understood.
+ Sometimes we should know to say "No" to some requirements. Some requirements are simplicity-killer that you feel and smell them(Feature creep).
+ We should allocate time for architectural design. Software design should have a parallel architectural design work. If architectural design is not controlled and developed by the architect, every programmer builds their own architecture. Software refactorings and reviwes help to eliminate architectural leaks and decay.