Hitchhiker's Guide to Software Architecture and Everything Else - by Michael Stal

Thursday, April 26, 2007

Danger Seekers - A day in the life of an anonymous software architect

  • 08.00h: Scanning the e-mail for any recent project catastrophes.
  • 08.15h: Asking the product manager and the requirements engineer what the customer means with "system should never fail but there is no extra budget for replication hardware and software".
  • 09.00h: Meeting with subsystem developers, because they didn't follow the architecture design nor any guidelines. Told me that they understood every detail from the strategic architecture document I have conceived and written. However, they assume, I were not able to understand my own architecture which is why they tried to illustrate what they think I should have meant.
  • 10.25h: Telephone conference with outsourcing site. They didn't know what interfaces to provide and what interfaces to use. Discussed the same issue over and over again with different people in the past but obviously they didn't share the information in the team.
  • 12.00h: Having lunch with the project manager and a software process consultant for iteration planning. We are behind schedule and aleady exceeded the budget. Our table was surrounded by staff from other projects where they kept complaining about architects, senior management and the rest of the universe. What a motivating lunch.
  • 13.15h: Talking to the testers. They are a little bit unhappy because some managers told them that they don't need to test as this would endanger the release date. According to this manager, the project got high quality developers who provide high-quality code. Thus, testing is overkill. Could raise their motivation after showing them the newest Dilbert cartoons.
  • 13.35h: Talking to an external consultant. He is not familar with the domain, nor with the technologies. To be honest, I am not sure whether he got any expertise. Only recently he got his university degree. But senior management is relying on his statements as he is employed at one of those famous consulting companies. "They never make any mistakes", one of my senior managers mentioned.
  • 14.30h: A middleware vendor is explaining why her product could solve all the project's problems. It can even solve problems we don't have. What a cool technology!
  • 15.56h: One of the developers is calling me. He asks me to explain again why patterns are so important. He fears, applying patterns is reducing his creativity. Moreover, his productivity will also suffer as he must read all those pattern descriptions.
  • 16.20h: The phone is ringing. It is our senior manager. He urgently needs some slides on the ROI of software architecture with some concrete numbers. He obviously knew that this presentation would be required for weeks, but one day before the meeting he is asking me for help. I must keep calm.
  • 16.45h: Speaking with some developers. I just found out they have never built any Java EE application before. Our management thought, they are so smart that they could learn Java EE in a few days. That is what they call training on the job. I've decided to schedule some seminar days where I will provide them a Java EE hands-on-training.
  • 17.20h: Some of the tests have failed. The product managers sent me some vague new customer requirements - all of them with highest priority. Software engineering drives me crazy.
  • 18.20h: Reading the news. The new operating system will be postponed to next year. It is the goal to add some new features in the meantime and to further improve the quality. Going where no man has gone before, as it seems. A friend of mine working on the OS had told me some other background stories. Something about severe quality problems and the like. For heaven's sake, we are not alone!
  • 19.45h: Drinking beer with some project members. Interesting to hear some war stories from earlier projects that failed. After several drinks my colleagues become very informative and open-minded about what they really think about the project. Should I quit tomorrow and search for a better job? No, that wouldn't make any difference.
  • 23.30h: Reading some new technology stuff and one of the cool architecture web sites. This way I am learning a lot about new technologies. But, of course, the best technologies in the hands of some insane software developers or, even worse, managers can cause a lot of harm.
  • 00.30h: Very exhausted now. It is time to go to bed.
  • 06.00h: The alarm clock is ringing. What a nightmare. I had a dream of a meeting with managers who presented their favourite software architecture to me. Then I got the task to improve their architecture. Whenever I was done, a new meeting was arranged where they confronted me with yet another architecture and the whole pain started again. I could feel Sysyphos' pain.
  • 08.00h: A new working day full of joy, honour and interesting conversations is waiting.

Monday, April 23, 2007

On Apples and Oranges

