Tuesday, February 27, 2007

Previous posts in the Factory 201 series:


After reading all of our previous posts (don’t tell me you skipped one!) we know what automated assets to build, which tools and technologies to use and how to build a factory. The final step here will focus on the quality attributes we have to keep in mind during the design and development of our factory in order to make it a stunning success! In other words, “What should we strive for?” when building our factories?

Abstraction

In a previous post “With what would you build it?” we already mentioned the power of using abstractions in our factory. Let’s dig in this a little further and see why abstraction is definitely the top thing we should strive for!

Increased Productivity

One of the advantages of using abstractions in our software factory is significantly increased productivity. We can think of an abstraction as a simplified representation of a specific problem. In fact, we are probably using abstractions in our job as a developer or architect every day. (i.e. 4G programming languages, class diagrams, API’s, class libraries, frameworks, DSL’s etc.)

Think about it, for example, if we build a helper class to perform a specific task (say: logging to a file) we are actually using abstraction. We create a class and some methods that simplify the details of logging, file management, rolling-over, naming, log details and formats etc. Once we have the helper class in place we can just re-use the helper class using some basic parameters without focussing on the details of how it works anymore. We solved our problem more simply by creating the helper class.

Now, let’s elaborate on this a little further. Suppose we only use this helper class in certain circumstances of a particular type of solution, let’s say only for certain components of a particular application. In this case, we don’t use or need all the possible parameters of the helper class (because we only use it in a certain way in this context), and let’s say we identify 5 different cases of use, each with different parameter values. Now we can build another higher-level helper class that wraps the original helper class, and defines only one parameter which describes one of our 5 cases – perhaps an enumeration. Our new helper class is now an abstraction of the older helper class – because it simplified its usage in this particular context. Typically, this new level of helper class becomes part of a framework for those types of applications, because we have made certain types of assumptions about how the helper class is to be used.

[To clarify this, using abstraction for simplifying a problem is something we see all time. For example, take ADO.NET that hides a lot of the low level details that are needed to communicate with a SQL Server database. On top of that we have Enterprise Library that adds another abstraction level to make data access even more straight forward. In the future, ADO.NET vNext and the Entity Framework will do an another attempt to raise the level of abstraction for “data programming”]

As we continue to build up the levels of abstraction, you can see that we can solve more specific problems at less level of detail, and also by using a higher level description or language. If we can get close to a point where we can provide these abstractions in terms of the specific problem we are solving, we are making it real easy to program our application (based on minimal information). This is where productivity is gained.

Abstraction Roadmap

When developing a solution you can think of many abstractions, when you add modelling and automation to this picture the possibilities for abstraction build upon each other vastly:

Platform API’s → Application API’s →Helper Functions →Class Libraries → Frameworks → Domain Models → Software Factories

Each of the abstractions denoted above increases the level of abstraction of the problem you are solving, and in each case also narrows the domain of the solution you are providing for – this is the simplification part. I am sure it’s obvious to you that using a domain model to configure an application is far quicker than writing programming API’s to describe the same thing.

The other key point here is that I can configure my application in terms of high level things that make sense to that application and not in the low level details of the API’s. This means that those using the domain model now don’t need to be programmers they can have higher level skill-sets like solution design, requirements gathering and interpretation etc. By raising the level of abstractions we are moving from a physical design (platform API’s) to a logical design (domain model) that exposes less details and is understandable for a much wider audience.

Automated Guidance

Another very important aspect we have to keep in mind during the development of our factory is the level of automated guidance we expose to our factory users. In our post “What would you build?” we have already discussed the different types of automation assets that we might want to use in our factory but we have make sure we use them in their most effective way.

Remove Mundane Tasks

A top quality attribute (from a developer’s perspective) for using our factory would be to provide automated assets that remove the rote and menial tasks from the factory user’s ToDo list. At best remove these types of duties completely from the developers ToDO list. We as solution developers, often have some predictable boring tasks that we have to do over and over again. By exposing automated assets for these types of tasks we save our factory users from completing these mundane and in-variable tasks and let them focus on the variable and more exciting design aspects of the solution they are building. Our factory users will definitely love us for that!

Transfer Domain Knowledge

