commons-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Rodney Waldhoff <rwaldh...@apache.org>
Subject Re: [general][lang] monolithic components considered harmful
Date Tue, 31 Dec 2002 22:04:58 GMT
In an attempt to make a coherent reply, I've snipped and reordered this
thread significantly.  I hope I haven't taken anything too far out of
context.

rw> 1) Monolithic components introduce
rw> false dependencies.

sc> Adding any dependency to your application
sc> adds a risk, and you need to get a
sc> reward for that risk. I would argue that
sc> adding more dependencies (more smaller jars,
sc> each with their own dependencies) makes the
sc> overall situation worse not better.

Again, the number JARs in my classpath is a poor metric for the magnitude
of my dependencies.

E.g., this:

jar -xf *.jar
rm *.jar
jar -cf monolithic.jar .

doesn't change my dependencies.

A class X depends upon class Y when a change in Y may cause a change in X.
We try to minimize X's dependencies so it is more resilient in the face of
change (this is what we mean by "adds a risk"--the risk that something we
depend upon may change).

For most users, a release is the unit of change.  When a new release
occurs, any of the classes in that bundle *may* have changed, so I
conservatively need to treat it as if they did--retest, perhaps even
modify my dependent code.

When Y and Z are only released and distributed together, I don't really
have a Y or Z, only the pair {Y,Z}.  Then a release of Z requires a
release of {Y,Z}, which is a release of Y.  But while a *change* in Z is a
change in {Y,Z}, it only *looks like* a change in Y.  This means I've had
to re-test X for no reason at all.  This has, for all practical purposes,
*added* a dependency on Z to X, since I can't readily distinguish changes
in Y from changes in Z.  This adds risk and effort to X, since the pair
{Y,Z} changes much more frequently than either of Y or Z, especially when
Y and Z are not mutually dependent or likely to be changed together.

rw> 4) Monolithic components make it more
rw> difficult for clients to track and
rw> communicate their dependencies.

sc> Maybe you place more store in version
sc> numbers than I do. If I pickup any new
sc> jar, I'd test it whatever the version
sc> number difference.

My point exactly.  When, say, I depend upon something in [math], and
[math] and [reflect] are only released together, and there's a new release
of [reflect], I need to "pickup a new jar" and therefore "test it", even
if [math] hasn't changed.  If [math] and [reflect] aren't released
together, users of [math] don't need to worry about changes to [reflect].
If they are not used together, they shouldn't be released together (the
Reuse/Release Equivalence Principle).

rw> Bundling unrelated code into a monolithic component
rw> means I need to synchronize development of that
rw> unrelated code: Maybe I'd like to do a new release
rw> of sub-component X, but I can't since sub-component
rw> Y is in the midst of a major refactoring.  Maybe
rw> I'd like to do a major refactoring of sub-component
rw> A but I can't since sub-component B is preparing
rw> for a release.

sc> Virtually all the [lang] classes are fundamentally
sc> independent, so refactoring isn't an issue. And
sc> this actually highlights that to be proper about
sc> this would require a component for virtually
sc> each class.

No. To be proper about this would require a component for each bundle of
classes that are likely at the same time (the Common Closure Principle).



rw> 6) Monolithic components only get bigger,
rw> making all of these problems worse.

sc> Not all of the ideas presented in
sc> the list above will end up in [lang]
sc> (some get rejected).

Of the 11 ideas I cited, all but one of them already exist in the HEAD of
lang, and that one is listed as an action item in the status file.
Specifically:

rw> * math utilities [12]
See o.a.c.lang.math.

rw> * serialization utilities [13]
See o.a.c.lang.SerializationUtils.

rw> * currency and unit classes [14]
See
<http://cvs.apache.org/viewcvs/~checkout~/jakarta-commons/lang/STATUS.html>.

rw> * date and time utilities [15]
See o.a.c.lang.time.

rw> * functors [17]
See o.a.c.lang.functor.

rw> * reflection and introspection utilities [16]
See o.a.c.lang.reflect.

rw> and much more [...]

rw> [18]
See o.a.c.lang.Notifier and others.

rw> [19]
See o.a.c.lang.StopWatch.

rw> [20]
See o.a.c.lang.exception, o.a.c.lang.functor,
o.a.c.lang.util.IdentifierUtils, o.a.c.lang.*Utils.

rw> [21]
See o.a.c.lang.util.Validator.

rw> [22]
See o.a.c.lang.util.BitField, o.a.c.lang.time.StopWatch,
o.a.c.lang.util.IdentifierUtils, and others


sc> Or viewed alternately, [lang] has had
sc> the community to grow and stay active
sc> while other components have not.