In almost all discussions about the maturity of software engineering, people refer to other disciplines claiming that software engineering is relatively immature. One of the arguments popping up all the time addresses the inability of software engineers to systematically map requirements to specifications. In addition, software components and their composition rather resemble primitive tools such as the ones used in the stone age according to those critical voices. Software enginering happens in an ad-hoc manner while other disciplines have learned how to apply a scientific formalism or a systematic engineering approach. What a shame to be a software developer. A point often made is the industrialization experienced in other domains such as hardware manufacturing where re-use is the norm and not the exception. It is my basic claim that most of these comparisons are more of the apples and oranges kind of style. So let me state some thoughts:
  • While it is certainly true that industrial hardware production happens with a huge amount of re-use of standardized components, software engineering is more like hardware engineering. At design time hardware developers face exactly the same kind of problems when identifying requirements and mapping these to concrete circuit plans. This is the same mixture of craft, art and science software engineers rely on.
  • Software systems experience a lot of more changes during design and implementation than their non-software cousins. Would you ever expect that shortly before a Boeing 767 is ready to be delivered someone will ask the engineers to change the body or engines. But that is exactly what happens in software engineering. Changing an airplane engine during flight? Impossible for aircraft engineers but expected from software systems. Add to this that customer requirements are much more unspecific, dynamic and unreliable in software development projects.
  • Software must run in a lot of different contexts. Take operating systems such as Mac OS X, Linux, Vista as an example. They must support different kinds of hardware systems, different kinds of drivers, different kinds of user preferences. Such flexibility is not provided by any other product. For the same reason, large-scale software re-use such as the component or service market places we often dream of will remain dreams despite of standardization.
  • Some people tend to make humorous comments about software engineering and rocket science. The problem is that in terms of complexity many software developent projects reveal a large degree of complexity. Software engineering sometimes IS like rocket science. Costomers and developers often ignore this complexity which is caused by operational and developmental qualities. The largest software projects are way larger than any comparable non-software projects. Keep in mind that the same holds for all the software tools and infrastructures.
  • An incredible amount of bugs lurk in software systems due to a lack of quality in software developemt. True or false? Have you ever seen the error list of hardware systems such as that of new CPUs? Believe it or not, but I claim that the error rate in software systems is not higher - at least not significantly - than the one in comparable non-software systems. In addition, we should keep in mind testing is much more difficult in software due to the higher diversity of the software ecosystem.

I propose the following experiment: take a bunch of hardware developers who have become software engineers and see whether they are more productive and offer higher quality when developing software than their colleages without that background. From my viewpoint the immaturity of software engineering is a myth. Wrong statements repeated often will not become correct statements. I am wondering what other people think about this.

Saturday, April 21, 2007

Contracts - continued

There are a lot of potential use cases for contracts:
  • In distributed application environments such as SOA (to add one of the inevitable buzzwords here) the interfaces provided by services and components have to be described by contracts, because generator tools require these contract descriptions to generate proxies and stubs.
  • In a container (no matter whether it is a lightweight container or a heavier one) contracts are required so that a container can handle component deployment in a proper way.
  • Developers may follow the guidelines of contract-based programming as introduced by Bertrand Meyer in Eiffel by leveraging preconditions, postconditions or invariants for their abstract data types.
  • All kinds of unit tests or integration tests require at least implicit knowledge of contracts as contract violations are one potential source of quality problems.
  • Contracts are the means to implement and validate Service Level Agreements.
  • In future scenarios of service compositions quality annotations available in contracts might help to determine if a particular workflow can meet quality requirements such as response times.
  • Security feautures and measures re based on contracts that specify who is allowed to do what in a system.

But how can we describe contracts? This is an important question that has to be addressed in this context. Again, we should differentiate between implicit and explicit contracts.

An implicit contract basically says: here is the implementation and whatever it provides to its consumers or expects from its consumers is part of the contract. Implicit does not necessarily imply that there is no documentation of the contract. There might be comments in the source code that describe pre- and postconditions checked or guaranteed by the implementation. Alternatively, the contract might be documented in an API document.

