Friday, December 29, 2006

Intelligent Exception Handling

Once a month or so I am required (Actually I enjoy doing it) to deliver a presentation to the other Java developers in our group relating to a Java Best Practice. Something that we (the enterprise team) have defined as a "Best" way of doing something. My partner in crime on this and I swap off depending on who's got an idea for one for this month. This is an example of a good idea that got wildly out of hand when management got hold of it because now we're required to do it once each month rather than when something came up which was the original idea. Anyway this best practice relates to the handling of exceptions in Java applications. Much of the information is drawn from the Sun tutorial on exception handling.

OL

The Problem at Hand

Regardless of the capability of the developer or the attention given to coding, exceptions are going to occur. By design in the Java programming language exceptions exist to allow for the resolution of problems or cases where the normal flow of the application does not support the condition at hand. The problem occurs when the handling of the exceptional condition is haphazard or undisciplined. If an exceptional condition is considered as an operational stream, an exception stream that doesn’t lead to a resolution of the condition represents a black hole into which operation will be diverted but never return resulting in lost data or potentially catastrophic failure.


The most obvious example occurs when control is diverted in to a catch block that doesn’t do anything. In this example, the try/catch block would catch problems that might occur in the attempt to read data from the database but all it does is print the resulting message out to the system console or log. No action is taken in relation to the problems that might occur and no effort is made to report the problem to the application or to the user. So what should be done? That depends on the nature of the problem, the applications ability to deal with it, and how catastrophic the problem is to the system. In the above example, it may be that the user provided a parameter for the query that wouldn’t return any rows of data from the database. This isn’t a critical problem but the user should certainly be notified of the nature of the problem. That is the focus of this paper, what should we as Java developers do when the inevitable, and in some cases hoped for, exception occurs?


What is an exception?

An exception is an event that occurs during the execution of a program that disrupts the normal flow of instructions. Java Tutorials: Exceptions


In general terms an exception is thought of as an error but in point of fact it is a condition that the code cannot deal with by following the normal flow of events. It may well represent an error or it may not. An object that is created and returned from a method call may very well be null if the key value that was passed into the method doesn’t resolve to a persisted object. Such an occurrence does not necessarily constitute an error rather a condition outside normal circumstances. This is a perfectly viable condition that can (and should) occur and be dealt with by the application.


Why do we care about exceptions?

The obvious answer is that we care about exceptions because they represent problems in the application.


The real questions at hand are;

  • What caused the exception?

  • Will the calling process be hampered by the exception?

  • Does the user need to be made aware of the exception?

  • Can we realistically do anything to correct the problem?


Catch or Specify Requirement

Valid Java code must honor the “Catch or Specify Requirement” meaning that code that might throw certain exceptions must be enclosed by a try/catch block or must specify in the method declaration that it throws the exception(s) potentially generated within the method body. A try must include a handler (catch) for the exception(s) within the try. *NOTE* Code that does not honor the Catch or Specify Requirement will not compile.


Not all exceptions are subject to the Catch or Specify Requirement. Of the three categories of exceptions only one is subject to the requirement.


Checked exceptions are conditions that a well written java application should anticipate and recover from. As an example, a method to create an object from a row of information persisted in a database would very likely take as a parameter a value that the method would use to identify the particular row of information desired. Under normal operations the identifier provided by the user would resolve to a valid row of information but what if the user provides an invalid or non-existent key value? Checked exceptions are subject to the Catch or Specify requirement.


An error is an exceptional condition that is external to the application. The application generally cannot anticipate or recover from this kind of exception. Errors generally originate from the Java Virtual Machine (JVM). When an exception of this type occurs, the ability of the application to resolve the issue is limited generally involving failing (at least) the process in which the error occurred but potentially setting the entire application into a failure state. Errors are not subject to the catch or specify requirement.


The third type of exception is a runtime exception. Runtime exceptions generally result from programming flaws that the application cannot anticipate or recover from and often indicate logic errors or a misused API. It is possible to catch and handle the runtime exception though it generally makes more sense to correct the flaw at its source. Runtime exceptions are not subject to the catch or specify requirement.


Collectively errors and runtime exceptions are referred to as “unchecked exceptions.”


Basic exception handling

Before an exception can be caught, it must be thrown either by the application code, a compiled package or from the Java Runtime Environment. Regardless of where the exception originated it was thrown with a throw statement such as the following example