sc> [...] As a group of functions, [lang] has ideas,
sc> momentum, growth and life. [...]

sc> [a monolithic lang] allows us to actually develop
sc> code without arguing about which should depend on
sc> which all the time, and that is terribly wasteful.

Either of these arguments would carry more weight if people were clamoring
to add code to [lang], but the script seems to run more like this:

 Person ${p} suggests feature ${f} for component ${c}. Person ${q} insists
it belongs in [lang].

and ${q} pulls from a very small set.

Here's 10 examples:

* p=Travis; f=currency and unit utilities; c=any; q=Stephen
<http://archives.apache.org/eyebrowse/ReadMsg?listId=15&msgNo=18957>

* p=various; f=reflection and introspection utilities; c=beanutils;
q=Stephen
<http://archives.apache.org/eyebrowse/ReadMsg?listId=15&msgId=411302>

* p=Rodney; f=ConstructorUtils; c=beanutils, q=Robert
<http://archives.apache.org/eyebrowse/ReadMsg?listId=15&msgNo=19847>

* p=Tom; f=additional functors; c=collections; q=Stephen
<http://archives.apache.org/eyebrowse/ReadMsg?listId=15&msgNo=19865>

* p=various; f=functors; c=collections; q=Stephen
<http://archives.apache.org/eyebrowse/ReadMsg?listId=15&msgNo=19885>
<http://archives.apache.org/eyebrowse/ReadMsg?listId=15&msgId=577713>
and others.

* p=Michael; f=SerializationUtils; c=io; q=Stephen
<http://archives.apache.org/eyebrowse/ReadMsg?listId=15&msgId=457636>

* p=various; f=reflection and introspection utilities; c=beanutils; q=Robert
<http://archives.apache.org/eyebrowse/ReadMsg?listId=15&msgId=519705>

* p=Rodney; f=functors; c=functors; q=Stephen
<http://archives.apache.org/eyebrowse/ReadMsg?listId=15&msgId=577713>

* p=various; f=type conversion utilities; c=beanutils; q=Stephen
<http://archives.apache.org/eyebrowse/ReadMsg?listId=15&msgNo=19885>

* p=Ola; f=type conversion utilities; c=converter; q=Stephen
<http://archives.apache.org/eyebrowse/ReadMsg?listId=15&msgId=551801>


sc> I see it more about having a viable community.
sc> [lang] has that community, I don't believe
sc> 10 separate components would.

sc> Because open source is about community first, code second.

This argument is a non-starter.

All the commons components share user and dev mailing lists, voting
rights, and karma.  By design, there is a high degree of overlap between
the both the active developers and the regular users of a number of
components.  There is a high degree of interdependency between components.
There is no reason to think that the "community" around, say,
[lang]/[functor]/[reflect], would be any different that the community
around [lang]/[lang.functor]/[lang.reflect]. There are a number of
"sub-communities" that have devloped around related components.

Components that have a readily identifiable community largely distinct
from or with little overlap with the rest of jakarta-commons are probably
destined to move out of jakarta-commons (like Cactus, HttpClient, Jelly,
maybe even Latka someday). (And I say that expecting to follow HttpClient,
Jelly and Latka wherever they end up.)

sc> As a small isolated component (in line with commons
sc> guidelines), [util] and [pattern] have both
sc> died through lack of interest.

sc> [util] languished for over a year with no
sc> action. No one took responsibility to promote,
sc> fix, manage, look after or release the code.
sc> This has now been noted on the recent Jakarta
sc> PMC report

I'd characterize both [util] and [pattern] as more similiar to [lang] than
anything else in commons, and I'll argue that none of them are in line
with the commons guidelines--all of them lack cohesion and clearly defined
purpose or scope.

Try this: fill in the blanks in the following

  If you want to ___, you may want to use ___.

For example:

* interact with JavaBeans via reflection and introspection; beanutils
* translate between JavaBeans and XML; betwixt
* parse command line arguments; cli
* work with abstract data structures; collections
* parse xml configuration files; digester
* discover services that have been externally configured; discovery
* pool database connections; dbcp
* implement an XML scripting language; jelly
* process multipart/form-data HTTP requests; fileupload
* interact with HTTP servers; httpclient
* work with XPath expresssions in java; jxpath
* functional test HTTP applications; latka
* write debugging and logging messages; logging
* support JMX via Model MBeans; modeler
* pool objects; pool
* validate user input; validator

Now try it with [lang], [util] or [pattern] and any scope signficantly
different from "develop software in Java".



--
To unsubscribe, e-mail:   <mailto:commons-dev-unsubscribe@jakarta.apache.org>
For additional commands, e-mail: <mailto:commons-dev-help@jakarta.apache.org>


Mime
View raw message