picture home | pixelblog | qt_tools

omino code blog

We need code. Lots of code.
entries for category "java"
David Van Brink // Sat 2011.11.5 10:10 // {code java}

Threads

Aah, threads, so beautiful and so dangerous.

In this note, I’ll jot down a couple of recently-encountered threading hazards, for future reference. Perhaps this post will evolve into a Basics note at some point.

My context for now is Java, but the concepts are somewhat general.

Life Before Threads

I got my start doing programming on an Autonetics Recomp III. My Junior High School teacher Elliot Myron had one of these even-then-antiques as a hobby. And a dozen paper tape punches where we hand-entered octal assembly programs to run. No interrupts! And so, no threads.

Later, I wrote a few Apple II video games. These incorporated 1-bit sound effects and animation and multiple agents acting simultaneously… again, no interrupts, and no threads.

Lately, I occasionally program 30-cent PIC chips to blink light patterns and such. Yeah, you got it, no interrupts, no threads.

So it turns out you can (and probably should) do all sorts of interesting things without lots of threads. Modern programming is rather far removed from the lowly “interrupt” and I’ll refrain from mentioning again… but interrupts is where threads come from. Mostly.

Clotho

First red-flag: If you find yourself starting a sentence with, “And there’s one thread per…” just stop right there. Do not have one thread per player, or one thread per incoming request, or one thread per anything.

Lachesis

Second red-flag: When you do your locks within a class, some say, “Lock on the most local object you can.” I found this lead to confusion. I found it far safer to do all locks on “this”, just to keep it consistent. By all means, hold them as briefly as possible.

By locking always on the same thing (this) it eliminates the possibility of out-of-order lock and unlock.

Consider locking and unlocking inside a loop instead of outside it. Although I’ve found cases where unlocking made things slower, as more task switching occurred overall.

Atropos

Lastly, here’s the least-obvious and possibly most-important. If you’re managing handlers and callbacks… release the lock before calling your client’s callback code. Do your work, set your safe copies and state, and then release the lock and call them.

You see, they’re likely to make calls back into the library which then have more locks. In a messaging system, they’ll send messages on another queue which also has locks. Avoid deadlock by letting their code run free.

Sounds Awful

It is, it is. A really great article about some really, really smart people trying to use threads is here.

The best thing to do is, Don’t use threads. Browser-page JavaScript and server-side node.js both run in a single-thread style. Emulate that as much as possible.

Of course, to emulate that you need to write some thread-hiding layers. Hence, the above advice.

Carry on!

oh, i dont know. what do you think?


David Van Brink // Thu 2011.08.4 21:32 // {java}

Stupid Java Tricks

Syntactic Sugar

You know what I hate? I hate that I always am writing code, usually for tests, that looks like:


	List<String> stuff = new ArrayList<String>();
	stuff.add("fish");
	stuff.add("cow");
	stuff.add("dog");

So now I just type:


	List<String> stuff = Om.list("fish","cow","dog");

Nothing exotic, but here’s the handy method that allows it, and several others of a related ilk. I particularly like the Om.map() method. Enjoy. I’ll share a few more soon.


package com.omino.roundabout;

/**
 * Generic and System utility methods, like printf.
 * @author poly
 */
public class Om 
{
	...

	/**
	 * Syntactic utility for inlining object lists without the tedium of creating a list and adding to it.
	 * @param <T> the implicit type of all the pieces
	 * @param pieces things to put into the list
	 * @return a mutable list. Add more to it or delete some if you like.
	 */
	public static <T> List<T> list(T...pieces)
	{
		List<T> result = new ArrayList<T>();
		result.addAll(Arrays.asList(pieces));
		return result;
	}

	
	/**
	 * Syntactic utility for inlining a map. The map signature is sussed from
	 * the first two items, but you can include as many pairs as you like.
	 * @param <K>
	 * @param <V>
	 * @param key1
	 * @param value1
	 * @param theRestOfTheKeyValuePairs
	 * @return a mutable map.
	 */
	@SuppressWarnings("unchecked")
	public static <K,V> Map<K,V> map(K key1,V value1,Object...theRestOfTheKeyValuePairs)
	{
		Map<K,V> result = new HashMap<K, V>();
		result.put(key1,value1);
		for(int i = 0; i < theRestOfTheKeyValuePairs.length - 1; i += 2)
		{
			K key = (K)theRestOfTheKeyValuePairs[i];
			V value = (V)theRestOfTheKeyValuePairs[i + 1];
			result.put(key,value);
		}
		
		return result;
	}

