I’ve been sold for some time on the benefits of highly modular software development (and deployment, for that matter, almost equally importantly).
However, I’m aware that not everyone is on the same page, or has the same definition of “modularity” in the first place, so I thought I’d collect and enumerate the things that have convinced me, as best I can.
I’ve been in the software business for a very long time, and sometimes I have a preference or leaning towards a certain technique or technology. Sometimes that preference has clear causes behind it, but sometimes not. Modularity is one of the latter, where I know it’s not only good, but critically important, but it’s hard for me to immediately say why or how I know that in a succinct way.
So, I apologize for the length of this explanation, as I’ve not yet taken the time to make it shorter
Modularity Defined First things first: What do I mean by modularity?
In short, I mean a system comprised of relatively small self-contained units. The idea of modularity has caught on much more completely in many other disciplines, but far less so in software. In manufacturing, especially mechanical and electronic, in business, especially finance, the idea of modularity is well established. Modularity is used to increase and maintain quality, and reduce cost while increasing output. In the automotive industry, for example, extensive use of standardization and modularity allows us all to afford a car, as opposed to just the wealthy or the mechanically inclined.
In the software world, the use of modularity is still woefully small, however. When it is employed, though, some spectacular successes have arisen. One of the oldest examples is Unix, arguably the most successful operating system ever. Since 1969, the “do one thing, do it well, and play well with others” modular design of Unix has been the basis of it’s success and flexibility, allowing it to span everything from embedded devices to mainframes.
The Java Virtual Machine (JVM) is another realm where modularity is on the rise, and starting to have an impact. Many different approaches are available, and some of them work with any of the large number of languages available on this platform.
Increase overall success rate of projects In the many software projects I’ve been involved with over the years, I’ve noticed a few trends. Smaller, well-contained projects have a higher success rate than larger and more poorly contained projects. (By contained, I mean the number of places and ways the project interacts with other projects is small). I’m sure there are a host of reasons for this, but part of it is simple comprehension – small projects are generally easier to understand than big one, so the number of times things get done wrong is reduced.
A system comprised of well-isolated modules tends to be more like a set of small projects that happen to work together, so I find this reason alone a plus for modularity.
But this hides much of the detailed reasoning behind why modularization works, so let’s dig a bit deeper…
Makes the API between components explicit, not hidden Sometimes I heard the argument that modularity is complex, that it actually increases the difficulty of working with a system. I find that doesn’t hold up under examination: when modularity seems complex, it’s probably because it’s being done wrong, or being retrofitted to a system that was not designed with modularity in mind. What you’re really seeing in this situation is that the attempt to modularize is exposing the complexity that was already there.
When a system is properly designed in a modular fashion, the APIs between modules are small and well-defined, with a limited number of cross-module dependencies.
Reduce unnecessary complexity By reducing a large complex system into a series of small and better understood modules, we’ve reduced the complexity we have to deal with at any one time. This means that the overall large system only has to deal with how the exposed APIs of our modules will be fitted together, treating the modules themselves as “black boxes” that perform specific functions, without worrying about how they perform it.
In both cases, we’ve reduced the complexity we need to deal with.
I specify here “unnecessary” complexity, as of course the complexity of the issue itself doesn’t get any simpler just because we modularize it – but if we don’t have to deal with a lot of tangled plumbing, we can concentrate on the real issue.
Increase testability A module that performs a single function is much easier to test exhaustively than an entire monolithic system. The number of interdependence is limited, meaning we can do more of our testing at the unit and orchestration level than at the functional or integration level, meaning our feedback time is improved and our coverage likely increased.
This means our development velocity can increase, as the impact of changes can be evaluated more easily and more quickly.
Support Composition When you have a number of smaller building blocks, it’s relatively easy to reshuffle those blocks to change the behaviour and functionality of your overall system. This is making use of composition – connecting blocks together to do more than they can on their own, and its a pattern that is supported well in a modular environment.
Although composition is often thought of in the context of re-usable modules, when building a new system, it’s also valuable in modules that are only used within one system, as it allows the large-grained behavior of the system to change much more rapidly than without modules.
Composition makes large complex systems easy to understand and work on, and vastly increase the maintainability of the overall system.
Impact of changes isolated The isolation of changes to an effected module is a major benefit of modularity, especially as it supports maintainability. If you’re constantly concerned with your changes affecting parts of the system you’re not actually working on, you’re not able to go as fast as you could if you knew that your changes would only apply to a small area of the whole system – the current module.
This is related to and similar to the support for refactoring that unit tests give – if you’re tests are all green after your refactor, you know you haven’t changed the functionality of your system. In modularity, if you’re isolated from other modules in the system, this goes a step further – you can change the functionality of the module freely without concern about other bits of your system breaking. This is true if you’re doing anything that’s not in the one package you’ve “exported” as the service definition.
Allows physical design to reflect logical design Modularity also allows developers to build systems where the logical design actually corresponds as needed to the physical design, an often overlooked abstraction. Kirk’s blog post on the topic describes this issue better than I could, so I refer you there.
Supports Refactoring Better Refactoring is the process of changing the implementation of a system without changing it’s behavior. One of the primary reasons to write unit tests, for instance, is to support and allow refactoring, so we know the system does the same thing when we’re done as when we started.
Modularization supports this ability by making clear the portions of a system that affect other modules, and the portions which do not. When you’re refactoring in a monolithic system, even with IDE support, the scope of your changes is the entire project. When you’re refactoring in a modularized system, if you’re “inside” the module, you have no need to consider scope on the outside, as there isn’t any. And when you’re refactoring the exported interfaces, you know ahead of time your scope is cross module, and can take it into account.
In short, modularization lets you refactor more freely within a module, and lets you be intentional and organized when refactoring between modules.
A colleague recently suggested that modularization would interfere with the ability to see a system as a whole. It’s been my experience that the exact opposite is true – a well structured set of modules can still be considered as a whole when necessary (an aggregator POM is any easy way to set this up in the Maven universe), but it also allows modules to be worked on “safely” in isolation, which a monolithic system does not. This ability to only look at one piece at a time is all the more critical the larger the system becomes.
The need to refactor “across” modules is a warning sign, in my opinion – it generally indicates that the API between modules is changing, and this is a change you want to be more aware of then any amount of changes within modules. If it happens often, it probably indicates that the module boundaries are not yet stabilized, and you have more design work to do.
Scalable Development A modular system lends itself well to many hands being involved in it’s development and maintenance. It’s not necessary for a team or a pair to understand the whole system well, or even at all – they can still work on a small well-defined and decoupled module, and can do it in parallel with development on other modules.
Helps prevent Design Rot As discussed in this blog post, design tends to degrade over time, especially in large monolithic systems. Modularity helps to prevent this rot, as pieces that become superseded by better ways of doing things can be replaced individually, and the impact to the overall system managed and isolated. This is akin to removing a brick from the wall and replacing it with a better brick, rather than tearing down the wall.
Solves the classpath hell problem entirely Classpath hell is the name for the condition where a large number of dependencies are “in scope” at once. It’s not unique to Java, or even the JVM. It is apparently called “DLL hell” on Windows environments, but I’m happy to report I know very little about that. I do know it can happen on platforms other than the JVM, however.
By isolating the influence of dependencies, modularization, especially the way OSGi does it, makes it an explicit process to import and export exactly what packages you wish to and from your modules. What the module uses in it’s private implementation under the hood is no longer relevant to the system at large.
What happens in the bundle stays in the bundle, in other words, with appropriate apologies to Las Vegas.
Management of dependencies is the cure for classpath hell, and modularization is key to this, as described in this excellent article.
Allows smaller pieces of the system to be versioned A key element in continuous releasability is the careful versioning of both individual modules and their dependency requirements. Modules give you the ability to release pieces of your system, not the whole system, better supporting the idea of continuous releasability and enhancing maintainability.
An article about software maintenance that a colleague recently sent me describes the problem perfectly – and independently versioned modules is a large part of the answer to this problem.
Re-Use Although re-use it touted often as a major benefit of modularity, I’d categorize it as the least important benefit, not the most. Although the idea of re-using well-designed modules in new projects is indeed very powerful, it’s beyond the technical and design capabilities of many teams, often causing them to reject the need for modularity because they see no reason to build for re-use.
In a way, they’re right: you don’t build for re-use. You build for just the specific case at hand, but when you separate concerns properly and adhere to modularity best practices, you end up with a module that may lend itself to later re-use much more easily. If such a re-use case emerges, well and good, then you can reap the benefit. It is a lot like a framework – you can’t design a good one in isolation from it’s use cases – a framework is an emergent artifact from the re-use of code on many different (although similar) projects. The same is true of re-usable modules: you don’t set out to build a module explicitly to be re-usable.
Allows dependencies to be isolated: e.g. all Weblogic-specific code in one module, or all XMLMill-specific code in one module, increasing ability to refactor safely
Suggested Reading: Agile Architecture Requires Modularity Modularity Patterns Runtime and Development time Modularity
In a later post we’ll look at some of the counter-arguments against modularity, and why I think they’re not especially valid or convincing in my experience.
In my experience, the future of modularity on the JVM is OSGi.
Many developers don’t seem to recognize the need for it, but other bloggers have covered this in great detail (see my Resources page for some links), so I’m not going to try to “sell” OSGi here at all – I assume you’re already sold, and looking at how to put OSGi to good use in your development and deployment process.
Extending my recent experiments with the Vaadin framework, I decided I wanted to have a Vaadin front-end talking to a set of OSGi services on the back end. Initially, these will be running within the same OSGi container, which in this case is FUSE 4, the commercially supported variant of Apache ServiceMix.
One of my goals was to acheive a loose coupling between the Vaadin webapp and the backing services, so that new services can readily be added, started, stopped, and updated, all without any impact on the running Vaadin app. I also wanted to maintain the convenience of being able to run and tinker with the UI portion of my app by just doing a “mvn jetty:run”, so the app needed to be able to start even if it wasn’t inside the OSGi container.
Fortunately, doing all this is pretty easy, and in the next series of articles I’ll describe how I went about it, and where the good parts and bad parts of such an approach became obvious.
In this part, we’ll start by describing the Vaadin app, and how it calls the back-end services. In later parts, I’ll describe the evolution of the back-end services themselves, as I experimented with more sophisticated techniques.
Vaadin Dependency I’m building all my apps with Apache Maven, so the first step was to create a POM file suitable for Vaadin. Fortunately, Vaadin is a single jar file, and trivial to add to the classpath. My POM needed this dependency:
<dependency> <groupId>vaadin</groupId> <artifactId>vaadin</artifactId> <version>6.1.3</version> <scope>system</scope> <systemPath>${basedir}/src/main/webapp/WEB-INF/lib/vaadin-6.1.3.jar</systemPath> </dependency>
Here I’m using the trick of specifying a systemPath for my jar, instead of retrieving it on demand from a local Nexus repository or from the internet, but that’s just one way of doing it – the main thing is to get this one Vaadin jar on your path.
web.xml Next I proceeded to tweak my web.xml to have my top-level Vaadin application object available on a URL. The main Vaadin object is an extension of a Servlet, so this is also very easy – here’s my web.xml in it’s entirety:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5"> <display-name>Admin</display-name> <servlet> <servlet-name>Admin</servlet-name> <servlet-class>com.vaadin.terminal.gwt.server.ApplicationServlet</servlet-class> <init-param> <param-name>application</param-name> <param-value>Admin</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>Admin</servlet-name> <url-pattern>/*</url-pattern> </servlet-mapping> </web-app>
In this situation my “Admin” class is in the default package, which is not generally a good practice, but you get the idea.
Menu and MenuDispatcher I then used the “Tree” class in Vaadin to build myself a nice tree-style menu, and added that to the layout on my main page. My main page has some chrome regions for a top banner and other assorted visual aids, then a left-side area where the menu lives, and a “main” area, where all the real action in the application will happen.
A class I called MenuDispatcher “listens” for events on the menu (e.g. the user clicking something), and does the appropriate action when a certain menu item is clicked.
Here’s the interesting bits from the MenuDispatcher – as you can see, it’s constructed with a reference to the “mainArea” layout when it’s first initialized.
public class MenuDispatcher implements ItemClickEvent.ItemClickListener { private VerticalLayout mainArea; public MenuDispatcher(VerticalLayout mainArea) { this.mainArea = mainArea; } public void itemClick(ItemClickEvent event) { if (event.getButton() == ItemClickEvent.BUTTON_LEFT) { String selected = event.getItemId().toString(); System.out.println("Selected " + selected + " from menu"); if (selected.equals("create dealer")) { createDealer(); } else if (selected.equals("edit dealers")) { editDealer(); } ... } System.err.println("Selected " + event.getItemId()); } } private void createDealer() { mainArea.removeAllComponents(); Component form = new CreateDealerForm(); mainArea.addComponent(form); mainArea.setComponentAlignment(form, Alignment.MIDDLE_CENTER); mainArea.requestRepaint(); } private void editDealer() { ... } ... }
Again, this code can be made more sophisticated – I’m thinking a little Spring magic could make adding new forms and such very convenient, but this gets us started.
Submitting the Form The “CreateDealerForm” object referred to in the Dispatcher then builds a Vaadin “Form” class, much like the example form built in the “Book of Vaadin”. The only interesting part of my form was that I chose not to back it with a Bean class, which is an option with Vaadin forms. If you back with a bean, then you essentially bind the form to the bean, and the form fields are generated for you from the bean.
If I wanted to then send the corresponding bean to the back-end service, then binding the bean to the form would be a good way to go. Instead, however, I don’t want my back-end services to be sharing beans with my UI application at all. I’ll explain why and how later on in more detail.
The interesting part of my form, then, is how I handle the submission of the form:
Assuming I have a button on my form, defined like so:
Button okbutton = new Button("Submit", dealerForm, "commit");
I can add a listener to this button (again, using Vaadin’s magic ability to route the Ajax events to my Java code) like thus:
okbutton.addListener(new Button.ClickListener() { public void buttonClick(Button.ClickEvent event) { Map<String, Object> parameters = new HashMap<String, Object>(); for (Object id: dealerForm.getItemPropertyIds()) { Field field = dealerForm.getField(id); parameters.put(id.toString(), field.getValue()); } System.out.println("*** Calling dealer service via dispatcher"); ServiceClient.call("dealer", "save", parameters, null, null); getWindow().showNotification("Dealer Saved"); } });
I’m using an anonymous inner class to listen to the event, and the “buttonClick” method gets called when the user says “Submit”.
The next steps are where the form meets the back-end service: First, I iterate over the form and build a map containing all the field values. The map is keyed with a string, for the name of the field or property, and the value in the map is the value the user entered. Note that these values are already typed – e.g. a checkbox can have a boolean, a TextField can have a string, a calendar field can have a java.util.Date. We retain these types, and wrap everything up in a map.
Now (after a quick println so I can see what’s going on), I call a static method on class called ServiceClient, sending along the name of the service I want to call, the operation on that service, and the parameter map I just built from my form.
The last line just shows a nice “fade away” non-modal notification to the user that the dealer he entered has been saved (assuming the call to ServiceClient didn’t throw an exception, which we’re assuming for the moment for simplicity).
So, now we have our “call” method to consider, where the map of values we built in our Vaadin front-end gets handed off to the appropriate back-end service.
Calling the Service
The only job of the ServiceClient object is to route a call from somewhere in our Vaadin app (in this case, the submission of a form) to the proper back-end service, and potentially return a response.
We identify our back-end services by a simple string, the “service name” (our first argument). The second argument to call tells us the “operation” we want from this service, as a single service can normally perform several different operations. For instance, our dealer service might have the ability to save a dealer, get a list of dealers, find a specific dealer, and so forth.
In Java parlance, a service operation might correspond to a method on the service interface, but we’re not coupling that tightly at this point – our service, after all, might not be written in Java for all we know at this point, and I prefer to keep it that way.
This is the “loose joint” in the mechanism between the UI and the back-end. To keep the joint loose, we don’t send a predefined Bean class to the back-end service to define the parameters to the service operation, we send a map, where that map contains a set of key/value pairs. The keys are always Strings, but the values can be any type – possibly even another map, for instance, which would allow us to express quite complex structures if required, in a type-independent fashion.
Let’s have a look at ServiceClient:
public class ServiceClient implements BundleActivator { private static DispatchService dispatchService = null; public void start(BundleContext context) throws Exception { ServiceReference reference = context.getServiceReference(DispatchService.class.getName()); if (reference == null) throw new RuntimeException("Cannot find the dispatch service"); dispatchService = (DispatchService) context.getService(reference); if (dispatchService == null) throw new RuntimeException("Didn't find dispatch service"); } public void stop(BundleContext context) throws Exception { System.out.println("Stopping bundle"); } public static List<Map<String, Object>> call(String serviceName, String operation, Map<String, Object> params, String versionPattern, String securityToken) throws Exception { if (dispatchService == null) { System.out.println("No OSGi dispatch service available - using dummy static data"); return StaticDataService.call(serviceName, operation, params, versionPattern, securityToken); } return dispatchService.call(serviceName, operation, params, versionPattern, securityToken); } }
Let’s go through that class piece by piece. First, you’ll notice that the class implements the BundleActivator interface – this tells the OSGi container that it is possible to call this class when the OSGi bundle containing it is started and stopped. During the start process, you can have the class receive a reference to the BundleContext. This is somewhat analagous to the Spring ApplicationContext, in that it gives our class access to the other services also deployed in OSGi. The Spring DM framework lets you do this kind of thing more declaratively, but it’s good to know how the low-level works before engaging the autopilot, I always find.
In order to allow BundleActivator to be found, we need another couple of things on our classpath, so we add this to our POM:
<dependency> <groupId>org.osgi</groupId> <artifactId>osgi_R4_core</artifactId> <version>1.0</version> </dependency> <dependency> <groupId>org.osgi</groupId> <artifactId>osgi_R4_compendium</artifactId> <version>1.0</version> </dependency>
This defines the BundleActivator interface and other OSGi requirements which we use.
As you can see, we use our reference to the OSGi context to get a ServiceReference to an interface called “DispatchService”. We’ll examine dispatch service in detail in my next posting, but for now you can see we hold on to our reference as an instance variable.
When the call to the “call” method happens, our DispatchService reference is already available if we’re running inside an OSGi container, all wired up and ready to go. To give us flexibility, though, we also allow for the case where dispatch service is null, meaning we’re not running inside and OSGi container.
Instead of crashing and burning, however, we simply redirect our call to a “StaticDataService” class, which does just what you might expect. For every call it understands, it returns a static “canned” response. This allows us to build and test our UI without actually having written any of the back-end services, and to continue to run our Vaadin app with a simple “mvn jetty:run”, when all we’re working on is look and feel, or logic that only affects the UI.
This means my “cycle time” to test a change in the UI code is a matter of seconds – when I do a “mvn jetty:run” my code is compiled and up and running in my browser in about 5 seconds, and that’s on my 5 year-old macbook laptop, so there’s no penalty for not having a dynamic language in the mix here, from my point of view.
If DispatchService is not null, however, then we’re running “for real” inside an OSGi container, so we use our stored reference to the dispatch service to “forward on” our call. The dispatch service works it’s magic, which we’ll examine in a later post, and returns a List of Map objects with our response. This list might only contain one Map, of course, if the service was a simple one.
The response is then returned to the caller elsewhere in the Vaadin application, to do whatever is necessary from a UI point of view – perhaps populate a form or table with the response data.
As we’ll see in detail in my next post, the dispatch service in this case acts as a “barrier” between the UI-oriented code in our Vaadin app and our domain-specific application logic contained in our services. It is responsible for mapping our generic map of parameters into whatever domain beans are used by our back-end services, and for figuring out which of those services should be called, and which operation on that service is required. Those services then return whatever domain-specific objects they return, and the dispatcher grinds them into non-type-bound maps, or lists of maps, if there is a whole series of returns.
This means our Vaadin app only ever has to talk to one thing: the dispatcher. We only change our Vaadin code for one reason: to change the UI, never in response to a change of service code, even if the beans and classes of that service change significantly.
Next time we’ll look at the dispatch service and the first of our application-specific domain-aware services, and see how they come together.
We’re probably all familiar with the term “technical debt”, meaning the cost of doing things in a non-optimal or non-quality way. While I can go on at length (as my colleagues can attest!) about how this is avoidable by baking in quality, and thus saving time and money at every turn, the fact is that many existing projects have considerable technical debt.
Setting aside for the moment the discussion of telling “good” technical debt from “bad” technical debt, let’s just focus on a projects “bad” technical debt.
If we describe this kind of debt as factors that slow down the ability to change and improve the system, then we see that we are paying the “interest” on this debt every time we touch the codebase or it’s deployed instances.
What I’d like to focus on in this posting is the point at which this technical debt is evaluated to be sufficient that it makes sense to do what we’d normally call a “re-write” of the offending system or subsystem. Basically, when the -ah- mud gets so deep that the hip-waders aren’t helping, it might be time to throw in the towel and start a do-over.
I call this point “technical debt bankruptcy”. Much like a real bankruptcy, it’s an admission that chipping away at the debt isn’t going to work and isn’t worth it – that it’s time to re-group, and in a chapter-11-ish way, fold our tent in a responsible fashion.
Of course, determining if you’re at this point is critical. If you’re not, then you might be throwing out the baby with the bathwater and losing valuable work and former effort for reasons that are not sufficient. Often, political reasons can get us into that kind of situation, where the pressure to do a re-write is not justified. If there are no or few changes to a system, and it’s working sufficiently well as is, then there may be no reason to declare bankruptcy.
If, however, the bill collectors of technical debt are knocking down the virtual door, it’s important to know when to make the right move. As the song says “know when to fold ‘em”.
Part of knowing this is to be able to measure the pace and cost of change, and to be able to estimate, or better, measure, the cost of change if a re-write were done. Let’s say you’ve got a legacy project and a new project. The legacy project is using some old technology and techniques that are painful to work with, and you know they’re causing you to burn more time than they should be. If you also have a newer project (maybe something nice and greenfield) that’s being done with the latest new and shiny tools, Agile techniques, and so forth, you can get a rough idea of what each feature point in the new project is costing you. Now you can compare this to the cost of a feature point in the old project and make a comparison.
If you can look at your backlog of epics and get an idea as to what the future cost of maintenance on the legacy project is going to cost you, then you can take this cost and estimate it instead using the ruler of the new technologies and techniques. The delta is the amount you’ve got available to “spend” on a re-write, essentially.
If the math is right, then declare your technical debt bankruptcy and begin anew!
In some further posts, I’ll explore how to do a well-organized and structured “chapter 11″ on a project, rather than just dropping the ball, including the part that functional tests play in this process, and look at a re-write as a form of highly aggressive refactoring, rather than a whole new project.
The Apache Group have recently released version 4 of the ServiceMix ESB. (Enterprise Service Bus), and I had a chance to work with it a bit over the weekend.
As we develop more and more services, the plumbing is starting to get complicated. We’re starting to ask questions like what versions of what services do we have? What dependencies do we have between services? How can/should we deploy services? Should we use REST or JMS for this service? How can we easily manage and monitor all these services in a fully clustered, scalable and high-availability environment?
In short, how can we develop powerful scalable services fast and not worry about the plumbing?
We’re not the only people to have asked these questions – and one powerful answer is to use an ESB.
ESB’s have come a long way, and the JBI standard and OSGi finally get together in this version of ServiceMix, and some pretty powerful magic happens as a result.
Just about every organization that writes sophisticated applications, particularly Java applications, have run into some of the problems that an ESB provides solutions for – the same is true of OSGi.
As Anthony Juckel put it on his blog, “Any sufficiently complicated Java program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of OSGi.“. This is quite true, in my experience, and can even be extended to include ESB’s, in that if you’ve got a number of JVM services interacting on a network to solve a problem, you’ve got some kind of an ESB going on, whether you call it that or not.
A common practice recently is to write software that provides “services”, then to combine these services into a complete system – in other words, we write services, but we compose systems (from these services). The awkward part comes (well, one awkward part) when we try to write the service in such a way that it can be used in many different ways – in other words, to maximize the potential for re-use – while at the same time trying to do the simplest thing that can possibly work.
No matter what service mechanism we choose, we lock ourselves in to some degree. If we write our service to use JMS (Java Messaging System), so we can support nice events and queues and other asynchronous goodness, we can’t easily then use our service from, let’s say, a client that looks for a REST service. If we choose SOAP, we can’t easily talk to our service from a client (which could be another service) that wants to post an event, instead of call a SOAP interface.
Normalized Messages The Java JBI (Java Business Integration) standard answers this problem: it provides a single message-oriented interface for services, called the Normalized Message. This Normalized Message contains some header info, some properties, and XML payload, and optionally, binary attachments. It is not specific to any one protocol – in other words, it’s not SOAP, it’s not REST, it’s not JMS, it’s not SMTP, it’s not any of those.
JBI is a JSR for defining Java Business Integration, a way to cut through the complexity of different types of communications mechanisms for component message-passing (e.g. things such as REST, JMS, SOAP, RPC, email, FTP, HTTP, file system, and so on).
ServiceMix is one of several choices – once we have services written to the JBI standard, we can deploy them in any JBI-compliant ESB. Glassfish’s OpenESB is another option, for example.
Instead of worrying about the plumbing of the specific protocol we want to use, we can concentrate on writing our business logic, then rely on the ESB to “wire up” the messages between our service-providing components – whether they’re local (e.g. running in the same ESB, in which case it’s a simple in-memory message), or distributed, on another box in our cluster, or halfway around the world. The ESB provides adapters for each of the different protocols – so we can take our one service and talk to it via REST, JMS, SOAP, Email, carrier pigeon (ok, maybe not the last one – but you could write an adapter!).
This means I can write my service without even knowing what kind of protocol I’m going to be talking to it with – maybe it receives some messages via JMS, some via REST calls, a couple of SOAP services, and one in a while via an email. All that is the problem of the ESB – I just write one simple method in one simple interface to swallow the appropriate NormalizedMessage, do my processing whatever it is, and spit back out another NormalizedMessage.
This can significantly speed up the development of new services, by taking decisions about the plumbing away from the process of writing the correct business logic. We don’t even need to decide up-front if our service will be used by other services or not – we can wire it up as required.
The other important thing about a Normalized Message is that it refers to services by an identifier, but not by a specific location – in other words, a message from a client might say “I need this service to process this message”, but it’s up to the bus to figure out how to get the message to the service – this provides the opportunity to decouple services from each other. Let’s say you need to write a service that takes some kind of business message and sends an email. You don’t have to worry about passing your service information to let it find the email service at all – you just send the message to a service name (maybe “email”), and the ESB figures out the rest.
This means you can swap the email service out for another service that provides the same mechanism without worrying about the details.
JBI and OSGi Together ServiceMix is one example of such an ESB, which marries the modularization advantages of OSGi with the above-described benefits of an ESB. This means each of our components can run in it’s own classpath space, with no interference with other components – even other components that might use different versions of the same jars. How many times have you discovered you’ve got 3 different versions of log4j on your classpath? This can lead to all sorts of weird and wonderful problems that only occur in certain circumstances, and are all-but-impossible to fix. By providing true modularization, OSGi solves this problem properly. Other solutions that I’ve seen applied in the past lead directly to the quote about every complex system having a half-baked OSGi implementation inside them
ServiceMix also provides a whack of existing components in two different JBI flavors: Service Engines and Binding Components. Services Engines are the ones that do the actual business logic, and Binding Components are the adapters, the pieces that route messages to and from various protocols. Many common tasks can be achieved by configuring the existing pieces, without writing a line of code.
When it is necessary to write some code, ServiceMix includes adapters to make that even easier as well – for instance, it provides a Service Engine to allow any POJO (plain old java object) to be used as a service, without that object having to actually implements the interface for handling NormalizedMessages directly. It doesn’t get any easier than that to write a service – just write the bean, plug in a bit of configuration, and you’re done – instance REST (JMS, email, SOAP, etc etc) service!
Spring integration is built into ServiceMix, so SE’s can have their dependencies injected automagically on deployment.
SE’s can be hot-deployed and hot-removed. Multiple versions of a single SE can be running without conflict, so “hot” upgrades can be done easily. You can have one version of a service that requires version 1.1 of another service running at the same time as some other service that requires 1.2 of the same service. This is flexibility in deployment, as it means that there’s no need to “drag along” other services when upgrading if you don’t want/need to.
Services can easily be managed and monitored via the administrative capabilities of the bus.
Binding Components Binding components are how we talk outside the backbone – they take messages from the backbone and transmit or receive via an external protocol to non-ESB services – e.g. via REST, JMS, HTTP, file system, etc etc. There are dozens of existing binding components to support just about every protocol we’d care about.
Of course, all this power comes with a price: you have to learn to manage and deploy your ESB of choice, and even the simplest ESB is still a fairly complex beast. ServiceMix is no exception. The good news, however, is that it’s possible to pretty much ignore much of the available complexity in order to get started small, then learn more as you need it to start to leverage the power available.
A caveat, however: It’s important to use an ESB the way it’s intended, and not try to shoehorn things that don’t fit into it’s structure. If you find yourself struggling to write services, or having to write a lot of Binding Components, chances are you’re making inappropriate design decisions, or that you haven’t separated the concerns of a Service Engine from a Binding Component fully. This is not uncommon, as in many environments these two concerns are handled by a single component. If you’re using to writing REST services, for instance, with things such as JRA or Jersey, you’ll find it very odd to separate the processing from the “presentation” (even when that presentation is simply into XML or JSON to the REST client).
Once this technique becomes second nature, however, the true power of an ESB becomes clearer.
A whole fleet of buses… Multiple ESB instances can be deployed and clustered, providing highly scalable and fault-tolerant system. The communication between ESB instances is handled entirely by the ESB itself – nothing about our components needs to be aware that they’re working in a clustered environment.
From my latest look at ServiceMix and it’s new release, I suspect I’ll be taking a deeper dive in the near future.
(Thanks to Craig Walls for pointing out that excellent quotation – and for all his help in understanding OSGi!)
Recently I’ve had the opportunity to consider the right qualities of a tool and/or framework for acceptance testing.
Acceptance tests can be found at a number of different levels, depending on how the story criteria is expressed. (See my previous post on levels of testing) . Often they’re called “executable specifications” if they’re written in such as way as to describe the behavior of a system in a given scenario.
Often they are functional or integration tests, and generally they are best expressed as “black box” tests, that is, tests that have no awareness of the internals of the code or component being tested – all they see is what goes in and what comes out, and they assert their success or failure based on those elements alone.
An acceptance test must be comprehensible to the story author, or whatever domain expert is going to actually do the “accepting” that the story has been satisfied. If they simply take the word of a developer that this big ball of code they see in front of them is testing what they said it should, that’s not as good as if they can actually read and understand the executable specification (test) themselves. Ideally, they should even be able to author their own acceptance tests, without a developer involved – perhaps using existing tests as an example.
So, some of the criteria for a good tool might be:
Given these criteria, should the tests be part of the build process for the piece of code under test? For acceptance tests that span modules, this is not practical – you can’t actually run the test when you build a module, you can only run it after a certain set of modules has been built (and perhaps even deployed).
I would propose that acceptance tests don’t even belong with the build they’re testing. I think they belong elsewhere, in an independent location where they can be updated as stories are written and verified. Ideally, there should be a shared space for stories for an entire system or suite of applications, as often a piece of criteria spans multiple components – so organizing the tests by component is artificial at best.
Another question that comes up when considering tools for these kinds of tests is whether or not they must be stored and versioned with the code they validate. As a developer, my immediate reaction is “yes” – until I think about it a bit. How does it help me to know what tests a previous version of my code passed or did not pass? All that tells me is where I was in the past, not where I am now. How much inconvenience am I willing to put up with on the part of test authors to get this capability? Dealing with any version control system is extra work, especially for a domain expert/BA who’s not also a developer. I’m now convinced that where I am now is more important than where I was, and being able to easily write and run and make visible tests is more important to me than knowing what happened back in history.
Let’s examine a few different tools and ways of doing acceptance tests and look at the pros and cons:
JUnit Developers working with Java have a choice of a number of excellent test frameworks, including TestNG and JUnit. They are likely already familiar with them, and the tests fit nicely into the build cycle of Java applications built with any of the more popular project build and management tools, such as Ant, Buildr, Maven, and so forth.
Our acceptance tests are written just like a functional test, in that they fire up whatever context our application should run within, then provide input to it and verify the output with assertions.
This kind of test is not well-suited for acceptance tests for a number of reasons. First, it’s hard to separate the code from the test to ensure we’re truly doing black-box testing. If, for example, we’re within the same VM as the thing we’re testing, it’s very tempting to manipulate objects directly in our test, as opposed to going through, lets say, the publish REST api. This also makes it more difficult to initialize a new testable instance of the application – to be truly independent from it, our test should spawn a whole new JVM from the system under test – which is non-trivial from within Java, although of course re-usable fixtures can be written to take the sting out of it.
This doesn’t help us much when our test spans multiple modules or components, however – then we must either create stubs for each of the components we depend on, or work in a much more complex deployment process to create a “live” version of each of our dependencies.
Finally, a JUnit test is not particularly readable by a non-developer, and not particularly visible, other than as a green or red bar in an IDE or CI server somewhere, so it fails a couple of our most important criteria.
JBehave, easyb, Specs, RSpec BDD frameworks such as those listed above take our testing power in a different direction. They mostly rely on sophisticated DSL’s, enabling us to write our tests in a much more english-like fashion, often quite readable to the non-developers who have taken the time to learn a bit of the DSL.
They still suffer from some of the other problems described above, however – although some of these tools do offer better output formats to make their results more visible (such as the excellent Forms capability in specs, which can show test results in HTML table formats).
They of course still have a learning curve, as the DSLs in each case are another whole language to be learned.
FitNesse A different approach can be seen in tools like FitNesse, which allows tests to be created in a Wiki, by editing web pages and inserting special markup to call test “fixtures”. These fixtures still have to be customized to the situation, of course, but with FitNesse we have the potential of being de-coupled from a single module or system under test, and of developing our tests independently of the code altogether.
The table approach of FitNesse still requires the test author to understand the fixtures available to FitNesse – this is essentially FitNesse’s DSL – but of course only a fairly small number of fixtures need be learned to be able to write a wide variety of tests.
Some projects, however, in an attempt to retain the ability to use FitNesse tests to verify previous versions of their application, take the step of checking the FitNesse tests in to their version control system, thereby losing some of that independence.
FitNesse has it’s own ability to track changes to it’s Wiki pages (and thus it’s tests), but this is not tied to the checkins or releases of the software under test.
FitNesse makes it easy for a non-developer domain expert to author tests by using existing tests as templates, then changing the inputs to the fixtures being used to create new tests from the existing building blocks.
These tests and their results are highly visible, especially if the FitNesse wiki is hosted and available to anyone (including developers) to use at any time to verify against a specific deployed instance of the entire suite of applications.
Of course, you’ve still got the issue of deploying a testable instance of your system to deal with in FitNesse. One approach I’ve seen to solve this is to actually have a fixture that can deploy the testable instance as part of the set up for the whole test suite, using versions to indicate the revision of code to be tested – e.g. you have a fixture that says “deploy module X version 123 to test environment 1″, “deploy module Y version 345 to test environment 1″ and so forth, then executes its tests against those deployed instances.
Of course, any database cleanup/reset to get to known state can happen before or just after the deployment, so your tests always start from a known point.
An important part of making this fully automatable is a mutex service: a way to make a call to a known location and say “check out test environment 1″, basically saying that test environment 1 is now busy until it’s released by another call. This ensures that you don’t start another test run on the same environment while it’s still in a transition state.
The mutex can also of course report on who has what environment in use, and since when, to detect failures that might not release a lock.
FitNesse has it’s warts, however, even in a scenario like this, but overall I’ve seen it succeed more often that other approaches for the high-visibility acceptance tests that many projects need, while tools such as easyb, specs, and RSpec are better for describing behaviours within a single module, and executing as part of that module’s build process.
Why do we want to go fast in the first place? Well, if we’re not accumulating technical debt, our quality is still within the bounds we’ve set for ourselves, and we’re satisfying user stories, then we want to be able to accomplish as much as we sustainably can each sprint. This way we can deliver value faster, and people tend to like to pay for that kind of thing
Well, first, we need a road: the basics of an agile environment need to exist before we can go very fast at all. If we’re working on the build system every sprint and trying to get the basics of a story understood, we’re still in road construction, and we shouldn’t expect much in the way of speed until we get some of this basic asphalt laid down and smooth. These includes such basics as a good development setup for our team, a basic understanding of agile principles and a grasp of the technology stack we’re using, and the support of a continuous integration server, to mention a few. If we’re attempting to bounce along over the potholes without setting up a proper environment for rapid delivery of software value, we’ll reap what we sow.
Assuming we’ve built the road, then, what things tend to hold us back? Just like on a real road, the only things stopping us from going faster and faster (to the mechanical limit of our vehicle, or in our case, our keyboards and brains) are either externally-imposed limitations (e.g. a speed limit and cops to enforce it), or our own ability to control the pace without going off the road. In software construction, as in life, we can go off the road in many varied ways, but they all tend to be spectacular, destructive, and painful. Unlike the real road, we can be cruising along for some time before we discover we’ve left the asphalt behind and are sailing over a cliff.
We’ll start by assuming that our corporate environment has eliminated externally imposed speed limits and political roadblocks – not always a safe assumption, but lets assume for the moment that we’re one of the luck developers who work in such a situation.
Our top-level speedometer, to overuse our analogy a bit, is our velocity, measured in features per iteration, or complexity points per iteration – in other words, how much business value are we adding per time period?
The most common way to go off the road is for quality to slip. This can be detected in one of a number of ways, including an ever-increasing defect rate. If most of your sprint is taken up by fixing defects or paying off code debt, then you’re probably trying to go too fast (or you’ve not finished laying the road after all). Of course, it’s possible you’ve just got a few bad drivers on your team, but we’ll assume that’s easier to see (if not necessarily easier to fix). What’s worse than seeing quality slip? Not seeing quality slip, even though it is. We can’t measure quality directly, per se, but we sure can measure a lot of other things. Once we know what the normal position of each guage is (e.g. once we establish reasonable code standards that we can measure), then we can watch them to get early warning of things going awry.
Just like on the real road, we need two categories of things, it seems, to help us go as fast as safely possible: I’ll call them headlights and guardrails.
Headlights
The most basic tools here are user stories, acceptance criteria/tests (ideally executable ones), and metrics such as defect rate and velocity measurements. None of these are trivial or straightforward, and it’s easy to think you’ve got a good view and suddenly discover you’ve been accumulating code debt without realizing it. The only proper reaction at that point is to slow down and correct the problem, as we’ll discuss below.
A good business understanding of the goals and epics behind our user stories gives us more range to see further ahead, and going fast requires looking further ahead, while at the same time paying attention to where you are at the moment.
Just like when driving we must be aware of the road immediately ahead, our user stories give us the close-focus we need to be doing the immediately useful thing. We can’t discard these in favor of looking further ahead exclusively, or we’ll never get to where we want, but we can combine that with an awareness of both the near future and an understanding of the overall destination to make better decisions in our day-to-day work.
If we concentrate exclusively on the user stories in hand for each iteration we can find we’ve lost sight of the forest, and may have a hard time fitting together features that should blend into an overall product. If we concentrate only on the distant horizon and not on the user story we’re working on we’ll never get anything done. The proper balance lets us go fast.
We don’t want to be like the driver in the joke with the punch line that ends “we’re lost… but we’re making bloody good time”!
Looking a bit further ahead also allows us to anticipate curves and obstacles in the road, and be ready to hand them when they arrive. If we know, for instance, from our long-range planning that we intend to scale our application to thousands of users, we might make different decisions than if we’re aware that a single user on a desktop box is the intended audience – even though neither of these factors is really represented directly by each user story we work on.
Executable acceptance tests from a tool like Greenpepper, Fitness, RSpec, or the like can be valuable headlights, freeing developer time from the repetitive manual verification and allowing BA/Customer Proxies to have control over the acceptance process – again freeing up developers to develop, and maximizing team velocity. As was mentioned in a recent stand-up meeting: if you’ve manually tested once, you’ve probably already spent more time than it takes to set up an automated test to do the same thing repeatedly, not to mention you’ve probably enjoyed it a lot less
Guardrails
There’s a big difference between guardrails and a stone wall built across the road ahead, however – it’s not hard to let a testing tool or technique turn into a straightjacket, with tons of brittle and hard-to-maintain tests that don’t help us at all. We need the right tool for the right job, and used the right way.
If we have guardrails ensuring the basics of our code quality, we can go faster with the confidence that when we look back at the end of each sprint we will not have accumulated more code debt that needs to be paid back later. For example: if we establish a test coverage metric that ensures we have a breaking build if our code coverage goes below a certain minimum level (I propose this always be 100%, but that’s another post), we can move forward with the assurance that there’s no code that’s being left untested, so we won’t find ourselves in the distinctly non-TDD-like position of having to go back and write tests for existing code, burning time that should be able to be used for the next story.
We can also refactor with better confidence if we know for a fact there are tests watching over our shoulders, ready to break should our refactor not be true. Refactoring code that is, at least in part, untested should always be an unacceptable risk.
If we have some checkstyle, PMD, FindBugs or other static analysis tools checking that our cyclomatic complexity is within bounds, that our class size and line length are readable, and other critical maintainability and coding standards factors are met, we can plunge forward without the fear of a huge cleanup being required just to make the code understandable down the road a ways.
Of course, just like guardrails and headlights are not infallible in the real world, all the tools and checks in the world don’t ensure good quality code. One area that’s particular hard to ensure quality within via automatic mechanisms is design. You can have code that’s 100% covered, passes every checkstyle rule known to man, and still represents a terrible design. This is where the human factor comes into play – the automation merely ensures that you’re spending valuable human attention span on the stuff that really requires a brain, as opposed to things that can be verified mechanically.
Discipline is the glue that makes all of this work together – often times developers themselves will have the “smell” of something done not quite right, but not feel like they’ve got the latitude to dig into it and clean it up, so they save it until the mythical “later”, which sometimes never comes. Management and team leads must also be disciplined enough to have the patience while that kind of refactor happens – with the firm knowledge that they’ll get paid back by better productity and a lower defect rate over the mid to long term.
A final warning: It’s easy to let headlights become leashes and for guardrails become cubicle walls. Many agile practitioners are concerned, and rightly so, that adding tools and techniques can turn into a new dogmatism and inflexible methodologies. It’s up to us in the trenches to make sure we don’t let this happen, while at the same time getting all the juice we can out of helpful techniques and tools.
Properly applied, though, headlights and guardrails can be valuable tools in letting us reach our maximum velocity, while still arriving safely at our destination.
No, I don’t mean design that uses trees and development on green laptops powered by solar panels
When I say “sustainable” in this context I’m talking about the ability to keep working on a piece of software over the long term without it becoming so difficult to add new business value that it’s easier to throw it away and start over, or at the least to refactor very significantly.
When working in an Agile methodology, we mustn’t think that everything we need to know is contained within the user story we’re working on. How that story fits into the existing system, how it affects other (existing and new) stories, how it affects system design, performance, maintainability – these are all factors that professional developers must be keeping in mind as they read a story. We must be ready to relay concerns over possible negative impacts in any of these areas to the story originator, as often there is sufficient flexibility in a story to allow it to be refined so that such concerns are removed. It is our responsibility as software practitioners to ensure that adding a new story doesn’t reduce our codebase’s quality, even if it requires work beyond the scope of the story itself.
This is where automated tools to help try to measure some facets of code related to code quality can come in handy: you then have a yardstick to ensure that quality is not heading downhill as new features get added. For instance, if you’ve got a static analysis tool that measures your cyclomatic complexity, you might set a max limit on that complexity that is not to be exceeded by any new code. Of course, that’s just one facet, and not every important aspect of software quality can be measured mechanically, but it does provide some assistance to have such tools. Code coverage is another area: if you’ve established 100% as your target for coverage, then new features should ensure they don’t violate that.
The trick is to make sure that every new story incorporated into code leaves the code at least as clean as it started, if not better, both from a pure code point of view and from a design point of view. This is of course, a tall order, but one well worth trying to hold to.
Dry defined DRY is the acronym for Don’t Repeat Yourself. It applies everywhere when writing good software, so I think it bears further examination.
Why Dry? The rationale behind dry is more than just efficiency: It helps reduce bugs and problems by enabling you to solve each problem only once, and when there’s a new problem, finding the one and only place it needs to be solved, whether that problem is where to configure something, or a piece of code implementing a specific algorithm.
Dry your code When most developers think of DRY, it’s in relation to code, where you want to not repeat a specific piece of code too often – ideally no more than once. Some development languages and environments lend themselves better to this than others. In some languages and environments, you find that if you attempt to get too DRY, you end up making code that’s hard to read.
A common example is with testing – if you abstract out everything in your tests until it hurts, you may find that you’ve made the test into a tangled web that requires reading 4 classes to understand. For example – you need test data set up, so you make a method in some super class called “setUpTestData”, then call it all over the place. The problem with this is that it’s not obvious what’s contained in that test data. Is the data appropriate to the test you’re writing? Dunno – you’ve got to go look in (at least) the super class to see where it’s defined. A better approach might be to DRY out in a mode DSL-like way with methods like so: createTestCustomer(name, id).withInvoices(invoiceNumber, amount).
Now you can see right in the code what’s going on, even though the details of actually how a customer (or an invoice) is created or related to one another are DRY-ed out into the parent class (or somewhere else). Sometimes (more rarely than it happens, IMO), this is a good place for a static method (but that’s a subject for a later rant)
Dry your design It’s not just your code you can DRY out, however. You can also apply DRY principals to your design, as you go to build either a refinement to an existing system or a whole new system. Part of this is simply good systems design, and keeping Separation of Concerns in mind. Have one class (module, whatever the right thing in your language of choice is) do one thing and do it well. Don’t have it depend on too many other things, and above all, don’t have it depend on how those other things do their thing.
Then design for re-use, instead of having to have 14 different variations of every class to do things that are subtly different. Maybe a few of those variations are significant enough to warrant a new class, but most probably aren’t. There’s of course a fine line here between a flexible class that does one thing and a class that does more than one thing depending on how it’s called or it’s configuration. You want the former, never the latter. As soon as a class is clearly responsible for more than one area of concern, it should be broken up into more than one class – this actually encourages re-usability. There seems to be a natural level of granularity for every project, where classes are doing enough but not too much, to maximize re-use and, therefore, most DRY-ness.
Dry your configuration Configuration is another area that is very often forgotten when applying DRY. If you’ve got several different ways to configure your application, or, worse, only one way to configure each part, but each part is configured differently, then you are repeating yourself. I’d argue you might also have other design issues, because how a class is configured is generally not a concern of the class itself, but of the container or environment in which the class is deployed, but that’s also another topic.
If you see a situation where you need to use a system property to configure the database, but you need to put stuff in a properties file to set up something else, and still another bit is configured with an XML file, you’re probably looking at a non-DRY situation where the classes are ending up doing their own configuration, rather than driving that configuration into them from elsewhere. This is hard to DRY-out, not to mention being awkward to change later – and there’s always a “later” when you need to change it