TDD/BDD, Architecture and Frameworks

How do frameworks and software architecture fit into a TDD or BDD environment? There are several currents of thought. Some representative statements might be:

  • In the presence of TDD, we don't need architecture. Everything will emerge as we go developing.
  • The framework will define the skeleton architecture and we'll implement the user stories with TDD
  • Architecture and a good domain driven design will take care of the majority of the requirements and once we know which classes we want to use, we evolve them using TDD
  • Give me a comprehensive domain model and we don't need TDD at all
  • Frameworks are evil. We don't want them anymore because the make us loose domain semantic. They are too restrictive, complex and make the code harder to understand.
  • Frameworks are the best tool ever! The framework we use is so complete that pretty much everything we need is made by itself so we just use it and develop very fast

I am starting thinking that all of them are partly incorrect. There is a place where the architecture, the framework and the practice of TDD meet and I believe it is in the integration level. Let see if I can explain this with my (apparently terrible) English.

Software products are most of the time designed to manage or manipulate data. There are exceptions, i.e a calculator doesn't need to store data, but most of the time we need a third party subsystem to help us. We need a data source and a data store. Frameworks help us integrating our code with all these third party subsystems avoiding the need to write the plumbing again and again. So at this point, I'd say it is silly to neglect the importance of a good framework. The problem arises when you want the framework to implement your business logic, that is, the user stories. When you design your product in a way, that changing the framework is impossible.

If the software construction process is really good, changing the framework you use, has to be possible and take just a couple of weeks (this depends on the size of the project but it should be decoupled enough). If you relay too much on the framework or don't take the time to design the project architecture, you might have trouble getting rid of the framework, even if you practice TDD/BDD.

The remaining question is, why do we need software architecture if we test-drive the design? Easy: because we have to make decisions beyond user stories, beyond business requirements. They add value to the business although it is not explicit.

Steve McConnell, in his book Code Complete enumerate the following architectural components:

  • Program Organization: Major building blocks of the product and its responsibilities.
  • Major classes
  • Data Design
  • Business Rules
  • User Interface Design
  • Resource Management
  • Security
  • Performance
  • Scalability
  • Interoperability
  • Internationalization/Localization (i18n, l10n)
  • Input/Output
  • Error Processing
  • Fault Tolerance

In my opinion, in the presence of TDD and a good framework, you don't have to think of some of these components. I like my classes, data design and business rules to emerge from doing TDD. I like to delegate security, resource management and interoperability in the framework and also part of other components.

However, I recognize that we have had trouble with our current project because of just doing TDD and not thinking of error processing, fault tolerance, user interface design and i18n/l10n. Fortunately we thought of security since the beginning so that with the tools provided by the framework we are comfortable so far. We made the mistake of not thinking in the architecture enough. I though,... "ok, the framework is MVC so that is our architecture and the rest of the things will emerge". I wrote a post on our architecture for the web here.
Michael Feathers has recently written on this excess of "TDD fixes all the problems". Mark Seemann has also written on this.

Because we never defined a way to process errors, at some screens we show the errors in a way, while other times we manage the errors in a different way. This is not terrible but affects the user experience. The worse thing is that in order to fix it, we need to review every feature to see what we decided at that point. With more thinking we could have applied TDD in a more homogeneous way so that all errors of a kind would have been managed the same.

Regarding fault tolerance, we have situations where we have to deal with a not sent email. Something went wrong, the email wasn't sent so we have to send it again. Same happens with messages to twitter or other social networks. As we didn't thought on this in high level, we have classes that solve the problem one way while other use a different mechanism. It is not terrible because it works, the problem is that in order to know how to recover from this faults, we have to inspect the code to see whether for that case we wrote something in the log, or we created a record in database or what. Again, the lack of homogeneity make us loose more time than we like and make the learning curve of the system bigger.

Not taking into account i18n and l10n (because the framework was supposed to deal with that) has lead to a situation where many encoding bugs went into production despite of doing TDD. I've written about encodings and Python here. Again the problem is that now, if we want our product to run in a new country or language, we have to do regression tests manually because there are so many things to take into account. Not only the language but the locales, geopositions and things like that. Some of this problems couldn't have been detected upfront, but we should have written an architectural document while solving them so that the next time, we know how to evolve the classes homogeneously.

These are the major drawbacks we've found so far, not thinking enough in the high level details.

Architecture is great to understand the big picture, frameworks are great for plumbing and TDD/BDD is great to design and evolve user stories, acceptance criteria, business requirements that customers understand. According to Steve, architecture should be as independent of the environment as possible because you avoid the temptation to overarchitect the system or to do a job that you can do better during construction (with TDD!). The architecture should tread the line between underspecifying and overspecifiying the system.

The problem I see with people that loves architecture is that they go too far into the details of the system. They force you to implement the business in a way that is awkward, wrong. Even if they use Domain Driven Design techniques, I find it a handicap to define some models upfront. Finding the right balance between architecture, use of frameworks and emerging design have a great impact in the quality of the product.