	/**
	 * Syntactic helper to make a set of objects.
	 * @param <T>
	 * @param items
	 * @return
	 */
	public static <T> Set<T> set(T...items) 
	{
		List<T> list = Om.list(items);
		Set<T> result = new HashSet<T>(list);
		return result;
	}

	...
}

oh, i dont know. what do you think?


David Van Brink // Tue 2008.09.30 17:01 // {broad generalities java}

Practical API Design by Jaroslav Tulach

Found and acquired this book today at Bookshop Santa Cruz. Very nice tome on good Java API style. Alas, slightly pricey ($75 retail). The fellow’s a bit ranty at times… but, ah, I can relate. The author was a founding architect of NetBeans , and is still a principal on that project.

Also, look, google lets you embed a book for up to 20% reading! (Embedded below, may take a moment to load.)

Search for “beatiful” or “symmetry” or “backwards compatibility”…

oh, i dont know. what do you think?


David Van Brink // Tue 2008.05.27 16:46 // {code java}

Java Gotcha

I was working on some code, and finding that it broke a bunch of unit tests. By the advanced method of “commenting out the new stuff”, I found that the line in red below appeared to be causing the problem.

If I commented out the line in red, everything worked! Put it back in, it all breaks!

for(IDocumentUrl doc : docs)
{
	@SuppressWarnings("unused") AltNode docUrlNode = new AltNode(DOCUMENT_URL);
//	docUrlNode.setValue(DISPLAY_NAME,doc.getTitle());
//	docUrlNode.setValue(TYPE,doc.getType().name());
//	docUrlNode.setValue(URL,doc.getUrl());
//	cardNode.addChild(docUrlNode);
}

What’s the deal?

The line in red is just allocating a harmless object. In fact… I replaced it with:

for(IDocumentUrl doc : docs)
{
	@SuppressWarnings("unused") Integer x = 3;
}

With the same symptoms. Comment out the line in red, and all is well.

Ah. Java does do some optimization! Removing the contents of the iterator loop allowed Java to omit the entire loop. Turns out docs was null; removing the contents of the iterator loop avoided an exception.

All fixed:

if(docs != null)
  for(IDocumentUrl doc : docs)
  {
    @SuppressWarnings("unused") AltNode docUrlNode = new AltNode(DOCUMENT_URL);
    docUrlNode.setValue(DISPLAY_NAME,doc.getTitle());
    docUrlNode.setValue(TYPE,doc.getType().name());
    docUrlNode.setValue(URL,doc.getUrl());
    cardNode.addChild(docUrlNode);
  }
oh, i dont know. what do you think?


David Van Brink // Wed 2007.08.8 23:04 // {java software architecture}

Basics: The Name of the Assertion

The Java JUnit framework is an incredibly useful tool for maintaining code integrity. This note focuses on the tiny but important matter of what messages to insert into your assertion statements.

Basics: The Name of the Assertion

Why Assert?

The philosophy behind unit testing is simple: If you make the appropriate small assertions about your software, you can believe that the system will operate correctly in the aggregate.

The Java JUnit framework provides a convenient way to structure a collection of these assertions. In JUnit, when an assertion is correct, or “true”, nothing happens. When an assertion is incorrect, an error is displayed, and the particular test is ended.

JUnit provides a handful of Assert methods. Each one has an optional first parameter: the failure message. This note shall offer guidance on maximizing the value of those failure messages.

JUnit In One Paragraph

