Creating Healthy Craftsmen
Posted in Jerry Andrews on January 18th, 2011 by adminThere’s been a healthy debate over on the software craftsmanship mailing list about what craftsmanship means in the context of software development. I’ve been a proponent of, among other things, some set of standards by which a practitioner’s capabilities can be measured. That’s been unpopular, as you might imagine.
George Dinwiddie (http://blog.gdinwiddie.com) has written a bit about what he believes. I respect and agree with most of his conclusions, but I do disagree with his attitude on measuring developer’s capabilities. I don’t want to pick on George, but his are the most recent articles I’ve read on this topic (http://blog.gdinwiddie.com/2011/01/15/software-craftsmanship/, http://blog.gdinwiddie.com/2011/01/17/trades-crafts-and-certification/), and they make the same mistakes I see everywhere when discussing performance tests. To wit:
G—- is a certified tradesman. G—- did horrible work. G—–’s work was certified acceptable by a third party, also certified. Therefore certification doesn’t guarantee excellent (or “craftsmanlike”) work.
Now here’s the fallacies in that argument:
- Certification, by itself, only guarantees that, at least at one point in time, the practitioner knew how to do excellent work (or at least, work up to the level of the certification standard). It doesn’t mean (s)he will always do it.
- One (or two, or ten) examples where certification doesn’t do what it was intended to do doesn’t mean that certification never works, or even that it seldom works.
- The certification in question when this sort of argument is made seldom, if ever, covers the issues of craftsmanship under discussion.
- A certified individual, like most people, may not do the same level of work every day. Even master craftsmen have bad days or weeks or even years. In trade work, unfortunately, you can’t always throw it out and fix it; there are schedules to meet.
So that particular argument is terminally flawed, and shouldn’t be made at all. To amplify a bit:
I’d be the last to contend that certification is the only ingredient of a healthy craftsmanship movement or in developing excellent programmers. It’s simply one tool–a way to measure oneself both for one’s own edification, and to help others gauge your breadth of knowledge. I think it’s a particularly important tool–a pillar, if you will, along with other pillars, such as the community of like-minded individuals against whom you measure yourself, and who can comment on your work.
I think it’s important to review what certification means. It usually means “at some point in time, this individual knew and could put into practice certain tools, rules, and practices which, by this certification, we have verified”. Usually the certification standard is generated by people who really know the subject, and are qualified to determine what parts of the subject are key to being effective. Certification doesn’t say anything about a practitioner’s willingness to apply what (s)he knows–that’s a different problem, solved in different ways (e.g. establishing and maintaining a reputation, publishing your work for review, etc.).
What I’d really like to see is some kind of actual data comparing the performance of programmers (or plumbers, or electricians) who are certified in some way to those who aren’t certified, on tasks related to the certification. I see a lot of opinions (mine included) bandied about without actual, real, verifiable data. One of the things that makes DeMarco and Lister (Peopleware) compelling reading is that they provide actual data. Real studies with results that aren’t anecdotal. They’re not just voicing “opinions developed over years of practice”–they (and we) should be conducting actual experiments to verify our theories.
Craftsmanship, in my mind, requires:
- an attitude that it’s worthwhile to create excellent work, to provide the necessary stimulus,
- access to people who can teach and/or materials from which a practitioner can learn what that means and how to accomplish it, to provide the necessary knowledge and experience,
- some standard ways to measure a practitioner’s progress from neophyte to mastery, to provide a mechanism for the practitioner to understand where (s)he is and where (s)he needs to go, and
- some mechanism for displaying the practitioner’s achievements, so that mastery carries real benefits in the working world, thereby incenting mastery in others, and helping people outside the craft or trade to select workers appropriate for their tasks.
Documentation in the wild
Posted in Jerry Andrews on January 12th, 2011 by adminI just started a new project. I has no documentation except the code.
In this particular project, this is generally a Good Thing–the code in question is clear, well written, and (ahem) completely without comments. I’m not having a lot of trouble understanding it, overall, because it really is very good code. But… I’ve spent 2 days trying to figure out how one important feature works.
http://thedailywtf.com/Articles/Documentation-Done-Right.aspx
‘Nuff said.
Kanban in IT
Posted in Jerry Andrews on October 27th, 2010 by adminHere’s a nice series of articles on using Kanban in an IT setting. I found it interesting because it shows not only what they did, but how their process evolved over time in several settings. http://blogs.lessthandot.com/index.php/ITProfessionals/ITProcesses/applying-kanban-to-it-processes-part-1
Sucking Less: Checking In More Often
Posted in Jerry Andrews on March 20th, 2010 by adminI’m fairly fearless when coding, which means that about once a week, I delete a huge chunk of something I should’ve kept, or change something into something unrecognizable, thereby inadvertently breaking a dozen unit tests. When I discover the problem, usually about 4 hours later, I no longer have any idea what I did that made the bad thing happen. Then I spend another 2 or 3 hours figuring out what I broke and fixing it. Ugly.
On my personal projects, I check in code every time I get a unit test working. My checkins are something like 15-20 minutes apart. On projects I get paid for, though, checking in means running the whole unit test suite, and that can take 10 minutes (on a good project) or 2 hours (on a bad one)–so I don’t do it very often. That’s when I get into trouble. I’ve been meaning to solve that problem for some time, and Joel Spolsky’s blog topic last Wednesday (Joel on Software) finally kicked me in the pants. It took 15 minutes to solve the problem; here’s how I did it.
The new breed of source code control systems, distributed systems like Git and Mercurial, have had my attention for awhile, as I usually work with a team that’s spread out geographically. I regularly need to share code that’s not quite ready to be delivered with another developer, and that developer is very likely in a different city. Typically we’re reduced to emailing files to each other. A distributed SCC system would resolve this, as we could sync changes between our personal development repositories, but I assumed setting up and learning a new SCC would be painful, as it has been in the past, so I never got around to it. Joel’s article on Mercurial, however, got me to thinking about it seriously, and since I had a few hours on my hands, I figured I’d give it a try. What I didn’t expect was that I’d be up and functional in 10 minutes.
I downloaded Mercurial and installed it on my main dev box, which is a Windows laptop (I know, I suck). That took about 2 minutes, including googling for the Mercurial web site (http://mercurial.selenic.com/). Then I changed to the main development directory of my current project, and typed (per Joel’s tutorial at http://hginit.com/):
hg init
hg add
The first impressive thing is these commands ‘just worked’. The second is that’s all that’s required to set up a local repository and add an entire project to it. Really. I’m so psyched!
The “add” command was naive, because it added everything, including build output and subversion control directories (**/.svn/**). I spent the next 10 minutes reverting my add, (“hg revert –all“), then building an “ignore” file, then adding again, and finally committing. To shorten your search (Mercurial has great documentation, by the way–it only took me a few minutes to figure this stuff out), here’s what I ended up doing.
1. I created an .hhrc file in my home directory (C:\Documents and Settings\Jerry) with the following content:
[ui]
username=jerry
editor=C:/bin/vim/vim71/gvim
The “username” entry preempts a request by the “commit” command for a username, and I prefer vi to the default editor, which is notepad.
2. In the root of my project directory, I created a .hgignore file with the following content:
syntax: glob
.svn
*.class
*.log
.hgignore
antbuild/*
build.properties
This tells Mercurial to ignore all files or directories named .svn (which is where subversion stores its status), all .class and .log files, the .hgignore file itself, the build.properties file, and anything in the antbuild directory (or its subdirectories). Mercurial ignore files support at least 3 different syntaxes for specifying files; the documentation is available on the Mercurial wiki and it’s quite complete.
Finally:
hg add
hg commit
Now I’m in a position to check in locally every few minutes, but when I have a small feature complete, I can deliver via svn to the project’s repository. That’s right: I’m using Mercurial locally, and SVN for the project.
On my next project, I hope to have a chance to specify that the whole team uses Mercurial for the whole project; working with another programmer who’s not physically nearby just got a whole lot easier; we can exchange our updates directly with Mercurial, then push them up to a central repository independently. Sweet!
Annotating Custom Types in Hibernate
Posted in Jerry Andrews on March 3rd, 2010 by adminHibernate has a lot of nice features, and it’s pretty well documented, but a recent need to add a simple custom type to an existing mapping left me flailing around for documentation on exactly how to do it. I wanted to do it with annotations, not by updating the Hibernate configuration (that approach is well-documented). Here’s how it’s done.
Two new classes are needed. You can do it with one (and the Hibernate examples do it that way), but they really have different functions, so I coded them separately.
The first is the class you want to use for the column. In my case, I needed a Date with no milliseconds, which is a thin wrapper over java.util.Date. Here’s my class:
/**
* Oracle stores dates in DATE columns down to the second; Java stores them to the millisecond.
* This occasionally can confuse Hibernate as to what data are stale. This class slices off
* any milliseconds which might be present in its representation.
*/
public class DateNoMs extends java.util.Date {
private static final long serialVersionUID = 1L;
/** @see java.util.Date() */
public DateNoMs() {
super();
long t = getTime();
setTime(t – t%1000);
}
/** @see java.util.Date(long) */
public DateNoMs(long time) {
super(time – time%1000);
}
/**
* @param value
*/
public DateNoMs(Date value) {
long t = value.getTime();
setTime(t – t%1000);
}
/** @see java.util.Date#setTime(long) */
@Override
public void setTime(long time) {
super.setTime(time – time%1000);
}
}
java.util.DuctTape
Posted in Jerry Andrews on February 21st, 2010 by adminOverview
The proposed class, java.util.DuctTape, is designed as a general purpose fix for a variety of commonly-observed situations in production code. It serves as a temporary patch until a permanent solution is developed and deployed.
Features
DuctTape has the following features:
- Transform any internal data representation into any external representation without creating dependencies on either side.
- Transform any component interface into the interface required by any caller, again without creating dependencies.
- Perform branches into any point in existing code, and returns from any point in existing code, allowing for maximal reuse without recoding where logic already exists to perform some function.
- Intercept attempted calls to any unimplemented method and send an email request to support to perform the operation manually
Discussion
The utility of a properly-executed DuctTape implementation seems obvious. Once code is in production, users inevitably find edge and corner cases (and sometimes whole use cases) not anticipated by the requirements, design, implementation, or QA teams. The lead time required to implement these cases is often not available; a quick-and-dirty DuctTape-based solution is required to keep things running smoothly. A standard DuctTape implementation is far preferable to the hacks commonly used to hold things together until the next point release.
We urge immediate action on this by the developer community.
Database/Code impedance mismatch
Posted in Jerry Andrews on February 17th, 2010 by adminI love natural keys in database design. You have to pay attention, though: the natural impedance mismatch between a programming language representation and the database representation of the key can bite you.
Consider an object whose primary key might contain a date–say, a change log record. Oracle and DB2 both store a DATE as a time containing year, month, day, hours, minutes, and seconds. No timezone. The natural mapping for a Java tool like Hibernate is to map to a java.util.Date, which stores the Date as a time in milliseconds since the epoch GMT, and then maps it to whatever timezone is set on the machine where the code is running for display and conversion.
Now consider what might happen (especially if our change log record is attached to some parent object);
- We create and save the object; it is persisted. The local cached copy contains a non-zero value for milliseconds, but the database has truncated the milliseconds value and saved it.
- Later on in the code somewhere, we have reason to save the object again, perhaps as part of some collection operation.
- Hibernate looks in its cache, compares it with the database, and notes that the values of the Date don’t match–so it tries to save the value again.
- The database dutifully tosses out the spare milliseconds, and bam! we have an attempt to re-insert an existing record, so it throws an exception.
This is all terribly confusing to the programmer, who, inspecting the objects in question, sees no difference between what’s in the database and what’s in her code, especially since the default display characteristics of her database browser and her debugger don’t show the milliseconds.
The easy fix in this case is to declare a class which matches the database representation–in this case, a good choice would be to declare a new class which truncates the milliseconds. A modest example is shown below:
/*** Public Domain; use or extend at will.*/import java.util.Date;
public class DbDate extends Date {/** increment if you change the state model */private static final long serialVersionUID = 1L;
/** @see java.util.Date#Date() */public DbDate() { long t = getTime(); setTime(t - t%1000);}
/** @see java.util.Date#Date(long) */public DbDate(long t) { super(t - t%1000);}
/** @see java.util.Date#setTime(long) */@Overridepublic void setTime(long time) { super.setTime(time - time%1000);}}
Also note that if you declared the database column as a TIMESTAMP, the Java and database representations more-or-less match–avoiding, in this case, this kind of problem. Note that Oracle doesn’t support TIMESTAMP_WITH_TIMEZONE in a primary key, and DB2 doesn’t implement TIMESTAMP_WITH_TIMEZONE at all–as of the last time I had access to DB2.
Dealing with timezones is another topic entirely–one which I’ll take up in a future post.
Limiting Irreversibility
Posted in Jerry Andrews on February 14th, 2010 by adminThis afternoon I was reading Martin Fowler’s commentary on architecture: http://www.martinfowler.com/ieeeSoftware/whoNeedsArchitect.pdf, and ran across the following: