Archive for the ‘Architecture’ Category.

Decoupling in all 7 areas of development

There are 7 main development areas and decoupling, especially decoupling using interface-based design, will be most successful when it occurs in all possible areas.

  1. Code – writing code, sharing code, linking to libraries
  2. Test Code – Unit Tests, Mocked tests, coded functional tests
  3. User Experience - UI design.  The UI should be decoupled from code.
  4. Source Control – Code repositories, branching
  5. Build – compiling, versioning, signing, publishing
  6. Install – Any install method such as MSI, EXE, RPM. Any updates or patches.
  7. Localization – Make this independent of build/compiling

If you decouple everywhere, it is easy to remain decoupled. This leads to good architecture – the overall design of your software.

Code

Code should be easy to write and maintain always.

Let’s face it. Coupled code is often the easiest to start with. However, while it is easy to start out using coupled code, it becomes very difficult to use coupled code as a project grows.

Interface-based design is one method of decoupling code. You are still coupled, just instead of coupled to code, you are coupled to an interface that rarely changes and the code that implements that interface is free to be refactored as long as the tests against the interface pass.

If your code is decoupled, you are free to write your code as you will.

Test Code

I often hear someone say they don’t need to use interface-based design because their code is never going to be an API.  This is not true. If you are Unit Testing properly, your code is always going to be an API even if only your Unit Tests and Functional Tests interface with your code/API.

In decoupled code using interface-based design, your interfaces should have 400% coverage.

100% for each of the following:

  • Expected values
  • Null values
  • Empty values
  • Exception causing values

Write Unit Tests that are exhaustive for your interfaces.  The object that implements the interface will be easier to refactor this way because the new refactoring can implement the interface either directly or through an adapter and you will know nothing can be broken in your refactored code because all the tests pass.

All code that touches the system or requires resources not on a clean build system should be mockable. If you use interface-based design your are mockable by default. For your development language, do a search for mocking libraries such as RhinoMocks for C#.  Also, be aware of how to use an adapter or wrapper to allow for mocking, such as SystemWrapper for C#.

User Experience

User Interfaces (UIs) should be developed independent of the back-end code. They should be developed first or at least simultaneously to the back-end code.  This allows for user experience tests to occur early in the process and changes based on the feedback to be easy and quick. Tools to design UIs that allow for customer feedback already exist, such as SketchFlow for rich Windows WPF UIs.

The UI should be decoupled from the code. You should probably have two separate UIs just so you can switch between them to show how the UI is not coupled to the code and can be switched at any time.

Source Control

Decouple your source control. I don’t mean that one team uses Subversion, another GIT, and another TFS. I mean that your project is in its own repository, and has few as possible prerequisites projects as possible. This is made possible by interface-based design. If you have a 10 features, all on top of core functionality, you should have one core functionality repository and 10 feature repositories.

Also, if you source is not decoupled you are tempted to couple your code with other code.  This lead us to build.

Build

Build does not just mean on a build system. Lets start with the developer building on their local machine. A developer should be able to check out a project and build it without any effort beyond the checkout.  Ok, maybe if you are C# developer you need to at least install Visual Studio. But then you should be able to check out a solution, open it, and press F5 to build it and the build should work. If not, your process is broken and developers’ time is wasted.

Now to the build system or CI builds.

Coupled code is hard to build. Dependency loops can occur. Builds have to occur in specific order, and this takes longer.

With interface-based design, you build your interface libraries first and they build almost instantly as they have no real code. Then you simultaneously build everything else.  Your build is extremely fast because you can build everything with different threads simultaneously (or even on different build machines), even for extremely large applications because they only link to the interfaces.

Different parts of your project can and should have different versions. Just because you release YourProduct 3.0 doesn’t mean that the a library hasn’t already matured beyond to 5.1.  And because of your decoupled interface-based design, you can upgrade YourProduct 3.0 to use version 5.2 of the library and it won’t know the difference. This leads right into install.

Install

Installing should be decoupled. If your project is big enough, it is ok to have 100 different installs. Sure, for Windows don’t show 100 items in Add / Remove Programs, just one or a few.  But that doesn’t mean that you don’t have each piece of your product separately installable, patchable, and upgradeable.

Think of a product with four different parts. You should be able to install, upgrade, and patch each of the four parts separately.  The product as a whole can be version 3.0 but different parts can be different versions. You may have a piece that is very mature, at version 5.1 and a piece that was just refactored for your 3.0 release and it is version 1.0.  Your four products could be: 3.0, 5.1, 1.0, 3.0.

If you are properly decoupled and using interface-based design, you can upgrade any of the four parts to a new version without the other three parts having any adverse affects at all.

Localization

It is frustrating to have to wait for a build to see and test your localization.  Sure some people only have to wait for the first build and then can generate their satellite assemblies from there. However, I disagree with any localization strategy that involves build. I cringe when I hear “localization lock down” because the release is soon.  Really? Does it have to be this way?

How can you do this? Decouple text from the software.