I’ll assume you either know or can easily find out the simplest mechanics of writing and using JUnit. For the purpose of this note, we need merely recall that:

  • assertTrue(boolean b) fails if b is false
  • assertNotNull(Object o) fails if o is null
  • assertEquals(Object a,Object b) fails of a does not equal b
  • …and that each takes an optional first argument with a String failure message.

Evolution of a Test

Let us now examine specimens of a test from various levels of the evolutionary ladder.


Homo Habilis

A test can’t be much simpler than this… or less informative.

  public void testTwoFish() 
  {
    assertTrue(getListOfTwoFish().size() == 2);
  }
junit.framework.AssertionFailedError at dvb.JunitExamples.testTwoFish(JunitExamples.java:12)

Well, it failed. It says right there, “AssertionFailedError”!


Homo Erectus

This version of the test shows the beginning of structure.

  public void testTwoFish() 
  {
    // "expected" goes before "actual"
    assertEquals(2,getListOfTwoFish().size());
  }
junit.framework.AssertionFailedError: expected:<2> but was:<3> at dvb.JunitExamples.testTwoFish(JunitExamples.java:15)

Better already! Our distant ancestor Homo erectus knew that assertEquals was a more powerful tool than assertTrue. It tells us that something was 3 but should have been 2. But what? Well, the test name gives a hint. But we can do better…


Homo Neandertalensis

We begin here to see a nearly modern JUnit test. Language begins to play a broader role in survival.

  public void testTwoFish() 
  {
    assertEquals("getListOfTwoFish()", 2, getListOfTwoFish().size());
  }
junit.framework.AssertionFailedError: getListOfTwoFish() expected:<2> but was:<3> at dvb.JunitExamples.testTwoFish(JunitExamples.java:20)

Now the message is somewhat helpful… and naturally raises the question, What did it return, if not two fish?


Homo Sapiens

We now see a specimen of a fully functional JUnit test.

  public void testTwoFish() 
  {
    List<String> x = getListOfTwoFish();
    assertEquals("getListOfTwoFish() " + x, 2, x.size());
  }
junit.framework.AssertionFailedError: getListOfTwoFish() [nemo, mr limpet, flipper] expected:<2> but was:<3> at dvb.JunitExamples.testTwoFish(JunitExamples.java:26)

Do you see it? We’ve made the actual list part of the error message. Handily, Java collections and arrays have a built-in toString() method which lists each element separated by commas. Now, when it fails, the problem is obvious.


Homo Superior

That last specimen may be satisfactory for human programmers, but my standards, and I hope yours, are higher still.

  public void testTwoFish() 
  {
    List<String> x = getListOfTwoFish();
    assertNotNull("getListOfTwoFish() was null",x);
    int size = x.size();
    assertEquals("getListOfTwoFish() " + x, 2, size);
  }

Two final flourishes round out our exercise. First, we check for null. It could happen to anyone, it could happen to you. And secondly, we pull out the size assignment to a local variable. This is a courtesy to those who come after us, stepping through our code.

The Common Theme

There is a common theme in all the above examples: augment the information that JUnit already returns. The assertEquals is wonderful, as it shows what you want and what you got. Our message augments it with a name, a list… a hint. The science fiction writer Theodore Sturgeon invented a symbol for “Ask the next question.”


The idea applies here. What went wrong? Oh, we expected one integer but got a different one. What were they? Ah, we got “3″ but it should have been “2″. So what was the third fish?

But Why All That Effort?

In practice we usually write a test once, and, assuming it passes, never touch it again. The above “evolution” may happen as a result of debugging a failure. But: the effort is small to do it up front, when the test is first written. Like most learned behaviors, it may take a few negative experiences before it becomes truly second nature. We all burn ourselves once, and then learn to use potholders. And wasn’t there something about seatbelts?

All images taken from Wikipedia, except Magneto.

oh, i dont know. what do you think?


An application typically stores its state as a file. As an application user, you expect this file to work, even if you don’t touch it for a while. This note offers some primal advice on implementing a file format in your application.

Basics: Know Your Document Format

Your Application’s Document