The design of our automated assets must ensure they capture knowledge and experiences and can be used to make that knowledge available for others. Using the example of a DSLs’ domain model which holds the knowledge about the underlying solution domain and how it can be and should be manipulated. By using the DSL, the user is presented with all the knowledge of how to describe the domain, the parts and relationships of that domain, and the description of the pieces within it.

The important part of this is that the domain model helps the user to figure out the key parts (concepts) in the domain. The domain model (and designer in case of the DSL) guides the user through the process of modelling a solution for the problem by using concepts that make sense to him. In fact, the user learns the domain and the important concepts of it but also how to provide a solution by using these abstractions.

Enforce Compliance and Completeness

When we design the automated assets (and factory as a whole) we encapsulate the architecture of the factory product in the domain model that is used by the factory – this is the model of the product.

We can build automation assets to populate or configure the product model and in this way ensure compliance with the implicit architecture of the product. Because these models have configuration to address their variance, validation can be used explicitly to ensure the factory user has not configured parts of it incorrectly with respect to the whole solution.

Automation assets can also be used to examine the model, check for completeness and use that information to guide the factory user in his next steps he needs to execute in the factory to complete the product.

Full Automation vs. Flexibility

When designing our automated assets we have to keep in mind to keep them limited and focused on one particular concern. It doesn’t make sense to build a huge fully automated asset that covers multiple concerns and generates a lot of unrelated artefacts (i.e. source code and configuration files). It also doesn’t make sense to build a DSL that tries to model to world. If we expose a number of small, highly focussed automation assets we have a much more flexible factory because we can, for example, change the order in which we manipulate the assets, combine them or even decide not to use some automation assets at all to better meet the requirements for the product we have to build.

However, if we go too far with variability and refining our abstractions and automated assets to a more detailed level we might end up with a factory that is less productive because there are more “moving parts” that needs to be configured which also increases the complexity of using the factory.

Finding a balance between full automation and flexibility is absolutely key here!

Usability

Because we are so busy developing our software factory we might forget that building a fully fledged and super cool software factory that generates the perfect product isn’t enough to make everyone want to use it. In the end it is often the factory user that has a huge influence in the adoption of our software factory. This means we have to make it cool to use the thing and make it flexible and intuitive enough to use and improve the experience of building solutions in this domain with it!

You don’t want people to discard you factory because it too hard to use, or discover how to use effectively. Don’t assume they will read the manual!

Promote Adoption

Another thing we have to keep in mind during the development of our factory is the fact that the factory is likely to be used by other less-skilled and less-knowledgeable developers than the ones that building the factory. They might not even have to be developers! This means, we might have an issue here in convincing other development teams to adopt the factory and use that instead of their existing tools, frameworks and utilities they are so familiar with. Or worse, have built themselves!

To get our factory adopted by our audience we have to lower the bar for them to start using the factory. Of course there are multiple ways to achieve this and they will probably differ dependent on the organization structure we are working in. Suppose we are working in an organization where the factory is build by a dedicated team that is known by the several project teams responsible for delivering products. In this example, the factory team can support the project teams in the start up phase of their project to get familiar with the factory and makes sure the project team gets up to speed on using the factory real quick. Once the factory is adopted by the project team responsible for building the product, the factory authoring team can start collecting the feedback from this team to enhance the factory and increase the usability of the factory even better. You see this in the process described in “How would you build it?

Role Sensitivity

A software factory, just like any other development tool, is potentially used by a lot of project team members (factory users). i.e. Developers, Architects, Testers, User Experience Engineers, Technical Writers etc. To get the full potential from our factory, and factory users, we must make sure the factory targets the audience that is supposed to use the factory in the way they want to use it. In other words before we start developing our factory we need to know what types of users we expect for our factory. Do we only target architects, developers, testers or is it a mix of these types of users that we are aiming at?

Why is it important to know the factory users? Well, each type of user is interested in a different set of concerns and views of those concerns (In factory terminology this is called a ViewPoint). For example, an architect might only be interested in the high level view of the system design while the developer is interested in much more technical details.

Once we know our audience, and those who we are building the factory for, we can attempt to provide them with the right tools, views and level of abstraction to support them do their job in the best possible way and be more productive when using the factory.