public Object pop() {

Object obj;


if (size == 0) {

throw new EmptyStackException();

}


obj = objectAt(size - 1);

setObjectAt(size - 1, null);

size--;

return obj;

}


try

{

pop() ;

}

catch(EmptyStackException e)

{

// Here is where we would handle the exception and do something useful

}


Any code that attempted to call pop() would have to catch or specify the EmptyStackException exception or it would not compile.


Now that we’ve thrown an exception, what do we do with it? As we know we have to catch or specify the exception, but which is the appropriate behavior? The answer is that it depends on several variables. At the very least we should almost always log the error and notify the user. There are circumstances where the exception can be resolved within the application logic which might indicate that the user doesn’t need to be notified.


Determination and implementation of the appropriate action in the event of an exception is the responsibility of the developer.


Avoid the urge to use a more general exception. Exception handling is a case where more specific information equates to greater clarity for debugging and for the user of that application. Using more general exceptions such as catching Exception when the real cause is an IOException is very likely to have unintended consequences. Granted that the exception handling code would be simpler with a more general exception but critical debugging information is lost and exceptions are more likely to be mishandled.


The incredible disappearing exception

Exceptions must be dealt with. A catch block that does nothing or prints the stack trace out to the console or log doesn’t resolve the problem.


try

{

// Do something here that would throw an exception

}

catch(SomeException e)

{

e.printStacktrace() ;

}


In the above example, if an exception is thrown in the try block, control is passed into the catch but nothing happens to notify the user that there is a problem and the method exits normally with no work being done. This is referred to as “swallowing the exception.” A better solution might be to log the exception and re-throw the exception to a higher level to be reported to the user.


Exceptions should be logged in ARF using the provided mil.arf.webcore.util.Logger utility API. The convention for printing the Exception to the standard out writer using System.out.println() or Exception.printStacktrace() is not acceptable. Exceptions should be logged using;


public static void log(Exception e){…}


An implementation using the log method might look like this.


public void build()