Be in control of your file format — it’s your most persistent API. Application versions will come and go, but you and your users expect the documents to last forever.

One lesson I’ve learned is simple: there are no shortcuts for mastering your own file format. You have to design it and implement it. By all means leverage existing parsers, such as for XML or RIFF or what not. But I honestly believe that there is no appropriate automation for translating your in-memory-model to a file format.

Tools like JAXB which generate XML serializing and deserializing code based on a schema can be quite useful, but I always add an additional translation layer between these generated classes and the “real” API.

Too Much Model

I’ve seen bad results from “built-in serializers”; one application developer I worked with relied entirely on the built-in serializer of Apple’s “Yellow Box” (this was essentially “Cocoa” running on Windows, after NeXT, before Mac OS X). Every build of the application produced a different file format, and some daisy chain of machine generated updaters was able to continue to read it in. The real problem was that we ourselves couldn’t reliably answer the question, “What’s in the file?”. We couldn’t easily write other tools to manipulate or debug our own documents, or stimulate test cases. (Staccato Systems’ “Synth Builder”.)

Too Much File

At the other extreme, I’ve seen bad results from reading an hierarchical file structure into memory, and attempting at all times to keep it identical to its on-disk representation. This required a lot of work, and bugs manifested as extra data in the file which other readers just needed to ignore. But not all readers ignored it, and so these stray file elements became de-facto parts of the file format. (Altera’s “SOPC Builder”, and its PTF file format, before our total overhaul.)

Just Right

It’s safest to define your file format separately from your in-memory model. They may be similar, but it’s valuable to implement the file reader and writer as a separate entity from your model. This will involve a translation layer where you read in your file format, perhaps to an in-memory tree structure of some sort, and then populate your in-memory objects. There are opportunities to make this more or less automatic, as appropriate to your particular development effort. But, importantly, this translation is very well-defined and therefore very testable.

Generally…

Hierarchy.

A file format will usually be hierarchical. That’s just the common design pattern that seems to cover the bases. As with your object design, it’s handy to make the hierarchy something that maps intuitively to the problem domain. This is easier said than done, but is an opportunity for artfulness… to say the least. As to the low level file format itself, in general in this day and age you can probably just skip any tedious meetings or debates and create an XML-based structure. If you need to store lots of binary data, and file size is a concern, your XML document can refer to neighbor files. And under no circumstances let the argument that XML is too user-unfriendly or heavyweight influence the decision; these remarks come from those who don’t know from Koolaid. Really.

No Redundancy.

The persistent document should capture each datum once and only once. As a thought experiment, imagine someone manually editing the file, perhaps even in a text editor. Ideally, they should be able to modify an entry in the file and have a predictable feature of the document change when loaded.

Human Readable.

And speaking of the above thought experiment… This isn’t always possible, and in rare cases is undesirable (for reasonable or less-reasonable reasons). But usually it’s a great benefit to be able to examine your document in a text editor. It’s great for debugging, that much I can promise. Another advantage of a text-based file format is it removes any questions of byte ordering or floating point format. What number is hex 01 02? You have to define whether it reads as 258 or 513. But if your file says “x = 258;” then it’s perfectly clear.

Easy In, Rigorous Out.

When reading in your document, be generous. If something is missing, insert a reasonable default value. This strategy can be used with cautiousness and, dare I utter it, cleverness to extend the file format and still be backwards compatible. On the converse however: Always write out everything! It is not safe to skip an entry because its value happens to be “the default”. Someday you’ll change the default, but old documents mustn’t change.

Be Careful With Scripts.

It may be tempting to use a scripting language as a file format. For example, TCL is very easy to embed in C or Java programs, but it’s not ideal as a document file format. Some will argue that philosophically code and data are the same thing. My counterargument used to be something about predictable computation time and the halting problem: if you read in a script, it might run briefly or it might run forever. But I have, I hope, an even more compelling distinction: code can erase your hard disk but data can’t. It’s that simple.