Customization

Software development is rapidly changing. New technologies, new paradigms happen almost every day. To be successful in software development we have to keep up with these changes, and therefore our software factory has to too!

In our post “How long will it take?” we have seen that our factory only start to pay back after we have used it for greater than ~5 times, so we better make sure we can customize our factory very easily to meet evolving requirements and additional product variants. This is exactly why we have an iterative development cycle as defined by “How would you build it?

One of the key requirements of any development tool like a factory is to be flexible enough to address as many use scenarios and development environments as possible. Otherwise its’ use is limited. There are two aspects of customization worth noting here:

Factory Customization

Firstly, there is customization of the factory itself, usually to accommodate a change in architecture or change in variability of the existing architecture that the factory models. This is hard to accommodate if the factory itself presents a closed programming interface. However, this can be accommodated by defining good extensible infrastructures to the architecture so that those using the factory can easily customize it to add or remove features or variability that the factory presents. We see this most when ‘horizontal factories’ are customized to suit certain vertical scenarios, where variability in most cases wants to be predefined, and fixed, and therefore requires removal from the user experience of the factory.

As an example. Consider a (horizontal) factory that builds web services. If I want to ‘verticalise’ this factory and customise it to suit building certain types of financial web services, then it’s likely that for financial web services, I want to pre-define certain Operations Contracts and Data Contracts to use. The Operation and Data Contracts are one point of variability of the horizontal factory, and the horizontal factory provides assets to allow the user to configure them. Now that I want to customise this factory to make a new verticalised version that is financial services based, I want to be able to say that the Operation and Data Contracts are fixed to certain industry standards, and no longer configurable by the factory user.

Product Customization

Secondly, there is customization of the automation assets used to create the product itself. Typically, the scenario here is that you want to alter the automation assets used to transform the model of the product into a physical solution.

As an example. Consider a factory that generates source code as its final product. The source code is determined by the factory in code templates, which are used to transfer the models of the factory into a physical solution. So consider, that the generated code does not agree with you in some way. Often, this needs to be changed to suit an organisations’ coding standards, or include some organisations standard copyright header, or perhaps you want to change the code that is output because you have a more optimal way to write the solution.

Extensibility & Composability

One of the key issues of building factories is in providing the right guidance and solutions to varying requirements. Creating a factory for a large domain increases the chance that the guidance it offers is limited in addressing wider usage scenarios.

For example, consider a factory that builds web services. This factory is likely at some point to define a mapping of the web service’s logical architecture to source code artefacts. In doing that, the factory itself will have to make decisions about what patterns and technologies to use to do that. The assumptions it makes to do that may not be valid for all cases, and it is such cases that may prohibit the use of the factory at all, if the factory user cannot customize or influence how that is done. In smaller domains the chances of meeting this issue are smaller, because the possible solutions are smaller in number. However, even in smaller domains, there can be several solutions that apply to different circumstances. This means that larger domains need to be composed of smaller domains, where each smaller domain could be swapped out for a different implementation.

Composite Factories

In order to practically achieve this we need to be able to compose larger domains from smaller domains, and have the domains interact with each other. Furthermore we need to be able to potentially swap-out each domain with a different implementation.

In factories, this can be achieved by providing extensibility mechanisms that accommodate plugging in and out various other factories to provide the sub-domains. We call these sub-domain factories ‘factorettes’. These extensibility points are key for expanding the use of your factory, so others could provide alternative implementations for the pieces of you factory to suit their needs.

One such use of this is to provide specific technology implementations or strategies for implementation such as the ‘EFx Factory’ does, so that factory users can pick and chose which technology to use to implement the Service Contract or Data Mapping layers, for example. Of course you can choose many other aspects to make extensible in your factory apart from technology. Perhaps you want to allow other factories to provide better views of a part of a domain, or provide better implementations of it as well.

Summary

In this post we tried to list the important quality attributes for software factories. Building factories isn’t (only) about using the available technologies to build some automated stuff that is just cool. We have to realize that we need to take special care to ensure the factory is useful for our users, improves their productivity and adds value to help them deliver high quality products.