An explicit contract is a contract that is expressed in a kind of contract DSL. A WSDL description is a typical example of an explicit contract description. Of course, the contract DSL can also be expressed with the means of the underlying programming language such as using annotations in Java or attributes in .NET.Another question in this context is whether contract violations are checked and whey they are checked. If a generator takes a contract DSL and generates implementation artifacts that are compliant with the contract, this helps with automating otherwise tedious and error-prone tasks. However, this is far from being sufficient. What about people taking and extending or changing generated code? Compile time checking of contracts by a compiler reveals the same issues. Runtime checking of contracts is the most secure approach while also causing potential performance penalties. Take security breaches such as buffer overflows as an example for the consuequences of not checking contracts. Or consider Java class loading which is checked at runtime. Another example for runtime contract checking is the .NET CLR security architecture where the security contracts may either be expressed programmatically or descriptively.

In architecture modelling languages such as UML you may apply language features like OCL to describe contracts. Obviously, in software architecture design all contracts must be described explicitely without prescribing how these contracts are eventually mapped to implementation artifacts. How do we determine the contracts of architectural entities? External entities we are integrating should already provide contracts. For all other constituents we should take functional and non-functional requirements as the base to defer contracts. For discussing design using CRC cards we could add contract information either on extra cards annotated to CRC cards or introduce a contract part at the bottom of the CRC card. With other words, contract-based programming should be an important discipline for software engineering in general.

Friday, April 20, 2007

What is (in) a Contract?

In component-based and service-oriented systems interfaces represent the core means for describing the boundary of the provider and consumer of a particular, semantically-related aggregation of functionality. In object-oriented systems these interfaces are typically message-based or method-based. Contracts help specifying the properties of such boundaries. Basically, they define constraints, properties, rules and other details that have to be considered when something is crossing the boundary. The most relevant part of the contract are rules defining which messages are permitted to be sent to a message-based interface or what methods might be called on a method-based interface. Obviously, everything returned from the provider (results or faults) to the consumer must also be expressed in terms of the contract. Constraints may define how messages are structured and what parts they contain. It is noteworthy that a contract does not just comprise syntactic sugar but may also specify semantics such as the order of message exchange or method invocations, pre- and postconditions, or even operational qualities like security or performance hehavior. For example, a contract may specify that calling a specific interface method only takes a specific amount of time. The order of method invocations could also be an ingredient of the contract such as the rule: "before invoking read(), you need to invoke open()". Same holds for messsage-based interface where a state machine might define the order and kind of message exchanges. In addition, a contract could contain infrastructural information. E.g.: Which kind of protocol may be used for accessing the interface? Or the location of the implementation artifact! A contract may also include relationships between interfaces such as the required interfaces a component needs or expects from other components.
There is another dimension of contracts. Contracts might be explicit or implicit. In the former case we need description languages to express the contract, while in the latter case contracts are either part of the infrastructure (such as a programming language) or implicitly hard-coded into application functionality.
In contracts we also may find different restrictions for different stakeholders. Think of a C++ class with private, protected and public sections. Depending on whether you subclass or use a class, you might experience more or less access restrictions.
In distributed systems contracts are ubiquitious. Think of EJB with its deployment contracts, component/container contracts, or client/component contracts and think of SOA platforms!
As we recognize, contracts are much more complex and important than we tend to believe.
Why - in the hell - are contracts relevant for softwae architecture? They are essential as they define various essential aspects when dealing with the boundaries of a particular entity such as a service or component. Contracts define the interactions that may take place between the service or components and their clients as well as the qualities and constraints associated with these interactions. Only if there are formal contract descriptions, tools can help with the (automatic) composition of artifacts (e.g., MDSD) as well as with the validation of architectures and implementations.
If you are going to model a new system, keep in mind to describe the contracts of the interfaces introduced. Otherwise, developers or architects won't be able to understand how to access this interface in a proper way. A surprisingly large amount of problems in software engineering is caused by missing or incomplete or inconsistent contracts. Hardware developers won't ever build any system without knowing the contracts of the system constituents a.k.a. specifications. Why should we as software engineers? Don't believe in statements like "contracts are for pussies". Only contract-based development leads to high-quality architectures and implementations.

Friday, April 13, 2007