When I’ve seen scripts used as a file format, invariably, eventually, there exists some need to “parse” the script without “executing” it, which is of course absurd. If you depend on being able to parse a subset of the language then you’ve reduced it to data. Use a data format, not a scripting language.

Closing Remarks

One day in the far future (but sooner than you expect) it will be time to rewrite your application from the ground up, or create a new application which interacts in some way with the old one. In either case, a well-defined and knowable file format will render these tasks possible.

oh, i dont know. what do you think?


David Van Brink // Wed 2007.08.1 18:03 // {java}

My “Basics” Notes…

As you might have noticed, I’m making an effort to assemble a few of my ill-considered and generally foolish ideas about code and architecture into lucid active-voiced essays. I’ve got a small handful of them in various stages of completion and proofreading. There’s a page on this site called the basics which links to all the “notes”.

So what’s this for? I work in a highly technical environment but, for many of my coworkers, how to put this… “software” is not their primary language. So these notes help me to formalize my thoughts to help, perhaps, guide them on the path of truth and rightness. (Or to at least suggest that such a path could exist, though it may differ from mine.)

Perhaps you will find them interesting.

(And if anyone’s keen to see early versions of these, to proofread or steer me right or just to be nosy, drop me a line. If you have my e-mail then you’re probably already on my spam whitelist; else comment here’ll reach me fastest. A little review helps a lot!)

And not to worry, we’ll return to the regularly scheduled weird blinking lights hardware experiments real soon, what with Burning Man coming up so fast!

oh, i dont know. what do you think?


David Van Brink // Wed 2007.08.1 17:43 // {broad generalities code java}

Basics: Null and Nuller

I write a lot of code which other people call. One of the basic problems to wrestle with is: “How do I handle unusual inputs?” This note shall discuss some aspects of this, focusing on the use of “null”. Although the examples are phrased in Java, the ideas may be of general interest

Basics: Null and Nuller

Exceptions: An Inconvenient Shape

Opinions vary regarding the use of Java exceptions as part of an API. Most agree that they should be only used to indicate an “error”. But what exactly is an error? It depends solely on your definition of valid input.

In general, I prefer not to throw exceptions. I find it cumbersome to pepper my code with lots of try and catch blocks; I presume that you feel similarly. In general I’d rather check a result for “null” or “false” or some other indication of “what happened”. (This narrows the range of possible outcomes, which could be seen as limiting, but I prefer to think of it as simplifying.) To avoid throwing exceptions I try to define widely the range of valid input to my methods.

Toleration

In general, I try to make my methods tolerate, or even expect, null inputs. A null value for a parameter should be treated in the most innocuous fashion. If it’s an input, give it some default behavior. If it’s an output — such as a logger or list to be modified — let it do nothing. Return the best possible output.

As a result, I often have related convenience method variants like this:

public static void foo(String name) {
   foo(name,null,0.0);
}
public static void foo(String name,Version version) {
   foo(name,version,0.0);
}
public static void foo(String name,Version version,double scanTime) {
   ...do the operation...
}

Crash Your Code, Not Mine

Since you may not have access to my source code for debugging, I’ll try not to crash and instead push the problem back to your code. So, specifically, I’ll make sure that my method won’t ever fail on null; instead, I’ll do nothing, or return a null, so that your code will see the problem. Perhaps your code will crash; if so, you have your own source code to debug and correct. Also, in some cases, I’ll return a boolean which indicates “true” for “success”. It’s your option to read it, but is available as needed.

Return Null or an Empty List?

Some methods return a Array, List or Map, or similar objects, based on some input. If the input is all legal and normal but produces no results, I’ll return an empty list. That way you can iterate on it, check the size, and so forth, without any special case for “no results”. On the other hand, if some “necessary” input is null, I’ll return null to indicate that. Your code can then explicitly check it if needed.

Masking A Problem

The notion has occasionally been put forth that tolerant code hides errors. After all, it can be argued, if it fixes stuff up, one might think all is well. In general, I reject this claim. A method that accepts a wide range of possible inputs and has a well-defined behavior for them is simply doing as its told.