{

try

{

initConnection() ;

prepareStatement("We need a sql statement here");

preparedStatement.setInt(0, widgetID);

resultSet = preparedStatement.executeQuery() ;


if(resultSet != null)

{

metaData = resultSet.getMetaData() ;

while(resultSet.next())

{

for(int i=0; i

{

put(metaData.getColumnName(i), resultSet.getObject(i)) ;

}

}

}

}

catch(SQLException sqle)

{

Logger.log(Exception e) ;

}

finally

{

closeResources() ;

}

}


Documenting exceptions

JavaDoc provides a tag for the basic documenting of which exceptions are thrown by a method. The @throws tag provides this basic functionality but does not provide a sufficient level of information without an explanation of the exception and the conditions under which it is thrown.


See the following example for the recommended JavaDoc implementation of @throws.


/**

* Returns the next available sequence.

* Note: Connection IS CLOSED upon completion of this method.

* @param connection

* @return the next available sequence as a String

* @throws SQLException if the sql call fails for any reason

*/


ARF specific exceptions

ARF provides exceptions for the processing of actions, commands and content. These exceptions should be used in preference to more generalized exceptions as the arf engine is designed to respond in a predictable manner to these exceptions by providing available handlers relieving the developer of the need to create handlers of their own. Commands should throw CommandException in the event of most checked and unchecked exceptions. ContentGenerators should throw ContentException and SQLProcessors should throw SQLExceptions. The developer can throw the appropriate ARF exception with specific information as the message and the caught exception as the root cause, which brings us to the topic of chained exceptions.


An application often responds to an exception by throwing another exception. In effect, the first exception causes the second exception” (“the Java Tutorials: Essential Classes: Exceptions (Chained Exceptions)”)


This is referred to as chaining exceptions. Chaining exceptions provides a greater degree of clarity for debugging purposes and the full stack trace should be logged at some point. However, the full depth of the exception chain need not be reported to the user in most cases.


What it all means

When handling exceptions it is important to ask if the caller will be able to handle the exception and if they need to be notified when an exceptional case occurs.

Work with as specific an exception as possible. Avoid the urge to generalize.

Do NOT use empty catch blocks

Do not simply print the stack trace (exception.printStackTrace())

Use the provided logging API (mil.arf.webcore.util.Logger)

Use (and understand) the provided specific ARF exceptions

Make intelligent exception choices that relate to the code at hand

Document (JavaDoc) the conditions and circumstances of the exceptions that will be thrown


Remember that it is the responsibility of the developer to provide intelligent and capable exception handling to developers calling the code that we write and to provide useful and informative exception reporting to the users of the applications that we build.


Appendix A: Examples of Bad Exception Handling


public boolean lookForTokenInCollection(Collection c, String token)

{

if(token == null)

{

return false ;

}


Object elem;

String tmpString ;

Iterator i = c.iterator() ;


while(i.hasNext())

{

elem = i.next() ;


if(elem instanceof ICriteria)

{

tmpString = ((ICriteria) elem).getSQLValue() ;


if(token.equalsIgnoreCase(tmpString))

{

return true ;

}

else if(elem instanceof String)

{

System.out.println(

"Working with a String in SQLutil::lookForTokenInCollection()") ;


if(token.equalsIgnoreCase(((String) elem)))

{

return true ;

}

else

{

/*

Got an object type I didn't want, so

returning false and throwing Exception

with stack trace and message

*/

System.out.println("Odd object of class <"

+ elem.getClass().getName()

+ "> in SQLUtil::lookForTokenInCollection() ----- With value of <"

+ elem + ">") ;


try

{

throw new Exception() ;

}

catch(Exception e)

{

e.printStackTrace() ;

}


return false ;

}

}


return false ;

}

}

}



public Map execute( Map input, Map context ) throws mil.arf.webcore.engine.CommandException

{

/**

*

*/


Map output = new HashMap();

mil.arf.reportcore.user.WebLIDBUser user = (mil.arf.reportcore.user.WebLIDBUser) input.get( "user" );

String niin = (String) input.get("niin");

ReportParamContainer rpc = (ReportParamContainer) input.get("keyValueContainer");


//String reportDescriptorNameTwo = rpc.getReportDescriptorName();

String reportDescriptorRoot = (String)rpc.getReportDescriptor().getProperties().valueForKey("reportNameChoserValue");


if( null != niin && !niin.trim().equalsIgnoreCase("") )

{

Connection con = null;


String theSQLString = "SELECT ITEM_CONTROL.SOS FROM ITEM_CONTROL WHERE ITEM_CONTROL.NIIN = '";

theSQLString += niin +"'";


try

{

con = ConnectionUtil.getUserConnection(user);

Statement theStatement = con.createStatement();

ResultSet theRS = theStatement.executeQuery(theSQLString);


while (theRS.next())

{

String theSOS = theRS.getString(1);

if("B14".equalsIgnoreCase( theSOS ) || "A12".equalsIgnoreCase( theSOS ) || "B17".equalsIgnoreCase( theSOS ) || "B16".equalsIgnoreCase( theSOS ) || "B46".equalsIgnoreCase( theSOS ) || "B56".equalsIgnoreCase( theSOS ) || "B64".equalsIgnoreCase( theSOS ) || "AKZ".equalsIgnoreCase( theSOS ))

{


reportDescriptorRoot += "CCSS";

}

// DLA gateway

else if( theSOS.indexOf("S9") >= 0 )

{

reportDescriptorRoot += "DLA";

}

else

{

// todo This is wrong, there will be cases where a NIIN SOS isnt part of the mess and some other action will need to be taken

// todo ---someone fix it

System.out.println(

"Something Broke in GatewayLookup. This is a unexpected SOS we need to deal with... <" + theSOS + "> ");

// not a gateway type niin but make it do ccss to to ensure a report descriptor

// and although report errors the resultant message matches client

reportDescriptorRoot += "CCSS";

}

}

theRS.close();

theStatement.close();

}

catch(Exception w)

{

}

finally

{

try { con.close();}catch (Exception e){}


}

}

output.put( "reportDescriptorName", reportDescriptorRoot);


return output;

}


Gentlemen Restart Your Engines

It's been almost a year, if not longer, since I posted anything here. Mea culpa I've allowed myself to get busy and get involved in other things. That's no excuse and if you've been waiting for me to post something else then I do sincerely appologize. It is my intention to restart this BLOG and get it back onto the path for which I created it which was and remains the discussion of topics relating to software development. To that end I've created another BLOG where I can write down my thoughts and comments and observations about the state of the universe at large.

What we're going to talk about here though is software development. Things of interest to me and things that I have some level of knowledge about. Which means there's going to be a great deal about Java, C++, WebDevelopment and Enterprise Development.

So with that said, stay tuned and very shortly there will be more.

Regards,

OL

Tuesday, January 03, 2006

The More Things Change

Changes

Changes … If there is only one constant in the universe it would be change. Work changes, relationships change, people change. What follows is a rambling waffle that may or may not have anything to do with anything or may have something to do with nothing or may have nothing to do with something.

On Thanksgiving day 1987 I saw one of the most beautiful things I’ve ever seen in my life. I looked into the eyes of my brand new baby daughter. As I write this, Hannah is now a young woman just turned 18 and with her life just waiting before her. A national honor scholar who is listed in Who’s Who Among American High School Students she scares the life out of the boys at her high school (and she’s maybe 5’2” tall on a tall day) which might in my opinion be a greater achievement. To say that I am immensely proud of my daughter would be one of the great understatements of all time. The downside to it is that a year from now she’ll be gone, off at college starting her own life and no longer where I can keep a surreptitious eye on her and keep her safe. That part is hard for a dad, and my wife gets teary every time the subject comes up.

Changes …

On Christmas eve my oldest son turned 17. During the week between Christmas and New Years he traveled to the nearest Military Entrance Processing Center for his physical, and to complete his paperwork. He intends to go to basic training for the Army this summer, come back and finish his senior year of high school, then go to a military college in Alabama. His chosen job in the Army is combat operations. He wants to lead a combat unit. I love my son dearly and have the greatest of respect for anyone who is willing to put their life at risk to protect the rest of us back here at home. The men that he will one day lead are very fortunate in having the commander that I know he will become. As a dad I am immensely proud of his willingness to put himself at risk. As a dad I really hope that I don’t have to pick my son up in a bag and bury him.

Changes …

On the Monday before Thanksgiving my boss called me to tell me that they were closing our office in the town where I live and that they didn’t have another slot for me. Great timing there buddy. On Wednesday of that same week I got a call with an offer for a new contract. So here I am several hundred miles away from home working on a project that if we can get it out of its pre-birth dangers will provide a lot of good for our customer, and in the process I’m making a lot of money. The downside, I’m several hundred miles from home and living out of a hotel for part of each week. The upside, I’m several hundred miles from home and living out of a hotel for part of each week so with every minute I’m away I’m reminded of how special and wonderful my beautiful wife and magnificent children are, how truly blessed I am, how incredibly fortunate I am in my life.

Changes …

The more things change the more we grow the more we grow the better we become as people. Sometimes we need changes to remind us of how lucky we are. My hope for each of you with the coming year is that the changes that will come to your life this year will help you grow.

Happy New Year …

Thursday, September 29, 2005

It's back to school time

After 16 plus years of professional software development, it occurred to me that there would come a day when I would not want to write code every day anymore. It might be that I’m content to be plunking away writing code when I’m 90, but probably not. So I said to myself, “Self, what do you want to do if you don’t want to write code?” Self thought about it for a while and decided that what he’d really like to do when he no longer wants to write code is to teach. Which sounded like a fabulous idea to Self. After 16 years of software development he’d like to think he’s learned a thing or two, and maybe that knowledge might be of benefit to somebody who is just starting out. Okay, I realize that I’m probably fooling myself, but hey we all have our illusions.

Well, when I went to college 20 years ago, it was to study art. I wanted to be an illustrator. Don’t ask how I backslid into a career in software development, it’s a rather long and sordid story. With this in mind I decided that what I would do this fall is go back to school, and work my way through until I reach the final goal of a PhD in computer science. With my work schedule and the other things that I have going on with my wife and children I decided that I could probably handle 6-9 hours per term. So I went to my handy dandy community college and enrolled. So here I am taking, Public Speaking (it’s required for all majors), Macroeconomics (It’s required for everyone in the business department, part of which includes the Computer Science department), and Microcomputer Applications (Read as how to use Microsoft Office), and actually having a fabulous time with it. Macroeconomics isn’t my cup of tea, but hey we all have to make sacrifices.

Public Speaking is actually the most fun of the three in part because as anyone who knows me will tell you I enjoy being in front of people and hearing the sound of my own voice, but mainly it’s because I get to take it with my wife. I haven’t gotten to do that in almost 20 years so it’s a real treat to get to spend that time with her.

I will continue to post here as things progress and who knows, maybe this rambling waffle will be of use to someone.

Tuesday, August 02, 2005

Randomness and Other Oddities

Ok, when I started this BLOG I had actually intended to keep my musings to the subject of software development. However, being me I felt the urge to wax elloquent on subjects that caught my eye. As such I've tended to ramble a bit and have probably strayed from the path that I set when I started this project. Ah well who knows where the path will end when they first set their foot upon it. Not sure who said that, but it sounds rather quote like. Possibly something Bilbo Baggins would have said to Frodo at some point though I'm pretty sure that there's nothing actually like that (Notwithstanding comments about paths being like rivers and such) in the works of Mr. Tolkien.

I won't appologize for my rambling waffle. You dear reader do of course have the option of not reading my foolishness, or of reading it and labeling me a fool.

My father worked as an Aeronautical Engineer for his entire life. His specialty was lateral stability. In short, (and I kid you not) my dad could make a brick fly. He had an incredible vision, and an artists feel for air frame design. His hobby was building and flying model airplanes. Not the motorized radio control versions that you can see flying around just about any American park on any given sunny weekend, rather my dad's special love when it came to his models was a special breed of aircraft.

Imagine if you will an airplane measuring 30cm from wingtip to wingtip and perhaps a bit more from nose to tail and weighing 1 (yes I said ONE) gram.

Some of my earliest memories are of being in giant buildings originally built to house blimps and derigibles. This giant building nearly silent and in the air overhead perhaps as many as twenty graceful airplanes making their slow way through the air as mostly middle aged men with intent expressions watched for any sign of distress on the part of their airplanes.

I've long had a deep love and fascination with airplanes. Specifically the airplanes of the golden age of aviation through WWII. Jets are wonderful and their power is awe inspiring, but there's not much artistry in a rocket with wings. However, a box kite with a motor now that's something special.

I don't build model airplanes anymore ... Might get back to it one day ... But I do still build models. I build models of things, of processes, of operations. Often when I'm writing software I think back on those gracefull swans gliding through the air, the dusty sunlight shimmering rainbow colored on their wings and tail surfaces and I'm reminded that the effort spent to make something elegant and graceful is worth every minute.

Tuesday, July 26, 2005

Discovery

As recently as 100 - 150 years ago we as a nation (the USA) were a people made up of explorers and seekers and adventurers. Theodore Roosevelt compared the nation to a young grizzly bear, "Big and strong and brave even if we are prone to stumbling around from time to time."

At our core I think the biggest failing we as a culture exhibit is this, we've lost our identity as a nation of explorers to become a nation of consumers. As a group we seem much more concerned with having the bigger house, or the nicer car or the cooler more hip pair of sneakers.

As I watch the shuttle Discovery on her current flight, I am at once hopeful and at the same time fearful that the promise of what Discovery means will not be realized because we as a people have lost our identity as explorers and discoverers.

Here then is my hope for a safe flight, and a rebirth of the United States as a people who explore, and stretch and discover. God speed Discovery, and come home safe and soon.

Friday, July 22, 2005

My Thoughts on Harry Potter and the Half Blood Prince

Let me start by saying that my introduction to the Harry Potter franchise was listening to Jim Dale read the first book on tape as I was traveling with my wife and children. I will forever hear Mr. Dale’s portrayal of the characters when I read the books.

We purchased the most recent installment on Saturday 16 July 2005. My younger daughter Sarah had dibs by virtue of some lottery or some such to which I was not privy. I did however get the second crack at the book, and so Sunday morning dove into the book admittedly with some trepidation. You see, I never really cared for the fifth book. By now all of you know that Harry’s godfather dies in the course of the book, a vehicle which I anticipated but never really liked. In the main though my complaint with book five had to do with Harry himself. I’ve never liked whining much and he was carrying on throughout the book. Ok, he’s feeling put upon and overwhelmed, I understand that, but it doesn’t make being a whiner any more appealing to me.

Within the first couple of chapters I discovered that I liked Harry much more in this book than I did in the previous one. He’s not comfortable with being the hero, or the “Chosen One” as he’s been dubbed by the “Daily Prophet”, but he’s no longer whining about it. He still has some growing to do some of which is forced on him in the course of this book, but he seems to me to have turned a personal corner.

I am trying studiously to avoid spoilers for those of you who have not read the book yet, but I do have a few things to say.

Severus Snape – That figures.

Teenage Romance – Enough already (more on this topic in a minute) I do realize that it’s the central factor in the lives of most teenagers, and for most teenagers life is a constant soap opera, but enough already.

Buckbe … err Whitherwings – Yay …

Draco – heh heh heh.

The ending of the book – Ok, when I finished the book I literally flung it.

All in all, Rowling’s best work to date as far as I’m concerned, and she has managed to accomplish something with this book that she did not with book five. She made me want to read the last one.

Just my thoughts on the subject, but as always your mileage may vary.

Wednesday, July 20, 2005

Good Code := Poetry

Steal this poem v1.2

This poem is copyleft,
You are free to distribute it, and diffuse it,
Dismantle it and abuse it,
Reproduce it, and improve it
And use it
For your own ends
And with your own ending.

This is an open source poem
Entering the public domain
Here’s the source code,
The rest remains
For you to shape, stretch and bend
Add some salt and pepper if you want
Share it you amongst your friends.

Because I didn’t write this poem, I molded it.
Picked up the lines out of a skip and refolded it
As I was walking on over here,
Rescued leftover ideas,
On their way to landfill,
Found screwed up fragments
And found them a use.

Because, think about it
I can’t tell you anything truly new.
There can only be few more new ideas to be thought through.
So should we treat them as rare commodities, high value oddities?
Probe the arctic reserves and other sensitive ecologies
For new ideas buried deep beneath the permafrost?
No! we should re use and recycle them
Pile our public places high with ideas beyond anyone’s imagining..

So I steal a riff here and a rhyme there,
A verse here and a line there
Pass them on around the circle,
Roll the words, add a joke
Here go on.. have a tok,
Does it get you high?
This poem is indebted to Abbie Hoffman, Gil Scott Heron, Jim Thomas and Sarah Jones,
This poem is indebted to all the words I’ve read and the voices I’ve known
This poem is a composit of intellect, yours and mine.
This poem RIPPED OFF every single time

Because intellectual property is theft
And piracy our only defense left against the thought police.
When no thought is new
It’s just rewired, refined, remastered and reproduced
The revolution will be plagiaraised
The revolution will not happen if our ideas are corporatised.
So STEAL THIS POEM
Take it and use it
For your own ends
And with your own ending

This poem is copyleft,
All rights reversed

Clair Fauset
Email: angelclare@riseup.net
Homepage: www.hammerandtongue.org

This poem was the lead in to an article in issue 50 of Linux User & Developer which can be found in bookstores as of the writing of this blog entry. The article goes on to discuss a topic very near and dear to my heart.

In the dim and distant past dear reader when yours truly piled the majority of his worldly possessions into the folded down back of his 1977 Dodge Colt and departed fresh faced and idealistic for college it was not to earn a degree in software engineering. My chosen course of study was art (much to my father’s chagrin I might add). Visions of Renaissance and Dutch masters spun through my head as the miles rolled by. Had anyone told me then that nearly 20 years later I would make my living writing software I would have scoffed and thought them mad. Who was truly mad I wonder?

Colleges and universities around the world teach software development the same way, as an engineering discipline. This is in large part a result of the fact that for several decades computers and the building of programs to run them has been the domain of lab coated scientists from the halls of academia.

I find it an interesting corollary though that most of the “good” software developers I know are also gifted in some form of art. I draw and paint and occasionally much to the suffering of my wife and children dabble in music. A number of developers of my acquaintance are musicians and still others writers and poets. My best friend and partner in crime (who’s blog can be found at http://bmccord.blogspot.com/ ) posits that good code is eloquent, even poetic. In his book on refactoring (Refactoring; Fowler, Martin; Addison-Wesley Publishing (ISBN:0-201-48567-2)) the author talks about code smelling good or bad.

Engineering as a practice focuses on the known, the quantifiable, the procedural and doctrinal. Art as a practice focuses on the unknown, the intangible, the unseen and improbable. Which is better? I suppose that depends on what you want to accomplish. Both approaches to the problem certainly have their benefits and their limitations.

As we move forward to educate software developers though we have an obligation to teach them not just the known and the predictable, but also how to seek how to explore how to find what lies beyond sight.

In art school everyone is required to take numerous drawing courses during which will inevitably be many exercises in drawing “negative space.” When one draws negative space one focuses on a single object (often a chair) and then erases the object from their vision and draws what lies around it and within the nooks and crannies. By doing so the artist defines the object not by what it is, but by what is around it. This type of exercise goes beyond teaching the student to draw what they see, into the realm of drawing not even what they don’t, but what could be.

Software developers often deal in the intangible and the unknown, the at best sketchily defined requirements that users and stakeholders provide. If we teach software developers only that which is known and not how to seek then we only half way arm them to accomplish the one goal of every development effort, placing usable software in front of the users.

But that’s just my opinion, your mileage may of course vary.