Programming

Sunday, November 18, 2007

Write more libraries

We keep hearing that code reuse is a Good Idea, that we should do more of it, and that it can drastically reduce costs etcetera etcetera. It makes common sense in an ideal world.

The reality is very different. Code gets build into applications, dependencies build up, behaviour becomes fragile, code becomes less maintainable, and eventually after the people who wrote the code have moved on, decided that the original design was poor, the maintenance costs rise and becomes a fire-fighting exercise, so the code gets thrown away and rewritten. However this can be a cycle: new code eventually also rots and gets thrown away.

Refactoring is supposed to rejuvenate your code and keep it current. I'm sure the theory works fine.

The problem is that fundamentally, people don't write code for reuse. They put a good design in (that is appropriate at the time), and think that's the end of it. However that code won't get reused properly. It might get ripped out and copied-and-pasted into another application, but that is far from ideal code reuse.

Instead I would suggest putting as much code as possible into reusable libraries. This means that as much as possible should work stand-alone, without dependencies. When coding a library, imagine its use for the application at hand, but also treat it as an independent work, with proper documentation, and proper anticipation of the different ways the code could be used. It may mean generalising prematurely, but the code becomes infinitely more useful.

Now in a typical project, I would organise it as follows:

lib1
lib2
test1
test2
applogic1
applogic2
testapplogic1
testapplogic2
app1
app2

lib1 and lib2 are the main code - as independent libraries, They depend on nothing, except perhaps each other (and without circular dependencies). Most of the code should go into these libraries, and follow proper documentation and coding guidelines as appropriate for libraries. Where specific implementations are mandated, try to provide a strategy pattern to make the library not specific to the application. Where code is too interdependent, break the dependency by providing interfaces.

The test1 and test2 projects are the unit tests. These test the functionality in lib1 and lib2 respectively. Part of the build process should be to run the unit tests, and fail the build if the unit tests fail.

applogic1 and applogic2 are libraries of business or application logic (you could also have bl1 and bl2 if that suits your application better). There are also unit tests for applogic1 and applogic2, which should also be run as part of the build.

app1 and app2 are the different applications. The point is that app1 and app2 should be as thin as possible. This is the bit you're going to throw away or port to another platform, whereas everything else should be cross platform. app1 and app2 can share common functionality from lib1 and lib2, and the logic from applogic1 and applogic2.

In terms of longevity, app1 and app2 have the shortest lifespan. They will be full of platform-specific GUI-code which will be hard to unit test. applogic1 and applogic2 contain logic specific to your application, which can be shared between applications. If possible, make the logic extensible without going over the top. The application logic will have a longer lifespan because it can be reused for different applications, but is in general tied to particular applications. The libraries should have an indefinite lifespan.

If you are serious about code reuse, you should push as much code as possible into libraries. Think: does this unit have functionality beyond the application? Can it be generalised? If the answer is yes, then move the code into a library.

In hindsight, I don't see how any code could be reused unless it is in a library, and I don't see how unit tests can be linked against code embedded in an application. Librarifying (!) your code clearly separates out the parts that you intend to reuse, and the parts you intend to throw away. Libraries are your crown jewels.