The situations alluded to where “tolerant” code hides a deep problem have, in my experience, been always linked to side effects. A typically terrifying example would be some innocent looking method thatlooks like a query but actually always fixes up your input document — just in case it was a little bit off. This is not “tolerant”, this is “toxic”.

Preflighting

How many times have you seen this code fragment:

    String s;
    int n;
    ...
    try {
      n = Integer.parseInt(s);
    } catch(NumberFormatException e) {
      n = 0;
    }

This lets any value of s, including null or a nondecimal value, evaluate to 0. But achoo, look at that try/catch peppering. I’ll always package that up in some tolerant parser. (And this gives a place to allow 0×123 or other numeric formats, if desired.) But sometimes you need to know if the input was rigorously acceptable, so we have a preflight method, as well.

    String s;
    int n;
    ...
    boolean formatGood = MyNumberUtils.isInt(s);
    n = MyNumberUtils.parseInt(s); // never excepts, 0 for bad number format or null

Neat and tidy! And all the information there, ready to steer the program as needed.

But That’s A Lot Of Bother

Hardening libraries against even these minor abuses takes effort. If some library is being used in many places, this effort — and much more — is well-spent. It offers value to every client. On the other hand, libraries always start out with zero users… but you have to start somewhere.

1 comments
weird of wbl // Wed 2007.08.8 05:565:56 am

when do we get the regularly scheduled weird blinking lights hardware experiments?

oh, i dont know. what do you think?


David Van Brink // Mon 2007.07.30 09:38 // {java levity}

@Override twoString

/**
 * @since unknown
 * @return again and again
 * @author poly@omino.com
 */
public class bar
{
  @SuppressWarnings("nls")
  @Override
  public String toString()
  {
    String result = "frayed knot";
    return result;
  }
}
oh, i dont know. what do you think?


David Van Brink // Sun 2007.07.29 20:12 // {code java software architecture}

Basics: Where To Put A New Method

In “Where to Put A New Method” we’ll consider the question: “Where should I put a new method?” We’ll be considering several different collaborative topologies. Although I’ll describe this in terms of Java methods, the dynamics — both technical and social — are, perhaps, of general interest.

Basics: Where To Put A New Method

I Wish The Platform Had [Feature X]

Here’s a perfectly reasonable idea for a bit of functionality: “Please write out this file unless the file already exists and has the same contents.” This is so you can write out a file, but not bump the timestamp if it’s going to be identical to the existing one.

In Java, you might reasonably wish that the File object had that method. That would be neat.

  /**
   * I wish we had this!
   * Write the file unless the existing one is the same.
   * @param String new contents for file.
   */
  public void writeIfDifferent(String newContents) {
    //...
  }

Sure, you think, everyone would love that. But the fortunate fact is that you can’t add it to File. That class is part of the platform and because you’re not one of the authors of Java, you just don’t have access to alter it. And this is probably a good thing. Someone owns the design of that class, and they have a vision for it, better considered than yours or mine. This proposed new method should only be added if they accept it. So put your request on a postcard, mail it to the North Pole, and wait.

Meanwhile, you need this functionality today. It’s really quite simple to implement. Create a class like so:

import java.io.File;
public class ThingsIWishJavaHad {
  /**
   * Write the file unless it would stay the same.
   * @param file
   * @param newContents
   */
  public static void writeIfDifferent(File file,String newContents) {
    //...
  }
}

This is an easy choice because it is no choice at all. Altering File simply isn’t an option. In the next section there is a more challenging moral dilemma.

(Hopefully, it’s clear that the static method is preferable to extending File. If we created our own class, BetterFile, we would have to use it throughout our code base to get these small additional features. The static method can be used directly. If you gave that static method to your friends, they could use it without changing their variable types.)

I Wish Your Library Had [Feature X]

Now let’s turn our attention to typical corporate software development. If you’re developing some software it’s quite likely that you rely on some in-house software libraries. Just because you can call up the owner doesn’t mean you can get everything you want! I have it on good authority that most employees at Sun can’t just go and add a method to File.