The Complexity Of Interface Extraction

From day 1 when remoting middleware appeared on the horizon, one of the main challenges for application developers consisted of extracting interfaces from application functionality to allow decomposing applications into a network of objects. The same issue is still relevant to appease the gods of Service-Oriented-Architecture. Unfortunately, many developers have been fooled by middleware proponents constantly praising the transparency caused by middleware. This transparency makes developers believe building a distributed system is exactly the same like building a non-distributed one. Just take your application classes and make them service interfaces. That's all what you need when migrating from a traditional to a distributed universe. Believe this and your project is doomed to fail. What, the hell, is the right approach to extract interfaces for a distributed software system and what are common pitfalls? Want some examples? Here we go:
  • Granularity: Suppose, you have developed a Java class that implements a graph-like structure such a s a tree. You got classes such as Tree and Node. As you are a smart programmer, you already have extracted interfaces such ITree, INode instead of bloating the class interfaces with oceans of methods. When you navigate to a subtree, a method such as navigate(theChildINeed)returns the right child tree. If you now naively convert all of your classes, i.e. the aforementioned interfaces, into remote interfaces, things will rapidly get messy. Each invocation of navigate will now return another remote object. All of a sudden, network traffic will explode. You can't simply develop remote classes as POJOs/POJIs or PONOs/PONIs. For the same reason, it does not make sense to transfer fine-grained data in service invocations. Sequences such as node.setName("Michael"), node.setStreet("Marketstreet"); node.setCity("SFO"); are DONOTs in any SOA or Remoting context. Firstly, they imply a lot of traffic. Secondly, the calls are related and might force the server to keep session state across method calls.
  • Impedance Mismatch: In heterogeneous networked environments (such as SOA) you'll always face the problem of mapping between the object model of your language and the object model of the remotung technology, and vice versa. Whenever you try to transmit a HashTable between a .NET application and a Java EE application over a SOAP connection, things will turn out to be like hell. This does not work without significant work arounds. As a relational database programmer or O/R victim you will now what I am talking about. Most remoting solutions will only provide the least common denominator approach in their object model. As soon as you are trying to integrate the richness of your preferred programming language into that middleware, you are calling for trouble.
  • Operational Qualities: Add to all this the complexity of operational qualities such as security or performance. Basically, you are now forced to combine the security infrastructure of your application with the security infrastructure of the middleware. And you have to take performance of your application objects into account, but also the costs of communication. And you have to integrate distributied transaction monitors with local transaction APIs. And ...
  • Contracts: An interface is not just a matter of syntax. Nor is it just a set of signatures. It is also about semantics such as preconditions and postconditions. A finite state automaton could specify in which order which methods are allowed to be called. The contract might also deal with local policies and define the protocols with which clients can bind to the service. Note, that this is much more than what you would expect from an interface in a non-networked environment.
  • Architectural Balance: Let us suppose, we have magically solved all these issues. How should we compose our application to remote components or services. We might end up in an ocean of components. Or we might end up in an ocean of service interfaces. Architectural patterns such as Layers or Extension Interface are helpful in this context. I must confess that no rule of thumb exists for this architectual structuring. It really depends on the concrete problem context how to address this issue. One really helpful way is to think in terms of roles and responsibilities.

Basically, what all this means is that when you start to develop your distributed system from scratch, you need to thoroughly consider the issues explained above. However, when you are in the situation to integrate legacy code, things are much more complex: you'll need a whole bunch of refactoring activities to reorganize your application in such a way that is will consist of classes and interfaces that easily fit into the middleware ecosystem. In the best case, it might be sufficient to add some wrapper {facade} objects. However, in the case of bad luck you will even need re-engineering efforts.

I tried to shed some light on the complexities of application integration and provisioning of remote interfaces. What appeared so obvious and simple in the beginning, in fact is one of the most complex tasks engineers have to cope with. Efficiently building efficient distributed systems implies thorough treatment of all these issues. Believe me, prevention is better than cure!

Wednesday, April 11, 2007

Oh, Happy Day !

