Friday, January 30, 2009
Greg Young interview about DDD State Transition
Tuesday, September 16, 2008
Why should I eat dog food?
One of the main points touched by the article was the long-term ineffectiveness of in-house frameworks. A solution that was developed internally, to bridge a gap in standard implementations, turns later into a drag factor; the short term advantage becomes burden of legacy code and so on.
The old java legacy
One of the reason of “the rise of java frameworks” at the very beginning of this millennium (sounds epical, doesn’t it?) was the low level of abstraction of the java specification itself. JDBC, EJB, J2EE Web were coming out-of-the box without a high level implementation. SUN accompanied the technology with publications such as “Core J2EE Patterns” that used to be a bible in these years. It really sounded like “here is a bunch of small lego pieces hat you can put together to make ... everything! And here (but not in the same place) are the guidelines to make something useful and working properly”.
Unfortunately this is not what many people wanted: people wanted an efficient and solid architecture, easy to set up and reuse. But J2EE offered too many choice-points (how to implement a MVC Model 2? how to deal with JDBC exception? How to make EJB perform? and having the right answer to all he questions was a tough job. So, frameworks were seen as a way to formalize and distill the body of knowledge captured in various patterns book, and to prevent the rest of the team from making awkward choices.
Later on, the implementation of the java platform got better: old EJBs were dropped and many related EJB patterns sunk with them and JSF finally looked like an official standard implementation of a Model 2 MVC framework. It has to be noted that this happened after Spring got momentum, and after the MVC landscape got overcrowded...
Put in this way, there were tons of reasons to build frameworks in those years, because there were holes (or craters) to be filled ...anyhow. Unfortunately some solutions were better than nothing, providing a short term advantage, but rapidly transforming into a legacy nightmare.
If you love somebody, set them free
Some in-house frameworks look definitely naive now, but they looked like (and probably were) a reasonable solution, at the time they were designed (well ...not all of them). Specific design decisions apart, what really makes the difference between a friendly framework and a “traitor” is the cost of getting rid of them. Vendor design frameworks with a very high vendor lock-in potential (it’s part of the game, on the business side), but in-house framework designers often do the same mistake, with no real good reason. Depending on third-party is generally seen as a risk. Depending on yourself is often a higher risk, but seldom considered.
Documentation quality and availability (as Mirco points out) are other key factors, that should make you favor a standard solutions instead of your in-house framework. We used to have a good MVC solution, but switched to Struts as soon as it became a viable option, and so on. Few companies are large enough to afford production of documentation for an in-house framework (one of the reasons why they prefer to sponsor open source projects), so you have to be ready to leap from your solution to the promising open source one, sooner or later.
A framework fills a gap. This means that there is no standard solution for the given problem (otherwise, a framework would probably already be available) and means also that the in-house solution might be non-optimal. This is fine, we’ll probably get more informations about the optimal solution by implementing ours. What we should know in advance, is that we should “design for replacement”, meaning that our framework design solutions should have the primary requirement to be easily discardable. Thus limiting pervasiveness of our solutions.
Often, this is not a technology issue. In-house frameworks have “fathers” that make it difficult to replace them (more or less consciously or willingly) or simply slowing down necessary decisions. Can you be in love with a piece of code? Some do.
Which framework now?
Does it still make sense to think frameworks? Well, it largely depends: applications frameworks such as Spring, Hibernate or also Grails offer an off-the-shelf solution to many common architectural problems, leaving architects free to concentrate on application-specific domain decisions. On the other hand they’re all domain-neutral, and a domain specific framework could be of great value for a product company, especially if it is designed to be technology-neutral, and could provide great value in the mid-term.
Wednesday, September 10, 2008
Sustainable Software Architecture
Such ecosystem is the result of many combined factors such as:
- project size
- team size
- team location (es. co-located vs distributed or offshore team)
- team skills (experienced developers, contractors, folks just hired, and so on)
- team members availability (not all teams are well-formed at the time the project starts)
- architect avalability
- turnover
- logistics
- marketing constraints
- deadline constraints
- etc. etc.
Thursday, September 04, 2008
How much Architecture is Enough?
During my London staying, I had also the chance to attend Simon Brown's in-the-brain event, which turned out to be more two-way than mine, with Simon softly facilitating a really open discussion. As it is often the case, interesting ideas emerged. Here are some of my side notes, mixed with some of my following ruminations.
Communicating the architecture
One of the biggest challenges seems to be finding the right way to effectively communicate the architecture. It turned out that architecture documents are not the best way to convey architecture information alternative and possibly more creative ways are needed, spanning from Wikis, to podcasts, to live sessions. Honestly, the Google Chrome Comics is a great way to deliver an architectural view. But maybe it's not a viable option for common teams.
Also, a prototype could bring so much information and is particularly efficient in edge situations, where the team is not properly trained or skilled and needs to produce some code anyway. Bad as it may sound, also cut&paste information propagation (the worst case scenario) might be preferable to paralysis or anarchy.
Championing the architecture
Without an active championing role, the architecture is at risk of being twisted or thwarted. Without an active role, an architectural specification is just a piece of paper.
Non Functional requirements
Managing non functional requirements seems to be a hot spot for many agile teams: requirements do not naturally show up as user stories, so their emergency has to be somehow facilitated. Also, automatic tests on nonfunctional requirements are necessary.
Strictly tying delivered software to business value might eventually put non functional requirements aside, but having a fully operating passed load test delivers both business value and confidence.
Resisting temptations
A pragmatic strategy to define if an occasional need needs to be transformed into an Architecture requirement is to count the number of times you've been hit by the need to refactor a given thing, with 'three' as a threshold for a rule of thumb. Before getting to the threshold one should "resist to the temptation" of refactoring the code to a more elegant solution.
Personal note: it's interesting to notice that this works only in teams where you have an efficient information flow within the team. If development process does not facilitate this sort of information exchange (es. no daily scrum meetings), your counting might reach '20' before the need for a given refactoring is communicated to the architect-in-charge (whatever that means).
Sustainable architectures
I also advocated the cause of "sustainable architectures": putting too much stuff on an architecture definition might result valueless if the team isn't able to grasp it. Sometimes it's better keep everything as simple as possible, or to step back and start with a less sophisticated solution, and invest in training or improving the team technical skills (or the architect communication ones) before building and obscure cathedral.
Deferrable architectures
A key point is that architecture is about things that are expensive to change in software applications. Interestingly, a too sophisticated architecture (architects love to create frameworks) is more frequent than the need to heavily refactor an existing one. Honestly, I think that in many cases, simply defining a clean layering architecture might be enough, to allow a clean solution of emerging architectural requirements.
Personal note: also, the cost of change has largely dropped: if we compare heavyweight frameworks that were popular some years ago, with what can be achieved with Spring and AOP in terms of pluggable, orthogonal requirements, it's clear that the scenario has changed radically, at least in the JEE landscape.
Should architects be part of the team?
It turned out that architects are sitting on the borderline of the team. There are architects that are definitely not in the team (and absolutely do not code), and some which are more willing to pair with developers, improving team skills and gaining feedback on the correctness of the architectural choices. At the end of the day, developers are architecture primary users, and, as an architect, I really would have my choices tested and challenged by a real feedback. On the other hand, an architect should preserve its ability to think long term, so can't be fully involved in coding, especially tasks which are on the critical path. Moreover, they might be external roles, hired on a short term basis, or with reduced availability, so being part of a fully involved Scrum team might be problematic. Doing Scrum by the book, the Team might ask for an external architecture support as long as the team thinks they need it.
Project start effect (personal note)
As a consultant, I might have a slightly biased perspective, but in many times, the architecture definition has just been the result of a "resource allocation incident". Sometimes, the team was just set and ready-to-code, so there wasn't really much time to have them wait for a good architecture specification to start coding. Sometimes, I joined a project when development had already started, so I had to impose an architecture over a (barely) existing another. Finally, some other projects would not even start before an "architectural view" documentation is developed, as this is part of the project budget negotiation by the management (SOA is a typical example of this case); so when the project starts you have more or less a "specification" already in place. In other words, the early days really influenced the whole project direction, and this rarely has to do with architecture, per se.
Friday, May 23, 2008
Cleaning up our test code – Part 2
Assuming that we've created our test object in a few moves (please read the previous post about it), now the focus switches to the way we use test code to assert correctness of production code. JUnit assertion family relies heavily on equals-based assertions. Unfortunately, the equals() method is far from being used consistently, so equals-based testing has some dangers we need to be aware of.
The equals() contract
Talking about the equals() method, there is a general behavioral contract, which is the one defined in the Java Specification, and it is used heavily in the Collections classes to retrieve objects from different container classes. As every good java developer knows, overriding equals() needs us to adhere to the implicit behavioral contract, and also that we override hashCode() to ensure consistent behavior with different container types. So, to effectively test or domain objects, we need to override both methods. So far so good.
There are also a few convenient methods to do this: Eclipse allows you to generate equals() and hashCode() from a given set of attributes. The resulting code quite good, but it's like a grey area in your source code, in terms of readability. Jakarta commons implementation is less "automatic" but provides the developer with better control over the final result.
Enter a new player
If you're using Hibernate to persist your domain objects, you'll probably know that this requires some attention on the way equals() and hashCode() are defined in your application. This is primarily tied to the hibernate persistence lifecycle (which generally populates id fields upon writing operations) and to lazy-loading (some fields are loaded only if they're explicitly accessed inside a Hibernate session). The Hibernate recommendation is to define equals() and hashCode() methods according to equality of the so-called business-key, which can be roughly be mapped to "a unique subset of required non-lazy fields, excluding id". Id-based equality should be managed only by Hibernate, while business operations should rely on an equals() method based on the business key. To purists, this sounds like an undesirable implicit dependency on the Hibernate framework (your POJOs are still POJOs, but not exactly the same POJOs you would have had without Hibernate).
Equality as a business-dependent concept
So far, we have 2 separate equality implementations: id-based equality (that should be used only bi Hibernate, behind the scenes) and business-key equality that will be used in our business code and will be implicitly used if we uses containers from the Collections framework. What should we use in testing? Unfortunately, there is no one-size-fits-all answer, but the choice depends heavily on what we are testing and what is precisely the desired behavior of your application. If we are adding information on some non-mandatory field, then simple equality won't check it. If we're changing the value of a mandatory field, and want to check that this doesn't trigger creation of a new Id, you need to explicitly check that field.
Often, applications with a nontrivial domain model can't rely only on a single notion of equality (are two BankAccount instances equal if they have a different balance?), this is more or less clear, during analysis, but the presence of an assertEquals() method in JUnit makes blindly using equals() so tempting…
Smarter predicates
Once we've realized that equality is too generic to be applied blindly, the following step is to try to apply the right context-dependent equality in the appropriate context. The obvious solution to do this is to decompose equality to an attribute-level check: so instead of having
assertEquals(a, b);
we end up with something like
assertEquals(a.getName(), b.getName());
assertEquals(a.getSurname(), b.getSurname());
// … you got the point
Which is longer, less maintainable, with a higher coupling, and … ugly. Most of the times, anyway a business relevant notion of equality doesn't show up only in tests. I would argue that 99% of the times the same equality is hidden somewhere in your code in form of some business rule. Why not having the same rule emerge and be used in the test layer as well?
A good way to do this is to rely on the Specification pattern, developed by Eric Evans and Martin Fowler which basically delegates to a dedicated object the assertion of the applicability of a given business rule on a domain object. Put in another way, Specifications are a way to express predicates or to verify expectations on domain objects, in a way that could look like:
assert(spec.hasJustBeenUpdated(a));
assert(spec.isEligibleForRefund(b));
assert(spec.sameCoreInformations(a,b));
After thoroughly testing the Specification (a thing that we should have done anyway, since it is a business implementation), we could be able to reuse the same logic as an assertion mechanism in our test layer, making our code shorter and cleaner. Not all business oriented assertions will be that useful in the test layer, but some normally do. As I said in the previous posts, one of the main goals was to be able to write a lot of tests, and to write them in few minutes. Being able to rely on a higher level of abstraction definitely helps
Thursday, April 24, 2008
Cleaning up our test code
In the last posts about testing in a TDD fashion, I tried to dig into the reasons why many developers tend to write Soap Opera tests which end up being less effective and maintainable that they should be. As I said earlier, test share a common logical structure, I stuck to this one:
- Set up
- Declare expected results
- Exercise the unit under test
- Get the actual result
- Assert that actual result match the expected results
In the popular essay Mocks Aren't Stubs, Martin Fowler uses a differently grained structure (setup, exercise, verify, teardown) but I'll stick on the original structure, at least for now.
Since "refactor" is the weaker step of the TDD mantra, I tend to keep a slightly different approach, trying to think at my tests in the cleanest possible way. There is normally not so much to do with step three – which is often no more than a single method call – but often a lot can be done to improve all the other phases.
Setting up the test
JUnit structurally poses a separation between the common set up shared by a family of test and the specific set up or initialization needed for a single test to run. The former is placed in the setUp() method, while the latter is generally the beginning of our test method. A common situation is to place here the definition of all the Domain Objects needed to perform the operation to be tested. This is also a good place to check if your application has been properly designed. Creating objects for testing purposes should be a straightforward activity. As a rule of thumb, creation of the necessary objects shouldn't need more than three-four lines of code. Does it sound thin? Let me explain.
Objects should be ideally created one-shot. In other words, you should have robust constructors available to create the domain objects without having to bother with setter methods. This might not be a completely viable options if you have a lot of optional attributes in your entities, which probably shouldn't be in you constructors anyway. You are definitely luckier if your architecture already has some Factory in place (such as the ones prescribed by DDD, by the way).
Complex object structures should be available off-the shelf: if I need to test Entity A, associated with B and a specific C instance, and this is a boundary condition for my application, I want this combination to be readily available for any single test that pops out in my brainless mind. Ok, you can achieve a similar result with cut-&-paste, but … please … (ok, the official reason is that you end up slowing test development and increasing unnecessary coupling). An off-the-shelf test object approach fits particularly well with the agile style of personalizing users and typical entities: if I am developing a banking system and Fred is my average user with a banking account and a VISA, while Randolph and Mortimer are very rich people with many accounts investment and so on, I want to have available in my test framework something like createFred() and createRandolph() or createMortimer(), to be used in many more short tests. Such convenience methods are particularly useful when business functions or business objects are complex, and such complexity ends up preventing people from writing the test they should write.
The worst case scenario you might have to deal with, happens when you have only the Java Bean empty constructor and a plethora of setter methods. Setting up complex objects will take pages, and the code is a perfect humus for small bugs in the test definition. In addition, I will hate you :-). In general, test code might just be as buggy as production code, so writing the shortest possible test sounds like a good advice, both from a robustness and readability point of view. In general, creating objects for testing greatly benefits from the presence of dedicated Factories, and this should be taken into account when designing the application. Creating objects should be easy because we want to do that over and over and over.
In Java, Spring helps a lot in managing creation of other type of objects, such as Services, Controllers, Managers or DAOs. After all, Spring is like a "pervasive factory" that takes care of object setup and initialization. Typically, services are Spring Managed, while entities are not. So we have to deal with entities by ourselves. If Factories are not available, I often end up writing factories for test specific purposes. Depending on the level of control I have over the application they often make it for a refactoring on production code as well. If factories are already in place, we can subclass or wrap them with a test-layer factory class that provides us with the aforementioned convenience methods
Tuesday, March 25, 2008
Why do test antipatterns emerge?
In the previous post I presented an example of what I call the Soap Opera Test Antipattern, and some possible side-effects like having test code implicitly coupled to the application code. Reasons for this post arose from a discussion which is still going on in the Bologna XP mailing list, and reinforced by this post by Jason Gorman. Of course, every methodology works perfectly well …in theory. But practice with testing systems leaves us with a bunch of challenging issues when applied (more or less blindly) to real world situations.
So why do we end up having Soap Operas test in our code? I think one reason is rooted in the heart of the TDD mantra "Red, Green, Refactor". Here's why:
- Red. You want to add a new requirement, you do so by adding the corresponding test. You're done when you added the test, and running it results in a red bar.
- Green. You get to the green bar as quick as possible. Hacks are allowed, to get to green because being far from the green makes you dive too deep and you have no idea about what it takes to get back to green. You're done when you have the green bar again in your xUnit test suite.
- Refactor. This is a green-to-green transition that allows you to clean up the code, remove duplications, and make the code look better than in step 2.
Step 3 looks a little weaker than the others for a few reasons
- It's the third step. If you're time-boxed, this is where you're gonna cut, by telling "done" to your boss, even if you feel that something's still missing.
- The termination condition is less defined, compared to step 1 and 2; "green" is a lot less disputable than "clean". To declare step 3 over you have to satisfy your "personal definition of code beauty", assumed you have one. Moreover, refactoring goals are often personal: TDD book suggests to write them on a paper and keep it for the day. This means that you refactoring goals are not shared with the team. This is not a mandatory approach, for example I am the kind of guy that normally starts polluting the bug tracking system with refactoring suggestions. But I also know that very few of them will actually make it to production code (unless I am supremely in charge of the project…). Anyway, I think that most of the time refactoring notes are something too trivial to be shared on the Bug Tracking System. But the best way to achieve that is to have them fixed before they have to become remainders.
- It's a matter of culture. If you're doing TDD but lack some crucial OOP skill, you're in danger of writing sloppy tests. There's a lot of good OO in a framework like JUnit, and designers made it good enough that the OO part is well hidden behind the scenes. But this does not mean that developers should code like neanderthalians when it comes to coding tests.
Putting it all together, the result is often test code which is less effective than it should be.
Friday, March 14, 2008
The soap opera test antipattern
If you are coming from a romantic programmer attitude, or simply didn't care about testing your code, then every single line of tests code is valuable and adds some stability to your system.
After a while, anyway, the testing code mass could increase significantly and become problematic if not correctly managed. I've pointed you to the Coplien vs Martin video in my previous post. Now I won't claim that I've found the solution of the issue, but some thoughts on the topic might be worth sharing.
Starting to test
When embracing TDD or test first, or – less ambitiously – when starting to use xUnit frameworks for testing, you simply have to start from somewhere. You choose the target class or component, define the test goal and code your test using assertions to check the result. If the light is green then the code is fine, if it's red… well, you have a problem. You solve the problem, refactor the solution to make it better, in a green-to-green transition, then move to the next feature, or the next test (which will be the same thing, if you are a TDD purist).
Every test adds stability and confidence to your code base, so it should be a good thing. Unfortunately, when the test code mass reaches a certain weight it starts making refactoring harder, because it looks like extra code to be affected in a refactoring process, making refactoring estimations more pessimistic, and the whole application less flexible.
Why does this happen? I suspect testing skills tend to be a little underestimated. JUnit examples are pretty simple, and some urban legends (like "JUnit is only for unit tests") are misleading. Testing somehow is a lot better that not testing at all. Put it all together in a large scale project and you're stuck.
The soap opera test antipattern
The most typical symptom of this situation is what I call the soap-opera test: a test that looks like an endless script.
@test
public void testSomething() {
// create object A
…
// do something with this A
…
// assert something about A
…
// do something else with A
…
// assert something about A
…
// create object B
…
// assert something about B
…
// do something with B
…
// assert something about B
…
// do something with B and A
…
// assert something about B and A
…
}
The main reason why I named this one "soap opera" is straightforward: there is no clear plot, there are many characters whose role is unclear, things are happening slowly, and conversations are filled with a lot of "do you really mean what you said?" and there is no defined end. The second reasons is that I always dreamed to name a pattern, or an antipattern… somehow.
Even if I was too lazy (or sensible) to put some real code in there, some issues are pretty evident:
- Test looks like a long script;
- if you're lucky, the purpose of the test is in the method name or in the javadoc, assertions are too many to make the test readable or to make out the purpose by simply reading the code;
- I bet a beer that 90% of the lines you have on a test like this are simply cut&paste from another test in the same class (if this is the only test you have in your system the bet is not valid);
- The test can get red for too many reasons;
- Really looks like the inertial test code mass mentioned before.
What's the point in "looks like a long script"? My opinion is simply that it doesn't have to look like that! A good test has a well defined structure which is
- Set up
- Declare the expected results
- Exercise the unit under test
- Get the actual results
- Assert that the actual results match the expected results
I grabbed the list from here, the original article talks about many JUnit antipatterns (but calls the soap opera antipattern "the overly complex test" which is a lot less glamorous). Setting up can't be accomplished completely by the setUp() method, cause some preparation is obviously test-specific. Steps 3 and 4 often overlap especially if you're testing a function. But the whole point is that this definitely is a structure, while a script is something less formed.
Multiplying the asserts has a thrilling effect: when something goes wrong all of your test start getting red. In theory a test should test one and only one feature. There are obviously dependent features, but a well formed test suite will help you a lot in problem determination by pointing right to the root cause. If the testing code for a feature is duplicated all over the test suite… you just get a lot of red lights but no hint about where the problem is.
Testing against implicit interfaces
Even if you clean up your testing code and refactor to be in one feature/one test situation you'll still experience some inertia, due to testing code. This definitely smells: we were told that unit tests are supposed to help refactoring, allowing us to change the implementation while controlling behavior on the interface. The problem is that we are often doing it only in step 3 of the above list, while we are depending on application implicit interfaces in creation of test objects and sometimes also in asserting correctness of the result. Creating a test object might me a nontrivial process – especially if the application does not provide you with a standard way of doing it, like Factories or the like – and tends to be repeated all over the testing code. If you're depending on a convention, changing it will have probably a heavier impact.
In general, when writing a test, step 3 is very short. Basically just a line of code, depending on the interface you've chosen. Dependencies and coupling sneak in from test preparation and test verification, but you've got to keep it under control to avoid getting stuck by your test code base.
Wednesday, March 12, 2008
TDD vs Architecture debate
Some days ago, I was watching this video on InfoQ, where James Coplien and Robert C. Martin were discussing about some undesired side effects of TDD, particularly on the architecture side. One of the key point was that testing code increases the overall weight of the code base, making it harder to eventually refactor the architecture.
Another interesting issue presented was that TDD doesn't necessarily enforce testing all the possible boundary conditions, but often ends up in a sort of heuristic testing, which is less effective that testing based on a design-by-contract assumption.
Honestly, TDD book put a lot of emphasis on efforts to remove duplications, also between production and testing code, but I have the impression that this portion of the message is often lost by test writers. I've got some ruminations on the topic that will probably make up enough stuff for some more posts in the following days.
Tuesday, November 20, 2007
Is Domain Modeling a “liberal” activity?
This post might sound quite stupid, but I guess this is nothing new… There's been a long debate going on between two parties: those who prefer modeling a system starting from an Object Oriented domain model, and those who prefer starting from a Data Model. I am not talking about legacy systems where this choice is somehow context-driven, I am talking about the rosy scenario: just starting from scratch. My personal style is to never-ever start modeling from the data. I did it, when I was younger, and was developing a MSAccess VBA application. But that's it. In a OO system an Object is a richer entity than a Table: there's behavior that comes into play and I think that the table is sort of a projection of the domain model. To be completely honest, I have to admit that I am far more familiar with UML and Java than with ER diagrams or SQL, but that's not the main reason for me to do that.
I started to suspect that there is something more deeply rooted here. Ok, we can't really classify UML or ER modeling as a "primal instinct" – although some graffitis might be classified as "Cro-Magnon UML profile" – but peers tend to choose one way or another one "by heart" unless when constrained by the tools in use. Getting to a precise, DDD-like, domain model is a complex task, which involves a deep understanding of the real domain, that can be achieved through empathic discussions with domain experts and users. The goal is to understand a different culture, and the way is to respect it. Doesn't it sound a little bit like "make love not war"? From the data-driven guys point of view, this sounds a lot like "he had a difficult childhood" or "we've got to consider the environment he's been growing in" which results in a big loss of time. The domain is just another word for the data model, which is made up by data and constraints, all the rest is "OO-hippy stuff".
I assure you I was sober, when I thought this. I still think this is fairly stupid, but it fits a lot of my collegues… ;-)
Monday, October 22, 2007
The eternal question
Thursday, October 18, 2007
Enabling factors for Domain Driven Design
There was a nice follow up to an Eric Evans interview on InfoQ, which led to this interesting post by Chris Beams. I totally agree with Chris: when I first read about Domain Driven Design it sounded a lot like the good old OOP that I learned in the university (shall we call it GOOOP?), just a little bit more pragmatic and organized. However, J2EE forced developers to drop that pure OOP model (but I still suspect that pure OOP is not for the masses…) in favor of a heavy procedural model disguised as a J2EE architecture.
In the last years technologies such as Spring and Hibernate became mainstream, laying the ground for lighter architectures. More specifically Spring brought the POJO model to the services part of the architecture, while Hibernate enabled POJO representation for the Domain Model. Freed from many of the constraints of the J2EE platform, architects may now aim to the best possible representation of the model, which currently is represented by DDD.
Friday, August 10, 2007
So …what is lock in anyway?
When designing an application architecture, one of the things to consider is how to deal with lock-in on third party components. The problem is generally multi-faced (a component might be freely available as open source, or being tied to a license fee) and is generally managed on a risk analysis basis. It can also be tied to large scale aspects such has hardware choices, Operative systems etc. I'll focus here on choosing 3rd party libraries and components for a generic enterprise application (thinking of JEE, but not necessarily only of it). On the practical side this is managed by taking care that the component substitution cost is kept as low as possible. Typical approaches were to intercept and wrap dependencies, with a proxy or an adapter class: no direct access to the 3rd party component is allowed, access to the provided service is available only via the adapter class. In a J2EE context looked like everybody was concerned about Log4J, so the logger wrapper became the architect-level counterpart of the String-manipulation class that every developer felt obliged to write at a certain point of his career.
Technically speaking, you want to reduce coupling between the caller and the provider of a given service, so you extract a more or less generic interface from the service, and provide a (usually brainless) adapter to the 3rd party component. This way you achieve compile time decoupling from the component, at the price of a small increase in complexity (a bit in the code, a little more in documentation, guidelines and control). This price is generally proportional to the complexity of the used portion of the component API (which is one of the reasons why everybody wrapped Log4J…).
The point is that this effort is not really worth a lot of times. For basically two reasons:
- You are trading a certain cost now for a possible one in the future (ok, you've done risk analysis on the subject, but… you've got the point.)
- You are probably overestimating substitution cost. Ok, I am telling you but I am thinking me, 'cause I know I did… Common refactoring techniques, enhanced IDE support, or even a trivial search and replace capability make this type of code-level change really faster. Developers are generally worried about the number of occurrences of a given required change more than the diversity of the single ones.
What's left out, is more subtle effects of lock in… which results in way of organizing the surrounding components, or in the way to structure the application. In those cases, you can still enhance the capability of your adapter, providing an higher level of service abstraction, but as before you end up paying more at the early stages for a possible risk that could be better mitigated some other way.