(Corporate cultures are wildly diverse. I’ve seen environments where anyone was allowed to change anything, and there was a presumption that, well, I guess everyone’s a professional, and if they typed commit it must be OK. In small organizations this may even work, but generally it is dysfunctional.)

Let us concoct an example. Let’s say you’re working at SmogCo, maker of SmogBooks, a publishing system. You are developing a processing tool for SmogBook documents, for a special Florida-based client. Naturally you’re building up your specialized tool using the SmogBook Java library from the main development team. The special client needs a feature to add oranges to every page. (Look, it’s just a f’rinstance, OK?) Naturally, you realize that smogbook.addOrangesToEveryPage() is too limited. What you really want, of course, is:

  public enum Fruit { ORANGE, APPLE, CHERRY } // we'll add more later!
  public void addFruitToEveryPage(Fruit pageFruit)
  {
    //...
  }

Brilliant, you think! Every SmogBook developer can leverage this; we do deal with agricultural clients a lot, come to think of it.

Now comes the moral dilemma. In many corporate settings, the revision control system is wide open. Furthermore, in many corporate settings the boundaries of code-ownership are not well-specified. So what do you do?

(Truly, in matters like this, there is great opportunity to witness, and participate in, classic social dominance games. And indeed, we should be grateful that our species has, largely, found an outlet for these tendencies in such harmless pursuits. But that is a topic for a different essay.)

You could just check the method in and hope that either everyone likes it or nobody notices. If your change is good, and you pull it off a few times, you could just wind up on the core development team as a result. But it’s far more likely that, even though it consumes your working hours, this “add fruit to every page” feature just isn’t of general interest. It’s human nature to see our immediate goals as globally important, but it’s good architectural sense to realize that they aren’t.

So I’ll have to advise that, instead, you create a tiny local implementation of the fruit feature, like so:

public class SmogBookFruitUtils {
  public enum Fruit { ORANGE, APPLE, CHERRY } // we'll add more later!
  public static void addFruitToEveryPage(SmogBook book,Fruit pageFruit) {
    //...
  }

By all means, show it to the core development team. They’ll be pleased to see their API and classes understood and used. Naturally, they don’t want to add just any old thing into their precious libraries: adding something in is a permanent support burden! But now they’ll know that at least one client needed the fruit feature; and if it’s a truly necessary feature, they’ll want to add it to the core library. And you might yet end up on their team.

I Wish My Library Had [Feature X]

And now we come to the third and penultimate challenge. We’ve covered Man Against Nature, and Man Against Man. Now we’ll examine Man Against Self.

If you yourself are maintaining a library, and you need a new feature, where do you add it? You have to ask yourself very carefully: Is this feature consistent with the vision of the library? If you add strangely asymmetric features to your library, it will become messier and harder to use. It may even feel less professional to others who are evaluating and using it.

Maintaining architectural clarity while developing and using your own libraries requires you to play both roles, that of invader and defender. As with libraries written by strangers and by peers, the safest place to add a new feature is somewhere else. If it proves out, promote it into the library.

They Wish My Library Had [Feature X]

And eventually, inevitably, you yourself will be providing core libraries, and you will have fans of your library, which I’ll here call users. And now they want features. Oh, how they do go on about how they wish things were different. What can you do? I can offer several bits of advice.

First, accept that these users, as troublesome as they are, are your friends. When you hear complaints you know you’re on to something useful. Trust me.

Keep an eye on the revision control system. Your users mean well, but that doesn’t mean they know what they’re doing. If they change your code, look at what they’ve done, and decide if that’s how you would do it. If not, you have a professional obligation to roll out their changes and advise them of a preferred solution. If the change is good, put a little gold star next to their name on the secret score sheet. Bring them into your team when you can.

Listen carefully to your users. They’ll ask for many things. What you should give them is almost never what they ask for. It’s your job to tastefully refashion their requests into beautiful elegant solutions.

To do this, you must know where to put things.

oh, i dont know. what do you think?



(c) 2003-2011 omino.com / contact poly@omino.com