Suppose, you are an architect of a new web portal framework. For this purpose, you came up with a prioritized list of requirements and use cases. Now, you are starting to design the framework. First use case of consideration is login of a user who should then redirected to his personal portal. But now you are starting to consider more details:
  • what if the user provides the wrong credentials?
  • what if you encounter memory outeage when instantiating new objects?
  • what if the communication breaks down?
  • what if you can't read the user's preferences from the file system?
  • what if data already has been locked by someone else?
  • what if the HTTP request does provide a user agent you don't support?
  • ...

You are a smart architect who never forgets any detail, a perfectionist in terms of software engineering. Thus, you are starting to cover all these exceptions in your architectural design. A few use cases later you are lost in design erosion, trapped in a web of entities and relationships.

On one hand, it is essential to consider all of these exceptional cases and list them in the use cases. Most of the exceptional issues are cross-cutting, re-apperaring in different places of your system. On the other hand, if you start to consider them from day 1, you will be doomed to fail in any non-trivial development project.

The recommendation in architectural design thus reads as follows:

  • In the beginning only start with the main flows (the happy day scenarios). Those should be the ones driving and guiding your core architectural design.
  • In addition, you should come up with project-wide strategies how to cope with exceptional cases. Strategic documentation should be made available and mandatory for all developers with description how to deal with exceptions, transactions, synchronization issues, and the like.
  • The exceptional cases will together with non-functional requirements help extending or changing the domain-specific architecture and infrastructural parts.

Of course, it would be fatal to rely on happy day scenarios only. How often, did I encounter finite state machines with all "happy day nodes and edges" where no one thought about the error states, because errors are for pussies. It is not the matter of ignoring exceptional cases. It is more on doing happy day scenarios first to obtain a stable strategic base-line and then thoroughly integrating the exceptional cases.

From now on, you should always think of "happy days" when modeling your architecture :-)

Monday, April 09, 2007

Brothers in arms - SOA and Product Line Engineering

How would you engineer a SOA based system? First of all, you'd need to define all the composite applications and processes which become part of your infrastructure. Then, you are able to defer common services. Obviously, a separate team should be in charge of service engineering and maintain all services as well as their implementations in central repositories. For example, Amazon organizes its development this way. Developers of applications then build upon these services as well as other common parts. They give feedback to the service developers so that existing services might be reorganized, changed, extended or supplemented. Consequently, we introduce a separation of service engineering and application engineering.
Now, compare all this to Software Product Line Engineering (SPLE, see my special posting on that issue):
  • Services and other common parts (such as legacy applications or platforms) are core assets
  • Applications (composite applications and business processes) denote the members of a product line
  • Service engineering resembles domain engineering and application engineering is identical.

Sooner or later you'll reconginze that SOA engineering is best organized as a SPLE process.

Scoping might be a little more difficult but you should read my last posting how we could cope with this challenge. You might also consider to provide a generative architecture as described in another recent posting as well as in what I wrote on ULS.

From my viewpoint the only conclusion to draw is that SPLE and SOA engineering should be considered as two sides of the same coin.

The Scope of Product Line Engineering