Text should stay where text belongs, in text files. Whether it be a property-value-list or an XML, localization should be dynamic. Let the software pull in text dynamically from a text file. Then switching language in a UI is just a matter of changing from one text file set to another (probably as simple as changing a two, three, or five-letter path).

And the text is patchable without build risk.  If you are 1 day from shipping and you realize you have a completely erroneous string, you can edit a text file to fix it without little to no risk.

Edit the text file(s) without a build and run time just picks up the corrected text.

Since the UI was done before or simultaneous to the back-end code, the localization will be done early and changing may be made often.

Conclusion

Decoupling is a massive undertaking and doesn’t just mean to decouple code. Decoupling is methodology that should be used everywhere in all aspects of the software development life cycle.

Feature branching fits the Kanban model – Branching by MMF

I have to say I am a fan of branching for feature or for team.  I think branching for feature, or branching for a Minimal Marketable Feature (MMF), really fits into the Kanban model.

However, decouple first. This article was really good about talking about a decoupling branching strategy.

http://continuousdelivery.com/2011/05/make-large-scale-changes-incrementally-with-branch-by-abstraction/

Once decouple, branch by feature or in the Kanban world, Branch by MMF.

Here are the positive and negatives.

Branch by feature or team (team and feature are similar)

Positive

Indifferent

Negative

Having read all of these. The negatives are important to read.

It seems that a lot of the negatives are eliminated by the following.

  1. Decoupled code
  2. Using Interface-based design (which is a method for decoupling code)
  3. Keeping MMFs as small as possible (which they should be anyway: “Minimal” marketable feature
  4. Merging main to the feature branch daily/weekly
  5. Running tests on CI on feature branches as well as on main
  6. Checkin’s are gated so a failed build/test prevents check in.

Also read the comments as there are real experiences shared there (assuming the author of the comments are sincere).

A successful branch by MMF strategy can be done with any source control tool.

Side Note 1:

I’ve been using GIT lately for a project in my Masters of Computer Science’s Security course. I finally understand why GIT has become the de facto source control for Open Source, because branching on your local machine can be helpful and efficient. The local check out on your dev box IS A BRANCH and can be branched and you can branch by feature or by task locally.

I recently wrote something I couldn’t check in and couldn’t branch it locally with our source control, but I could have with GIT.

Side Note 2:

Another side note: TortoiseGit and TortoiseSVN are both on my dev box now and explorer.exe is constantly at 45% CPU slowing my system.

Architecting a large application with interface-based architecture

Design and architect and write your software solution so that its different pieces can be independent in the following ways:

  1. Develop Independently
  2. Build Independently
  3. Install Independently
  4. Test Independently
  5. Refactor/Replaced Independently

Don’t just separate the concerns in one way and call it good. Separate your concerns all the ways listed above.

What will this do for your project?

  • It will make it so it can be easily unit tested.
  • It will make it so it can be easily built.
  • It will make it so it can be easily installed.
  • It will make it so it can be easily changed: Patched, Refactored, or Replaced.

Wow, all that sounds great, why doesn’t everyone do this?

Because the one area that is harder is the initial development. You have to design a lot more. You have to code different. It can be hard.

However, it is the easiest way overall, just not the easiest way at the start developing.

How do I separate my code into pieces?

There is a though among many that a software tool should do one thing only, but it should do it really well. When you think of your entire project, that should be kept in mind.

The smallest item in a project is the likely the “object”.

The term object is very abstract. It is like the work “thing” in that it can be anything.

So look at it from a high level view. Imagine your software has four pieces.

Decoupled Architecture

Ok, how do these pieces interact? Can you build each piece without having to link to another piece? You should be able to do this.

However, you have to link to something in someway in order to use its code so what do you link to?

Interfaces.

Ok, so you double the size of your pieces by having a piece and an interface for it. These could be eight separate dlls.

If Piece1 needs Peice2, it should link to IPiece2 and not to Piece2. This allows for Piece2 to be:

  1. Any version of Piece2 that implements the interface.
  2. Refactored without breaking the Piece1 build.
  3. Replaced by a completely different implementation
  4. Mocked for testing (really the same as three but used for Unit tests)

Real life example

In real life, applications are database driven. So what if your Piece2 is a database tool and Peice1, Piece3, and Piece4 all need to access the database using Piece2.

If you link directly to Piece2, you have a problem where you must have a real database in order to test your code in Piece1, Piece3, and Piece4.

This is a huge testing burden. This prevents your tests from running as Unit Tests and instead they must be run as Functional Tests.

However, by designing properly using interfaces, you can get back to Unit Tests for Piece1, Piece3, and Piece4 by mocking Piece2.

You use IPiece2 to create a new block called Mock2, which is just a Mock of Piece2, which can be run during a Unit Test. Now all the other blocks are testable.

Lets look at our Independent Architecture rules and see if this design works.

  • Yes – It will make it so it can be easily unit tested.
  • Yes – It will make it so it can be easily built.
  • Yes – It will make it so it can be easily installed.
  • Yes – It will make it so it can be easily changed: Patched, Refactored, or Replaced.

If you have a large application, this is one design pattern you should look at.

Other Resources