In Software Product Line Engineering the first activity comprises scoping which basically means to anticipate all upcoming products that should be in the scope. I was asked by another engineer whether this implies that we need to know all applications in advance which will be part of the product line and if yes, what if the number of applications would be VERY large. So, what does scoping actually require? Scoping does not mean that you really know everything in the beginning. Otherwise it would be very easy to define the commonalities and variabilities. As always, architects have to deal with vagueness and uncertainty in this context. My understandimg of scoping is as follows. We need to have an idea of the products in the program family we are going to build. Thus, the first step would be to define use cases / scenarios that are typically expected by an application in the program family. If use cases differ or if they appear in some applications but not in others they will help discovering variabilities. If they reveal common (generic) parts or are identical for all applications they will reveal the commonalities. But how do we obtain those use cases? Either, the domain itself inherently defines such use cases. Take ATMs as an example. Or, we could take a representative sample of applications from which we derive the use cases.
This brings me to another important point. I was also asked whether product lines are always a simple partitioning in domain engineering and application engineering. No, they are not. Take car manufacturing as an example which many consider as the prototype of Product Line Enginering. If a car type is manufactured as part of a product family, it will contain common parts all members of the family share as well as variabilities such as different colors, chassis, and so forth. Thus, we have domain engineering where we produce the common core assets and application engineering where we build and customize the individual cars. So far, so good. If we think this a little bit further, some of the assets we use will themselves be parts of other product lines. Maybe, the supplier of the entertainment and navigation system will also leverage product line engineering. Or maybe, the car manufacturer will provide other assembly lines where core parts such as the doors of the car are produced in a product line approach. In other words, a product line can be a combination of different product lines intertwined with each other in a pipeline.
But it is also possible to find the other way round? Suppose, we are going to build a generic web shop application for different customers. We are setting up two separate product lines, one for SOHO and another one for enterprise customers. After a while we find that both product lines can be based upon an additional product line where the common parts for the SOHO as well as for the enterprise web shop are developed. In the SOHO and enterprise product lines we are then just adding the deltas. Obviously, such a setting only makes sense if the SOHO and Enterprise Web shop solutions reveal a lot of differences. Otherwise we would better establish one single product line and handle the differences of both shop variants by variabilities. By the way, finding the appropriate approach is also an important outcome of scoping.
In one of my next postings I will address the issue of why SOA engineering can be considered Product Line Engineering. So, stay tuned!

Sunday, April 08, 2007

Generative Software Architecture

When designing really huge and dynamic software and system infrastructures such as the Internet or cellular networks it is very unlikely to come up with a complete structural design in the beginning. You cannot simply draw some architectural viewpoints that cover those systems as a whole. The reason for this is straightforward: such systems are designed to grow all the time and in unanticipated ways. On one hand these systems represent product line architectures. On the other hand they cannot be treated as such in terms of engineering because they have virtually infinite scope. But how can we successfully design and implement systems of that scale or complexity? You might have encountered the term Ultra Large Scale Systems (ULS) in the past months which exactly deals with this problem. As I already explained in another posting a ULS is a system of systems as Linda Northrop and her peers pointed out. An appropriate metaphor often mentioned in this context is building cities. So far, we have built software systems that resemble building single houses or city blocks. In the next step, we are going to build complete cities and even countries. Complexity represents one of the core problems in developing such large systems, because inherent complexity won't disappear. Hence a possible solution must master complexity instead of getting rid of it. The key strategy as in many other disciplines is emergent behavior. In other words: How can we partition our large problem into a set of simple problems and ways to combine these problems to an overall solution? In the world of software architecture this could basically mean different things depending on the scale we encounter:
  • we could define the basic infrastructure and let complex parts be integrated into this infrastructure
  • we could define a set of rules to dynamically build and evolve such an infrastructure as well decribe integration constraints for parts
    interacting with and over the infrastructure or
  • we could combine the aforementioned approaches by defining a small set of rules and smaller predefined infrastructural parts

Those rules are not only axiomatic rules as in a language grammar, but also define constraints and requirements for the parts that are integrated into the infrastructure. In such an approach, the non-functional properties must be derived from those rules and constraints.
They cannot be evaluated in advance for the reasons already introduced. Obviuosly, to cope with future challenges, such a system must also provide extension rules and hooks to even adapt the infrastructure to changing requirements.If you think about it: Why could we come up with a technology such as the Internet with all of its complexity? Internet technologies already
fulfil the properties mentioned above. The Internet consists of smaller infrastructures, e.g., Internet backbones. Technologies such as IP, DHCP, or DNS define a rather simple (but not simplistic!) rule set for extending and evolving the infrastructure as well as for integrating servers and clients into the infrastructure. All those very simple technologies are combined to a highly evolvable and dynamic, large-scale system which is open for extension. Basically, HTTP and the Web are only one of those extensions. As a real-life example take the brain which consists of rather simple constituents and combinations between those constituents, but reveals the most complex emergent behavior we have ever dealt with. From my viewpoint, we should direct our research activities to those generative architectures, especially when dealing with large-scale software systems. Emergent behavior is the key to master complexity in such systems.

Saturday, April 07, 2007

Non-functional requirements

Many software projects don't fail because of functional aspects. In general, experts know very well how to build a system in their domain. The number one technical reason for failure are non-functional requirements. Issues such as reliability, availability or performance have to be addressed in addition to all the domain logic. Unfortunately, many domain experts are not necessarily experts in these non-functional topics. And the efforts to get consistency or availability right can easily outnumber those for functionality. Ask what the CTO of Amazon thinks about this challenge. Thus, my first rule could be described as "always define key developers for the requirements with the highest risks and relevance". These key developers are in charge of teams that explicitely address one or a small number of these issues. The size of these teams and the number of teams depends on your overall project size.
Another important point in this context is desribed as requirements elicitation and requirements engineering. It is interesting how unusable many requirement specifications are. Statements such as "System should provide flexibility" are worthless. What exactly do these stakeholders mean by flexibility? Which parts of the systems should be flexible and when? Of course, you are also doomed to fail if you get a bunch of requirements with no priorization. The same application would look completely different in case security is the number one requirement compared to the same application when performance had the highest priority. So, my second rule simply says "Architects and key developers are in charge to clarify all requirements including their priorities. For each pair of requirements, it should be clear which one has precedence in concrete design decisions". As many requirements might contradict each other, this is inevitable to survive. If your management does not agree to detail requirements or prioritize them, either refuse to be in the project or, more realistic, document this fact and possible implications so that all stakeholders know they might encounter project failure. Unfortunately, non-functional requirements are incredibly diverse. Performance is totally different to security. Even security might mean a lot of various things such as authentication, authorization, confidentiality, name it. Thus, my rule 3 is: "Detail all non-functional requirements in the beginning of the project, maybe establish checklists, and use these information to clarify requirements".
Developmental requirements such as flexibility (e.g., changeability) have to be addressed early only if you are going to design a product line where a Commonality/Variability analysis is of foremost importance. If this is not your primary setting, developmental issues should be treated as a tactical refinement of your software architecture. Thus, the strategic base line architecture is primary influenced by the domain logic and by operational requirements. The integration of developmental issues is a further refinement activity. Considering issues such as extensibility in early phases typically leads to generic and inperformant architectures with instantiations of patterns such as Observer, Interceptor or Strategy. In addition, your job as an architect tends to continue forever in such environments. This is where I'd like to add my fourth rule "Domain logic dominates infrastructure; infrastructure dominates operational requirements; operational requirements dominate developmental requirements". Of course, it is also important to specify when operational requirements appear in the running system (run-time, install time, compile time, ...) and who is in charge to inject them (operator, user, developer, ...).
As of the huge diversity of requirements it is not easy to come up with general rules of thumb for all of them. Some observations might be interesting in tbis context: A given requirement might be invasive (i.e., cross cutting through other parts of your system) or non-invasive (i.e., it is sufficient to add some additional infrastructural components to the architecture). The same requirement might be invasive for one system (security in all its aspects must be supported everywhere) or non-invasive (we just can add a firewall and don't have to care about security at all). A requirement might me measurable such as the response time to a method request or not (such as removability). Requirements that can be measured can be covered by testing, all other requirements must be covered by architecture and code analysis. Last, but not least, my rule 5: "Classify your requirements and provide strategies how to deal with them in architecture design activities".

Of course the overall goal of software engineering must be to
  • Let engineers focus on their domain by strong separation of domain aspects from other aspects.
  • Automate where possible and useful.

There are several efforts to support these goals such as Model-Driven-Software Development, Product Line Engineering, Aspect-Oriented Software Development, SOA.

One way could be to provide DSLs for the domain, additional domains for the most important non-functional requirements, a technology that easily lets project teams integrate these DSLs under one umbrella (e.g., by leveraging AOSD techniques, Semantic Integration). A Product Line Engineering approach could define core assets which become then part of platforms or frameworks. With SOA, platform dependencies could be overcome. Is this just Science Fiction or a reasonable future vision? Let's work on this :-;